3 exports.__esModule = true;
5 var _component = require('./component.js');
7 var _component2 = _interopRequireDefault(_component);
9 var _document = require('global/document');
11 var _document2 = _interopRequireDefault(_document);
13 var _window = require('global/window');
15 var _window2 = _interopRequireDefault(_window);
17 var _events = require('./utils/events.js');
19 var Events = _interopRequireWildcard(_events);
21 var _dom = require('./utils/dom.js');
23 var Dom = _interopRequireWildcard(_dom);
25 var _fn = require('./utils/fn.js');
27 var Fn = _interopRequireWildcard(_fn);
29 var _guid = require('./utils/guid.js');
31 var Guid = _interopRequireWildcard(_guid);
33 var _browser = require('./utils/browser.js');
35 var browser = _interopRequireWildcard(_browser);
37 var _log = require('./utils/log.js');
39 var _log2 = _interopRequireDefault(_log);
41 var _toTitleCase = require('./utils/to-title-case.js');
43 var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
45 var _timeRanges = require('./utils/time-ranges.js');
47 var _buffer = require('./utils/buffer.js');
49 var _stylesheet = require('./utils/stylesheet.js');
51 var stylesheet = _interopRequireWildcard(_stylesheet);
53 var _fullscreenApi = require('./fullscreen-api.js');
55 var _fullscreenApi2 = _interopRequireDefault(_fullscreenApi);
57 var _mediaError = require('./media-error.js');
59 var _mediaError2 = _interopRequireDefault(_mediaError);
61 var _tuple = require('safe-json-parse/tuple');
63 var _tuple2 = _interopRequireDefault(_tuple);
65 var _obj = require('./utils/obj');
67 var _mergeOptions = require('./utils/merge-options.js');
69 var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
71 var _textTrackListConverter = require('./tracks/text-track-list-converter.js');
73 var _textTrackListConverter2 = _interopRequireDefault(_textTrackListConverter);
75 var _modalDialog = require('./modal-dialog');
77 var _modalDialog2 = _interopRequireDefault(_modalDialog);
79 var _tech = require('./tech/tech.js');
81 var _tech2 = _interopRequireDefault(_tech);
83 var _audioTrackList = require('./tracks/audio-track-list.js');
85 var _audioTrackList2 = _interopRequireDefault(_audioTrackList);
87 var _videoTrackList = require('./tracks/video-track-list.js');
89 var _videoTrackList2 = _interopRequireDefault(_videoTrackList);
91 require('./tech/loader.js');
93 require('./tech/flash.js');
95 require('./poster-image.js');
97 require('./tracks/text-track-display.js');
99 require('./loading-spinner.js');
101 require('./big-play-button.js');
103 require('./close-button.js');
105 require('./control-bar/control-bar.js');
107 require('./error-display.js');
109 require('./tracks/text-track-settings.js');
111 require('./tech/html5.js');
113 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; } }
115 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
117 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
119 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; }
121 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; } /**
124 // Subclasses Component
127 // The following imports are used only to ensure that the corresponding modules
128 // are always included in the video.js package. Importing the modules will
129 // execute them and they will register themselves with video.js.
132 // Import Html5 tech, at least for disposing the original video tag.
135 // The following tech events are simply re-triggered
136 // on the player when they happen
137 var TECH_EVENTS_RETRIGGER = [
139 * Fired while the user agent is downloading media data.
141 * @event Player#progress
142 * @type {EventTarget~Event}
145 * Retrigger the `progress` event that was triggered by the {@link Tech}.
148 * @method Player#handleTechProgress_
149 * @fires Player#progress
150 * @listens Tech#progress
155 * Fires when the loading of an audio/video is aborted.
157 * @event Player#abort
158 * @type {EventTarget~Event}
161 * Retrigger the `abort` event that was triggered by the {@link Tech}.
164 * @method Player#handleTechAbort_
165 * @fires Player#abort
166 * @listens Tech#abort
171 * Fires when the browser is intentionally not getting media data.
173 * @event Player#suspend
174 * @type {EventTarget~Event}
177 * Retrigger the `suspend` event that was triggered by the {@link Tech}.
180 * @method Player#handleTechSuspend_
181 * @fires Player#suspend
182 * @listens Tech#suspend
187 * Fires when the current playlist is empty.
189 * @event Player#emptied
190 * @type {EventTarget~Event}
193 * Retrigger the `emptied` event that was triggered by the {@link Tech}.
196 * @method Player#handleTechEmptied_
197 * @fires Player#emptied
198 * @listens Tech#emptied
202 * Fires when the browser is trying to get media data, but data is not available.
204 * @event Player#stalled
205 * @type {EventTarget~Event}
208 * Retrigger the `stalled` event that was triggered by the {@link Tech}.
211 * @method Player#handleTechStalled_
212 * @fires Player#stalled
213 * @listens Tech#stalled
218 * Fires when the browser has loaded meta data for the audio/video.
220 * @event Player#loadedmetadata
221 * @type {EventTarget~Event}
224 * Retrigger the `stalled` event that was triggered by the {@link Tech}.
227 * @method Player#handleTechLoadedmetadata_
228 * @fires Player#loadedmetadata
229 * @listens Tech#loadedmetadata
234 * Fires when the browser has loaded the current frame of the audio/video.
236 * @event player#loadeddata
240 * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
243 * @method Player#handleTechLoaddeddata_
244 * @fires Player#loadeddata
245 * @listens Tech#loadeddata
250 * Fires when the current playback position has changed.
252 * @event player#timeupdate
256 * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
259 * @method Player#handleTechTimeUpdate_
260 * @fires Player#timeupdate
261 * @listens Tech#timeupdate
266 * Fires when the playing speed of the audio/video is changed
268 * @event player#ratechange
272 * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
275 * @method Player#handleTechRatechange_
276 * @fires Player#ratechange
277 * @listens Tech#ratechange
282 * Fires when the volume has been changed
284 * @event player#volumechange
288 * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
291 * @method Player#handleTechVolumechange_
292 * @fires Player#volumechange
293 * @listens Tech#volumechange
298 * Fires when the text track has been changed
300 * @event player#texttrackchange
304 * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
307 * @method Player#handleTechTexttrackchange_
308 * @fires Player#texttrackchange
309 * @listens Tech#texttrackchange
314 * An instance of the `Player` class is created when any of the Video.js setup methods
315 * are used to initialize a video.
317 * After an instance has been created it can be accessed globally in two ways:
318 * 1. By calling `videojs('example_video_1');`
319 * 2. By using it directly via `videojs.players.example_video_1;`
324 var Player = function (_Component) {
325 _inherits(Player, _Component);
328 * Create an instance of this class.
330 * @param {Element} tag
331 * The original video DOM element used for configuring options.
333 * @param {Object} [options]
334 * Object of option names and values.
336 * @param {Component~ReadyCallback} [ready]
337 * Ready callback function.
339 function Player(tag, options, ready) {
340 _classCallCheck(this, Player);
342 // Make sure tag ID exists
343 tag.id = tag.id || 'vjs_video_' + Guid.newGUID();
346 // The options argument overrides options set in the video tag
347 // which overrides globally set options.
348 // This latter part coincides with the load order
349 // (tag must exist before Player)
350 options = (0, _obj.assign)(Player.getTagSettings(tag), options);
352 // Delay the initialization of children because we need to set up
353 // player properties first, and can't use `this` before `super()`
354 options.initChildren = false;
356 // Same with creating the element
357 options.createEl = false;
359 // we don't want the player to report touch activity on itself
360 // see enableTouchActivity in Component
361 options.reportTouchActivity = false;
363 // If language is not set, get the closest lang attribute
364 if (!options.language) {
365 if (typeof tag.closest === 'function') {
366 var closest = tag.closest('[lang]');
369 options.language = closest.getAttribute('lang');
374 while (element && element.nodeType === 1) {
375 if (Dom.getElAttributes(element).hasOwnProperty('lang')) {
376 options.language = element.getAttribute('lang');
379 element = element.parentNode;
384 // Run base component initializing with new options
386 // if the global option object was accidentally blown away by
387 // someone, bail early with an informative error
388 var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready));
390 if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
391 throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
394 // Store the original tag used to set options
397 // Store the tag attributes used to restore html5 element
398 _this.tagAttributes = tag && Dom.getElAttributes(tag);
400 // Update current language
401 _this.language(_this.options_.language);
403 // Update Supported Languages
404 if (options.languages) {
405 // Normalise player option languages to lowercase
406 var languagesToLower = {};
408 Object.getOwnPropertyNames(options.languages).forEach(function (name) {
409 languagesToLower[name.toLowerCase()] = options.languages[name];
411 _this.languages_ = languagesToLower;
413 _this.languages_ = Player.prototype.options_.languages;
416 // Cache for video property values.
420 _this.poster_ = options.poster || '';
423 _this.controls_ = !!options.controls;
425 // Original tag settings stored in options
426 // now remove immediately so native controls don't flash.
427 // May be turned back on by HTML5 tech if nativeControlsForTouch is true
428 tag.controls = false;
431 * Store the internal state of scrubbing
434 * @return {Boolean} True if the user is scrubbing
436 _this.scrubbing_ = false;
438 _this.el_ = _this.createEl();
440 // We also want to pass the original player options to each component and plugin
441 // as well so they don't need to reach back into the player for options later.
442 // We also need to do another copy of this.options_ so we don't end up with
444 var playerOptionsCopy = (0, _mergeOptions2['default'])(_this.options_);
447 if (options.plugins) {
448 var plugins = options.plugins;
450 Object.getOwnPropertyNames(plugins).forEach(function (name) {
451 if (typeof this[name] === 'function') {
452 this[name](plugins[name]);
454 _log2['default'].error('Unable to find plugin:', name);
459 _this.options_.playerOptions = playerOptionsCopy;
461 _this.initChildren();
463 // Set isAudio based on whether or not an audio tag was used
464 _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
466 // Update controls className. Can't do this when the controls are initially
467 // set because the element doesn't exist yet.
468 if (_this.controls()) {
469 _this.addClass('vjs-controls-enabled');
471 _this.addClass('vjs-controls-disabled');
474 // Set ARIA label and region role depending on player type
475 _this.el_.setAttribute('role', 'region');
476 if (_this.isAudio()) {
477 _this.el_.setAttribute('aria-label', 'audio player');
479 _this.el_.setAttribute('aria-label', 'video player');
482 if (_this.isAudio()) {
483 _this.addClass('vjs-audio');
486 if (_this.flexNotSupported_()) {
487 _this.addClass('vjs-no-flex');
490 // TODO: Make this smarter. Toggle user state between touching/mousing
491 // using events, since devices can have both touch and mouse events.
492 // if (browser.TOUCH_ENABLED) {
493 // this.addClass('vjs-touch-enabled');
496 // iOS Safari has broken hover handling
497 if (!browser.IS_IOS) {
498 _this.addClass('vjs-workinghover');
501 // Make player easily findable by ID
502 Player.players[_this.id_] = _this;
504 // When the player is first initialized, trigger activity so components
505 // like the control bar show themselves if needed
506 _this.userActive(true);
507 _this.reportUserActivity();
508 _this.listenForUserActivity_();
510 _this.on('fullscreenchange', _this.handleFullscreenChange_);
511 _this.on('stageclick', _this.handleStageClick_);
516 * Destroys the video player and does any necessary cleanup.
518 * This is especially helpful if you are dynamically adding and removing videos
521 * @fires Player#dispose
525 Player.prototype.dispose = function dispose() {
527 * Called when the player is being disposed of.
529 * @event Player#dispose
530 * @type {EventTarget~Event}
532 this.trigger('dispose');
533 // prevent dispose from being called twice
536 if (this.styleEl_ && this.styleEl_.parentNode) {
537 this.styleEl_.parentNode.removeChild(this.styleEl_);
540 // Kill reference to this player
541 Player.players[this.id_] = null;
543 if (this.tag && this.tag.player) {
544 this.tag.player = null;
547 if (this.el_ && this.el_.player) {
548 this.el_.player = null;
552 this.tech_.dispose();
555 _Component.prototype.dispose.call(this);
559 * Create the `Player`'s DOM element.
562 * The DOM element that gets created.
566 Player.prototype.createEl = function createEl() {
569 var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
571 if (playerElIngest) {
572 el = this.el_ = tag.parentNode;
574 el = this.el_ = _Component.prototype.createEl.call(this, 'div');
577 // set tabindex to -1 so we could focus on the player element
578 tag.setAttribute('tabindex', '-1');
580 // Remove width/height attrs from tag so CSS can make it 100% width/height
581 tag.removeAttribute('width');
582 tag.removeAttribute('height');
584 // Copy over all the attributes from the tag, including ID and class
585 // ID will now reference player box, not the video tag
586 var attrs = Dom.getElAttributes(tag);
588 Object.getOwnPropertyNames(attrs).forEach(function (attr) {
589 // workaround so we don't totally break IE7
590 // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
591 if (attr === 'class') {
592 el.className += ' ' + attrs[attr];
594 el.setAttribute(attr, attrs[attr]);
598 // Update tag id/class for use as HTML5 playback tech
599 // Might think we should do this after embedding in container so .vjs-tech class
600 // doesn't flash 100% width/height, but class only applies with .video-js parent
601 tag.playerId = tag.id;
602 tag.id += '_html5_api';
603 tag.className = 'vjs-tech';
605 // Make player findable on elements
606 tag.player = el.player = this;
607 // Default state of video is paused
608 this.addClass('vjs-paused');
610 // Add a style element in the player that we'll use to set the width/height
611 // of the player in a way that's still overrideable by CSS, just like the
613 if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true) {
614 this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
615 var defaultsStyleEl = Dom.$('.vjs-styles-defaults');
616 var head = Dom.$('head');
618 head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
621 // Pass in the width/height/aspectRatio options which will update the style el
622 this.width(this.options_.width);
623 this.height(this.options_.height);
624 this.fluid(this.options_.fluid);
625 this.aspectRatio(this.options_.aspectRatio);
627 // Hide any links within the video/audio tag, because IE doesn't hide them completely.
628 var links = tag.getElementsByTagName('a');
630 for (var i = 0; i < links.length; i++) {
631 var linkEl = links.item(i);
633 Dom.addElClass(linkEl, 'vjs-hidden');
634 linkEl.setAttribute('hidden', 'hidden');
637 // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
638 // keep track of the original for later so we can know if the source originally failed
639 tag.initNetworkState_ = tag.networkState;
641 // Wrap video tag in div (el/box) container
642 if (tag.parentNode && !playerElIngest) {
643 tag.parentNode.insertBefore(el, tag);
646 // insert the tag as the first child of the player element
647 // then manually add it to the children array so that this.addChild
648 // will work properly for other components
650 // Breaks iPhone, fixed in HTML5 setup.
651 Dom.insertElFirst(tag, el);
652 this.children_.unshift(tag);
660 * A getter/setter for the `Player`'s width.
662 * @param {number} [value]
663 * The value to set the `Player's width to.
666 * The current width of the `Player`.
670 Player.prototype.width = function width(value) {
671 return this.dimension('width', value);
675 * A getter/setter for the `Player`'s height.
677 * @param {number} [value]
678 * The value to set the `Player's heigth to.
681 * The current heigth of the `Player`.
685 Player.prototype.height = function height(value) {
686 return this.dimension('height', value);
690 * A getter/setter for the `Player`'s width & height.
692 * @param {string} dimension
693 * This string can be:
697 * @param {number} [value]
698 * Value for dimension specified in the first argument.
700 * @return {Player|number}
701 * - Returns itself when setting; method can be chained.
702 * - The dimension arguments value when getting (width/height).
706 Player.prototype.dimension = function dimension(_dimension, value) {
707 var privDimension = _dimension + '_';
709 if (value === undefined) {
710 return this[privDimension] || 0;
714 // If an empty string is given, reset the dimension to be automatic
715 this[privDimension] = undefined;
717 var parsedVal = parseFloat(value);
719 if (isNaN(parsedVal)) {
720 _log2['default'].error('Improper value "' + value + '" supplied for for ' + _dimension);
724 this[privDimension] = parsedVal;
727 this.updateStyleEl_();
732 * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
734 * @param {boolean} [bool]
735 * - A value of true adds the class.
736 * - A value of false removes the class.
737 * - No value will toggle the fluid class.
739 * @return {boolean|undefined}
740 * - The value of fluid when getting.
741 * - `undefined` when setting.
745 Player.prototype.fluid = function fluid(bool) {
746 if (bool === undefined) {
747 return !!this.fluid_;
750 this.fluid_ = !!bool;
753 this.addClass('vjs-fluid');
755 this.removeClass('vjs-fluid');
758 this.updateStyleEl_();
762 * Get/Set the aspect ratio
764 * @param {string} [ratio]
765 * Aspect ratio for player
767 * @return {string|undefined}
768 * returns the current aspect ratio when getting
772 * A getter/setter for the `Player`'s aspect ratio.
774 * @param {string} [ratio]
775 * The value to set the `Player's aspect ratio to.
777 * @return {string|undefined}
778 * - The current aspect ratio of the `Player` when getting.
779 * - undefined when setting
783 Player.prototype.aspectRatio = function aspectRatio(ratio) {
784 if (ratio === undefined) {
785 return this.aspectRatio_;
788 // Check for width:height format
789 if (!/^\d+\:\d+$/.test(ratio)) {
790 throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
792 this.aspectRatio_ = ratio;
794 // We're assuming if you set an aspect ratio you want fluid mode,
795 // because in fixed mode you could calculate width and height yourself.
798 this.updateStyleEl_();
802 * Update styles of the `Player` element (height, width and aspect ratio).
805 * @listens Tech#loadedmetadata
809 Player.prototype.updateStyleEl_ = function updateStyleEl_() {
810 if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE === true) {
811 var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
812 var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
813 var techEl = this.tech_ && this.tech_.el();
817 techEl.width = _width;
820 techEl.height = _height;
829 var aspectRatio = void 0;
830 var idClass = void 0;
832 // The aspect ratio is either used directly or to calculate width and height.
833 if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
834 // Use any aspectRatio that's been specifically set
835 aspectRatio = this.aspectRatio_;
836 } else if (this.videoWidth() > 0) {
837 // Otherwise try to get the aspect ratio from the video metadata
838 aspectRatio = this.videoWidth() + ':' + this.videoHeight();
840 // Or use a default. The video element's is 2:1, but 16:9 is more common.
841 aspectRatio = '16:9';
844 // Get the ratio as a decimal we can use to calculate dimensions
845 var ratioParts = aspectRatio.split(':');
846 var ratioMultiplier = ratioParts[1] / ratioParts[0];
848 if (this.width_ !== undefined) {
849 // Use any width that's been specifically set
851 } else if (this.height_ !== undefined) {
852 // Or calulate the width from the aspect ratio if a height has been set
853 width = this.height_ / ratioMultiplier;
855 // Or use the video's metadata, or use the video el's default of 300
856 width = this.videoWidth() || 300;
859 if (this.height_ !== undefined) {
860 // Use any height that's been specifically set
861 height = this.height_;
863 // Otherwise calculate the height from the ratio and the width
864 height = width * ratioMultiplier;
867 // Ensure the CSS class is valid by starting with an alpha character
868 if (/^[^a-zA-Z]/.test(this.id())) {
869 idClass = 'dimensions-' + this.id();
871 idClass = this.id() + '-dimensions';
874 // Ensure the right class is still on the player for the style element
875 this.addClass(idClass);
877 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 ');
881 * Load/Create an instance of playback {@link Tech} including element
882 * and API methods. Then append the `Tech` element in `Player` as a child.
884 * @param {string} techName
885 * name of the playback technology
887 * @param {string} source
894 Player.prototype.loadTech_ = function loadTech_(techName, source) {
897 // Pause and remove current playback technology
902 // get rid of the HTML5 video tag as soon as we are using another tech
903 if (techName !== 'Html5' && this.tag) {
904 _tech2['default'].getTech('Html5').disposeMediaElement(this.tag);
905 this.tag.player = null;
909 this.techName_ = techName;
911 // Turn off API access because we're loading a new tech that might load asynchronously
912 this.isReady_ = false;
914 // Grab tech-specific options from player options and add source and parent element to use.
915 var techOptions = (0, _obj.assign)({
917 'nativeControlsForTouch': this.options_.nativeControlsForTouch,
918 'playerId': this.id(),
919 'techId': this.id() + '_' + techName + '_api',
920 'videoTracks': this.videoTracks_,
921 'textTracks': this.textTracks_,
922 'audioTracks': this.audioTracks_,
923 'autoplay': this.options_.autoplay,
924 'preload': this.options_.preload,
925 'loop': this.options_.loop,
926 'muted': this.options_.muted,
927 'poster': this.poster(),
928 'language': this.language(),
929 'playerElIngest': this.playerElIngest_ || false,
930 'vtt.js': this.options_['vtt.js']
931 }, this.options_[techName.toLowerCase()]);
934 techOptions.tag = this.tag;
938 this.currentType_ = source.type;
940 if (source.src === this.cache_.src && this.cache_.currentTime > 0) {
941 techOptions.startTime = this.cache_.currentTime;
944 this.cache_.sources = null;
945 this.cache_.source = source;
946 this.cache_.src = source.src;
949 // Initialize tech instance
950 var TechComponent = _tech2['default'].getTech(techName);
952 // Support old behavior of techs being registered as components.
953 // Remove once that deprecated behavior is removed.
954 if (!TechComponent) {
955 TechComponent = _component2['default'].getComponent(techName);
957 this.tech_ = new TechComponent(techOptions);
959 // player.triggerReady is always async, so don't need this to be async
960 this.tech_.ready(Fn.bind(this, this.handleTechReady_), true);
962 _textTrackListConverter2['default'].jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
964 // Listen to all HTML5-defined events and trigger them on the player
965 TECH_EVENTS_RETRIGGER.forEach(function (event) {
966 _this2.on(_this2.tech_, event, _this2['handleTech' + (0, _toTitleCase2['default'])(event) + '_']);
968 this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
969 this.on(this.tech_, 'waiting', this.handleTechWaiting_);
970 this.on(this.tech_, 'canplay', this.handleTechCanPlay_);
971 this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_);
972 this.on(this.tech_, 'playing', this.handleTechPlaying_);
973 this.on(this.tech_, 'ended', this.handleTechEnded_);
974 this.on(this.tech_, 'seeking', this.handleTechSeeking_);
975 this.on(this.tech_, 'seeked', this.handleTechSeeked_);
976 this.on(this.tech_, 'play', this.handleTechPlay_);
977 this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
978 this.on(this.tech_, 'pause', this.handleTechPause_);
979 this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
980 this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
981 this.on(this.tech_, 'error', this.handleTechError_);
982 this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
983 this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
984 this.on(this.tech_, 'textdata', this.handleTechTextData_);
986 this.usingNativeControls(this.techGet_('controls'));
988 if (this.controls() && !this.usingNativeControls()) {
989 this.addTechControlsListeners_();
992 // Add the tech element in the DOM if it was not already there
993 // Make sure to not insert the original video element if using Html5
994 if (this.tech_.el().parentNode !== this.el() && (techName !== 'Html5' || !this.tag)) {
995 Dom.insertElFirst(this.tech_.el(), this.el());
998 // Get rid of the original video tag reference after the first tech is loaded
1000 this.tag.player = null;
1006 * Unload and dispose of the current playback {@link Tech}.
1012 Player.prototype.unloadTech_ = function unloadTech_() {
1013 // Save the current text tracks so that we can reuse the same text tracks with the next tech
1014 this.videoTracks_ = this.videoTracks();
1015 this.textTracks_ = this.textTracks();
1016 this.audioTracks_ = this.audioTracks();
1017 this.textTracksJson_ = _textTrackListConverter2['default'].textTracksToJson(this.tech_);
1019 this.isReady_ = false;
1021 this.tech_.dispose();
1027 * Return a reference to the current {@link Tech}, but only if given an object with the
1028 * `IWillNotUseThisInPlugins` property having a true value. This is try and prevent misuse
1029 * of techs by plugins.
1031 * @param {Object} safety
1032 * An object that must contain `{IWillNotUseThisInPlugins: true}`
1034 * @param {boolean} safety.IWillNotUseThisInPlugins
1035 * Must be set to true or else this function will throw an error.
1042 Player.prototype.tech = function tech(safety) {
1043 if (safety && safety.IWillNotUseThisInPlugins) {
1046 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 ';
1048 _window2['default'].alert(errorText);
1049 throw new Error(errorText);
1053 * Set up click and touch listeners for the playback element
1055 * - On desktops: a click on the video itself will toggle playback
1056 * - On mobile devices: a click on the video toggles controls
1057 * which is done by toggling the user state between active and
1059 * - A tap can signal that a user has become active or has become inactive
1060 * e.g. a quick tap on an iPhone movie should reveal the controls. Another
1061 * quick tap should hide them again (signaling the user is in an inactive
1063 * - In addition to this, we still want the user to be considered inactive after
1064 * a few seconds of inactivity.
1066 * > Note: the only part of iOS interaction we can't mimic with this setup
1067 * is a touch and hold on the video element counting as activity in order to
1068 * keep the controls showing, but that shouldn't be an issue. A touch and hold
1069 * on any controls will still keep the user active
1075 Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
1076 // Make sure to remove all the previous listeners in case we are called multiple times.
1077 this.removeTechControlsListeners_();
1079 // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
1080 // trigger mousedown/up.
1081 // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
1082 // Any touch events are set to block the mousedown event from happening
1083 this.on(this.tech_, 'mousedown', this.handleTechClick_);
1085 // If the controls were hidden we don't want that to change without a tap event
1086 // so we'll check if the controls were already showing before reporting user
1088 this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
1089 this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
1090 this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
1092 // The tap listener needs to come after the touchend listener because the tap
1093 // listener cancels out any reportedUserActivity when setting userActive(false)
1094 this.on(this.tech_, 'tap', this.handleTechTap_);
1098 * Remove the listeners used for click and tap controls. This is needed for
1099 * toggling to controls disabled, where a tap/touch should do nothing.
1105 Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
1106 // We don't want to just use `this.off()` because there might be other needed
1107 // listeners added by techs that extend this.
1108 this.off(this.tech_, 'tap', this.handleTechTap_);
1109 this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
1110 this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
1111 this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
1112 this.off(this.tech_, 'mousedown', this.handleTechClick_);
1116 * Player waits for the tech to be ready
1122 Player.prototype.handleTechReady_ = function handleTechReady_() {
1123 this.triggerReady();
1125 // Keep the same volume as before
1126 if (this.cache_.volume) {
1127 this.techCall_('setVolume', this.cache_.volume);
1130 // Look if the tech found a higher resolution poster while loading
1131 this.handleTechPosterChange_();
1133 // Update the duration if available
1134 this.handleTechDurationChange_();
1136 // Chrome and Safari both have issues with autoplay.
1137 // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
1138 // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
1139 // This fixes both issues. Need to wait for API, so it updates displays correctly
1140 if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
1142 // Chrome Fix. Fixed in Chrome v16.
1143 delete this.tag.poster;
1145 (0, _log2['default'])('deleting tag.poster throws in some browsers', e);
1152 * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
1153 * function will also trigger {@link Player#firstplay} if it is the first loadstart
1156 * @fires Player#loadstart
1157 * @fires Player#firstplay
1158 * @listens Tech#loadstart
1163 Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
1164 // TODO: Update to use `emptied` event instead. See #1277.
1166 this.removeClass('vjs-ended');
1167 this.removeClass('vjs-seeking');
1169 // reset the error state
1172 // If it's already playing we want to trigger a firstplay event now.
1173 // The firstplay event relies on both the play and loadstart events
1174 // which can happen in any order for a new source
1175 if (!this.paused()) {
1177 * Fired when the user agent begins looking for media data
1179 * @event Player#loadstart
1180 * @type {EventTarget~Event}
1182 this.trigger('loadstart');
1183 this.trigger('firstplay');
1185 // reset the hasStarted state
1186 this.hasStarted(false);
1187 this.trigger('loadstart');
1192 * Add/remove the vjs-has-started class
1194 * @fires Player#firstplay
1196 * @param {boolean} hasStarted
1197 * - true: adds the class
1198 * - false: remove the class
1201 * the boolean value of hasStarted
1205 Player.prototype.hasStarted = function hasStarted(_hasStarted) {
1206 if (_hasStarted !== undefined) {
1207 // only update if this is a new value
1208 if (this.hasStarted_ !== _hasStarted) {
1209 this.hasStarted_ = _hasStarted;
1211 this.addClass('vjs-has-started');
1212 // trigger the firstplay event if this newly has played
1213 this.trigger('firstplay');
1215 this.removeClass('vjs-has-started');
1220 return !!this.hasStarted_;
1224 * Fired whenever the media begins or resumes playback
1226 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
1227 * @fires Player#play
1228 * @listens Tech#play
1233 Player.prototype.handleTechPlay_ = function handleTechPlay_() {
1234 this.removeClass('vjs-ended');
1235 this.removeClass('vjs-paused');
1236 this.addClass('vjs-playing');
1238 // hide the poster when the user hits play
1239 this.hasStarted(true);
1241 * Triggered whenever an {@link Tech#play} event happens. Indicates that
1242 * playback has started or resumed.
1244 * @event Player#play
1245 * @type {EventTarget~Event}
1247 this.trigger('play');
1251 * Retrigger the `waiting` event that was triggered by the {@link Tech}.
1253 * @fires Player#waiting
1254 * @listens Tech#waiting
1259 Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
1262 this.addClass('vjs-waiting');
1264 * A readyState change on the DOM element has caused playback to stop.
1266 * @event Player#waiting
1267 * @type {EventTarget~Event}
1269 this.trigger('waiting');
1270 this.one('timeupdate', function () {
1271 return _this3.removeClass('vjs-waiting');
1276 * Retrigger the `canplay` event that was triggered by the {@link Tech}.
1277 * > Note: This is not consistent between browsers. See #1351
1279 * @fires Player#canplay
1280 * @listens Tech#canplay
1285 Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
1286 this.removeClass('vjs-waiting');
1288 * The media has a readyState of HAVE_FUTURE_DATA or greater.
1290 * @event Player#canplay
1291 * @type {EventTarget~Event}
1293 this.trigger('canplay');
1297 * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
1299 * @fires Player#canplaythrough
1300 * @listens Tech#canplaythrough
1305 Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
1306 this.removeClass('vjs-waiting');
1308 * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
1309 * entire media file can be played without buffering.
1311 * @event Player#canplaythrough
1312 * @type {EventTarget~Event}
1314 this.trigger('canplaythrough');
1318 * Retrigger the `playing` event that was triggered by the {@link Tech}.
1320 * @fires Player#playing
1321 * @listens Tech#playing
1326 Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
1327 this.removeClass('vjs-waiting');
1329 * The media is no longer blocked from playback, and has started playing.
1331 * @event Player#playing
1332 * @type {EventTarget~Event}
1334 this.trigger('playing');
1338 * Retrigger the `seeking` event that was triggered by the {@link Tech}.
1340 * @fires Player#seeking
1341 * @listens Tech#seeking
1346 Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
1347 this.addClass('vjs-seeking');
1349 * Fired whenever the player is jumping to a new time
1351 * @event Player#seeking
1352 * @type {EventTarget~Event}
1354 this.trigger('seeking');
1358 * Retrigger the `seeked` event that was triggered by the {@link Tech}.
1360 * @fires Player#seeked
1361 * @listens Tech#seeked
1366 Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
1367 this.removeClass('vjs-seeking');
1369 * Fired when the player has finished jumping to a new time
1371 * @event Player#seeked
1372 * @type {EventTarget~Event}
1374 this.trigger('seeked');
1378 * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
1380 * @fires Player#firstplay
1381 * @listens Tech#firstplay
1382 * @deprecated As of 6.0 passing the `starttime` option to the player will be deprecated
1387 Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
1388 // If the first starttime attribute is specified
1389 // then we will start at the given offset in seconds
1390 if (this.options_.starttime) {
1391 _log2['default'].warn('Passing the `starttime` option to the player will be deprecated in 6.0');
1392 this.currentTime(this.options_.starttime);
1395 this.addClass('vjs-has-started');
1397 * Fired the first time a video is played. Not part of the HLS spec, and this is
1398 * probably not the best implementation yet, so use sparingly. If you don't have a
1399 * reason to prevent playback, use `myPlayer.one('play');` instead.
1401 * @event Player#firstplay
1402 * @type {EventTarget~Event}
1404 this.trigger('firstplay');
1408 * Retrigger the `pause` event that was triggered by the {@link Tech}.
1410 * @fires Player#pause
1411 * @listens Tech#pause
1416 Player.prototype.handleTechPause_ = function handleTechPause_() {
1417 this.removeClass('vjs-playing');
1418 this.addClass('vjs-paused');
1420 * Fired whenever the media has been paused
1422 * @event Player#pause
1423 * @type {EventTarget~Event}
1425 this.trigger('pause');
1429 * Retrigger the `ended` event that was triggered by the {@link Tech}.
1431 * @fires Player#ended
1432 * @listens Tech#ended
1437 Player.prototype.handleTechEnded_ = function handleTechEnded_() {
1438 this.addClass('vjs-ended');
1439 if (this.options_.loop) {
1440 this.currentTime(0);
1442 } else if (!this.paused()) {
1447 * Fired when the end of the media resource is reached (currentTime == duration)
1449 * @event Player#ended
1450 * @type {EventTarget~Event}
1452 this.trigger('ended');
1456 * Fired when the duration of the media resource is first known or changed
1458 * @listens Tech#durationchange
1463 Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
1464 this.duration(this.techGet_('duration'));
1468 * Handle a click on the media element to play/pause
1470 * @param {EventTarget~Event} event
1471 * the event that caused this function to trigger
1473 * @listens Tech#mousedown
1478 Player.prototype.handleTechClick_ = function handleTechClick_(event) {
1479 // We're using mousedown to detect clicks thanks to Flash, but mousedown
1480 // will also be triggered with right-clicks, so we need to prevent that
1481 if (event.button !== 0) {
1485 // When controls are disabled a click should not toggle playback because
1486 // the click is considered a control
1487 if (this.controls()) {
1488 if (this.paused()) {
1497 * Handle a tap on the media element. It will toggle the user
1498 * activity state, which hides and shows the controls.
1505 Player.prototype.handleTechTap_ = function handleTechTap_() {
1506 this.userActive(!this.userActive());
1510 * Handle touch to start
1512 * @listens Tech#touchstart
1517 Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
1518 this.userWasActive = this.userActive();
1522 * Handle touch to move
1524 * @listens Tech#touchmove
1529 Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
1530 if (this.userWasActive) {
1531 this.reportUserActivity();
1536 * Handle touch to end
1538 * @param {EventTarget~Event} event
1539 * the touchend event that triggered
1542 * @listens Tech#touchend
1547 Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
1548 // Stop the mouse events from also happening
1549 event.preventDefault();
1553 * Fired when the player switches in or out of fullscreen mode
1556 * @listens Player#fullscreenchange
1560 Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
1561 if (this.isFullscreen()) {
1562 this.addClass('vjs-fullscreen');
1564 this.removeClass('vjs-fullscreen');
1569 * native click events on the SWF aren't triggered on IE11, Win8.1RT
1570 * use stageclick events triggered from inside the SWF instead
1573 * @listens stageclick
1577 Player.prototype.handleStageClick_ = function handleStageClick_() {
1578 this.reportUserActivity();
1582 * Handle Tech Fullscreen Change
1584 * @param {EventTarget~Event} event
1585 * the fullscreenchange event that triggered this function
1587 * @param {Object} data
1588 * the data that was sent with the event
1591 * @listens Tech#fullscreenchange
1592 * @fires Player#fullscreenchange
1596 Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
1598 this.isFullscreen(data.isFullscreen);
1601 * Fired when going in and out of fullscreen.
1603 * @event Player#fullscreenchange
1604 * @type {EventTarget~Event}
1606 this.trigger('fullscreenchange');
1610 * Fires when an error occurred during the loading of an audio/video.
1613 * @listens Tech#error
1617 Player.prototype.handleTechError_ = function handleTechError_() {
1618 var error = this.tech_.error();
1624 * Retrigger the `textdata` event that was triggered by the {@link Tech}.
1626 * @fires Player#textdata
1627 * @listens Tech#textdata
1632 Player.prototype.handleTechTextData_ = function handleTechTextData_() {
1635 if (arguments.length > 1) {
1636 data = arguments[1];
1640 * Fires when we get a textdata event from tech
1642 * @event Player#textdata
1643 * @type {EventTarget~Event}
1645 this.trigger('textdata', data);
1649 * Get object for cached values.
1652 * get the current object cache
1656 Player.prototype.getCache = function getCache() {
1661 * Pass values to the playback tech
1663 * @param {string} [method]
1664 * the method to call
1666 * @param {Object} arg
1667 * the argument to pass
1673 Player.prototype.techCall_ = function techCall_(method, arg) {
1674 // If it's not ready yet, call method when it is
1675 if (this.tech_ && !this.tech_.isReady_) {
1676 this.tech_.ready(function () {
1680 // Otherwise call method now
1684 this.tech_[method](arg);
1687 (0, _log2['default'])(e);
1694 * Get calls can't wait for the tech, and sometimes don't need to.
1696 * @param {string} method
1699 * @return {Function|undefined}
1700 * the method or undefined
1706 Player.prototype.techGet_ = function techGet_(method) {
1707 if (this.tech_ && this.tech_.isReady_) {
1709 // Flash likes to die and reload when you hide or reposition it.
1710 // In these cases the object methods go away and we get errors.
1711 // When that happens we'll catch the errors and inform tech that it's not ready any more.
1713 return this.tech_[method]();
1715 // When building additional tech libs, an expected method may not be defined yet
1716 if (this.tech_[method] === undefined) {
1717 (0, _log2['default'])('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
1719 // When a method isn't available on the object it throws a TypeError
1720 } else if (e.name === 'TypeError') {
1721 (0, _log2['default'])('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
1722 this.tech_.isReady_ = false;
1724 (0, _log2['default'])(e);
1734 * start media playback
1737 * A reference to the player object this function was called on
1741 Player.prototype.play = function play() {
1742 // Only calls the tech's play if we already have a src loaded
1743 if (this.src() || this.currentSrc()) {
1744 this.techCall_('play');
1746 this.tech_.one('loadstart', function () {
1755 * Pause the video playback
1758 * A reference to the player object this function was called on
1762 Player.prototype.pause = function pause() {
1763 this.techCall_('pause');
1768 * Check if the player is paused or has yet to play
1771 * - false: if the media is currently playing
1772 * - true: if media is not currently playing
1776 Player.prototype.paused = function paused() {
1777 // The initial state of paused should be true (in Safari it's actually false)
1778 return this.techGet_('paused') === false ? false : true;
1782 * Returns whether or not the user is "scrubbing". Scrubbing is
1783 * when the user has clicked the progress bar handle and is
1784 * dragging it along the progress bar.
1786 * @param {boolean} [isScrubbing]
1787 * wether the user is or is not scrubbing
1789 * @return {boolean|Player}
1790 * A instance of the player that called this function when setting,
1791 * and the value of scrubbing when getting
1795 Player.prototype.scrubbing = function scrubbing(isScrubbing) {
1796 if (isScrubbing !== undefined) {
1797 this.scrubbing_ = !!isScrubbing;
1800 this.addClass('vjs-scrubbing');
1802 this.removeClass('vjs-scrubbing');
1808 return this.scrubbing_;
1812 * Get or set the current time (in seconds)
1814 * @param {number|string} [seconds]
1815 * The time to seek to in seconds
1817 * @return {Player|number}
1818 * - the current time in seconds when getting
1819 * - a reference to the current player object when setting
1823 Player.prototype.currentTime = function currentTime(seconds) {
1824 if (seconds !== undefined) {
1826 this.techCall_('setCurrentTime', seconds);
1831 // cache last currentTime and return. default to 0 seconds
1833 // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
1834 // currentTime when scrubbing, but may not provide much performance benefit afterall.
1835 // Should be tested. Also something has to read the actual current time or the cache will
1836 // never get updated.
1837 this.cache_.currentTime = this.techGet_('currentTime') || 0;
1838 return this.cache_.currentTime;
1842 * Normally gets the length in time of the video in seconds;
1843 * in all but the rarest use cases an argument will NOT be passed to the method
1845 * > **NOTE**: The video must have started loading before the duration can be
1846 * known, and in the case of Flash, may not be known until the video starts
1849 * @fires Player#durationchange
1851 * @param {number} [seconds]
1852 * The duration of the video to set in seconds
1854 * @return {number|Player}
1855 * - The duration of the video in seconds when getting
1856 * - A reference to the player that called this function
1861 Player.prototype.duration = function duration(seconds) {
1862 if (seconds === undefined) {
1863 return this.cache_.duration || 0;
1866 seconds = parseFloat(seconds) || 0;
1868 // Standardize on Inifity for signaling video is live
1873 if (seconds !== this.cache_.duration) {
1874 // Cache the last set value for optimized scrubbing (esp. Flash)
1875 this.cache_.duration = seconds;
1877 if (seconds === Infinity) {
1878 this.addClass('vjs-live');
1880 this.removeClass('vjs-live');
1883 * @event Player#durationchange
1884 * @type {EventTarget~Event}
1886 this.trigger('durationchange');
1893 * Calculates how much time is left in the video. Not part
1894 * of the native video API.
1897 * The time remaining in seconds
1901 Player.prototype.remainingTime = function remainingTime() {
1902 return this.duration() - this.currentTime();
1906 // Kind of like an array of portions of the video that have been downloaded.
1909 * Get a TimeRange object with an array of the times of the video
1910 * that have been downloaded. If you just want the percent of the
1911 * video that's been downloaded, use bufferedPercent.
1913 * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
1915 * @return {TimeRange}
1916 * A mock TimeRange object (following HTML spec)
1920 Player.prototype.buffered = function buffered() {
1921 var buffered = this.techGet_('buffered');
1923 if (!buffered || !buffered.length) {
1924 buffered = (0, _timeRanges.createTimeRange)(0, 0);
1931 * Get the percent (as a decimal) of the video that's been downloaded.
1932 * This method is not a part of the native HTML video API.
1935 * A decimal between 0 and 1 representing the percent
1936 * that is bufferred 0 being 0% and 1 being 100%
1940 Player.prototype.bufferedPercent = function bufferedPercent() {
1941 return (0, _buffer.bufferedPercent)(this.buffered(), this.duration());
1945 * Get the ending time of the last buffered time range
1946 * This is used in the progress bar to encapsulate all time ranges.
1949 * The end of the last buffered time range
1953 Player.prototype.bufferedEnd = function bufferedEnd() {
1954 var buffered = this.buffered();
1955 var duration = this.duration();
1956 var end = buffered.end(buffered.length - 1);
1958 if (end > duration) {
1966 * Get or set the current volume of the media
1968 * @param {number} [percentAsDecimal]
1969 * The new volume as a decimal percent:
1970 * - 0 is muted/0%/off
1971 * - 1.0 is 100%/full
1972 * - 0.5 is half volume or 50%
1974 * @return {Player|number}
1975 * a reference to the calling player when setting and the
1976 * current volume as a percent when getting
1980 Player.prototype.volume = function volume(percentAsDecimal) {
1983 if (percentAsDecimal !== undefined) {
1984 // Force value to between 0 and 1
1985 vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
1986 this.cache_.volume = vol;
1987 this.techCall_('setVolume', vol);
1992 // Default to 1 when returning current volume.
1993 vol = parseFloat(this.techGet_('volume'));
1994 return isNaN(vol) ? 1 : vol;
1998 * Get the current muted state, or turn mute on or off
2000 * @param {boolean} [muted]
2004 * @return {boolean|Player}
2005 * - true if mute is on and getting
2006 * - false if mute is off and getting
2007 * - A reference to the current player when setting
2011 Player.prototype.muted = function muted(_muted) {
2012 if (_muted !== undefined) {
2013 this.techCall_('setMuted', _muted);
2016 return this.techGet_('muted') || false;
2020 * Check if current tech can support native fullscreen
2021 * (e.g. with built in controls like iOS, so not our flash swf)
2024 * if native fullscreen is supported
2028 Player.prototype.supportsFullScreen = function supportsFullScreen() {
2029 return this.techGet_('supportsFullScreen') || false;
2033 * Check if the player is in fullscreen mode or tell the player that it
2034 * is or is not in fullscreen mode.
2036 * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
2037 * property and instead document.fullscreenElement is used. But isFullscreen is
2038 * still a valuable property for internal player workings.
2040 * @param {boolean} [isFS]
2041 * Set the players current fullscreen state
2043 * @return {boolean|Player}
2044 * - true if fullscreen is on and getting
2045 * - false if fullscreen is off and getting
2046 * - A reference to the current player when setting
2050 Player.prototype.isFullscreen = function isFullscreen(isFS) {
2051 if (isFS !== undefined) {
2052 this.isFullscreen_ = !!isFS;
2055 return !!this.isFullscreen_;
2059 * Increase the size of the video to full screen
2060 * In some browsers, full screen is not supported natively, so it enters
2061 * "full window mode", where the video fills the browser window.
2062 * In browsers and devices that support native full screen, sometimes the
2063 * browser's default controls will be shown, and not the Video.js custom skin.
2064 * This includes most mobile devices (iOS, Android) and older versions of
2067 * @fires Player#fullscreenchange
2069 * A reference to the current player
2073 Player.prototype.requestFullscreen = function requestFullscreen() {
2074 var fsApi = _fullscreenApi2['default'];
2076 this.isFullscreen(true);
2078 if (fsApi.requestFullscreen) {
2079 // the browser supports going fullscreen at the element level so we can
2080 // take the controls fullscreen as well as the video
2082 // Trigger fullscreenchange event after change
2083 // We have to specifically add this each time, and remove
2084 // when canceling fullscreen. Otherwise if there's multiple
2085 // players on a page, they would all be reacting to the same fullscreen
2087 Events.on(_document2['default'], fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) {
2088 this.isFullscreen(_document2['default'][fsApi.fullscreenElement]);
2090 // If cancelling fullscreen, remove event listener.
2091 if (this.isFullscreen() === false) {
2092 Events.off(_document2['default'], fsApi.fullscreenchange, documentFullscreenChange);
2095 * @event Player#fullscreenchange
2096 * @type {EventTarget~Event}
2098 this.trigger('fullscreenchange');
2101 this.el_[fsApi.requestFullscreen]();
2102 } else if (this.tech_.supportsFullScreen()) {
2103 // we can't take the video.js controls fullscreen but we can go fullscreen
2104 // with native controls
2105 this.techCall_('enterFullScreen');
2107 // fullscreen isn't supported so we'll just stretch the video element to
2108 // fill the viewport
2109 this.enterFullWindow();
2111 * @event Player#fullscreenchange
2112 * @type {EventTarget~Event}
2114 this.trigger('fullscreenchange');
2121 * Return the video to its normal size after having been in full screen mode
2123 * @fires Player#fullscreenchange
2126 * A reference to the current player
2130 Player.prototype.exitFullscreen = function exitFullscreen() {
2131 var fsApi = _fullscreenApi2['default'];
2133 this.isFullscreen(false);
2135 // Check for browser element fullscreen support
2136 if (fsApi.requestFullscreen) {
2137 _document2['default'][fsApi.exitFullscreen]();
2138 } else if (this.tech_.supportsFullScreen()) {
2139 this.techCall_('exitFullScreen');
2141 this.exitFullWindow();
2143 * @event Player#fullscreenchange
2144 * @type {EventTarget~Event}
2146 this.trigger('fullscreenchange');
2153 * When fullscreen isn't supported we can stretch the
2154 * video container to as wide as the browser will let us.
2156 * @fires Player#enterFullWindow
2160 Player.prototype.enterFullWindow = function enterFullWindow() {
2161 this.isFullWindow = true;
2163 // Storing original doc overflow value to return to when fullscreen is off
2164 this.docOrigOverflow = _document2['default'].documentElement.style.overflow;
2166 // Add listener for esc key to exit fullscreen
2167 Events.on(_document2['default'], 'keydown', Fn.bind(this, this.fullWindowOnEscKey));
2169 // Hide any scroll bars
2170 _document2['default'].documentElement.style.overflow = 'hidden';
2172 // Apply fullscreen styles
2173 Dom.addElClass(_document2['default'].body, 'vjs-full-window');
2176 * @event Player#enterFullWindow
2177 * @type {EventTarget~Event}
2179 this.trigger('enterFullWindow');
2183 * Check for call to either exit full window or
2184 * full screen on ESC key
2186 * @param {string} event
2187 * Event to check for key press
2191 Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
2192 if (event.keyCode === 27) {
2193 if (this.isFullscreen() === true) {
2194 this.exitFullscreen();
2196 this.exitFullWindow();
2204 * @fires Player#exitFullWindow
2208 Player.prototype.exitFullWindow = function exitFullWindow() {
2209 this.isFullWindow = false;
2210 Events.off(_document2['default'], 'keydown', this.fullWindowOnEscKey);
2212 // Unhide scroll bars.
2213 _document2['default'].documentElement.style.overflow = this.docOrigOverflow;
2215 // Remove fullscreen styles
2216 Dom.removeElClass(_document2['default'].body, 'vjs-full-window');
2218 // Resize the box, controller, and poster to original sizes
2219 // this.positionAll();
2221 * @event Player#exitFullWindow
2222 * @type {EventTarget~Event}
2224 this.trigger('exitFullWindow');
2228 * Check whether the player can play a given mimetype
2230 * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
2232 * @param {string} type
2233 * The mimetype to check
2236 * 'probably', 'maybe', or '' (empty string)
2240 Player.prototype.canPlayType = function canPlayType(type) {
2243 // Loop through each playback technology in the options order
2244 for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
2245 var techName = (0, _toTitleCase2['default'])(j[i]);
2246 var tech = _tech2['default'].getTech(techName);
2248 // Support old behavior of techs being registered as components.
2249 // Remove once that deprecated behavior is removed.
2251 tech = _component2['default'].getComponent(techName);
2254 // Check if the current tech is defined before continuing
2256 _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
2260 // Check if the browser supports this technology
2261 if (tech.isSupported()) {
2262 can = tech.canPlayType(type);
2274 * Select source based on tech-order or source-order
2275 * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
2276 * defaults to tech-order selection
2278 * @param {Array} sources
2279 * The sources for a media asset
2281 * @return {Object|boolean}
2282 * Object of source and tech order or false
2286 Player.prototype.selectSource = function selectSource(sources) {
2289 // Get only the techs specified in `techOrder` that exist and are supported by the
2291 var techs = this.options_.techOrder.map(_toTitleCase2['default']).map(function (techName) {
2292 // `Component.getComponent(...)` is for support of old behavior of techs
2293 // being registered as components.
2294 // Remove once that deprecated behavior is removed.
2295 return [techName, _tech2['default'].getTech(techName) || _component2['default'].getComponent(techName)];
2296 }).filter(function (_ref) {
2297 var techName = _ref[0],
2300 // Check if the current tech is defined before continuing
2302 // Check if the browser supports this technology
2303 return tech.isSupported();
2306 _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
2310 // Iterate over each `innerArray` element once per `outerArray` element and execute
2311 // `tester` with both. If `tester` returns a non-falsy value, exit early and return
2313 var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
2316 outerArray.some(function (outerChoice) {
2317 return innerArray.some(function (innerChoice) {
2318 found = tester(outerChoice, innerChoice);
2329 var foundSourceAndTech = void 0;
2330 var flip = function flip(fn) {
2331 return function (a, b) {
2335 var finder = function finder(_ref2, source) {
2336 var techName = _ref2[0],
2339 if (tech.canPlaySource(source, _this4.options_[techName.toLowerCase()])) {
2340 return { source: source, tech: techName };
2344 // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
2345 // to select from them based on their priority.
2346 if (this.options_.sourceOrder) {
2347 // Source-first ordering
2348 foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
2350 // Tech-first ordering
2351 foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
2354 return foundSourceAndTech || false;
2358 * The source function updates the video source
2359 * There are three types of variables you can pass as the argument.
2360 * **URL string**: A URL to the the video file. Use this method if you are sure
2361 * the current playback technology (HTML5/Flash) can support the source you
2362 * provide. Currently only MP4 files can be used in both HTML5 and Flash.
2364 * @param {Tech~SourceObject|Tech~SourceObject[]} [source]
2365 * One SourceObject or an array of SourceObjects
2367 * @return {string|Player}
2368 * - The current video source when getting
2369 * - The player when setting
2373 Player.prototype.src = function src(source) {
2374 if (source === undefined) {
2375 return this.techGet_('src');
2378 var currentTech = _tech2['default'].getTech(this.techName_);
2380 // Support old behavior of techs being registered as components.
2381 // Remove once that deprecated behavior is removed.
2383 currentTech = _component2['default'].getComponent(this.techName_);
2386 // case: Array of source objects to choose from and pick the best to play
2387 if (Array.isArray(source)) {
2388 this.sourceList_(source);
2390 // case: URL String (http://myvideo...)
2391 } else if (typeof source === 'string') {
2392 // create a source object from the string
2393 this.src({ src: source });
2395 // case: Source object { src: '', type: '' ... }
2396 } else if (source instanceof Object) {
2397 // check if the source has a type and the loaded tech cannot play the source
2398 // if there's no type we'll just try the current tech
2399 if (source.type && !currentTech.canPlaySource(source, this.options_[this.techName_.toLowerCase()])) {
2400 // create a source list with the current source and send through
2401 // the tech loop to check for a compatible technology
2402 this.sourceList_([source]);
2404 this.cache_.sources = null;
2405 this.cache_.source = source;
2406 this.cache_.src = source.src;
2408 this.currentType_ = source.type || '';
2410 // wait until the tech is ready to set the source
2411 this.ready(function () {
2413 // The setSource tech method was added with source handlers
2414 // so older techs won't support it
2415 // We need to check the direct prototype for the case where subclasses
2416 // of the tech do not support source handlers
2417 if (currentTech.prototype.hasOwnProperty('setSource')) {
2418 this.techCall_('setSource', source);
2420 this.techCall_('src', source.src);
2423 if (this.options_.preload === 'auto') {
2427 if (this.options_.autoplay) {
2431 // Set the source synchronously if possible (#2326)
2440 * Handle an array of source objects
2442 * @param {Tech~SourceObject[]} sources
2443 * Array of source objects
2449 Player.prototype.sourceList_ = function sourceList_(sources) {
2450 var sourceTech = this.selectSource(sources);
2453 if (sourceTech.tech === this.techName_) {
2454 // if this technology is already loaded, set the source
2455 this.src(sourceTech.source);
2457 // load this technology with the chosen source
2458 this.loadTech_(sourceTech.tech, sourceTech.source);
2461 this.cache_.sources = sources;
2463 // We need to wrap this in a timeout to give folks a chance to add error event handlers
2464 this.setTimeout(function () {
2465 this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
2468 // we could not find an appropriate tech, but let's still notify the delegate that this is it
2469 // this needs a better comment about why this is needed
2470 this.triggerReady();
2475 * Begin loading the src data.
2478 * A reference to the player
2482 Player.prototype.load = function load() {
2483 this.techCall_('load');
2488 * Reset the player. Loads the first tech in the techOrder,
2489 * and calls `reset` on the tech`.
2492 * A reference to the player
2496 Player.prototype.reset = function reset() {
2497 this.loadTech_((0, _toTitleCase2['default'])(this.options_.techOrder[0]), null);
2498 this.techCall_('reset');
2503 * Returns all of the current source objects.
2505 * @return {Tech~SourceObject[]}
2506 * The current source objects
2510 Player.prototype.currentSources = function currentSources() {
2511 var source = this.currentSource();
2514 // assume `{}` or `{ src }`
2515 if (Object.keys(source).length !== 0) {
2516 sources.push(source);
2519 return this.cache_.sources || sources;
2523 * Returns the current source object.
2525 * @return {Tech~SourceObject}
2526 * The current source object
2530 Player.prototype.currentSource = function currentSource() {
2532 var src = this.currentSrc();
2538 return this.cache_.source || source;
2542 * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
2543 * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
2546 * The current source
2550 Player.prototype.currentSrc = function currentSrc() {
2551 return this.techGet_('currentSrc') || this.cache_.src || '';
2555 * Get the current source type e.g. video/mp4
2556 * This can allow you rebuild the current source object so that you could load the same
2557 * source and tech later
2560 * The source MIME type
2564 Player.prototype.currentType = function currentType() {
2565 return this.currentType_ || '';
2569 * Get or set the preload attribute
2571 * @param {boolean} [value]
2572 * - true means that we should preload
2573 * - false maens that we should not preload
2575 * @return {string|Player}
2576 * - the preload attribute value when getting
2577 * - the player when setting
2581 Player.prototype.preload = function preload(value) {
2582 if (value !== undefined) {
2583 this.techCall_('setPreload', value);
2584 this.options_.preload = value;
2587 return this.techGet_('preload');
2591 * Get or set the autoplay attribute.
2593 * @param {boolean} [value]
2594 * - true means that we should autoplay
2595 * - false maens that we should not autoplay
2597 * @return {string|Player}
2598 * - the current value of autoplay
2599 * - the player when setting
2603 Player.prototype.autoplay = function autoplay(value) {
2604 if (value !== undefined) {
2605 this.techCall_('setAutoplay', value);
2606 this.options_.autoplay = value;
2609 return this.techGet_('autoplay', value);
2613 * Get or set the loop attribute on the video element.
2615 * @param {boolean} [value]
2616 * - true means that we should loop the video
2617 * - false means that we should not loop the video
2619 * @return {string|Player}
2620 * - the current value of loop when getting
2621 * - the player when setting
2625 Player.prototype.loop = function loop(value) {
2626 if (value !== undefined) {
2627 this.techCall_('setLoop', value);
2628 this.options_.loop = value;
2631 return this.techGet_('loop');
2635 * Get or set the poster image source url
2637 * @fires Player#posterchange
2639 * @param {string} [src]
2640 * Poster image source URL
2642 * @return {string|Player}
2643 * - the current value of poster when getting
2644 * - the player when setting
2648 Player.prototype.poster = function poster(src) {
2649 if (src === undefined) {
2650 return this.poster_;
2653 // The correct way to remove a poster is to set as an empty string
2654 // other falsey values will throw errors
2659 // update the internal poster variable
2662 // update the tech's poster
2663 this.techCall_('setPoster', src);
2665 // alert components that the poster has been set
2667 * This event fires when the poster image is changed on the player.
2669 * @event Player#posterchange
2670 * @type {EventTarget~Event}
2672 this.trigger('posterchange');
2678 * Some techs (e.g. YouTube) can provide a poster source in an
2679 * asynchronous way. We want the poster component to use this
2680 * poster source so that it covers up the tech's controls.
2681 * (YouTube's play button). However we only want to use this
2682 * soruce if the player user hasn't set a poster through
2685 * @fires Player#posterchange
2686 * @listens Tech#posterchange
2691 Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
2692 if (!this.poster_ && this.tech_ && this.tech_.poster) {
2693 this.poster_ = this.tech_.poster() || '';
2695 // Let components know the poster has changed
2696 this.trigger('posterchange');
2701 * Get or set whether or not the controls are showing.
2703 * @fires Player#controlsenabled
2705 * @param {boolean} [bool]
2706 * - true to turn controls on
2707 * - false to turn controls off
2709 * @return {boolean|Player}
2710 * - the current value of controls when getting
2711 * - the player when setting
2715 Player.prototype.controls = function controls(bool) {
2716 if (bool !== undefined) {
2719 // Don't trigger a change event unless it actually changed
2720 if (this.controls_ !== bool) {
2721 this.controls_ = bool;
2723 if (this.usingNativeControls()) {
2724 this.techCall_('setControls', bool);
2728 this.removeClass('vjs-controls-disabled');
2729 this.addClass('vjs-controls-enabled');
2731 * @event Player#controlsenabled
2732 * @type {EventTarget~Event}
2734 this.trigger('controlsenabled');
2736 if (!this.usingNativeControls()) {
2737 this.addTechControlsListeners_();
2740 this.removeClass('vjs-controls-enabled');
2741 this.addClass('vjs-controls-disabled');
2743 * @event Player#controlsdisabled
2744 * @type {EventTarget~Event}
2746 this.trigger('controlsdisabled');
2748 if (!this.usingNativeControls()) {
2749 this.removeTechControlsListeners_();
2755 return !!this.controls_;
2759 * Toggle native controls on/off. Native controls are the controls built into
2760 * devices (e.g. default iPhone controls), Flash, or other techs
2761 * (e.g. Vimeo Controls)
2762 * **This should only be set by the current tech, because only the tech knows
2763 * if it can support native controls**
2765 * @fires Player#usingnativecontrols
2766 * @fires Player#usingcustomcontrols
2768 * @param {boolean} [bool]
2769 * - true to turn native controls on
2770 * - false to turn native controls off
2772 * @return {boolean|Player}
2773 * - the current value of native controls when getting
2774 * - the player when setting
2778 Player.prototype.usingNativeControls = function usingNativeControls(bool) {
2779 if (bool !== undefined) {
2782 // Don't trigger a change event unless it actually changed
2783 if (this.usingNativeControls_ !== bool) {
2784 this.usingNativeControls_ = bool;
2786 this.addClass('vjs-using-native-controls');
2789 * player is using the native device controls
2791 * @event Player#usingnativecontrols
2792 * @type {EventTarget~Event}
2794 this.trigger('usingnativecontrols');
2796 this.removeClass('vjs-using-native-controls');
2799 * player is using the custom HTML controls
2801 * @event Player#usingcustomcontrols
2802 * @type {EventTarget~Event}
2804 this.trigger('usingcustomcontrols');
2809 return !!this.usingNativeControls_;
2813 * Set or get the current MediaError
2815 * @fires Player#error
2817 * @param {MediaError|string|number} [err]
2818 * A MediaError or a string/number to be turned
2821 * @return {MediaError|null|Player}
2822 * - The current MediaError when getting (or null)
2823 * - The player when setting
2827 Player.prototype.error = function error(err) {
2828 if (err === undefined) {
2829 return this.error_ || null;
2832 // restoring to default
2835 this.removeClass('vjs-error');
2836 if (this.errorDisplay) {
2837 this.errorDisplay.close();
2842 this.error_ = new _mediaError2['default'](err);
2844 // add the vjs-error classname to the player
2845 this.addClass('vjs-error');
2847 // log the name of the error type and any message
2848 // ie8 just logs "[object object]" if you just log the error object
2849 _log2['default'].error('(CODE:' + this.error_.code + ' ' + _mediaError2['default'].errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
2852 * @event Player#error
2853 * @type {EventTarget~Event}
2855 this.trigger('error');
2861 * Report user activity
2863 * @param {Object} event
2868 Player.prototype.reportUserActivity = function reportUserActivity(event) {
2869 this.userActivity_ = true;
2873 * Get/set if user is active
2875 * @fires Player#useractive
2876 * @fires Player#userinactive
2878 * @param {boolean} [bool]
2879 * - true if the user is active
2880 * - false if the user is inactive
2881 * @return {boolean|Player}
2882 * - the current value of userActive when getting
2883 * - the player when setting
2887 Player.prototype.userActive = function userActive(bool) {
2888 if (bool !== undefined) {
2890 if (bool !== this.userActive_) {
2891 this.userActive_ = bool;
2893 // If the user was inactive and is now active we want to reset the
2895 this.userActivity_ = true;
2896 this.removeClass('vjs-user-inactive');
2897 this.addClass('vjs-user-active');
2899 * @event Player#useractive
2900 * @type {EventTarget~Event}
2902 this.trigger('useractive');
2904 // We're switching the state to inactive manually, so erase any other
2906 this.userActivity_ = false;
2908 // Chrome/Safari/IE have bugs where when you change the cursor it can
2909 // trigger a mousemove event. This causes an issue when you're hiding
2910 // the cursor when the user is inactive, and a mousemove signals user
2911 // activity. Making it impossible to go into inactive mode. Specifically
2912 // this happens in fullscreen when we really need to hide the cursor.
2914 // When this gets resolved in ALL browsers it can be removed
2915 // https://code.google.com/p/chromium/issues/detail?id=103041
2917 this.tech_.one('mousemove', function (e) {
2918 e.stopPropagation();
2923 this.removeClass('vjs-user-active');
2924 this.addClass('vjs-user-inactive');
2926 * @event Player#userinactive
2927 * @type {EventTarget~Event}
2929 this.trigger('userinactive');
2934 return this.userActive_;
2938 * Listen for user activity based on timeout value
2944 Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
2945 var mouseInProgress = void 0;
2946 var lastMoveX = void 0;
2947 var lastMoveY = void 0;
2948 var handleActivity = Fn.bind(this, this.reportUserActivity);
2950 var handleMouseMove = function handleMouseMove(e) {
2951 // #1068 - Prevent mousemove spamming
2952 // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
2953 if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
2954 lastMoveX = e.screenX;
2955 lastMoveY = e.screenY;
2960 var handleMouseDown = function handleMouseDown() {
2962 // For as long as the they are touching the device or have their mouse down,
2963 // we consider them active even if they're not moving their finger or mouse.
2964 // So we want to continue to update that they are active
2965 this.clearInterval(mouseInProgress);
2966 // Setting userActivity=true now and setting the interval to the same time
2967 // as the activityCheck interval (250) should ensure we never miss the
2968 // next activityCheck
2969 mouseInProgress = this.setInterval(handleActivity, 250);
2972 var handleMouseUp = function handleMouseUp(event) {
2974 // Stop the interval that maintains activity if the mouse/touch is down
2975 this.clearInterval(mouseInProgress);
2978 // Any mouse movement will be considered user activity
2979 this.on('mousedown', handleMouseDown);
2980 this.on('mousemove', handleMouseMove);
2981 this.on('mouseup', handleMouseUp);
2983 // Listen for keyboard navigation
2984 // Shouldn't need to use inProgress interval because of key repeat
2985 this.on('keydown', handleActivity);
2986 this.on('keyup', handleActivity);
2988 // Run an interval every 250 milliseconds instead of stuffing everything into
2989 // the mousemove/touchmove function itself, to prevent performance degradation.
2990 // `this.reportUserActivity` simply sets this.userActivity_ to true, which
2991 // then gets picked up by this loop
2992 // http://ejohn.org/blog/learning-from-twitter/
2993 var inactivityTimeout = void 0;
2995 this.setInterval(function () {
2996 // Check to see if mouse/touch activity has happened
2997 if (this.userActivity_) {
2998 // Reset the activity tracker
2999 this.userActivity_ = false;
3001 // If the user state was inactive, set the state to active
3002 this.userActive(true);
3004 // Clear any existing inactivity timeout to start the timer over
3005 this.clearTimeout(inactivityTimeout);
3007 var timeout = this.options_.inactivityTimeout;
3010 // In <timeout> milliseconds, if no more activity has occurred the
3011 // user will be considered inactive
3012 inactivityTimeout = this.setTimeout(function () {
3013 // Protect against the case where the inactivityTimeout can trigger just
3014 // before the next user activity is picked up by the activity check loop
3015 // causing a flicker
3016 if (!this.userActivity_) {
3017 this.userActive(false);
3026 * Gets or sets the current playback rate. A playback rate of
3027 * 1.0 represents normal speed and 0.5 would indicate half-speed
3028 * playback, for instance.
3030 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
3032 * @param {number} [rate]
3033 * New playback rate to set.
3035 * @return {number|Player}
3036 * - The current playback rate when getting or 1.0
3037 * - the player when setting
3041 Player.prototype.playbackRate = function playbackRate(rate) {
3042 if (rate !== undefined) {
3043 this.techCall_('setPlaybackRate', rate);
3047 if (this.tech_ && this.tech_.featuresPlaybackRate) {
3048 return this.techGet_('playbackRate');
3054 * Gets or sets the audio flag
3056 * @param {boolean} bool
3057 * - true signals that this is an audio player
3058 * - false signals that this is not an audio player
3060 * @return {Player|boolean}
3061 * - the current value of isAudio when getting
3062 * - the player if setting
3066 Player.prototype.isAudio = function isAudio(bool) {
3067 if (bool !== undefined) {
3068 this.isAudio_ = !!bool;
3072 return !!this.isAudio_;
3076 * Get the {@link VideoTrackList}
3078 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
3080 * @return {VideoTrackList}
3081 * the current video track list
3085 Player.prototype.videoTracks = function videoTracks() {
3086 // if we have not yet loadTech_, we create videoTracks_
3087 // these will be passed to the tech during loading
3089 this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default']();
3090 return this.videoTracks_;
3093 return this.tech_.videoTracks();
3097 * Get the {@link AudioTrackList}
3099 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
3101 * @return {AudioTrackList}
3102 * the current audio track list
3106 Player.prototype.audioTracks = function audioTracks() {
3107 // if we have not yet loadTech_, we create videoTracks_
3108 // these will be passed to the tech during loading
3110 this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default']();
3111 return this.audioTracks_;
3114 return this.tech_.audioTracks();
3118 * Get the {@link TextTrackList}
3120 * Text tracks are tracks of timed text events.
3121 * - Captions: text displayed over the video
3122 * for the hearing impaired
3123 * - Subtitles: text displayed over the video for
3124 * those who don't understand language in the video
3125 * - Chapters: text displayed in a menu allowing the user to jump
3126 * to particular points (chapters) in the video
3127 * - Descriptions: (not yet implemented) audio descriptions that are read back to
3128 * the user by a screen reading device
3130 * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
3132 * @return {TextTrackList|undefined}
3133 * The current TextTrackList or undefined if
3134 * or undefined if we don't have a tech
3138 Player.prototype.textTracks = function textTracks() {
3139 // cannot use techGet_ directly because it checks to see whether the tech is ready.
3140 // Flash is unlikely to be ready in time but textTracks should still work.
3142 return this.tech_.textTracks();
3147 * Get the "remote" {@link TextTrackList}. Remote Text Tracks
3148 * are tracks that were added to the HTML video element and can
3149 * be removed, whereas normal texttracks cannot be removed.
3152 * @return {TextTrackList|undefined}
3153 * The current remote text track list or undefined
3154 * if we don't have a tech
3158 Player.prototype.remoteTextTracks = function remoteTextTracks() {
3160 return this.tech_.remoteTextTracks();
3165 * Get the "remote" {@link HTMLTrackElementList}.
3166 * This gives the user all of the DOM elements that match up
3167 * with the remote {@link TextTrackList}.
3169 * @return {HTMLTrackElementList}
3170 * The current remote text track list elements
3171 * or undefined if we don't have a tech
3175 Player.prototype.remoteTextTrackEls = function remoteTextTrackEls() {
3177 return this.tech_.remoteTextTrackEls();
3182 * A helper method for adding a {@link TextTrack} to our
3183 * {@link TextTrackList}.
3185 * In addition to the W3C settings we allow adding additional info through options.
3187 * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
3189 * @param {string} [kind]
3190 * the kind of TextTrack you are adding
3192 * @param {string} [label]
3193 * the label to give the TextTrack label
3195 * @param {string} [language]
3196 * the language to set on the TextTrack
3198 * @return {TextTrack|undefined}
3199 * the TextTrack that was added or undefined
3200 * if there is no tech
3204 Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
3206 return this.tech_.addTextTrack(kind, label, language);
3211 * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
3212 * automatically removed from the video element whenever the source changes, unless
3213 * manualCleanup is set to false.
3215 * @param {Object} options
3216 * Options to pass to {@link HTMLTrackElement} during creation. See
3217 * {@link HTMLTrackElement} for object properties that you should use.
3219 * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
3221 * @return {HTMLTrackElement}
3222 * the HTMLTrackElement that was created and added
3223 * to the HTMLTrackElementList and the remote
3226 * @deprecated The default value of the "manualCleanup" parameter will default
3227 * to "false" in upcoming versions of Video.js
3231 Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
3233 return this.tech_.addRemoteTextTrack(options, manualCleanup);
3238 * Remove a remote {@link TextTrack} from the respective
3239 * {@link TextTrackList} and {@link HTMLTrackElementList}.
3241 * @param {Object} track
3242 * Remote {@link TextTrack} to remove
3244 * @return {undefined}
3245 * does not return anything
3249 Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
3250 var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
3251 _ref3$track = _ref3.track,
3252 track = _ref3$track === undefined ? arguments[0] : _ref3$track;
3254 // destructure the input into an object with a track argument, defaulting to arguments[0]
3255 // default the whole argument to an empty object if nothing was passed in
3258 return this.tech_.removeRemoteTextTrack(track);
3266 * current video width
3270 Player.prototype.videoWidth = function videoWidth() {
3271 return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
3278 * current video height
3282 Player.prototype.videoHeight = function videoHeight() {
3283 return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
3286 // Methods to add support for
3287 // initialTime: function() { return this.techCall_('initialTime'); },
3288 // startOffsetTime: function() { return this.techCall_('startOffsetTime'); },
3289 // played: function() { return this.techCall_('played'); },
3290 // defaultPlaybackRate: function() { return this.techCall_('defaultPlaybackRate'); },
3291 // defaultMuted: function() { return this.techCall_('defaultMuted'); }
3294 * The player's language code
3295 * NOTE: The language should be set in the player options if you want the
3296 * the controls to be built with a specific language. Changing the lanugage
3297 * later will not update controls text.
3299 * @param {string} [code]
3300 * the language code to set the player to
3302 * @return {string|Player}
3303 * - The current language code when getting
3304 * - A reference to the player when setting
3308 Player.prototype.language = function language(code) {
3309 if (code === undefined) {
3310 return this.language_;
3313 this.language_ = String(code).toLowerCase();
3318 * Get the player's language dictionary
3319 * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
3320 * Languages specified directly in the player options have precedence
3323 * An array of of supported languages
3327 Player.prototype.languages = function languages() {
3328 return (0, _mergeOptions2['default'])(Player.prototype.options_.languages, this.languages_);
3332 * returns a JavaScript object reperesenting the current track
3333 * information. **DOES not return it as JSON**
3336 * Object representing the current of track info
3340 Player.prototype.toJSON = function toJSON() {
3341 var options = (0, _mergeOptions2['default'])(this.options_);
3342 var tracks = options.tracks;
3344 options.tracks = [];
3346 for (var i = 0; i < tracks.length; i++) {
3347 var track = tracks[i];
3349 // deep merge tracks and null out player so no circular references
3350 track = (0, _mergeOptions2['default'])(track);
3351 track.player = undefined;
3352 options.tracks[i] = track;
3359 * Creates a simple modal dialog (an instance of the {@link ModalDialog}
3360 * component) that immediately overlays the player with arbitrary
3361 * content and removes itself when closed.
3363 * @param {string|Function|Element|Array|null} content
3364 * Same as {@link ModalDialog#content}'s param of the same name.
3365 * The most straight-forward usage is to provide a string or DOM
3368 * @param {Object} [options]
3369 * Extra options which will be passed on to the {@link ModalDialog}.
3371 * @return {ModalDialog}
3372 * the {@link ModalDialog} that was created
3376 Player.prototype.createModal = function createModal(content, options) {
3379 options = options || {};
3380 options.content = content || '';
3382 var modal = new _modalDialog2['default'](this, options);
3384 this.addChild(modal);
3385 modal.on('dispose', function () {
3386 _this5.removeChild(modal);
3389 return modal.open();
3395 * @param {Element} tag
3399 * An object containing all of the settings
3404 Player.getTagSettings = function getTagSettings(tag) {
3410 var tagOptions = Dom.getElAttributes(tag);
3411 var dataSetup = tagOptions['data-setup'];
3413 if (Dom.hasElClass(tag, 'vjs-fluid')) {
3414 tagOptions.fluid = true;
3417 // Check if data-setup attr exists.
3418 if (dataSetup !== null) {
3419 // Parse options JSON
3420 // If empty string, make it a parsable json object.
3421 var _safeParseTuple = (0, _tuple2['default'])(dataSetup || '{}'),
3422 err = _safeParseTuple[0],
3423 data = _safeParseTuple[1];
3426 _log2['default'].error(err);
3428 (0, _obj.assign)(tagOptions, data);
3431 (0, _obj.assign)(baseOptions, tagOptions);
3433 // Get tag children settings
3434 if (tag.hasChildNodes()) {
3435 var children = tag.childNodes;
3437 for (var i = 0, j = children.length; i < j; i++) {
3438 var child = children[i];
3439 // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
3440 var childName = child.nodeName.toLowerCase();
3442 if (childName === 'source') {
3443 baseOptions.sources.push(Dom.getElAttributes(child));
3444 } else if (childName === 'track') {
3445 baseOptions.tracks.push(Dom.getElAttributes(child));
3454 * Determine wether or not flexbox is supported
3457 * - true if flexbox is supported
3458 * - false if flexbox is not supported
3462 Player.prototype.flexNotSupported_ = function flexNotSupported_() {
3463 var elem = _document2['default'].createElement('i');
3465 // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
3466 // common flex features that we can rely on when checking for flex support.
3467 return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
3468 // IE10-specific (2012 flex spec)
3469 'msFlexOrder' in elem.style);
3473 }(_component2['default']);
3476 * Global player list
3482 Player.players = {};
3484 var navigator = _window2['default'].navigator;
3487 * Player instance options, surfaced using options
3488 * options = Player.prototype.options_
3489 * Make changes in options, not here.
3494 Player.prototype.options_ = {
3495 // Default order of fallback technology
3496 techOrder: ['html5', 'flash'],
3497 // techOrder: ['flash','html5'],
3502 // defaultVolume: 0.85,
3503 defaultVolume: 0.00,
3505 // default inactivity timeout
3506 inactivityTimeout: 2000,
3508 // default playback rates
3510 // Add playback rate selection by adding rates
3511 // 'playbackRates': [0.5, 1, 1.5, 2],
3513 // Included control sets
3514 children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
3516 language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
3518 // locales and their language translations
3521 // Default message to show when a video cannot be played.
3522 notSupportedMessage: 'No compatible source was found for this media.'
3527 * Returns whether or not the player is in the "ended" state.
3529 * @return {Boolean} True if the player is in the ended state, false if not.
3530 * @method Player#ended
3534 * Returns whether or not the player is in the "seeking" state.
3536 * @return {Boolean} True if the player is in the seeking state, false if not.
3537 * @method Player#seeking
3541 * Returns the TimeRanges of the media that are currently available
3544 * @return {TimeRanges} the seekable intervals of the media timeline
3545 * @method Player#seekable
3549 * Returns the current state of network activity for the element, from
3550 * the codes in the list below.
3551 * - NETWORK_EMPTY (numeric value 0)
3552 * The element has not yet been initialised. All attributes are in
3553 * their initial states.
3554 * - NETWORK_IDLE (numeric value 1)
3555 * The element's resource selection algorithm is active and has
3556 * selected a resource, but it is not actually using the network at
3558 * - NETWORK_LOADING (numeric value 2)
3559 * The user agent is actively trying to download data.
3560 * - NETWORK_NO_SOURCE (numeric value 3)
3561 * The element's resource selection algorithm is active, but it has
3562 * not yet found a resource to use.
3564 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
3565 * @return {number} the current network activity state
3566 * @method Player#networkState
3570 * Returns a value that expresses the current state of the element
3571 * with respect to rendering the current playback position, from the
3572 * codes in the list below.
3573 * - HAVE_NOTHING (numeric value 0)
3574 * No information regarding the media resource is available.
3575 * - HAVE_METADATA (numeric value 1)
3576 * Enough of the resource has been obtained that the duration of the
3577 * resource is available.
3578 * - HAVE_CURRENT_DATA (numeric value 2)
3579 * Data for the immediate current playback position is available.
3580 * - HAVE_FUTURE_DATA (numeric value 3)
3581 * Data for the immediate current playback position is available, as
3582 * well as enough data for the user agent to advance the current
3583 * playback position in the direction of playback.
3584 * - HAVE_ENOUGH_DATA (numeric value 4)
3585 * The user agent estimates that enough data is available for
3586 * playback to proceed uninterrupted.
3588 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
3589 * @return {number} the current playback rendering state
3590 * @method Player#readyState
3592 'readyState'].forEach(function (fn) {
3593 Player.prototype[fn] = function () {
3594 return this.techGet_(fn);
3598 TECH_EVENTS_RETRIGGER.forEach(function (event) {
3599 Player.prototype['handleTech' + (0, _toTitleCase2['default'])(event) + '_'] = function () {
3600 return this.trigger(event);
3605 * Fired when the player has initial duration and dimension information
3607 * @event Player#loadedmetadata
3608 * @type {EventTarget~Event}
3612 * Fired when the player has downloaded data at the current playback position
3614 * @event Player#loadeddata
3615 * @type {EventTarget~Event}
3619 * Fired when the current playback position has changed *
3620 * During playback this is fired every 15-250 milliseconds, depending on the
3621 * playback technology in use.
3623 * @event Player#timeupdate
3624 * @type {EventTarget~Event}
3628 * Fired when the volume changes
3630 * @event Player#volumechange
3631 * @type {EventTarget~Event}
3634 _component2['default'].registerComponent('Player', Player);
3635 exports['default'] = Player;