--- /dev/null
+'use strict';
+
+exports.__esModule = true;
+
+var _component = require('./component.js');
+
+var _component2 = _interopRequireDefault(_component);
+
+var _document = require('global/document');
+
+var _document2 = _interopRequireDefault(_document);
+
+var _window = require('global/window');
+
+var _window2 = _interopRequireDefault(_window);
+
+var _events = require('./utils/events.js');
+
+var Events = _interopRequireWildcard(_events);
+
+var _dom = require('./utils/dom.js');
+
+var Dom = _interopRequireWildcard(_dom);
+
+var _fn = require('./utils/fn.js');
+
+var Fn = _interopRequireWildcard(_fn);
+
+var _guid = require('./utils/guid.js');
+
+var Guid = _interopRequireWildcard(_guid);
+
+var _browser = require('./utils/browser.js');
+
+var browser = _interopRequireWildcard(_browser);
+
+var _log = require('./utils/log.js');
+
+var _log2 = _interopRequireDefault(_log);
+
+var _toTitleCase = require('./utils/to-title-case.js');
+
+var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
+
+var _timeRanges = require('./utils/time-ranges.js');
+
+var _buffer = require('./utils/buffer.js');
+
+var _stylesheet = require('./utils/stylesheet.js');
+
+var stylesheet = _interopRequireWildcard(_stylesheet);
+
+var _fullscreenApi = require('./fullscreen-api.js');
+
+var _fullscreenApi2 = _interopRequireDefault(_fullscreenApi);
+
+var _mediaError = require('./media-error.js');
+
+var _mediaError2 = _interopRequireDefault(_mediaError);
+
+var _tuple = require('safe-json-parse/tuple');
+
+var _tuple2 = _interopRequireDefault(_tuple);
+
+var _obj = require('./utils/obj');
+
+var _mergeOptions = require('./utils/merge-options.js');
+
+var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
+
+var _textTrackListConverter = require('./tracks/text-track-list-converter.js');
+
+var _textTrackListConverter2 = _interopRequireDefault(_textTrackListConverter);
+
+var _modalDialog = require('./modal-dialog');
+
+var _modalDialog2 = _interopRequireDefault(_modalDialog);
+
+var _tech = require('./tech/tech.js');
+
+var _tech2 = _interopRequireDefault(_tech);
+
+var _audioTrackList = require('./tracks/audio-track-list.js');
+
+var _audioTrackList2 = _interopRequireDefault(_audioTrackList);
+
+var _videoTrackList = require('./tracks/video-track-list.js');
+
+var _videoTrackList2 = _interopRequireDefault(_videoTrackList);
+
+require('./tech/loader.js');
+
+require('./tech/flash.js');
+
+require('./poster-image.js');
+
+require('./tracks/text-track-display.js');
+
+require('./loading-spinner.js');
+
+require('./big-play-button.js');
+
+require('./close-button.js');
+
+require('./control-bar/control-bar.js');
+
+require('./error-display.js');
+
+require('./tracks/text-track-settings.js');
+
+require('./tech/html5.js');
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
+
+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; } /**
+ * @file player.js
+ */
+// Subclasses Component
+
+
+// The following imports are used only to ensure that the corresponding modules
+// are always included in the video.js package. Importing the modules will
+// execute them and they will register themselves with video.js.
+
+
+// Import Html5 tech, at least for disposing the original video tag.
+
+
+// The following tech events are simply re-triggered
+// on the player when they happen
+var TECH_EVENTS_RETRIGGER = [
+/**
+ * Fired while the user agent is downloading media data.
+ *
+ * @event Player#progress
+ * @type {EventTarget~Event}
+ */
+/**
+ * Retrigger the `progress` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechProgress_
+ * @fires Player#progress
+ * @listens Tech#progress
+ */
+'progress',
+
+/**
+ * Fires when the loading of an audio/video is aborted.
+ *
+ * @event Player#abort
+ * @type {EventTarget~Event}
+ */
+/**
+ * Retrigger the `abort` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechAbort_
+ * @fires Player#abort
+ * @listens Tech#abort
+ */
+'abort',
+
+/**
+ * Fires when the browser is intentionally not getting media data.
+ *
+ * @event Player#suspend
+ * @type {EventTarget~Event}
+ */
+/**
+ * Retrigger the `suspend` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechSuspend_
+ * @fires Player#suspend
+ * @listens Tech#suspend
+ */
+'suspend',
+
+/**
+ * Fires when the current playlist is empty.
+ *
+ * @event Player#emptied
+ * @type {EventTarget~Event}
+ */
+/**
+ * Retrigger the `emptied` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechEmptied_
+ * @fires Player#emptied
+ * @listens Tech#emptied
+ */
+'emptied',
+/**
+ * Fires when the browser is trying to get media data, but data is not available.
+ *
+ * @event Player#stalled
+ * @type {EventTarget~Event}
+ */
+/**
+ * Retrigger the `stalled` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechStalled_
+ * @fires Player#stalled
+ * @listens Tech#stalled
+ */
+'stalled',
+
+/**
+ * Fires when the browser has loaded meta data for the audio/video.
+ *
+ * @event Player#loadedmetadata
+ * @type {EventTarget~Event}
+ */
+/**
+ * Retrigger the `stalled` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechLoadedmetadata_
+ * @fires Player#loadedmetadata
+ * @listens Tech#loadedmetadata
+ */
+'loadedmetadata',
+
+/**
+ * Fires when the browser has loaded the current frame of the audio/video.
+ *
+ * @event player#loadeddata
+ * @type {event}
+ */
+/**
+ * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechLoaddeddata_
+ * @fires Player#loadeddata
+ * @listens Tech#loadeddata
+ */
+'loadeddata',
+
+/**
+ * Fires when the current playback position has changed.
+ *
+ * @event player#timeupdate
+ * @type {event}
+ */
+/**
+ * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechTimeUpdate_
+ * @fires Player#timeupdate
+ * @listens Tech#timeupdate
+ */
+'timeupdate',
+
+/**
+ * Fires when the playing speed of the audio/video is changed
+ *
+ * @event player#ratechange
+ * @type {event}
+ */
+/**
+ * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechRatechange_
+ * @fires Player#ratechange
+ * @listens Tech#ratechange
+ */
+'ratechange',
+
+/**
+ * Fires when the volume has been changed
+ *
+ * @event player#volumechange
+ * @type {event}
+ */
+/**
+ * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechVolumechange_
+ * @fires Player#volumechange
+ * @listens Tech#volumechange
+ */
+'volumechange',
+
+/**
+ * Fires when the text track has been changed
+ *
+ * @event player#texttrackchange
+ * @type {event}
+ */
+/**
+ * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
+ *
+ * @private
+ * @method Player#handleTechTexttrackchange_
+ * @fires Player#texttrackchange
+ * @listens Tech#texttrackchange
+ */
+'texttrackchange'];
+
+/**
+ * An instance of the `Player` class is created when any of the Video.js setup methods
+ * are used to initialize a video.
+ *
+ * After an instance has been created it can be accessed globally in two ways:
+ * 1. By calling `videojs('example_video_1');`
+ * 2. By using it directly via `videojs.players.example_video_1;`
+ *
+ * @extends Component
+ */
+
+var Player = function (_Component) {
+ _inherits(Player, _Component);
+
+ /**
+ * Create an instance of this class.
+ *
+ * @param {Element} tag
+ * The original video DOM element used for configuring options.
+ *
+ * @param {Object} [options]
+ * Object of option names and values.
+ *
+ * @param {Component~ReadyCallback} [ready]
+ * Ready callback function.
+ */
+ function Player(tag, options, ready) {
+ _classCallCheck(this, Player);
+
+ // Make sure tag ID exists
+ tag.id = tag.id || 'vjs_video_' + Guid.newGUID();
+
+ // Set Options
+ // The options argument overrides options set in the video tag
+ // which overrides globally set options.
+ // This latter part coincides with the load order
+ // (tag must exist before Player)
+ options = (0, _obj.assign)(Player.getTagSettings(tag), options);
+
+ // Delay the initialization of children because we need to set up
+ // player properties first, and can't use `this` before `super()`
+ options.initChildren = false;
+
+ // Same with creating the element
+ options.createEl = false;
+
+ // we don't want the player to report touch activity on itself
+ // see enableTouchActivity in Component
+ options.reportTouchActivity = false;
+
+ // If language is not set, get the closest lang attribute
+ if (!options.language) {
+ if (typeof tag.closest === 'function') {
+ var closest = tag.closest('[lang]');
+
+ if (closest) {
+ options.language = closest.getAttribute('lang');
+ }
+ } else {
+ var element = tag;
+
+ while (element && element.nodeType === 1) {
+ if (Dom.getElAttributes(element).hasOwnProperty('lang')) {
+ options.language = element.getAttribute('lang');
+ break;
+ }
+ element = element.parentNode;
+ }
+ }
+ }
+
+ // Run base component initializing with new options
+
+ // if the global option object was accidentally blown away by
+ // someone, bail early with an informative error
+ var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready));
+
+ if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
+ throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
+ }
+
+ // Store the original tag used to set options
+ _this.tag = tag;
+
+ // Store the tag attributes used to restore html5 element
+ _this.tagAttributes = tag && Dom.getElAttributes(tag);
+
+ // Update current language
+ _this.language(_this.options_.language);
+
+ // Update Supported Languages
+ if (options.languages) {
+ // Normalise player option languages to lowercase
+ var languagesToLower = {};
+
+ Object.getOwnPropertyNames(options.languages).forEach(function (name) {
+ languagesToLower[name.toLowerCase()] = options.languages[name];
+ });
+ _this.languages_ = languagesToLower;
+ } else {
+ _this.languages_ = Player.prototype.options_.languages;
+ }
+
+ // Cache for video property values.
+ _this.cache_ = {};
+
+ // Set poster
+ _this.poster_ = options.poster || '';
+
+ // Set controls
+ _this.controls_ = !!options.controls;
+
+ // Original tag settings stored in options
+ // now remove immediately so native controls don't flash.
+ // May be turned back on by HTML5 tech if nativeControlsForTouch is true
+ tag.controls = false;
+
+ /*
+ * Store the internal state of scrubbing
+ *
+ * @private
+ * @return {Boolean} True if the user is scrubbing
+ */
+ _this.scrubbing_ = false;
+
+ _this.el_ = _this.createEl();
+
+ // We also want to pass the original player options to each component and plugin
+ // as well so they don't need to reach back into the player for options later.
+ // We also need to do another copy of this.options_ so we don't end up with
+ // an infinite loop.
+ var playerOptionsCopy = (0, _mergeOptions2['default'])(_this.options_);
+
+ // Load plugins
+ if (options.plugins) {
+ var plugins = options.plugins;
+
+ Object.getOwnPropertyNames(plugins).forEach(function (name) {
+ if (typeof this[name] === 'function') {
+ this[name](plugins[name]);
+ } else {
+ _log2['default'].error('Unable to find plugin:', name);
+ }
+ }, _this);
+ }
+
+ _this.options_.playerOptions = playerOptionsCopy;
+
+ _this.initChildren();
+
+ // Set isAudio based on whether or not an audio tag was used
+ _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
+
+ // Update controls className. Can't do this when the controls are initially
+ // set because the element doesn't exist yet.
+ if (_this.controls()) {
+ _this.addClass('vjs-controls-enabled');
+ } else {
+ _this.addClass('vjs-controls-disabled');
+ }
+
+ // Set ARIA label and region role depending on player type
+ _this.el_.setAttribute('role', 'region');
+ if (_this.isAudio()) {
+ _this.el_.setAttribute('aria-label', 'audio player');
+ } else {
+ _this.el_.setAttribute('aria-label', 'video player');
+ }
+
+ if (_this.isAudio()) {
+ _this.addClass('vjs-audio');
+ }
+
+ if (_this.flexNotSupported_()) {
+ _this.addClass('vjs-no-flex');
+ }
+
+ // TODO: Make this smarter. Toggle user state between touching/mousing
+ // using events, since devices can have both touch and mouse events.
+ // if (browser.TOUCH_ENABLED) {
+ // this.addClass('vjs-touch-enabled');
+ // }
+
+ // iOS Safari has broken hover handling
+ if (!browser.IS_IOS) {
+ _this.addClass('vjs-workinghover');
+ }
+
+ // Make player easily findable by ID
+ Player.players[_this.id_] = _this;
+
+ // When the player is first initialized, trigger activity so components
+ // like the control bar show themselves if needed
+ _this.userActive(true);
+ _this.reportUserActivity();
+ _this.listenForUserActivity_();
+
+ _this.on('fullscreenchange', _this.handleFullscreenChange_);
+ _this.on('stageclick', _this.handleStageClick_);
+ return _this;
+ }
+
+ /**
+ * Destroys the video player and does any necessary cleanup.
+ *
+ * This is especially helpful if you are dynamically adding and removing videos
+ * to/from the DOM.
+ *
+ * @fires Player#dispose
+ */
+
+
+ Player.prototype.dispose = function dispose() {
+ /**
+ * Called when the player is being disposed of.
+ *
+ * @event Player#dispose
+ * @type {EventTarget~Event}
+ */
+ this.trigger('dispose');
+ // prevent dispose from being called twice
+ this.off('dispose');
+
+ if (this.styleEl_ && this.styleEl_.parentNode) {
+ this.styleEl_.parentNode.removeChild(this.styleEl_);
+ }
+
+ // Kill reference to this player
+ Player.players[this.id_] = null;
+
+ if (this.tag && this.tag.player) {
+ this.tag.player = null;
+ }
+
+ if (this.el_ && this.el_.player) {
+ this.el_.player = null;
+ }
+
+ if (this.tech_) {
+ this.tech_.dispose();
+ }
+
+ _Component.prototype.dispose.call(this);
+ };
+
+ /**
+ * Create the `Player`'s DOM element.
+ *
+ * @return {Element}
+ * The DOM element that gets created.
+ */
+
+
+ Player.prototype.createEl = function createEl() {
+ var tag = this.tag;
+ var el = void 0;
+ var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
+
+ if (playerElIngest) {
+ el = this.el_ = tag.parentNode;
+ } else {
+ el = this.el_ = _Component.prototype.createEl.call(this, 'div');
+ }
+
+ // set tabindex to -1 so we could focus on the player element
+ tag.setAttribute('tabindex', '-1');
+
+ // Remove width/height attrs from tag so CSS can make it 100% width/height
+ tag.removeAttribute('width');
+ tag.removeAttribute('height');
+
+ // Copy over all the attributes from the tag, including ID and class
+ // ID will now reference player box, not the video tag
+ var attrs = Dom.getElAttributes(tag);
+
+ Object.getOwnPropertyNames(attrs).forEach(function (attr) {
+ // workaround so we don't totally break IE7
+ // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
+ if (attr === 'class') {
+ el.className += ' ' + attrs[attr];
+ } else {
+ el.setAttribute(attr, attrs[attr]);
+ }
+ });
+
+ // Update tag id/class for use as HTML5 playback tech
+ // Might think we should do this after embedding in container so .vjs-tech class
+ // doesn't flash 100% width/height, but class only applies with .video-js parent
+ tag.playerId = tag.id;
+ tag.id += '_html5_api';
+ tag.className = 'vjs-tech';
+
+ // Make player findable on elements
+ tag.player = el.player = this;
+ // Default state of video is paused
+ this.addClass('vjs-paused');
+
+ // Add a style element in the player that we'll use to set the width/height
+ // of the player in a way that's still overrideable by CSS, just like the
+ // video element
+ if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true) {
+ this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
+ var defaultsStyleEl = Dom.$('.vjs-styles-defaults');
+ var head = Dom.$('head');
+
+ head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
+ }
+
+ // Pass in the width/height/aspectRatio options which will update the style el
+ this.width(this.options_.width);
+ this.height(this.options_.height);
+ this.fluid(this.options_.fluid);
+ this.aspectRatio(this.options_.aspectRatio);
+
+ // Hide any links within the video/audio tag, because IE doesn't hide them completely.
+ var links = tag.getElementsByTagName('a');
+
+ for (var i = 0; i < links.length; i++) {
+ var linkEl = links.item(i);
+
+ Dom.addElClass(linkEl, 'vjs-hidden');
+ linkEl.setAttribute('hidden', 'hidden');
+ }
+
+ // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
+ // keep track of the original for later so we can know if the source originally failed
+ tag.initNetworkState_ = tag.networkState;
+
+ // Wrap video tag in div (el/box) container
+ if (tag.parentNode && !playerElIngest) {
+ tag.parentNode.insertBefore(el, tag);
+ }
+
+ // insert the tag as the first child of the player element
+ // then manually add it to the children array so that this.addChild
+ // will work properly for other components
+ //
+ // Breaks iPhone, fixed in HTML5 setup.
+ Dom.insertElFirst(tag, el);
+ this.children_.unshift(tag);
+
+ this.el_ = el;
+
+ return el;
+ };
+
+ /**
+ * A getter/setter for the `Player`'s width.
+ *
+ * @param {number} [value]
+ * The value to set the `Player's width to.
+ *
+ * @return {number}
+ * The current width of the `Player`.
+ */
+
+
+ Player.prototype.width = function width(value) {
+ return this.dimension('width', value);
+ };
+
+ /**
+ * A getter/setter for the `Player`'s height.
+ *
+ * @param {number} [value]
+ * The value to set the `Player's heigth to.
+ *
+ * @return {number}
+ * The current heigth of the `Player`.
+ */
+
+
+ Player.prototype.height = function height(value) {
+ return this.dimension('height', value);
+ };
+
+ /**
+ * A getter/setter for the `Player`'s width & height.
+ *
+ * @param {string} dimension
+ * This string can be:
+ * - 'width'
+ * - 'height'
+ *
+ * @param {number} [value]
+ * Value for dimension specified in the first argument.
+ *
+ * @return {Player|number}
+ * - Returns itself when setting; method can be chained.
+ * - The dimension arguments value when getting (width/height).
+ */
+
+
+ Player.prototype.dimension = function dimension(_dimension, value) {
+ var privDimension = _dimension + '_';
+
+ if (value === undefined) {
+ return this[privDimension] || 0;
+ }
+
+ if (value === '') {
+ // If an empty string is given, reset the dimension to be automatic
+ this[privDimension] = undefined;
+ } else {
+ var parsedVal = parseFloat(value);
+
+ if (isNaN(parsedVal)) {
+ _log2['default'].error('Improper value "' + value + '" supplied for for ' + _dimension);
+ return this;
+ }
+
+ this[privDimension] = parsedVal;
+ }
+
+ this.updateStyleEl_();
+ return this;
+ };
+
+ /**
+ * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
+ *
+ * @param {boolean} [bool]
+ * - A value of true adds the class.
+ * - A value of false removes the class.
+ * - No value will toggle the fluid class.
+ *
+ * @return {boolean|undefined}
+ * - The value of fluid when getting.
+ * - `undefined` when setting.
+ */
+
+
+ Player.prototype.fluid = function fluid(bool) {
+ if (bool === undefined) {
+ return !!this.fluid_;
+ }
+
+ this.fluid_ = !!bool;
+
+ if (bool) {
+ this.addClass('vjs-fluid');
+ } else {
+ this.removeClass('vjs-fluid');
+ }
+
+ this.updateStyleEl_();
+ };
+
+ /**
+ * Get/Set the aspect ratio
+ *
+ * @param {string} [ratio]
+ * Aspect ratio for player
+ *
+ * @return {string|undefined}
+ * returns the current aspect ratio when getting
+ */
+
+ /**
+ * A getter/setter for the `Player`'s aspect ratio.
+ *
+ * @param {string} [ratio]
+ * The value to set the `Player's aspect ratio to.
+ *
+ * @return {string|undefined}
+ * - The current aspect ratio of the `Player` when getting.
+ * - undefined when setting
+ */
+
+
+ Player.prototype.aspectRatio = function aspectRatio(ratio) {
+ if (ratio === undefined) {
+ return this.aspectRatio_;
+ }
+
+ // Check for width:height format
+ if (!/^\d+\:\d+$/.test(ratio)) {
+ throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
+ }
+ this.aspectRatio_ = ratio;
+
+ // We're assuming if you set an aspect ratio you want fluid mode,
+ // because in fixed mode you could calculate width and height yourself.
+ this.fluid(true);
+
+ this.updateStyleEl_();
+ };
+
+ /**
+ * Update styles of the `Player` element (height, width and aspect ratio).
+ *
+ * @private
+ * @listens Tech#loadedmetadata
+ */
+
+
+ Player.prototype.updateStyleEl_ = function updateStyleEl_() {
+ if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE === true) {
+ var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
+ var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
+ var techEl = this.tech_ && this.tech_.el();
+
+ if (techEl) {
+ if (_width >= 0) {
+ techEl.width = _width;
+ }
+ if (_height >= 0) {
+ techEl.height = _height;
+ }
+ }
+
+ return;
+ }
+
+ var width = void 0;
+ var height = void 0;
+ var aspectRatio = void 0;
+ var idClass = void 0;
+
+ // The aspect ratio is either used directly or to calculate width and height.
+ if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
+ // Use any aspectRatio that's been specifically set
+ aspectRatio = this.aspectRatio_;
+ } else if (this.videoWidth() > 0) {
+ // Otherwise try to get the aspect ratio from the video metadata
+ aspectRatio = this.videoWidth() + ':' + this.videoHeight();
+ } else {
+ // Or use a default. The video element's is 2:1, but 16:9 is more common.
+ aspectRatio = '16:9';
+ }
+
+ // Get the ratio as a decimal we can use to calculate dimensions
+ var ratioParts = aspectRatio.split(':');
+ var ratioMultiplier = ratioParts[1] / ratioParts[0];
+
+ if (this.width_ !== undefined) {
+ // Use any width that's been specifically set
+ width = this.width_;
+ } else if (this.height_ !== undefined) {
+ // Or calulate the width from the aspect ratio if a height has been set
+ width = this.height_ / ratioMultiplier;
+ } else {
+ // Or use the video's metadata, or use the video el's default of 300
+ width = this.videoWidth() || 300;
+ }
+
+ if (this.height_ !== undefined) {
+ // Use any height that's been specifically set
+ height = this.height_;
+ } else {
+ // Otherwise calculate the height from the ratio and the width
+ height = width * ratioMultiplier;
+ }
+
+ // Ensure the CSS class is valid by starting with an alpha character
+ if (/^[^a-zA-Z]/.test(this.id())) {
+ idClass = 'dimensions-' + this.id();
+ } else {
+ idClass = this.id() + '-dimensions';
+ }
+
+ // Ensure the right class is still on the player for the style element
+ this.addClass(idClass);
+
+ stylesheet.setTextContent(this.styleEl_, '\n .' + idClass + ' {\n width: ' + width + 'px;\n height: ' + height + 'px;\n }\n\n .' + idClass + '.vjs-fluid {\n padding-top: ' + ratioMultiplier * 100 + '%;\n }\n ');
+ };
+
+ /**
+ * Load/Create an instance of playback {@link Tech} including element
+ * and API methods. Then append the `Tech` element in `Player` as a child.
+ *
+ * @param {string} techName
+ * name of the playback technology
+ *
+ * @param {string} source
+ * video source
+ *
+ * @private
+ */
+
+
+ Player.prototype.loadTech_ = function loadTech_(techName, source) {
+ var _this2 = this;
+
+ // Pause and remove current playback technology
+ if (this.tech_) {
+ this.unloadTech_();
+ }
+
+ // get rid of the HTML5 video tag as soon as we are using another tech
+ if (techName !== 'Html5' && this.tag) {
+ _tech2['default'].getTech('Html5').disposeMediaElement(this.tag);
+ this.tag.player = null;
+ this.tag = null;
+ }
+
+ this.techName_ = techName;
+
+ // Turn off API access because we're loading a new tech that might load asynchronously
+ this.isReady_ = false;
+
+ // Grab tech-specific options from player options and add source and parent element to use.
+ var techOptions = (0, _obj.assign)({
+ source: source,
+ 'nativeControlsForTouch': this.options_.nativeControlsForTouch,
+ 'playerId': this.id(),
+ 'techId': this.id() + '_' + techName + '_api',
+ 'videoTracks': this.videoTracks_,
+ 'textTracks': this.textTracks_,
+ 'audioTracks': this.audioTracks_,
+ 'autoplay': this.options_.autoplay,
+ 'preload': this.options_.preload,
+ 'loop': this.options_.loop,
+ 'muted': this.options_.muted,
+ 'poster': this.poster(),
+ 'language': this.language(),
+ 'playerElIngest': this.playerElIngest_ || false,
+ 'vtt.js': this.options_['vtt.js']
+ }, this.options_[techName.toLowerCase()]);
+
+ if (this.tag) {
+ techOptions.tag = this.tag;
+ }
+
+ if (source) {
+ this.currentType_ = source.type;
+
+ if (source.src === this.cache_.src && this.cache_.currentTime > 0) {
+ techOptions.startTime = this.cache_.currentTime;
+ }
+
+ this.cache_.sources = null;
+ this.cache_.source = source;
+ this.cache_.src = source.src;
+ }
+
+ // Initialize tech instance
+ var TechComponent = _tech2['default'].getTech(techName);
+
+ // Support old behavior of techs being registered as components.
+ // Remove once that deprecated behavior is removed.
+ if (!TechComponent) {
+ TechComponent = _component2['default'].getComponent(techName);
+ }
+ this.tech_ = new TechComponent(techOptions);
+
+ // player.triggerReady is always async, so don't need this to be async
+ this.tech_.ready(Fn.bind(this, this.handleTechReady_), true);
+
+ _textTrackListConverter2['default'].jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
+
+ // Listen to all HTML5-defined events and trigger them on the player
+ TECH_EVENTS_RETRIGGER.forEach(function (event) {
+ _this2.on(_this2.tech_, event, _this2['handleTech' + (0, _toTitleCase2['default'])(event) + '_']);
+ });
+ this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
+ this.on(this.tech_, 'waiting', this.handleTechWaiting_);
+ this.on(this.tech_, 'canplay', this.handleTechCanPlay_);
+ this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_);
+ this.on(this.tech_, 'playing', this.handleTechPlaying_);
+ this.on(this.tech_, 'ended', this.handleTechEnded_);
+ this.on(this.tech_, 'seeking', this.handleTechSeeking_);
+ this.on(this.tech_, 'seeked', this.handleTechSeeked_);
+ this.on(this.tech_, 'play', this.handleTechPlay_);
+ this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
+ this.on(this.tech_, 'pause', this.handleTechPause_);
+ this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
+ this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
+ this.on(this.tech_, 'error', this.handleTechError_);
+ this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
+ this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
+ this.on(this.tech_, 'textdata', this.handleTechTextData_);
+
+ this.usingNativeControls(this.techGet_('controls'));
+
+ if (this.controls() && !this.usingNativeControls()) {
+ this.addTechControlsListeners_();
+ }
+
+ // Add the tech element in the DOM if it was not already there
+ // Make sure to not insert the original video element if using Html5
+ if (this.tech_.el().parentNode !== this.el() && (techName !== 'Html5' || !this.tag)) {
+ Dom.insertElFirst(this.tech_.el(), this.el());
+ }
+
+ // Get rid of the original video tag reference after the first tech is loaded
+ if (this.tag) {
+ this.tag.player = null;
+ this.tag = null;
+ }
+ };
+
+ /**
+ * Unload and dispose of the current playback {@link Tech}.
+ *
+ * @private
+ */
+
+
+ Player.prototype.unloadTech_ = function unloadTech_() {
+ // Save the current text tracks so that we can reuse the same text tracks with the next tech
+ this.videoTracks_ = this.videoTracks();
+ this.textTracks_ = this.textTracks();
+ this.audioTracks_ = this.audioTracks();
+ this.textTracksJson_ = _textTrackListConverter2['default'].textTracksToJson(this.tech_);
+
+ this.isReady_ = false;
+
+ this.tech_.dispose();
+
+ this.tech_ = false;
+ };
+
+ /**
+ * Return a reference to the current {@link Tech}, but only if given an object with the
+ * `IWillNotUseThisInPlugins` property having a true value. This is try and prevent misuse
+ * of techs by plugins.
+ *
+ * @param {Object} safety
+ * An object that must contain `{IWillNotUseThisInPlugins: true}`
+ *
+ * @param {boolean} safety.IWillNotUseThisInPlugins
+ * Must be set to true or else this function will throw an error.
+ *
+ * @return {Tech}
+ * The Tech
+ */
+
+
+ Player.prototype.tech = function tech(safety) {
+ if (safety && safety.IWillNotUseThisInPlugins) {
+ return this.tech_;
+ }
+ var errorText = '\n Please make sure that you are not using this inside of a plugin.\n To disable this alert and error, please pass in an object with\n `IWillNotUseThisInPlugins` to the `tech` method. See\n https://github.com/videojs/video.js/issues/2617 for more info.\n ';
+
+ _window2['default'].alert(errorText);
+ throw new Error(errorText);
+ };
+
+ /**
+ * Set up click and touch listeners for the playback element
+ *
+ * - On desktops: a click on the video itself will toggle playback
+ * - On mobile devices: a click on the video toggles controls
+ * which is done by toggling the user state between active and
+ * inactive
+ * - A tap can signal that a user has become active or has become inactive
+ * e.g. a quick tap on an iPhone movie should reveal the controls. Another
+ * quick tap should hide them again (signaling the user is in an inactive
+ * viewing state)
+ * - In addition to this, we still want the user to be considered inactive after
+ * a few seconds of inactivity.
+ *
+ * > Note: the only part of iOS interaction we can't mimic with this setup
+ * is a touch and hold on the video element counting as activity in order to
+ * keep the controls showing, but that shouldn't be an issue. A touch and hold
+ * on any controls will still keep the user active
+ *
+ * @private
+ */
+
+
+ Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
+ // Make sure to remove all the previous listeners in case we are called multiple times.
+ this.removeTechControlsListeners_();
+
+ // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
+ // trigger mousedown/up.
+ // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
+ // Any touch events are set to block the mousedown event from happening
+ this.on(this.tech_, 'mousedown', this.handleTechClick_);
+
+ // If the controls were hidden we don't want that to change without a tap event
+ // so we'll check if the controls were already showing before reporting user
+ // activity
+ this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
+ this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
+ this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
+
+ // The tap listener needs to come after the touchend listener because the tap
+ // listener cancels out any reportedUserActivity when setting userActive(false)
+ this.on(this.tech_, 'tap', this.handleTechTap_);
+ };
+
+ /**
+ * Remove the listeners used for click and tap controls. This is needed for
+ * toggling to controls disabled, where a tap/touch should do nothing.
+ *
+ * @private
+ */
+
+
+ Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
+ // We don't want to just use `this.off()` because there might be other needed
+ // listeners added by techs that extend this.
+ this.off(this.tech_, 'tap', this.handleTechTap_);
+ this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
+ this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
+ this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
+ this.off(this.tech_, 'mousedown', this.handleTechClick_);
+ };
+
+ /**
+ * Player waits for the tech to be ready
+ *
+ * @private
+ */
+
+
+ Player.prototype.handleTechReady_ = function handleTechReady_() {
+ this.triggerReady();
+
+ // Keep the same volume as before
+ if (this.cache_.volume) {
+ this.techCall_('setVolume', this.cache_.volume);
+ }
+
+ // Look if the tech found a higher resolution poster while loading
+ this.handleTechPosterChange_();
+
+ // Update the duration if available
+ this.handleTechDurationChange_();
+
+ // Chrome and Safari both have issues with autoplay.
+ // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
+ // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
+ // This fixes both issues. Need to wait for API, so it updates displays correctly
+ if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
+ try {
+ // Chrome Fix. Fixed in Chrome v16.
+ delete this.tag.poster;
+ } catch (e) {
+ (0, _log2['default'])('deleting tag.poster throws in some browsers', e);
+ }
+ this.play();
+ }
+ };
+
+ /**
+ * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
+ * function will also trigger {@link Player#firstplay} if it is the first loadstart
+ * for a video.
+ *
+ * @fires Player#loadstart
+ * @fires Player#firstplay
+ * @listens Tech#loadstart
+ * @private
+ */
+
+
+ Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
+ // TODO: Update to use `emptied` event instead. See #1277.
+
+ this.removeClass('vjs-ended');
+ this.removeClass('vjs-seeking');
+
+ // reset the error state
+ this.error(null);
+
+ // If it's already playing we want to trigger a firstplay event now.
+ // The firstplay event relies on both the play and loadstart events
+ // which can happen in any order for a new source
+ if (!this.paused()) {
+ /**
+ * Fired when the user agent begins looking for media data
+ *
+ * @event Player#loadstart
+ * @type {EventTarget~Event}
+ */
+ this.trigger('loadstart');
+ this.trigger('firstplay');
+ } else {
+ // reset the hasStarted state
+ this.hasStarted(false);
+ this.trigger('loadstart');
+ }
+ };
+
+ /**
+ * Add/remove the vjs-has-started class
+ *
+ * @fires Player#firstplay
+ *
+ * @param {boolean} hasStarted
+ * - true: adds the class
+ * - false: remove the class
+ *
+ * @return {boolean}
+ * the boolean value of hasStarted
+ */
+
+
+ Player.prototype.hasStarted = function hasStarted(_hasStarted) {
+ if (_hasStarted !== undefined) {
+ // only update if this is a new value
+ if (this.hasStarted_ !== _hasStarted) {
+ this.hasStarted_ = _hasStarted;
+ if (_hasStarted) {
+ this.addClass('vjs-has-started');
+ // trigger the firstplay event if this newly has played
+ this.trigger('firstplay');
+ } else {
+ this.removeClass('vjs-has-started');
+ }
+ }
+ return this;
+ }
+ return !!this.hasStarted_;
+ };
+
+ /**
+ * Fired whenever the media begins or resumes playback
+ *
+ * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
+ * @fires Player#play
+ * @listens Tech#play
+ * @private
+ */
+
+
+ Player.prototype.handleTechPlay_ = function handleTechPlay_() {
+ this.removeClass('vjs-ended');
+ this.removeClass('vjs-paused');
+ this.addClass('vjs-playing');
+
+ // hide the poster when the user hits play
+ this.hasStarted(true);
+ /**
+ * Triggered whenever an {@link Tech#play} event happens. Indicates that
+ * playback has started or resumed.
+ *
+ * @event Player#play
+ * @type {EventTarget~Event}
+ */
+ this.trigger('play');
+ };
+
+ /**
+ * Retrigger the `waiting` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#waiting
+ * @listens Tech#waiting
+ * @private
+ */
+
+
+ Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
+ var _this3 = this;
+
+ this.addClass('vjs-waiting');
+ /**
+ * A readyState change on the DOM element has caused playback to stop.
+ *
+ * @event Player#waiting
+ * @type {EventTarget~Event}
+ */
+ this.trigger('waiting');
+ this.one('timeupdate', function () {
+ return _this3.removeClass('vjs-waiting');
+ });
+ };
+
+ /**
+ * Retrigger the `canplay` event that was triggered by the {@link Tech}.
+ * > Note: This is not consistent between browsers. See #1351
+ *
+ * @fires Player#canplay
+ * @listens Tech#canplay
+ * @private
+ */
+
+
+ Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
+ this.removeClass('vjs-waiting');
+ /**
+ * The media has a readyState of HAVE_FUTURE_DATA or greater.
+ *
+ * @event Player#canplay
+ * @type {EventTarget~Event}
+ */
+ this.trigger('canplay');
+ };
+
+ /**
+ * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#canplaythrough
+ * @listens Tech#canplaythrough
+ * @private
+ */
+
+
+ Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
+ this.removeClass('vjs-waiting');
+ /**
+ * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
+ * entire media file can be played without buffering.
+ *
+ * @event Player#canplaythrough
+ * @type {EventTarget~Event}
+ */
+ this.trigger('canplaythrough');
+ };
+
+ /**
+ * Retrigger the `playing` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#playing
+ * @listens Tech#playing
+ * @private
+ */
+
+
+ Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
+ this.removeClass('vjs-waiting');
+ /**
+ * The media is no longer blocked from playback, and has started playing.
+ *
+ * @event Player#playing
+ * @type {EventTarget~Event}
+ */
+ this.trigger('playing');
+ };
+
+ /**
+ * Retrigger the `seeking` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#seeking
+ * @listens Tech#seeking
+ * @private
+ */
+
+
+ Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
+ this.addClass('vjs-seeking');
+ /**
+ * Fired whenever the player is jumping to a new time
+ *
+ * @event Player#seeking
+ * @type {EventTarget~Event}
+ */
+ this.trigger('seeking');
+ };
+
+ /**
+ * Retrigger the `seeked` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#seeked
+ * @listens Tech#seeked
+ * @private
+ */
+
+
+ Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
+ this.removeClass('vjs-seeking');
+ /**
+ * Fired when the player has finished jumping to a new time
+ *
+ * @event Player#seeked
+ * @type {EventTarget~Event}
+ */
+ this.trigger('seeked');
+ };
+
+ /**
+ * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#firstplay
+ * @listens Tech#firstplay
+ * @deprecated As of 6.0 passing the `starttime` option to the player will be deprecated
+ * @private
+ */
+
+
+ Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
+ // If the first starttime attribute is specified
+ // then we will start at the given offset in seconds
+ if (this.options_.starttime) {
+ _log2['default'].warn('Passing the `starttime` option to the player will be deprecated in 6.0');
+ this.currentTime(this.options_.starttime);
+ }
+
+ this.addClass('vjs-has-started');
+ /**
+ * Fired the first time a video is played. Not part of the HLS spec, and this is
+ * probably not the best implementation yet, so use sparingly. If you don't have a
+ * reason to prevent playback, use `myPlayer.one('play');` instead.
+ *
+ * @event Player#firstplay
+ * @type {EventTarget~Event}
+ */
+ this.trigger('firstplay');
+ };
+
+ /**
+ * Retrigger the `pause` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#pause
+ * @listens Tech#pause
+ * @private
+ */
+
+
+ Player.prototype.handleTechPause_ = function handleTechPause_() {
+ this.removeClass('vjs-playing');
+ this.addClass('vjs-paused');
+ /**
+ * Fired whenever the media has been paused
+ *
+ * @event Player#pause
+ * @type {EventTarget~Event}
+ */
+ this.trigger('pause');
+ };
+
+ /**
+ * Retrigger the `ended` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#ended
+ * @listens Tech#ended
+ * @private
+ */
+
+
+ Player.prototype.handleTechEnded_ = function handleTechEnded_() {
+ this.addClass('vjs-ended');
+ if (this.options_.loop) {
+ this.currentTime(0);
+ this.play();
+ } else if (!this.paused()) {
+ this.pause();
+ }
+
+ /**
+ * Fired when the end of the media resource is reached (currentTime == duration)
+ *
+ * @event Player#ended
+ * @type {EventTarget~Event}
+ */
+ this.trigger('ended');
+ };
+
+ /**
+ * Fired when the duration of the media resource is first known or changed
+ *
+ * @listens Tech#durationchange
+ * @private
+ */
+
+
+ Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
+ this.duration(this.techGet_('duration'));
+ };
+
+ /**
+ * Handle a click on the media element to play/pause
+ *
+ * @param {EventTarget~Event} event
+ * the event that caused this function to trigger
+ *
+ * @listens Tech#mousedown
+ * @private
+ */
+
+
+ Player.prototype.handleTechClick_ = function handleTechClick_(event) {
+ // We're using mousedown to detect clicks thanks to Flash, but mousedown
+ // will also be triggered with right-clicks, so we need to prevent that
+ if (event.button !== 0) {
+ return;
+ }
+
+ // When controls are disabled a click should not toggle playback because
+ // the click is considered a control
+ if (this.controls()) {
+ if (this.paused()) {
+ this.play();
+ } else {
+ this.pause();
+ }
+ }
+ };
+
+ /**
+ * Handle a tap on the media element. It will toggle the user
+ * activity state, which hides and shows the controls.
+ *
+ * @listens Tech#tap
+ * @private
+ */
+
+
+ Player.prototype.handleTechTap_ = function handleTechTap_() {
+ this.userActive(!this.userActive());
+ };
+
+ /**
+ * Handle touch to start
+ *
+ * @listens Tech#touchstart
+ * @private
+ */
+
+
+ Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
+ this.userWasActive = this.userActive();
+ };
+
+ /**
+ * Handle touch to move
+ *
+ * @listens Tech#touchmove
+ * @private
+ */
+
+
+ Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
+ if (this.userWasActive) {
+ this.reportUserActivity();
+ }
+ };
+
+ /**
+ * Handle touch to end
+ *
+ * @param {EventTarget~Event} event
+ * the touchend event that triggered
+ * this function
+ *
+ * @listens Tech#touchend
+ * @private
+ */
+
+
+ Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
+ // Stop the mouse events from also happening
+ event.preventDefault();
+ };
+
+ /**
+ * Fired when the player switches in or out of fullscreen mode
+ *
+ * @private
+ * @listens Player#fullscreenchange
+ */
+
+
+ Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
+ if (this.isFullscreen()) {
+ this.addClass('vjs-fullscreen');
+ } else {
+ this.removeClass('vjs-fullscreen');
+ }
+ };
+
+ /**
+ * native click events on the SWF aren't triggered on IE11, Win8.1RT
+ * use stageclick events triggered from inside the SWF instead
+ *
+ * @private
+ * @listens stageclick
+ */
+
+
+ Player.prototype.handleStageClick_ = function handleStageClick_() {
+ this.reportUserActivity();
+ };
+
+ /**
+ * Handle Tech Fullscreen Change
+ *
+ * @param {EventTarget~Event} event
+ * the fullscreenchange event that triggered this function
+ *
+ * @param {Object} data
+ * the data that was sent with the event
+ *
+ * @private
+ * @listens Tech#fullscreenchange
+ * @fires Player#fullscreenchange
+ */
+
+
+ Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
+ if (data) {
+ this.isFullscreen(data.isFullscreen);
+ }
+ /**
+ * Fired when going in and out of fullscreen.
+ *
+ * @event Player#fullscreenchange
+ * @type {EventTarget~Event}
+ */
+ this.trigger('fullscreenchange');
+ };
+
+ /**
+ * Fires when an error occurred during the loading of an audio/video.
+ *
+ * @private
+ * @listens Tech#error
+ */
+
+
+ Player.prototype.handleTechError_ = function handleTechError_() {
+ var error = this.tech_.error();
+
+ this.error(error);
+ };
+
+ /**
+ * Retrigger the `textdata` event that was triggered by the {@link Tech}.
+ *
+ * @fires Player#textdata
+ * @listens Tech#textdata
+ * @private
+ */
+
+
+ Player.prototype.handleTechTextData_ = function handleTechTextData_() {
+ var data = null;
+
+ if (arguments.length > 1) {
+ data = arguments[1];
+ }
+
+ /**
+ * Fires when we get a textdata event from tech
+ *
+ * @event Player#textdata
+ * @type {EventTarget~Event}
+ */
+ this.trigger('textdata', data);
+ };
+
+ /**
+ * Get object for cached values.
+ *
+ * @return {Object}
+ * get the current object cache
+ */
+
+
+ Player.prototype.getCache = function getCache() {
+ return this.cache_;
+ };
+
+ /**
+ * Pass values to the playback tech
+ *
+ * @param {string} [method]
+ * the method to call
+ *
+ * @param {Object} arg
+ * the argument to pass
+ *
+ * @private
+ */
+
+
+ Player.prototype.techCall_ = function techCall_(method, arg) {
+ // If it's not ready yet, call method when it is
+ if (this.tech_ && !this.tech_.isReady_) {
+ this.tech_.ready(function () {
+ this[method](arg);
+ }, true);
+
+ // Otherwise call method now
+ } else {
+ try {
+ if (this.tech_) {
+ this.tech_[method](arg);
+ }
+ } catch (e) {
+ (0, _log2['default'])(e);
+ throw e;
+ }
+ }
+ };
+
+ /**
+ * Get calls can't wait for the tech, and sometimes don't need to.
+ *
+ * @param {string} method
+ * Tech method
+ *
+ * @return {Function|undefined}
+ * the method or undefined
+ *
+ * @private
+ */
+
+
+ Player.prototype.techGet_ = function techGet_(method) {
+ if (this.tech_ && this.tech_.isReady_) {
+
+ // Flash likes to die and reload when you hide or reposition it.
+ // In these cases the object methods go away and we get errors.
+ // When that happens we'll catch the errors and inform tech that it's not ready any more.
+ try {
+ return this.tech_[method]();
+ } catch (e) {
+ // When building additional tech libs, an expected method may not be defined yet
+ if (this.tech_[method] === undefined) {
+ (0, _log2['default'])('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
+
+ // When a method isn't available on the object it throws a TypeError
+ } else if (e.name === 'TypeError') {
+ (0, _log2['default'])('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
+ this.tech_.isReady_ = false;
+ } else {
+ (0, _log2['default'])(e);
+ }
+ throw e;
+ }
+ }
+
+ return;
+ };
+
+ /**
+ * start media playback
+ *
+ * @return {Player}
+ * A reference to the player object this function was called on
+ */
+
+
+ Player.prototype.play = function play() {
+ // Only calls the tech's play if we already have a src loaded
+ if (this.src() || this.currentSrc()) {
+ this.techCall_('play');
+ } else {
+ this.tech_.one('loadstart', function () {
+ this.play();
+ });
+ }
+
+ return this;
+ };
+
+ /**
+ * Pause the video playback
+ *
+ * @return {Player}
+ * A reference to the player object this function was called on
+ */
+
+
+ Player.prototype.pause = function pause() {
+ this.techCall_('pause');
+ return this;
+ };
+
+ /**
+ * Check if the player is paused or has yet to play
+ *
+ * @return {boolean}
+ * - false: if the media is currently playing
+ * - true: if media is not currently playing
+ */
+
+
+ Player.prototype.paused = function paused() {
+ // The initial state of paused should be true (in Safari it's actually false)
+ return this.techGet_('paused') === false ? false : true;
+ };
+
+ /**
+ * Returns whether or not the user is "scrubbing". Scrubbing is
+ * when the user has clicked the progress bar handle and is
+ * dragging it along the progress bar.
+ *
+ * @param {boolean} [isScrubbing]
+ * wether the user is or is not scrubbing
+ *
+ * @return {boolean|Player}
+ * A instance of the player that called this function when setting,
+ * and the value of scrubbing when getting
+ */
+
+
+ Player.prototype.scrubbing = function scrubbing(isScrubbing) {
+ if (isScrubbing !== undefined) {
+ this.scrubbing_ = !!isScrubbing;
+
+ if (isScrubbing) {
+ this.addClass('vjs-scrubbing');
+ } else {
+ this.removeClass('vjs-scrubbing');
+ }
+
+ return this;
+ }
+
+ return this.scrubbing_;
+ };
+
+ /**
+ * Get or set the current time (in seconds)
+ *
+ * @param {number|string} [seconds]
+ * The time to seek to in seconds
+ *
+ * @return {Player|number}
+ * - the current time in seconds when getting
+ * - a reference to the current player object when setting
+ */
+
+
+ Player.prototype.currentTime = function currentTime(seconds) {
+ if (seconds !== undefined) {
+
+ this.techCall_('setCurrentTime', seconds);
+
+ return this;
+ }
+
+ // cache last currentTime and return. default to 0 seconds
+ //
+ // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
+ // currentTime when scrubbing, but may not provide much performance benefit afterall.
+ // Should be tested. Also something has to read the actual current time or the cache will
+ // never get updated.
+ this.cache_.currentTime = this.techGet_('currentTime') || 0;
+ return this.cache_.currentTime;
+ };
+
+ /**
+ * Normally gets the length in time of the video in seconds;
+ * in all but the rarest use cases an argument will NOT be passed to the method
+ *
+ * > **NOTE**: The video must have started loading before the duration can be
+ * known, and in the case of Flash, may not be known until the video starts
+ * playing.
+ *
+ * @fires Player#durationchange
+ *
+ * @param {number} [seconds]
+ * The duration of the video to set in seconds
+ *
+ * @return {number|Player}
+ * - The duration of the video in seconds when getting
+ * - A reference to the player that called this function
+ * when setting
+ */
+
+
+ Player.prototype.duration = function duration(seconds) {
+ if (seconds === undefined) {
+ return this.cache_.duration || 0;
+ }
+
+ seconds = parseFloat(seconds) || 0;
+
+ // Standardize on Inifity for signaling video is live
+ if (seconds < 0) {
+ seconds = Infinity;
+ }
+
+ if (seconds !== this.cache_.duration) {
+ // Cache the last set value for optimized scrubbing (esp. Flash)
+ this.cache_.duration = seconds;
+
+ if (seconds === Infinity) {
+ this.addClass('vjs-live');
+ } else {
+ this.removeClass('vjs-live');
+ }
+ /**
+ * @event Player#durationchange
+ * @type {EventTarget~Event}
+ */
+ this.trigger('durationchange');
+ }
+
+ return this;
+ };
+
+ /**
+ * Calculates how much time is left in the video. Not part
+ * of the native video API.
+ *
+ * @return {number}
+ * The time remaining in seconds
+ */
+
+
+ Player.prototype.remainingTime = function remainingTime() {
+ return this.duration() - this.currentTime();
+ };
+
+ //
+ // Kind of like an array of portions of the video that have been downloaded.
+
+ /**
+ * Get a TimeRange object with an array of the times of the video
+ * that have been downloaded. If you just want the percent of the
+ * video that's been downloaded, use bufferedPercent.
+ *
+ * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
+ *
+ * @return {TimeRange}
+ * A mock TimeRange object (following HTML spec)
+ */
+
+
+ Player.prototype.buffered = function buffered() {
+ var buffered = this.techGet_('buffered');
+
+ if (!buffered || !buffered.length) {
+ buffered = (0, _timeRanges.createTimeRange)(0, 0);
+ }
+
+ return buffered;
+ };
+
+ /**
+ * Get the percent (as a decimal) of the video that's been downloaded.
+ * This method is not a part of the native HTML video API.
+ *
+ * @return {number}
+ * A decimal between 0 and 1 representing the percent
+ * that is bufferred 0 being 0% and 1 being 100%
+ */
+
+
+ Player.prototype.bufferedPercent = function bufferedPercent() {
+ return (0, _buffer.bufferedPercent)(this.buffered(), this.duration());
+ };
+
+ /**
+ * Get the ending time of the last buffered time range
+ * This is used in the progress bar to encapsulate all time ranges.
+ *
+ * @return {number}
+ * The end of the last buffered time range
+ */
+
+
+ Player.prototype.bufferedEnd = function bufferedEnd() {
+ var buffered = this.buffered();
+ var duration = this.duration();
+ var end = buffered.end(buffered.length - 1);
+
+ if (end > duration) {
+ end = duration;
+ }
+
+ return end;
+ };
+
+ /**
+ * Get or set the current volume of the media
+ *
+ * @param {number} [percentAsDecimal]
+ * The new volume as a decimal percent:
+ * - 0 is muted/0%/off
+ * - 1.0 is 100%/full
+ * - 0.5 is half volume or 50%
+ *
+ * @return {Player|number}
+ * a reference to the calling player when setting and the
+ * current volume as a percent when getting
+ */
+
+
+ Player.prototype.volume = function volume(percentAsDecimal) {
+ var vol = void 0;
+
+ if (percentAsDecimal !== undefined) {
+ // Force value to between 0 and 1
+ vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
+ this.cache_.volume = vol;
+ this.techCall_('setVolume', vol);
+
+ return this;
+ }
+
+ // Default to 1 when returning current volume.
+ vol = parseFloat(this.techGet_('volume'));
+ return isNaN(vol) ? 1 : vol;
+ };
+
+ /**
+ * Get the current muted state, or turn mute on or off
+ *
+ * @param {boolean} [muted]
+ * - true to mute
+ * - false to unmute
+ *
+ * @return {boolean|Player}
+ * - true if mute is on and getting
+ * - false if mute is off and getting
+ * - A reference to the current player when setting
+ */
+
+
+ Player.prototype.muted = function muted(_muted) {
+ if (_muted !== undefined) {
+ this.techCall_('setMuted', _muted);
+ return this;
+ }
+ return this.techGet_('muted') || false;
+ };
+
+ /**
+ * Check if current tech can support native fullscreen
+ * (e.g. with built in controls like iOS, so not our flash swf)
+ *
+ * @return {boolean}
+ * if native fullscreen is supported
+ */
+
+
+ Player.prototype.supportsFullScreen = function supportsFullScreen() {
+ return this.techGet_('supportsFullScreen') || false;
+ };
+
+ /**
+ * Check if the player is in fullscreen mode or tell the player that it
+ * is or is not in fullscreen mode.
+ *
+ * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
+ * property and instead document.fullscreenElement is used. But isFullscreen is
+ * still a valuable property for internal player workings.
+ *
+ * @param {boolean} [isFS]
+ * Set the players current fullscreen state
+ *
+ * @return {boolean|Player}
+ * - true if fullscreen is on and getting
+ * - false if fullscreen is off and getting
+ * - A reference to the current player when setting
+ */
+
+
+ Player.prototype.isFullscreen = function isFullscreen(isFS) {
+ if (isFS !== undefined) {
+ this.isFullscreen_ = !!isFS;
+ return this;
+ }
+ return !!this.isFullscreen_;
+ };
+
+ /**
+ * Increase the size of the video to full screen
+ * In some browsers, full screen is not supported natively, so it enters
+ * "full window mode", where the video fills the browser window.
+ * In browsers and devices that support native full screen, sometimes the
+ * browser's default controls will be shown, and not the Video.js custom skin.
+ * This includes most mobile devices (iOS, Android) and older versions of
+ * Safari.
+ *
+ * @fires Player#fullscreenchange
+ * @return {Player}
+ * A reference to the current player
+ */
+
+
+ Player.prototype.requestFullscreen = function requestFullscreen() {
+ var fsApi = _fullscreenApi2['default'];
+
+ this.isFullscreen(true);
+
+ if (fsApi.requestFullscreen) {
+ // the browser supports going fullscreen at the element level so we can
+ // take the controls fullscreen as well as the video
+
+ // Trigger fullscreenchange event after change
+ // We have to specifically add this each time, and remove
+ // when canceling fullscreen. Otherwise if there's multiple
+ // players on a page, they would all be reacting to the same fullscreen
+ // events
+ Events.on(_document2['default'], fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) {
+ this.isFullscreen(_document2['default'][fsApi.fullscreenElement]);
+
+ // If cancelling fullscreen, remove event listener.
+ if (this.isFullscreen() === false) {
+ Events.off(_document2['default'], fsApi.fullscreenchange, documentFullscreenChange);
+ }
+ /**
+ * @event Player#fullscreenchange
+ * @type {EventTarget~Event}
+ */
+ this.trigger('fullscreenchange');
+ }));
+
+ this.el_[fsApi.requestFullscreen]();
+ } else if (this.tech_.supportsFullScreen()) {
+ // we can't take the video.js controls fullscreen but we can go fullscreen
+ // with native controls
+ this.techCall_('enterFullScreen');
+ } else {
+ // fullscreen isn't supported so we'll just stretch the video element to
+ // fill the viewport
+ this.enterFullWindow();
+ /**
+ * @event Player#fullscreenchange
+ * @type {EventTarget~Event}
+ */
+ this.trigger('fullscreenchange');
+ }
+
+ return this;
+ };
+
+ /**
+ * Return the video to its normal size after having been in full screen mode
+ *
+ * @fires Player#fullscreenchange
+ *
+ * @return {Player}
+ * A reference to the current player
+ */
+
+
+ Player.prototype.exitFullscreen = function exitFullscreen() {
+ var fsApi = _fullscreenApi2['default'];
+
+ this.isFullscreen(false);
+
+ // Check for browser element fullscreen support
+ if (fsApi.requestFullscreen) {
+ _document2['default'][fsApi.exitFullscreen]();
+ } else if (this.tech_.supportsFullScreen()) {
+ this.techCall_('exitFullScreen');
+ } else {
+ this.exitFullWindow();
+ /**
+ * @event Player#fullscreenchange
+ * @type {EventTarget~Event}
+ */
+ this.trigger('fullscreenchange');
+ }
+
+ return this;
+ };
+
+ /**
+ * When fullscreen isn't supported we can stretch the
+ * video container to as wide as the browser will let us.
+ *
+ * @fires Player#enterFullWindow
+ */
+
+
+ Player.prototype.enterFullWindow = function enterFullWindow() {
+ this.isFullWindow = true;
+
+ // Storing original doc overflow value to return to when fullscreen is off
+ this.docOrigOverflow = _document2['default'].documentElement.style.overflow;
+
+ // Add listener for esc key to exit fullscreen
+ Events.on(_document2['default'], 'keydown', Fn.bind(this, this.fullWindowOnEscKey));
+
+ // Hide any scroll bars
+ _document2['default'].documentElement.style.overflow = 'hidden';
+
+ // Apply fullscreen styles
+ Dom.addElClass(_document2['default'].body, 'vjs-full-window');
+
+ /**
+ * @event Player#enterFullWindow
+ * @type {EventTarget~Event}
+ */
+ this.trigger('enterFullWindow');
+ };
+
+ /**
+ * Check for call to either exit full window or
+ * full screen on ESC key
+ *
+ * @param {string} event
+ * Event to check for key press
+ */
+
+
+ Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
+ if (event.keyCode === 27) {
+ if (this.isFullscreen() === true) {
+ this.exitFullscreen();
+ } else {
+ this.exitFullWindow();
+ }
+ }
+ };
+
+ /**
+ * Exit full window
+ *
+ * @fires Player#exitFullWindow
+ */
+
+
+ Player.prototype.exitFullWindow = function exitFullWindow() {
+ this.isFullWindow = false;
+ Events.off(_document2['default'], 'keydown', this.fullWindowOnEscKey);
+
+ // Unhide scroll bars.
+ _document2['default'].documentElement.style.overflow = this.docOrigOverflow;
+
+ // Remove fullscreen styles
+ Dom.removeElClass(_document2['default'].body, 'vjs-full-window');
+
+ // Resize the box, controller, and poster to original sizes
+ // this.positionAll();
+ /**
+ * @event Player#exitFullWindow
+ * @type {EventTarget~Event}
+ */
+ this.trigger('exitFullWindow');
+ };
+
+ /**
+ * Check whether the player can play a given mimetype
+ *
+ * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
+ *
+ * @param {string} type
+ * The mimetype to check
+ *
+ * @return {string}
+ * 'probably', 'maybe', or '' (empty string)
+ */
+
+
+ Player.prototype.canPlayType = function canPlayType(type) {
+ var can = void 0;
+
+ // Loop through each playback technology in the options order
+ for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
+ var techName = (0, _toTitleCase2['default'])(j[i]);
+ var tech = _tech2['default'].getTech(techName);
+
+ // Support old behavior of techs being registered as components.
+ // Remove once that deprecated behavior is removed.
+ if (!tech) {
+ tech = _component2['default'].getComponent(techName);
+ }
+
+ // Check if the current tech is defined before continuing
+ if (!tech) {
+ _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
+ continue;
+ }
+
+ // Check if the browser supports this technology
+ if (tech.isSupported()) {
+ can = tech.canPlayType(type);
+
+ if (can) {
+ return can;
+ }
+ }
+ }
+
+ return '';
+ };
+
+ /**
+ * Select source based on tech-order or source-order
+ * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
+ * defaults to tech-order selection
+ *
+ * @param {Array} sources
+ * The sources for a media asset
+ *
+ * @return {Object|boolean}
+ * Object of source and tech order or false
+ */
+
+
+ Player.prototype.selectSource = function selectSource(sources) {
+ var _this4 = this;
+
+ // Get only the techs specified in `techOrder` that exist and are supported by the
+ // current platform
+ var techs = this.options_.techOrder.map(_toTitleCase2['default']).map(function (techName) {
+ // `Component.getComponent(...)` is for support of old behavior of techs
+ // being registered as components.
+ // Remove once that deprecated behavior is removed.
+ return [techName, _tech2['default'].getTech(techName) || _component2['default'].getComponent(techName)];
+ }).filter(function (_ref) {
+ var techName = _ref[0],
+ tech = _ref[1];
+
+ // Check if the current tech is defined before continuing
+ if (tech) {
+ // Check if the browser supports this technology
+ return tech.isSupported();
+ }
+
+ _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
+ return false;
+ });
+
+ // Iterate over each `innerArray` element once per `outerArray` element and execute
+ // `tester` with both. If `tester` returns a non-falsy value, exit early and return
+ // that value.
+ var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
+ var found = void 0;
+
+ outerArray.some(function (outerChoice) {
+ return innerArray.some(function (innerChoice) {
+ found = tester(outerChoice, innerChoice);
+
+ if (found) {
+ return true;
+ }
+ });
+ });
+
+ return found;
+ };
+
+ var foundSourceAndTech = void 0;
+ var flip = function flip(fn) {
+ return function (a, b) {
+ return fn(b, a);
+ };
+ };
+ var finder = function finder(_ref2, source) {
+ var techName = _ref2[0],
+ tech = _ref2[1];
+
+ if (tech.canPlaySource(source, _this4.options_[techName.toLowerCase()])) {
+ return { source: source, tech: techName };
+ }
+ };
+
+ // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
+ // to select from them based on their priority.
+ if (this.options_.sourceOrder) {
+ // Source-first ordering
+ foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
+ } else {
+ // Tech-first ordering
+ foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
+ }
+
+ return foundSourceAndTech || false;
+ };
+
+ /**
+ * The source function updates the video source
+ * There are three types of variables you can pass as the argument.
+ * **URL string**: A URL to the the video file. Use this method if you are sure
+ * the current playback technology (HTML5/Flash) can support the source you
+ * provide. Currently only MP4 files can be used in both HTML5 and Flash.
+ *
+ * @param {Tech~SourceObject|Tech~SourceObject[]} [source]
+ * One SourceObject or an array of SourceObjects
+ *
+ * @return {string|Player}
+ * - The current video source when getting
+ * - The player when setting
+ */
+
+
+ Player.prototype.src = function src(source) {
+ if (source === undefined) {
+ return this.techGet_('src');
+ }
+
+ var currentTech = _tech2['default'].getTech(this.techName_);
+
+ // Support old behavior of techs being registered as components.
+ // Remove once that deprecated behavior is removed.
+ if (!currentTech) {
+ currentTech = _component2['default'].getComponent(this.techName_);
+ }
+
+ // case: Array of source objects to choose from and pick the best to play
+ if (Array.isArray(source)) {
+ this.sourceList_(source);
+
+ // case: URL String (http://myvideo...)
+ } else if (typeof source === 'string') {
+ // create a source object from the string
+ this.src({ src: source });
+
+ // case: Source object { src: '', type: '' ... }
+ } else if (source instanceof Object) {
+ // check if the source has a type and the loaded tech cannot play the source
+ // if there's no type we'll just try the current tech
+ if (source.type && !currentTech.canPlaySource(source, this.options_[this.techName_.toLowerCase()])) {
+ // create a source list with the current source and send through
+ // the tech loop to check for a compatible technology
+ this.sourceList_([source]);
+ } else {
+ this.cache_.sources = null;
+ this.cache_.source = source;
+ this.cache_.src = source.src;
+
+ this.currentType_ = source.type || '';
+
+ // wait until the tech is ready to set the source
+ this.ready(function () {
+
+ // The setSource tech method was added with source handlers
+ // so older techs won't support it
+ // We need to check the direct prototype for the case where subclasses
+ // of the tech do not support source handlers
+ if (currentTech.prototype.hasOwnProperty('setSource')) {
+ this.techCall_('setSource', source);
+ } else {
+ this.techCall_('src', source.src);
+ }
+
+ if (this.options_.preload === 'auto') {
+ this.load();
+ }
+
+ if (this.options_.autoplay) {
+ this.play();
+ }
+
+ // Set the source synchronously if possible (#2326)
+ }, true);
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Handle an array of source objects
+ *
+ * @param {Tech~SourceObject[]} sources
+ * Array of source objects
+ *
+ * @private
+ */
+
+
+ Player.prototype.sourceList_ = function sourceList_(sources) {
+ var sourceTech = this.selectSource(sources);
+
+ if (sourceTech) {
+ if (sourceTech.tech === this.techName_) {
+ // if this technology is already loaded, set the source
+ this.src(sourceTech.source);
+ } else {
+ // load this technology with the chosen source
+ this.loadTech_(sourceTech.tech, sourceTech.source);
+ }
+
+ this.cache_.sources = sources;
+ } else {
+ // We need to wrap this in a timeout to give folks a chance to add error event handlers
+ this.setTimeout(function () {
+ this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
+ }, 0);
+
+ // we could not find an appropriate tech, but let's still notify the delegate that this is it
+ // this needs a better comment about why this is needed
+ this.triggerReady();
+ }
+ };
+
+ /**
+ * Begin loading the src data.
+ *
+ * @return {Player}
+ * A reference to the player
+ */
+
+
+ Player.prototype.load = function load() {
+ this.techCall_('load');
+ return this;
+ };
+
+ /**
+ * Reset the player. Loads the first tech in the techOrder,
+ * and calls `reset` on the tech`.
+ *
+ * @return {Player}
+ * A reference to the player
+ */
+
+
+ Player.prototype.reset = function reset() {
+ this.loadTech_((0, _toTitleCase2['default'])(this.options_.techOrder[0]), null);
+ this.techCall_('reset');
+ return this;
+ };
+
+ /**
+ * Returns all of the current source objects.
+ *
+ * @return {Tech~SourceObject[]}
+ * The current source objects
+ */
+
+
+ Player.prototype.currentSources = function currentSources() {
+ var source = this.currentSource();
+ var sources = [];
+
+ // assume `{}` or `{ src }`
+ if (Object.keys(source).length !== 0) {
+ sources.push(source);
+ }
+
+ return this.cache_.sources || sources;
+ };
+
+ /**
+ * Returns the current source object.
+ *
+ * @return {Tech~SourceObject}
+ * The current source object
+ */
+
+
+ Player.prototype.currentSource = function currentSource() {
+ var source = {};
+ var src = this.currentSrc();
+
+ if (src) {
+ source.src = src;
+ }
+
+ return this.cache_.source || source;
+ };
+
+ /**
+ * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
+ * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
+ *
+ * @return {string}
+ * The current source
+ */
+
+
+ Player.prototype.currentSrc = function currentSrc() {
+ return this.techGet_('currentSrc') || this.cache_.src || '';
+ };
+
+ /**
+ * Get the current source type e.g. video/mp4
+ * This can allow you rebuild the current source object so that you could load the same
+ * source and tech later
+ *
+ * @return {string}
+ * The source MIME type
+ */
+
+
+ Player.prototype.currentType = function currentType() {
+ return this.currentType_ || '';
+ };
+
+ /**
+ * Get or set the preload attribute
+ *
+ * @param {boolean} [value]
+ * - true means that we should preload
+ * - false maens that we should not preload
+ *
+ * @return {string|Player}
+ * - the preload attribute value when getting
+ * - the player when setting
+ */
+
+
+ Player.prototype.preload = function preload(value) {
+ if (value !== undefined) {
+ this.techCall_('setPreload', value);
+ this.options_.preload = value;
+ return this;
+ }
+ return this.techGet_('preload');
+ };
+
+ /**
+ * Get or set the autoplay attribute.
+ *
+ * @param {boolean} [value]
+ * - true means that we should autoplay
+ * - false maens that we should not autoplay
+ *
+ * @return {string|Player}
+ * - the current value of autoplay
+ * - the player when setting
+ */
+
+
+ Player.prototype.autoplay = function autoplay(value) {
+ if (value !== undefined) {
+ this.techCall_('setAutoplay', value);
+ this.options_.autoplay = value;
+ return this;
+ }
+ return this.techGet_('autoplay', value);
+ };
+
+ /**
+ * Get or set the loop attribute on the video element.
+ *
+ * @param {boolean} [value]
+ * - true means that we should loop the video
+ * - false means that we should not loop the video
+ *
+ * @return {string|Player}
+ * - the current value of loop when getting
+ * - the player when setting
+ */
+
+
+ Player.prototype.loop = function loop(value) {
+ if (value !== undefined) {
+ this.techCall_('setLoop', value);
+ this.options_.loop = value;
+ return this;
+ }
+ return this.techGet_('loop');
+ };
+
+ /**
+ * Get or set the poster image source url
+ *
+ * @fires Player#posterchange
+ *
+ * @param {string} [src]
+ * Poster image source URL
+ *
+ * @return {string|Player}
+ * - the current value of poster when getting
+ * - the player when setting
+ */
+
+
+ Player.prototype.poster = function poster(src) {
+ if (src === undefined) {
+ return this.poster_;
+ }
+
+ // The correct way to remove a poster is to set as an empty string
+ // other falsey values will throw errors
+ if (!src) {
+ src = '';
+ }
+
+ // update the internal poster variable
+ this.poster_ = src;
+
+ // update the tech's poster
+ this.techCall_('setPoster', src);
+
+ // alert components that the poster has been set
+ /**
+ * This event fires when the poster image is changed on the player.
+ *
+ * @event Player#posterchange
+ * @type {EventTarget~Event}
+ */
+ this.trigger('posterchange');
+
+ return this;
+ };
+
+ /**
+ * Some techs (e.g. YouTube) can provide a poster source in an
+ * asynchronous way. We want the poster component to use this
+ * poster source so that it covers up the tech's controls.
+ * (YouTube's play button). However we only want to use this
+ * soruce if the player user hasn't set a poster through
+ * the normal APIs.
+ *
+ * @fires Player#posterchange
+ * @listens Tech#posterchange
+ * @private
+ */
+
+
+ Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
+ if (!this.poster_ && this.tech_ && this.tech_.poster) {
+ this.poster_ = this.tech_.poster() || '';
+
+ // Let components know the poster has changed
+ this.trigger('posterchange');
+ }
+ };
+
+ /**
+ * Get or set whether or not the controls are showing.
+ *
+ * @fires Player#controlsenabled
+ *
+ * @param {boolean} [bool]
+ * - true to turn controls on
+ * - false to turn controls off
+ *
+ * @return {boolean|Player}
+ * - the current value of controls when getting
+ * - the player when setting
+ */
+
+
+ Player.prototype.controls = function controls(bool) {
+ if (bool !== undefined) {
+ bool = !!bool;
+
+ // Don't trigger a change event unless it actually changed
+ if (this.controls_ !== bool) {
+ this.controls_ = bool;
+
+ if (this.usingNativeControls()) {
+ this.techCall_('setControls', bool);
+ }
+
+ if (bool) {
+ this.removeClass('vjs-controls-disabled');
+ this.addClass('vjs-controls-enabled');
+ /**
+ * @event Player#controlsenabled
+ * @type {EventTarget~Event}
+ */
+ this.trigger('controlsenabled');
+
+ if (!this.usingNativeControls()) {
+ this.addTechControlsListeners_();
+ }
+ } else {
+ this.removeClass('vjs-controls-enabled');
+ this.addClass('vjs-controls-disabled');
+ /**
+ * @event Player#controlsdisabled
+ * @type {EventTarget~Event}
+ */
+ this.trigger('controlsdisabled');
+
+ if (!this.usingNativeControls()) {
+ this.removeTechControlsListeners_();
+ }
+ }
+ }
+ return this;
+ }
+ return !!this.controls_;
+ };
+
+ /**
+ * Toggle native controls on/off. Native controls are the controls built into
+ * devices (e.g. default iPhone controls), Flash, or other techs
+ * (e.g. Vimeo Controls)
+ * **This should only be set by the current tech, because only the tech knows
+ * if it can support native controls**
+ *
+ * @fires Player#usingnativecontrols
+ * @fires Player#usingcustomcontrols
+ *
+ * @param {boolean} [bool]
+ * - true to turn native controls on
+ * - false to turn native controls off
+ *
+ * @return {boolean|Player}
+ * - the current value of native controls when getting
+ * - the player when setting
+ */
+
+
+ Player.prototype.usingNativeControls = function usingNativeControls(bool) {
+ if (bool !== undefined) {
+ bool = !!bool;
+
+ // Don't trigger a change event unless it actually changed
+ if (this.usingNativeControls_ !== bool) {
+ this.usingNativeControls_ = bool;
+ if (bool) {
+ this.addClass('vjs-using-native-controls');
+
+ /**
+ * player is using the native device controls
+ *
+ * @event Player#usingnativecontrols
+ * @type {EventTarget~Event}
+ */
+ this.trigger('usingnativecontrols');
+ } else {
+ this.removeClass('vjs-using-native-controls');
+
+ /**
+ * player is using the custom HTML controls
+ *
+ * @event Player#usingcustomcontrols
+ * @type {EventTarget~Event}
+ */
+ this.trigger('usingcustomcontrols');
+ }
+ }
+ return this;
+ }
+ return !!this.usingNativeControls_;
+ };
+
+ /**
+ * Set or get the current MediaError
+ *
+ * @fires Player#error
+ *
+ * @param {MediaError|string|number} [err]
+ * A MediaError or a string/number to be turned
+ * into a MediaError
+ *
+ * @return {MediaError|null|Player}
+ * - The current MediaError when getting (or null)
+ * - The player when setting
+ */
+
+
+ Player.prototype.error = function error(err) {
+ if (err === undefined) {
+ return this.error_ || null;
+ }
+
+ // restoring to default
+ if (err === null) {
+ this.error_ = err;
+ this.removeClass('vjs-error');
+ if (this.errorDisplay) {
+ this.errorDisplay.close();
+ }
+ return this;
+ }
+
+ this.error_ = new _mediaError2['default'](err);
+
+ // add the vjs-error classname to the player
+ this.addClass('vjs-error');
+
+ // log the name of the error type and any message
+ // ie8 just logs "[object object]" if you just log the error object
+ _log2['default'].error('(CODE:' + this.error_.code + ' ' + _mediaError2['default'].errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
+
+ /**
+ * @event Player#error
+ * @type {EventTarget~Event}
+ */
+ this.trigger('error');
+
+ return this;
+ };
+
+ /**
+ * Report user activity
+ *
+ * @param {Object} event
+ * Event object
+ */
+
+
+ Player.prototype.reportUserActivity = function reportUserActivity(event) {
+ this.userActivity_ = true;
+ };
+
+ /**
+ * Get/set if user is active
+ *
+ * @fires Player#useractive
+ * @fires Player#userinactive
+ *
+ * @param {boolean} [bool]
+ * - true if the user is active
+ * - false if the user is inactive
+ * @return {boolean|Player}
+ * - the current value of userActive when getting
+ * - the player when setting
+ */
+
+
+ Player.prototype.userActive = function userActive(bool) {
+ if (bool !== undefined) {
+ bool = !!bool;
+ if (bool !== this.userActive_) {
+ this.userActive_ = bool;
+ if (bool) {
+ // If the user was inactive and is now active we want to reset the
+ // inactivity timer
+ this.userActivity_ = true;
+ this.removeClass('vjs-user-inactive');
+ this.addClass('vjs-user-active');
+ /**
+ * @event Player#useractive
+ * @type {EventTarget~Event}
+ */
+ this.trigger('useractive');
+ } else {
+ // We're switching the state to inactive manually, so erase any other
+ // activity
+ this.userActivity_ = false;
+
+ // Chrome/Safari/IE have bugs where when you change the cursor it can
+ // trigger a mousemove event. This causes an issue when you're hiding
+ // the cursor when the user is inactive, and a mousemove signals user
+ // activity. Making it impossible to go into inactive mode. Specifically
+ // this happens in fullscreen when we really need to hide the cursor.
+ //
+ // When this gets resolved in ALL browsers it can be removed
+ // https://code.google.com/p/chromium/issues/detail?id=103041
+ if (this.tech_) {
+ this.tech_.one('mousemove', function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ });
+ }
+
+ this.removeClass('vjs-user-active');
+ this.addClass('vjs-user-inactive');
+ /**
+ * @event Player#userinactive
+ * @type {EventTarget~Event}
+ */
+ this.trigger('userinactive');
+ }
+ }
+ return this;
+ }
+ return this.userActive_;
+ };
+
+ /**
+ * Listen for user activity based on timeout value
+ *
+ * @private
+ */
+
+
+ Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
+ var mouseInProgress = void 0;
+ var lastMoveX = void 0;
+ var lastMoveY = void 0;
+ var handleActivity = Fn.bind(this, this.reportUserActivity);
+
+ var handleMouseMove = function handleMouseMove(e) {
+ // #1068 - Prevent mousemove spamming
+ // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
+ if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
+ lastMoveX = e.screenX;
+ lastMoveY = e.screenY;
+ handleActivity();
+ }
+ };
+
+ var handleMouseDown = function handleMouseDown() {
+ handleActivity();
+ // For as long as the they are touching the device or have their mouse down,
+ // we consider them active even if they're not moving their finger or mouse.
+ // So we want to continue to update that they are active
+ this.clearInterval(mouseInProgress);
+ // Setting userActivity=true now and setting the interval to the same time
+ // as the activityCheck interval (250) should ensure we never miss the
+ // next activityCheck
+ mouseInProgress = this.setInterval(handleActivity, 250);
+ };
+
+ var handleMouseUp = function handleMouseUp(event) {
+ handleActivity();
+ // Stop the interval that maintains activity if the mouse/touch is down
+ this.clearInterval(mouseInProgress);
+ };
+
+ // Any mouse movement will be considered user activity
+ this.on('mousedown', handleMouseDown);
+ this.on('mousemove', handleMouseMove);
+ this.on('mouseup', handleMouseUp);
+
+ // Listen for keyboard navigation
+ // Shouldn't need to use inProgress interval because of key repeat
+ this.on('keydown', handleActivity);
+ this.on('keyup', handleActivity);
+
+ // Run an interval every 250 milliseconds instead of stuffing everything into
+ // the mousemove/touchmove function itself, to prevent performance degradation.
+ // `this.reportUserActivity` simply sets this.userActivity_ to true, which
+ // then gets picked up by this loop
+ // http://ejohn.org/blog/learning-from-twitter/
+ var inactivityTimeout = void 0;
+
+ this.setInterval(function () {
+ // Check to see if mouse/touch activity has happened
+ if (this.userActivity_) {
+ // Reset the activity tracker
+ this.userActivity_ = false;
+
+ // If the user state was inactive, set the state to active
+ this.userActive(true);
+
+ // Clear any existing inactivity timeout to start the timer over
+ this.clearTimeout(inactivityTimeout);
+
+ var timeout = this.options_.inactivityTimeout;
+
+ if (timeout > 0) {
+ // In <timeout> milliseconds, if no more activity has occurred the
+ // user will be considered inactive
+ inactivityTimeout = this.setTimeout(function () {
+ // Protect against the case where the inactivityTimeout can trigger just
+ // before the next user activity is picked up by the activity check loop
+ // causing a flicker
+ if (!this.userActivity_) {
+ this.userActive(false);
+ }
+ }, timeout);
+ }
+ }
+ }, 250);
+ };
+
+ /**
+ * Gets or sets the current playback rate. A playback rate of
+ * 1.0 represents normal speed and 0.5 would indicate half-speed
+ * playback, for instance.
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
+ *
+ * @param {number} [rate]
+ * New playback rate to set.
+ *
+ * @return {number|Player}
+ * - The current playback rate when getting or 1.0
+ * - the player when setting
+ */
+
+
+ Player.prototype.playbackRate = function playbackRate(rate) {
+ if (rate !== undefined) {
+ this.techCall_('setPlaybackRate', rate);
+ return this;
+ }
+
+ if (this.tech_ && this.tech_.featuresPlaybackRate) {
+ return this.techGet_('playbackRate');
+ }
+ return 1.0;
+ };
+
+ /**
+ * Gets or sets the audio flag
+ *
+ * @param {boolean} bool
+ * - true signals that this is an audio player
+ * - false signals that this is not an audio player
+ *
+ * @return {Player|boolean}
+ * - the current value of isAudio when getting
+ * - the player if setting
+ */
+
+
+ Player.prototype.isAudio = function isAudio(bool) {
+ if (bool !== undefined) {
+ this.isAudio_ = !!bool;
+ return this;
+ }
+
+ return !!this.isAudio_;
+ };
+
+ /**
+ * Get the {@link VideoTrackList}
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
+ *
+ * @return {VideoTrackList}
+ * the current video track list
+ */
+
+
+ Player.prototype.videoTracks = function videoTracks() {
+ // if we have not yet loadTech_, we create videoTracks_
+ // these will be passed to the tech during loading
+ if (!this.tech_) {
+ this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default']();
+ return this.videoTracks_;
+ }
+
+ return this.tech_.videoTracks();
+ };
+
+ /**
+ * Get the {@link AudioTrackList}
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
+ *
+ * @return {AudioTrackList}
+ * the current audio track list
+ */
+
+
+ Player.prototype.audioTracks = function audioTracks() {
+ // if we have not yet loadTech_, we create videoTracks_
+ // these will be passed to the tech during loading
+ if (!this.tech_) {
+ this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default']();
+ return this.audioTracks_;
+ }
+
+ return this.tech_.audioTracks();
+ };
+
+ /**
+ * Get the {@link TextTrackList}
+ *
+ * Text tracks are tracks of timed text events.
+ * - Captions: text displayed over the video
+ * for the hearing impaired
+ * - Subtitles: text displayed over the video for
+ * those who don't understand language in the video
+ * - Chapters: text displayed in a menu allowing the user to jump
+ * to particular points (chapters) in the video
+ * - Descriptions: (not yet implemented) audio descriptions that are read back to
+ * the user by a screen reading device
+ *
+ * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
+ *
+ * @return {TextTrackList|undefined}
+ * The current TextTrackList or undefined if
+ * or undefined if we don't have a tech
+ */
+
+
+ Player.prototype.textTracks = function textTracks() {
+ // cannot use techGet_ directly because it checks to see whether the tech is ready.
+ // Flash is unlikely to be ready in time but textTracks should still work.
+ if (this.tech_) {
+ return this.tech_.textTracks();
+ }
+ };
+
+ /**
+ * Get the "remote" {@link TextTrackList}. Remote Text Tracks
+ * are tracks that were added to the HTML video element and can
+ * be removed, whereas normal texttracks cannot be removed.
+ *
+ *
+ * @return {TextTrackList|undefined}
+ * The current remote text track list or undefined
+ * if we don't have a tech
+ */
+
+
+ Player.prototype.remoteTextTracks = function remoteTextTracks() {
+ if (this.tech_) {
+ return this.tech_.remoteTextTracks();
+ }
+ };
+
+ /**
+ * Get the "remote" {@link HTMLTrackElementList}.
+ * This gives the user all of the DOM elements that match up
+ * with the remote {@link TextTrackList}.
+ *
+ * @return {HTMLTrackElementList}
+ * The current remote text track list elements
+ * or undefined if we don't have a tech
+ */
+
+
+ Player.prototype.remoteTextTrackEls = function remoteTextTrackEls() {
+ if (this.tech_) {
+ return this.tech_.remoteTextTrackEls();
+ }
+ };
+
+ /**
+ * A helper method for adding a {@link TextTrack} to our
+ * {@link TextTrackList}.
+ *
+ * In addition to the W3C settings we allow adding additional info through options.
+ *
+ * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
+ *
+ * @param {string} [kind]
+ * the kind of TextTrack you are adding
+ *
+ * @param {string} [label]
+ * the label to give the TextTrack label
+ *
+ * @param {string} [language]
+ * the language to set on the TextTrack
+ *
+ * @return {TextTrack|undefined}
+ * the TextTrack that was added or undefined
+ * if there is no tech
+ */
+
+
+ Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
+ if (this.tech_) {
+ return this.tech_.addTextTrack(kind, label, language);
+ }
+ };
+
+ /**
+ * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
+ * automatically removed from the video element whenever the source changes, unless
+ * manualCleanup is set to false.
+ *
+ * @param {Object} options
+ * Options to pass to {@link HTMLTrackElement} during creation. See
+ * {@link HTMLTrackElement} for object properties that you should use.
+ *
+ * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
+ *
+ * @return {HTMLTrackElement}
+ * the HTMLTrackElement that was created and added
+ * to the HTMLTrackElementList and the remote
+ * TextTrackList
+ *
+ * @deprecated The default value of the "manualCleanup" parameter will default
+ * to "false" in upcoming versions of Video.js
+ */
+
+
+ Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
+ if (this.tech_) {
+ return this.tech_.addRemoteTextTrack(options, manualCleanup);
+ }
+ };
+
+ /**
+ * Remove a remote {@link TextTrack} from the respective
+ * {@link TextTrackList} and {@link HTMLTrackElementList}.
+ *
+ * @param {Object} track
+ * Remote {@link TextTrack} to remove
+ *
+ * @return {undefined}
+ * does not return anything
+ */
+
+
+ Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
+ var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
+ _ref3$track = _ref3.track,
+ track = _ref3$track === undefined ? arguments[0] : _ref3$track;
+
+ // destructure the input into an object with a track argument, defaulting to arguments[0]
+ // default the whole argument to an empty object if nothing was passed in
+
+ if (this.tech_) {
+ return this.tech_.removeRemoteTextTrack(track);
+ }
+ };
+
+ /**
+ * Get video width
+ *
+ * @return {number}
+ * current video width
+ */
+
+
+ Player.prototype.videoWidth = function videoWidth() {
+ return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
+ };
+
+ /**
+ * Get video height
+ *
+ * @return {number}
+ * current video height
+ */
+
+
+ Player.prototype.videoHeight = function videoHeight() {
+ return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
+ };
+
+ // Methods to add support for
+ // initialTime: function() { return this.techCall_('initialTime'); },
+ // startOffsetTime: function() { return this.techCall_('startOffsetTime'); },
+ // played: function() { return this.techCall_('played'); },
+ // defaultPlaybackRate: function() { return this.techCall_('defaultPlaybackRate'); },
+ // defaultMuted: function() { return this.techCall_('defaultMuted'); }
+
+ /**
+ * The player's language code
+ * NOTE: The language should be set in the player options if you want the
+ * the controls to be built with a specific language. Changing the lanugage
+ * later will not update controls text.
+ *
+ * @param {string} [code]
+ * the language code to set the player to
+ *
+ * @return {string|Player}
+ * - The current language code when getting
+ * - A reference to the player when setting
+ */
+
+
+ Player.prototype.language = function language(code) {
+ if (code === undefined) {
+ return this.language_;
+ }
+
+ this.language_ = String(code).toLowerCase();
+ return this;
+ };
+
+ /**
+ * Get the player's language dictionary
+ * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
+ * Languages specified directly in the player options have precedence
+ *
+ * @return {Array}
+ * An array of of supported languages
+ */
+
+
+ Player.prototype.languages = function languages() {
+ return (0, _mergeOptions2['default'])(Player.prototype.options_.languages, this.languages_);
+ };
+
+ /**
+ * returns a JavaScript object reperesenting the current track
+ * information. **DOES not return it as JSON**
+ *
+ * @return {Object}
+ * Object representing the current of track info
+ */
+
+
+ Player.prototype.toJSON = function toJSON() {
+ var options = (0, _mergeOptions2['default'])(this.options_);
+ var tracks = options.tracks;
+
+ options.tracks = [];
+
+ for (var i = 0; i < tracks.length; i++) {
+ var track = tracks[i];
+
+ // deep merge tracks and null out player so no circular references
+ track = (0, _mergeOptions2['default'])(track);
+ track.player = undefined;
+ options.tracks[i] = track;
+ }
+
+ return options;
+ };
+
+ /**
+ * Creates a simple modal dialog (an instance of the {@link ModalDialog}
+ * component) that immediately overlays the player with arbitrary
+ * content and removes itself when closed.
+ *
+ * @param {string|Function|Element|Array|null} content
+ * Same as {@link ModalDialog#content}'s param of the same name.
+ * The most straight-forward usage is to provide a string or DOM
+ * element.
+ *
+ * @param {Object} [options]
+ * Extra options which will be passed on to the {@link ModalDialog}.
+ *
+ * @return {ModalDialog}
+ * the {@link ModalDialog} that was created
+ */
+
+
+ Player.prototype.createModal = function createModal(content, options) {
+ var _this5 = this;
+
+ options = options || {};
+ options.content = content || '';
+
+ var modal = new _modalDialog2['default'](this, options);
+
+ this.addChild(modal);
+ modal.on('dispose', function () {
+ _this5.removeChild(modal);
+ });
+
+ return modal.open();
+ };
+
+ /**
+ * Gets tag settings
+ *
+ * @param {Element} tag
+ * The player tag
+ *
+ * @return {Object}
+ * An object containing all of the settings
+ * for a player tag
+ */
+
+
+ Player.getTagSettings = function getTagSettings(tag) {
+ var baseOptions = {
+ sources: [],
+ tracks: []
+ };
+
+ var tagOptions = Dom.getElAttributes(tag);
+ var dataSetup = tagOptions['data-setup'];
+
+ if (Dom.hasElClass(tag, 'vjs-fluid')) {
+ tagOptions.fluid = true;
+ }
+
+ // Check if data-setup attr exists.
+ if (dataSetup !== null) {
+ // Parse options JSON
+ // If empty string, make it a parsable json object.
+ var _safeParseTuple = (0, _tuple2['default'])(dataSetup || '{}'),
+ err = _safeParseTuple[0],
+ data = _safeParseTuple[1];
+
+ if (err) {
+ _log2['default'].error(err);
+ }
+ (0, _obj.assign)(tagOptions, data);
+ }
+
+ (0, _obj.assign)(baseOptions, tagOptions);
+
+ // Get tag children settings
+ if (tag.hasChildNodes()) {
+ var children = tag.childNodes;
+
+ for (var i = 0, j = children.length; i < j; i++) {
+ var child = children[i];
+ // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
+ var childName = child.nodeName.toLowerCase();
+
+ if (childName === 'source') {
+ baseOptions.sources.push(Dom.getElAttributes(child));
+ } else if (childName === 'track') {
+ baseOptions.tracks.push(Dom.getElAttributes(child));
+ }
+ }
+ }
+
+ return baseOptions;
+ };
+
+ /**
+ * Determine wether or not flexbox is supported
+ *
+ * @return {boolean}
+ * - true if flexbox is supported
+ * - false if flexbox is not supported
+ */
+
+
+ Player.prototype.flexNotSupported_ = function flexNotSupported_() {
+ var elem = _document2['default'].createElement('i');
+
+ // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
+ // common flex features that we can rely on when checking for flex support.
+ return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
+ // IE10-specific (2012 flex spec)
+ 'msFlexOrder' in elem.style);
+ };
+
+ return Player;
+}(_component2['default']);
+
+/**
+ * Global player list
+ *
+ * @type {Object}
+ */
+
+
+Player.players = {};
+
+var navigator = _window2['default'].navigator;
+
+/*
+ * Player instance options, surfaced using options
+ * options = Player.prototype.options_
+ * Make changes in options, not here.
+ *
+ * @type {Object}
+ * @private
+ */
+Player.prototype.options_ = {
+ // Default order of fallback technology
+ techOrder: ['html5', 'flash'],
+ // techOrder: ['flash','html5'],
+
+ html5: {},
+ flash: {},
+
+ // defaultVolume: 0.85,
+ defaultVolume: 0.00,
+
+ // default inactivity timeout
+ inactivityTimeout: 2000,
+
+ // default playback rates
+ playbackRates: [],
+ // Add playback rate selection by adding rates
+ // 'playbackRates': [0.5, 1, 1.5, 2],
+
+ // Included control sets
+ children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
+
+ language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
+
+ // locales and their language translations
+ languages: {},
+
+ // Default message to show when a video cannot be played.
+ notSupportedMessage: 'No compatible source was found for this media.'
+};
+
+[
+/**
+ * Returns whether or not the player is in the "ended" state.
+ *
+ * @return {Boolean} True if the player is in the ended state, false if not.
+ * @method Player#ended
+ */
+'ended',
+/**
+ * Returns whether or not the player is in the "seeking" state.
+ *
+ * @return {Boolean} True if the player is in the seeking state, false if not.
+ * @method Player#seeking
+ */
+'seeking',
+/**
+ * Returns the TimeRanges of the media that are currently available
+ * for seeking to.
+ *
+ * @return {TimeRanges} the seekable intervals of the media timeline
+ * @method Player#seekable
+ */
+'seekable',
+/**
+ * Returns the current state of network activity for the element, from
+ * the codes in the list below.
+ * - NETWORK_EMPTY (numeric value 0)
+ * The element has not yet been initialised. All attributes are in
+ * their initial states.
+ * - NETWORK_IDLE (numeric value 1)
+ * The element's resource selection algorithm is active and has
+ * selected a resource, but it is not actually using the network at
+ * this time.
+ * - NETWORK_LOADING (numeric value 2)
+ * The user agent is actively trying to download data.
+ * - NETWORK_NO_SOURCE (numeric value 3)
+ * The element's resource selection algorithm is active, but it has
+ * not yet found a resource to use.
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
+ * @return {number} the current network activity state
+ * @method Player#networkState
+ */
+'networkState',
+/**
+ * Returns a value that expresses the current state of the element
+ * with respect to rendering the current playback position, from the
+ * codes in the list below.
+ * - HAVE_NOTHING (numeric value 0)
+ * No information regarding the media resource is available.
+ * - HAVE_METADATA (numeric value 1)
+ * Enough of the resource has been obtained that the duration of the
+ * resource is available.
+ * - HAVE_CURRENT_DATA (numeric value 2)
+ * Data for the immediate current playback position is available.
+ * - HAVE_FUTURE_DATA (numeric value 3)
+ * Data for the immediate current playback position is available, as
+ * well as enough data for the user agent to advance the current
+ * playback position in the direction of playback.
+ * - HAVE_ENOUGH_DATA (numeric value 4)
+ * The user agent estimates that enough data is available for
+ * playback to proceed uninterrupted.
+ *
+ * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
+ * @return {number} the current playback rendering state
+ * @method Player#readyState
+ */
+'readyState'].forEach(function (fn) {
+ Player.prototype[fn] = function () {
+ return this.techGet_(fn);
+ };
+});
+
+TECH_EVENTS_RETRIGGER.forEach(function (event) {
+ Player.prototype['handleTech' + (0, _toTitleCase2['default'])(event) + '_'] = function () {
+ return this.trigger(event);
+ };
+});
+
+/**
+ * Fired when the player has initial duration and dimension information
+ *
+ * @event Player#loadedmetadata
+ * @type {EventTarget~Event}
+ */
+
+/**
+ * Fired when the player has downloaded data at the current playback position
+ *
+ * @event Player#loadeddata
+ * @type {EventTarget~Event}
+ */
+
+/**
+ * Fired when the current playback position has changed *
+ * During playback this is fired every 15-250 milliseconds, depending on the
+ * playback technology in use.
+ *
+ * @event Player#timeupdate
+ * @type {EventTarget~Event}
+ */
+
+/**
+ * Fired when the volume changes
+ *
+ * @event Player#volumechange
+ * @type {EventTarget~Event}
+ */
+
+_component2['default'].registerComponent('Player', Player);
+exports['default'] = Player;