1
0
mirror of https://github.com/videojs/video.js.git synced 2025-07-15 01:34:23 +02:00

@misteroneill updated source code to pass linter

This commit is contained in:
Pat O'Neill
2016-07-25 09:49:38 -04:00
committed by Gary Katsevman
parent c89b75699e
commit e85c1c0391
86 changed files with 1890 additions and 1610 deletions

View File

@ -1,47 +0,0 @@
{
"evil" : true,
"validthis": true,
"node" : true,
"debug" : true,
"boss" : true,
"expr" : true,
"eqnull" : true,
"quotmark" : "single",
"sub" : true,
"trailing" : true,
"undef" : true,
"laxbreak" : true,
"esnext" : true,
"eqeqeq" : true,
"predef" : [
"_V_",
"goog",
"console",
"require",
"define",
"module",
"exports",
"process",
"q",
"asyncTest",
"deepEqual",
"equal",
"expect",
"module",
"notDeepEqual",
"notEqual",
"notStrictEqual",
"ok",
"throws",
"QUnit",
"raises",
"start",
"stop",
"strictEqual",
"test",
"throws",
"sinon"
]
}

View File

@ -114,14 +114,6 @@ module.exports = function(grunt) {
build: ['build/temp/*'],
dist: ['dist/*']
},
jshint: {
src: {
src: ['src/js/**/*.js', 'Gruntfile.js', 'test/unit/**/*.js'],
options: {
jshintrc: '.jshintrc'
}
}
},
uglify: {
options: {
sourceMap: true,
@ -160,10 +152,6 @@ module.exports = function(grunt) {
skin: {
files: ['src/css/**/*'],
tasks: ['sass']
},
jshint: {
files: ['src/**/*', 'test/unit/**/*.js', 'Gruntfile.js'],
tasks: 'jshint'
}
},
connect: {
@ -443,6 +431,14 @@ module.exports = function(grunt) {
src: ['build/temp/video.js']
}
}
},
shell: {
lint: {
command: 'vjsstandard',
options: {
preferLocal: true
}
}
}
});
@ -455,7 +451,6 @@ module.exports = function(grunt) {
const buildDependents = [
'clean:build',
'jshint',
'browserify:build',
'exorcise:build',
'concat:novtt',

View File

@ -14,6 +14,7 @@
"homepage": "http://videojs.com",
"author": "Steve Heffernan",
"scripts": {
"lint": "vjsstandard",
"test": "grunt test"
},
"repository": {
@ -57,7 +58,6 @@
"grunt-contrib-connect": "~0.7.1",
"grunt-contrib-copy": "^0.8.0",
"grunt-contrib-cssmin": "~0.6.0",
"grunt-contrib-jshint": "~0.11.3",
"grunt-contrib-less": "~0.6.4",
"grunt-contrib-uglify": "^0.8.0",
"grunt-contrib-watch": "~0.1.4",
@ -66,6 +66,7 @@
"grunt-fastly": "^0.1.3",
"grunt-github-releaser": "^0.1.17",
"grunt-karma": "^0.8.3",
"grunt-shell": "^1.3.0",
"grunt-version": "~0.3.0",
"grunt-videojs-languages": "0.0.4",
"grunt-zip": "0.10.2",
@ -87,9 +88,10 @@
"sinon": "^1.16.1",
"time-grunt": "^1.1.1",
"uglify-js": "~2.3.6",
"videojs-doc-generator": "0.0.1"
"videojs-doc-generator": "0.0.1",
"videojs-standard": "^5.0.0"
},
"standard": {
"vjsstandard": {
"ignore": [
"**/Gruntfile.js",
"**/build/**",
@ -97,7 +99,9 @@
"**/docs/**",
"**/lang/**",
"**/sandbox/**",
"**/test/**"
"**/test/api/**",
"**/test/coverage/**",
"**/test/karma.conf.js"
]
}
}

View File

@ -6,13 +6,17 @@
import window from 'global/window';
import document from 'global/document';
if (window.VIDEOJS_NO_BASE_THEME) return;
if (window.VIDEOJS_NO_BASE_THEME) {
return;
}
const styles = '{{GENERATED_STYLES}}';
if (styles === '{{GENERATED'+'_STYLES}}');
// Don't think we need this as it's a noop?
// if (styles === '{{GENERATED'+'_STYLES}}');
const styleNode = document.createElement('style');
styleNode.innerHTML = styles;
document.head.insertBefore(styleNode, document.head.firstChild);

View File

@ -3,10 +3,7 @@
*/
import ClickableComponent from './clickable-component.js';
import Component from './component';
import * as Events from './utils/events.js';
import * as Fn from './utils/fn.js';
import log from './utils/log.js';
import document from 'global/document';
import assign from 'object.assign';
/**
@ -32,7 +29,7 @@ class Button extends ClickableComponent {
* @return {Element}
* @method createEl
*/
createEl(tag='button', props={}, attributes={}) {
createEl(tag = 'button', props = {}, attributes = {}) {
props = assign({
className: this.buildCSSClass()
}, props);
@ -53,8 +50,12 @@ class Button extends ClickableComponent {
// Add attributes for button element
attributes = assign({
type: 'button', // Necessary since the default button type is "submit"
'aria-live': 'polite' // let the screen reader user know that the text of the button may change
// Necessary since the default button type is "submit"
'type': 'button',
// let the screen reader user know that the text of the button may change
'aria-live': 'polite'
}, attributes);
let el = Component.prototype.createEl.call(this, tag, props, attributes);
@ -73,8 +74,9 @@ class Button extends ClickableComponent {
* @deprecated
* @method addChild
*/
addChild(child, options={}) {
addChild(child, options = {}) {
let className = this.constructor.name;
log.warn(`Adding an actionable (user controllable) child to a Button (${className}) is not supported; use a ClickableComponent instead.`);
// Avoid the error message generated by ClickableComponent's addChild method
@ -87,13 +89,15 @@ class Button extends ClickableComponent {
* @method handleKeyPress
*/
handleKeyPress(event) {
// Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
if (event.which === 32 || event.which === 13) {
} else {
super.handleKeyPress(event); // Pass keypress handling up for unsupported keys
return;
}
}
// Pass keypress handling up for unsupported keys
super.handleKeyPress(event);
}
}
Component.registerComponent('Button', Button);

View File

@ -39,7 +39,7 @@ class ClickableComponent extends Component {
* @return {Element}
* @method createEl
*/
createEl(tag='div', props={}, attributes={}) {
createEl(tag = 'div', props = {}, attributes = {}) {
props = assign({
className: this.buildCSSClass(),
tabIndex: 0
@ -51,8 +51,10 @@ class ClickableComponent extends Component {
// Add ARIA attributes for clickable element which is not a native HTML button
attributes = assign({
role: 'button',
'aria-live': 'polite' // let the screen reader user know that the text of the element may change
'role': 'button',
// let the screen reader user know that the text of the element may change
'aria-live': 'polite'
}, attributes);
let el = super.createEl(tag, props, attributes);
@ -91,9 +93,11 @@ class ClickableComponent extends Component {
* @return {String}
* @method controlText
*/
controlText(text, el=this.el()) {
if (!text) return this.controlText_ || 'Need Text';
controlText(text, el = this.el()) {
if (!text) {
return this.controlText_ || 'Need Text';
}
const localizedText = this.localize(text);
this.controlText_ = text;
@ -121,14 +125,14 @@ class ClickableComponent extends Component {
* @return {Component} The child component (created by this process if a string was used)
* @method addChild
*/
addChild(child, options={}) {
addChild(child, options = {}) {
// TODO: Fix adding an actionable child to a ClickableComponent; currently
// it will cause issues with assistive technology (e.g. screen readers)
// which support ARIA, since an element with role="button" cannot have
// actionable child elements.
//let className = this.constructor.name;
//log.warn(`Adding a child to a ClickableComponent (${className}) can cause issues with assistive technology which supports ARIA, since an element with role="button" cannot have actionable child elements.`);
// let className = this.constructor.name;
// log.warn(`Adding a child to a ClickableComponent (${className}) can cause issues with assistive technology which supports ARIA, since an element with role="button" cannot have actionable child elements.`);
return super.addChild(child, options);
}
@ -179,12 +183,15 @@ class ClickableComponent extends Component {
* @method handleKeyPress
*/
handleKeyPress(event) {
// Support Space (32) or Enter (13) key operation to fire a click event
if (event.which === 32 || event.which === 13) {
event.preventDefault();
this.handleClick(event);
} else if (super.handleKeyPress) {
super.handleKeyPress(event); // Pass keypress handling up for unsupported keys
// Pass keypress handling up for unsupported keys
super.handleKeyPress(event);
}
}

View File

@ -3,7 +3,6 @@
*
* Player Component - Base class for all UI objects
*/
import window from 'global/window';
import * as Dom from './utils/dom.js';
import * as Fn from './utils/fn.js';
@ -14,7 +13,6 @@ import toTitleCase from './utils/to-title-case.js';
import assign from 'object.assign';
import mergeOptions from './utils/merge-options.js';
/**
* Base UI Component class
* Components are embeddable UI objects that are represented by both a
@ -32,7 +30,7 @@ import mergeOptions from './utils/merge-options.js';
* ```
* Components are also event targets.
* ```js
* button.on('click', function(){
* button.on('click', function() {
* console.log('Button Clicked!');
* });
* button.trigger('customevent');
@ -340,7 +338,7 @@ class Component {
* @return {Component} The child component (created by this process if a string was used)
* @method addChild
*/
addChild(child, options={}, index=this.children_.length) {
addChild(child, options = {}, index = this.children_.length) {
let component;
let componentName;
@ -408,6 +406,7 @@ class Component {
if (typeof component.el === 'function' && component.el()) {
let childNodes = this.contentEl().children;
let refNode = childNodes[index] || null;
this.contentEl().insertBefore(component.el(), refNode);
}
@ -540,6 +539,7 @@ class Component {
// If two of the same component are used, different names should be supplied
// for each
let newChild = this.addChild(name, opts);
if (newChild) {
this[name] = newChild;
}
@ -563,13 +563,13 @@ class Component {
return !workingChildren.some(function(wchild) {
if (typeof wchild === 'string') {
return child === wchild;
} else {
return child === wchild.name;
}
return child === wchild.name;
});
}))
.map((child) => {
let name, opts;
let name;
let opts;
if (typeof child === 'string') {
name = child;
@ -587,6 +587,7 @@ class Component {
// See https://github.com/videojs/video.js/issues/2772
let c = Component.getComponent(child.opts.componentClass ||
toTitleCase(child.name));
return c && !Tech.isTech(c);
})
.forEach(handleAdd);
@ -608,7 +609,7 @@ class Component {
/**
* Add an event listener to this component's element
* ```js
* var myFunc = function(){
* var myFunc = function() {
* var myComponent = this;
* // Do something when the event is fired
* };
@ -797,7 +798,7 @@ class Component {
* @return {Component}
* @method ready
*/
ready(fn, sync=false) {
ready(fn, sync = false) {
if (fn) {
if (this.isReady_) {
if (sync) {
@ -824,14 +825,14 @@ class Component {
this.isReady_ = true;
// Ensure ready is triggerd asynchronously
this.setTimeout(function(){
this.setTimeout(function() {
let readyQueue = this.readyQueue_;
// Reset Ready Queue
this.readyQueue_ = [];
if (readyQueue && readyQueue.length > 0) {
readyQueue.forEach(function(fn){
readyQueue.forEach(function(fn) {
fn.call(this);
}, this);
}
@ -1106,11 +1107,13 @@ class Component {
if (typeof window.getComputedStyle === 'function') {
const computedStyle = window.getComputedStyle(this.el_);
computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
} else if (this.el_.currentStyle) {
// ie 8 doesn't support computed style, shim it
// return clientWidth or clientHeight instead for better accuracy
const rule = `offset${toTitleCase(widthOrHeight)}`;
computedWidthOrHeight = this.el_[rule];
}
@ -1194,7 +1197,7 @@ class Component {
// So, if we moved only a small distance, this could still be a tap
const xdiff = event.touches[0].pageX - firstTouch.pageX;
const ydiff = event.touches[0].pageY - firstTouch.pageY;
const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
if (touchDistance > tapMovementThreshold) {
couldBeTap = false;

View File

@ -3,7 +3,6 @@
*/
import TrackButton from '../track-button.js';
import Component from '../../component.js';
import * as Fn from '../../utils/fn.js';
import AudioTrackMenuItem from './audio-track-menu-item.js';
/**
@ -40,19 +39,19 @@ class AudioTrackButton extends TrackButton {
* @method createItems
*/
createItems(items = []) {
let tracks = this.player_.audioTracks && this.player_.audioTracks();
const tracks = this.player_.audioTracks && this.player_.audioTracks();
if (!tracks) {
return items;
}
for (let i = 0; i < tracks.length; i++) {
let track = tracks[i];
const track = tracks[i];
items.push(new AudioTrackMenuItem(this.player_, {
track,
// MenuItem is selectable
'selectable': true,
'track': track
selectable: true
}));
}

View File

@ -15,8 +15,8 @@ import * as Fn from '../../utils/fn.js';
*/
class AudioTrackMenuItem extends MenuItem {
constructor(player, options) {
let track = options.track;
let tracks = player.audioTracks();
const track = options.track;
const tracks = player.audioTracks();
// Modify options for parent MenuItem class's init.
options.label = track.label || track.language || 'Unknown';
@ -27,7 +27,7 @@ class AudioTrackMenuItem extends MenuItem {
this.track = track;
if (tracks) {
let changeHandler = Fn.bind(this, this.handleTracksChange);
const changeHandler = Fn.bind(this, this.handleTracksChange);
tracks.addEventListener('change', changeHandler);
this.on('dispose', () => {
@ -42,14 +42,16 @@ class AudioTrackMenuItem extends MenuItem {
* @method handleClick
*/
handleClick(event) {
let tracks = this.player_.audioTracks();
const tracks = this.player_.audioTracks();
super.handleClick(event);
if (!tracks) return;
if (!tracks) {
return;
}
for (let i = 0; i < tracks.length; i++) {
let track = tracks[i];
const track = tracks[i];
if (track === this.track) {
track.enabled = true;

View File

@ -4,24 +4,24 @@
import Component from '../component.js';
// Required children
import PlayToggle from './play-toggle.js';
import CurrentTimeDisplay from './time-controls/current-time-display.js';
import DurationDisplay from './time-controls/duration-display.js';
import TimeDivider from './time-controls/time-divider.js';
import RemainingTimeDisplay from './time-controls/remaining-time-display.js';
import LiveDisplay from './live-display.js';
import ProgressControl from './progress-control/progress-control.js';
import FullscreenToggle from './fullscreen-toggle.js';
import VolumeControl from './volume-control/volume-control.js';
import VolumeMenuButton from './volume-menu-button.js';
import MuteToggle from './mute-toggle.js';
import ChaptersButton from './text-track-controls/chapters-button.js';
import DescriptionsButton from './text-track-controls/descriptions-button.js';
import SubtitlesButton from './text-track-controls/subtitles-button.js';
import CaptionsButton from './text-track-controls/captions-button.js';
import AudioTrackButton from './audio-track-controls/audio-track-button.js';
import PlaybackRateMenuButton from './playback-rate-menu/playback-rate-menu-button.js';
import CustomControlSpacer from './spacer-controls/custom-control-spacer.js';
import './play-toggle.js';
import './time-controls/current-time-display.js';
import './time-controls/duration-display.js';
import './time-controls/time-divider.js';
import './time-controls/remaining-time-display.js';
import './live-display.js';
import './progress-control/progress-control.js';
import './fullscreen-toggle.js';
import './volume-control/volume-control.js';
import './volume-menu-button.js';
import './mute-toggle.js';
import './text-track-controls/chapters-button.js';
import './text-track-controls/descriptions-button.js';
import './text-track-controls/subtitles-button.js';
import './text-track-controls/captions-button.js';
import './audio-track-controls/audio-track-button.js';
import './playback-rate-menu/playback-rate-menu-button.js';
import './spacer-controls/custom-control-spacer.js';
/**
* Container of main controls
@ -42,7 +42,8 @@ class ControlBar extends Component {
className: 'vjs-control-bar',
dir: 'ltr'
}, {
'role': 'group' // The control bar is a group, so it can contain menuitems
// The control bar is a group, so it can contain menuitems
role: 'group'
});
}
}

View File

@ -27,7 +27,7 @@ class LiveDisplay extends Component {
* @method createEl
*/
createEl() {
var el = super.createEl('div', {
const el = super.createEl('div', {
className: 'vjs-live-control vjs-control'
});

View File

@ -21,14 +21,15 @@ class MuteToggle extends Button {
this.on(player, 'volumechange', this.update);
// hide mute toggle if the current tech doesn't support volume control
if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
if (player.tech_ && player.tech_.featuresVolumeControl === false) {
this.addClass('vjs-hidden');
}
this.on(player, 'loadstart', function() {
this.update(); // We need to update the button to account for a default muted state.
// We need to update the button to account for a default muted state.
this.update();
if (player.tech_['featuresVolumeControl'] === false) {
if (player.tech_.featuresVolumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');
@ -52,7 +53,7 @@ class MuteToggle extends Button {
* @method handleClick
*/
handleClick() {
this.player_.muted( this.player_.muted() ? false : true );
this.player_.muted(this.player_.muted() ? false : true);
}
/**
@ -61,8 +62,8 @@ class MuteToggle extends Button {
* @method update
*/
update() {
var vol = this.player_.volume(),
level = 3;
const vol = this.player_.volume();
let level = 3;
if (vol === 0 || this.player_.muted()) {
level = 0;
@ -75,13 +76,14 @@ class MuteToggle extends Button {
// Don't rewrite the button text if the actual text doesn't change.
// This causes unnecessary and confusing information for screen reader users.
// This check is needed because this function gets called every time the volume level is changed.
let toMute = this.player_.muted() ? 'Unmute' : 'Mute';
const toMute = this.player_.muted() ? 'Unmute' : 'Mute';
if (this.controlText() !== toMute) {
this.controlText(toMute);
}
/* TODO improve muted icon classes */
for (var i = 0; i < 4; i++) {
// TODO improve muted icon classes
for (let i = 0; i < 4; i++) {
Dom.removeElClass(this.el_, `vjs-vol-${i}`);
}
Dom.addElClass(this.el_, `vjs-vol-${level}`);

View File

@ -14,7 +14,7 @@ import Component from '../component.js';
*/
class PlayToggle extends Button {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.on(player, 'play', this.handlePlay);
@ -52,7 +52,8 @@ class PlayToggle extends Button {
handlePlay() {
this.removeClass('vjs-paused');
this.addClass('vjs-playing');
this.controlText('Pause'); // change the button text to "Pause"
// change the button text to "Pause"
this.controlText('Pause');
}
/**
@ -63,7 +64,8 @@ class PlayToggle extends Button {
handlePause() {
this.removeClass('vjs-playing');
this.addClass('vjs-paused');
this.controlText('Play'); // change the button text to "Play"
// change the button text to "Play"
this.controlText('Play');
}
}

View File

@ -17,7 +17,7 @@ import * as Dom from '../../utils/dom.js';
*/
class PlaybackRateMenuButton extends MenuButton {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.updateVisibility();
@ -34,7 +34,7 @@ class PlaybackRateMenuButton extends MenuButton {
* @method createEl
*/
createEl() {
let el = super.createEl();
const el = super.createEl();
this.labelEl_ = Dom.createEl('div', {
className: 'vjs-playback-rate-value',
@ -63,13 +63,13 @@ class PlaybackRateMenuButton extends MenuButton {
* @method createMenu
*/
createMenu() {
let menu = new Menu(this.player());
let rates = this.playbackRates();
const menu = new Menu(this.player());
const rates = this.playbackRates();
if (rates) {
for (let i = rates.length - 1; i >= 0; i--) {
menu.addChild(
new PlaybackRateMenuItem(this.player(), { 'rate': rates[i] + 'x'})
new PlaybackRateMenuItem(this.player(), {rate: rates[i] + 'x'})
);
}
}
@ -94,12 +94,13 @@ class PlaybackRateMenuButton extends MenuButton {
*/
handleClick() {
// select next rate option
let currentRate = this.player().playbackRate();
let rates = this.playbackRates();
const currentRate = this.player().playbackRate();
const rates = this.playbackRates();
// this will select first one if the last one currently selected
let newRate = rates[0];
for (let i = 0; i < rates.length ; i++) {
for (let i = 0; i < rates.length; i++) {
if (rates[i] > currentRate) {
newRate = rates[i];
break;
@ -115,7 +116,7 @@ class PlaybackRateMenuButton extends MenuButton {
* @method playbackRates
*/
playbackRates() {
return this.options_['playbackRates'] || (this.options_.playerOptions && this.options_.playerOptions['playbackRates']);
return this.options_.playbackRates || (this.options_.playerOptions && this.options_.playerOptions.playbackRates);
}
/**
@ -126,10 +127,10 @@ class PlaybackRateMenuButton extends MenuButton {
* @method playbackRateSupported
*/
playbackRateSupported() {
return this.player().tech_
&& this.player().tech_['featuresPlaybackRate']
&& this.playbackRates()
&& this.playbackRates().length > 0
return this.player().tech_ &&
this.player().tech_.featuresPlaybackRate &&
this.playbackRates() &&
this.playbackRates().length > 0
;
}

View File

@ -14,13 +14,13 @@ import Component from '../../component.js';
*/
class PlaybackRateMenuItem extends MenuItem {
constructor(player, options){
let label = options['rate'];
let rate = parseFloat(label, 10);
constructor(player, options) {
const label = options.rate;
const rate = parseFloat(label, 10);
// Modify options for parent MenuItem class's init.
options['label'] = label;
options['selected'] = rate === 1;
options.label = label;
options.selected = rate === 1;
super(player, options);
this.label = label;

View File

@ -14,7 +14,7 @@ import * as Dom from '../../utils/dom.js';
*/
class LoadProgressBar extends Component {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.on(player, 'progress', this.update);
}
@ -38,14 +38,16 @@ class LoadProgressBar extends Component {
* @method update
*/
update() {
let buffered = this.player_.buffered();
let duration = this.player_.duration();
let bufferedEnd = this.player_.bufferedEnd();
let children = this.el_.children;
const buffered = this.player_.buffered();
const duration = this.player_.duration();
const bufferedEnd = this.player_.bufferedEnd();
const children = this.el_.children;
// get the percent width of a time compared to the total end
let percentify = function (time, end){
let percent = (time / end) || 0; // no NaN
const percentify = function(time, end) {
// no NaN
const percent = (time / end) || 0;
return ((percent >= 1 ? 1 : percent) * 100) + '%';
};
@ -54,8 +56,8 @@ class LoadProgressBar extends Component {
// add child elements to represent the individual buffered time ranges
for (let i = 0; i < buffered.length; i++) {
let start = buffered.start(i);
let end = buffered.end(i);
const start = buffered.start(i);
const end = buffered.end(i);
let part = children[i];
if (!part) {
@ -69,7 +71,7 @@ class LoadProgressBar extends Component {
// remove unused buffered range elements
for (let i = children.length; i > buffered.length; i--) {
this.el_.removeChild(children[i-1]);
this.el_.removeChild(children[i - 1]);
}
}

View File

@ -55,24 +55,24 @@ class MouseTimeDisplay extends Component {
}
handleMouseMove(event) {
let duration = this.player_.duration();
let newTime = this.calculateDistance(event) * duration;
let position = event.pageX - Dom.findElPosition(this.el().parentNode).left;
const duration = this.player_.duration();
const newTime = this.calculateDistance(event) * duration;
const position = event.pageX - Dom.findElPosition(this.el().parentNode).left;
this.update(newTime, position);
}
update(newTime, position) {
let time = formatTime(newTime, this.player_.duration());
const time = formatTime(newTime, this.player_.duration());
this.el().style.left = position + 'px';
this.el().setAttribute('data-current-time', time);
if (this.keepTooltipsInside) {
let clampedPosition = this.clampPosition_(position);
let difference = position - clampedPosition + 1;
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
let tooltipWidthHalf = tooltipWidth / 2;
const clampedPosition = this.clampPosition_(position);
const difference = position - clampedPosition + 1;
const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
const tooltipWidthHalf = tooltipWidth / 2;
this.tooltip.innerHTML = time;
this.tooltip.style.right = `-${tooltipWidthHalf - difference}px`;
@ -98,9 +98,9 @@ class MouseTimeDisplay extends Component {
return position;
}
let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
let tooltipWidthHalf = tooltipWidth / 2;
const playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltip).width);
const tooltipWidthHalf = tooltipWidth / 2;
let actualPosition = position;
if (position < tooltipWidthHalf) {

View File

@ -3,7 +3,6 @@
*/
import Component from '../../component.js';
import * as Fn from '../../utils/fn.js';
import * as Dom from '../../utils/dom.js';
import formatTime from '../../utils/format-time.js';
/**
@ -16,7 +15,7 @@ import formatTime from '../../utils/format-time.js';
*/
class PlayProgressBar extends Component {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.updateDataAttr();
this.on(player, 'timeupdate', this.updateDataAttr);
@ -48,7 +47,8 @@ class PlayProgressBar extends Component {
}
updateDataAttr() {
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
this.el_.setAttribute('data-current-time', formatTime(time, this.player_.duration()));
}

View File

@ -2,8 +2,9 @@
* @file progress-control.js
*/
import Component from '../../component.js';
import SeekBar from './seek-bar.js';
import MouseTimeDisplay from './mouse-time-display.js';
import './seek-bar.js';
import './mouse-time-display.js';
/**
* The Progress Control component contains the seek bar, load progress,

View File

@ -4,12 +4,12 @@
import window from 'global/window';
import Slider from '../../slider/slider.js';
import Component from '../../component.js';
import LoadProgressBar from './load-progress-bar.js';
import PlayProgressBar from './play-progress-bar.js';
import TooltipProgressBar from './tooltip-progress-bar.js';
import * as Fn from '../../utils/fn.js';
import formatTime from '../../utils/format-time.js';
import assign from 'object.assign';
import './load-progress-bar.js';
import './play-progress-bar.js';
import './tooltip-progress-bar.js';
/**
* Seek Bar and holder for the progress bars
@ -21,7 +21,7 @@ import assign from 'object.assign';
*/
class SeekBar extends Slider {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.on(player, 'timeupdate', this.updateProgress);
this.on(player, 'ended', this.updateProgress);
@ -65,9 +65,10 @@ class SeekBar extends Slider {
this.updateAriaAttributes(this.tooltipProgressBar.el_);
this.tooltipProgressBar.el_.style.width = this.bar.el_.style.width;
let playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
let tooltipWidth = parseFloat(window.getComputedStyle(this.tooltipProgressBar.tooltip).width);
let tooltipStyle = this.tooltipProgressBar.el().style;
const playerWidth = parseFloat(window.getComputedStyle(this.player().el()).width);
const tooltipWidth = parseFloat(window.getComputedStyle(this.tooltipProgressBar.tooltip).width);
const tooltipStyle = this.tooltipProgressBar.el().style;
tooltipStyle.maxWidth = Math.floor(playerWidth - (tooltipWidth / 2)) + 'px';
tooltipStyle.minWidth = Math.ceil(tooltipWidth / 2) + 'px';
tooltipStyle.right = `-${tooltipWidth / 2}px`;
@ -76,9 +77,12 @@ class SeekBar extends Slider {
updateAriaAttributes(el) {
// Allows for smooth scrubbing, when player can't keep up.
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); // machine readable value of progress bar (percentage complete)
el.setAttribute('aria-valuetext', formatTime(time, this.player_.duration())); // human readable value of progress bar (time complete)
const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
// machine readable value of progress bar (percentage complete)
el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2));
// human readable value of progress bar (time complete)
el.setAttribute('aria-valuetext', formatTime(time, this.player_.duration()));
}
/**
@ -88,7 +92,8 @@ class SeekBar extends Slider {
* @method getPercent
*/
getPercent() {
let percent = this.player_.currentTime() / this.player_.duration();
const percent = this.player_.currentTime() / this.player_.duration();
return percent >= 1 ? 1 : percent;
}
@ -115,7 +120,9 @@ class SeekBar extends Slider {
let newTime = this.calculateDistance(event) * this.player_.duration();
// Don't let video end while scrubbing.
if (newTime === this.player_.duration()) { newTime = newTime - 0.1; }
if (newTime === this.player_.duration()) {
newTime = newTime - 0.1;
}
// Set new time (tell player to seek to new time)
this.player_.currentTime(newTime);
@ -141,7 +148,8 @@ class SeekBar extends Slider {
* @method stepForward
*/
stepForward() {
this.player_.currentTime(this.player_.currentTime() + 5); // more quickly fast forward for keyboard-only users
// more quickly fast forward for keyboard-only users
this.player_.currentTime(this.player_.currentTime() + 5);
}
/**
@ -150,7 +158,8 @@ class SeekBar extends Slider {
* @method stepBack
*/
stepBack() {
this.player_.currentTime(this.player_.currentTime() - 5); // more quickly rewind for keyboard-only users
// more quickly rewind for keyboard-only users
this.player_.currentTime(this.player_.currentTime() - 5);
}
}
@ -161,7 +170,7 @@ SeekBar.prototype.options_ = {
'mouseTimeDisplay',
'playProgressBar'
],
'barName': 'playProgressBar'
barName: 'playProgressBar'
};
SeekBar.prototype.playerEvent = 'timeupdate';

View File

@ -3,7 +3,6 @@
*/
import Component from '../../component.js';
import * as Fn from '../../utils/fn.js';
import * as Dom from '../../utils/dom.js';
import formatTime from '../../utils/format-time.js';
/**
@ -16,7 +15,7 @@ import formatTime from '../../utils/format-time.js';
*/
class TooltipProgressBar extends Component {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.updateDataAttr();
this.on(player, 'timeupdate', this.updateDataAttr);
@ -30,7 +29,7 @@ class TooltipProgressBar extends Component {
* @method createEl
*/
createEl() {
let el = super.createEl('div', {
const el = super.createEl('div', {
className: 'vjs-tooltip-progress-bar vjs-slider-bar',
innerHTML: `<div class="vjs-time-tooltip"></div>
<span class="vjs-control-text"><span>${this.localize('Progress')}</span>: 0%</span>`
@ -42,8 +41,9 @@ class TooltipProgressBar extends Component {
}
updateDataAttr() {
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
let formattedTime = formatTime(time, this.player_.duration());
const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
const formattedTime = formatTime(time, this.player_.duration());
this.el_.setAttribute('data-current-time', formattedTime);
this.tooltip.innerHTML = formattedTime;
}

View File

@ -29,8 +29,8 @@ class CustomControlSpacer extends Spacer {
* @method createEl
*/
createEl() {
let el = super.createEl({
className: this.buildCSSClass(),
const el = super.createEl({
className: this.buildCSSClass()
});
// No-flex/table-cell mode requires there be some content

View File

@ -12,24 +12,24 @@ import Component from '../../component.js';
* @extends TextTrackMenuItem
* @class CaptionSettingsMenuItem
*/
class CaptionSettingsMenuItem extends TextTrackMenuItem {
class CaptionSettingsMenuItem extends TextTrackMenuItem {
constructor(player, options) {
options['track'] = {
'kind': options['kind'],
'player': player,
'label': options['kind'] + ' settings',
'selectable': false,
'default': false,
options.track = {
player,
kind: options.kind,
label: options.kind + ' settings',
selectable: false,
default: false,
mode: 'disabled'
};
// CaptionSettingsMenuItem has no concept of 'selected'
options['selectable'] = false;
options.selectable = false;
super(player, options);
this.addClass('vjs-texttrack-settings');
this.controlText(', opens ' + options['kind'] + ' settings dialog');
this.controlText(', opens ' + options.kind + ' settings dialog');
}
/**

View File

@ -16,9 +16,9 @@ import CaptionSettingsMenuItem from './caption-settings-menu-item.js';
*/
class CaptionsButton extends TextTrackButton {
constructor(player, options, ready){
constructor(player, options, ready) {
super(player, options, ready);
this.el_.setAttribute('aria-label','Captions Menu');
this.el_.setAttribute('aria-label', 'Captions Menu');
}
/**
@ -38,10 +38,11 @@ class CaptionsButton extends TextTrackButton {
*/
update() {
let threshold = 2;
super.update();
// if native, then threshold is 1 because no settings button
if (this.player().tech_ && this.player().tech_['featuresNativeTextTracks']) {
if (this.player().tech_ && this.player().tech_.featuresNativeTextTracks) {
threshold = 1;
}
@ -59,10 +60,10 @@ class CaptionsButton extends TextTrackButton {
* @method createItems
*/
createItems() {
let items = [];
const items = [];
if (!(this.player().tech_ && this.player().tech_['featuresNativeTextTracks'])) {
items.push(new CaptionSettingsMenuItem(this.player_, { 'kind': this.kind_ }));
if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) {
items.push(new CaptionSettingsMenuItem(this.player_, {kind: this.kind_}));
}
return super.createItems(items);

View File

@ -7,9 +7,7 @@ import TextTrackMenuItem from './text-track-menu-item.js';
import ChaptersTrackMenuItem from './chapters-track-menu-item.js';
import Menu from '../../menu/menu.js';
import * as Dom from '../../utils/dom.js';
import * as Fn from '../../utils/fn.js';
import toTitleCase from '../../utils/to-title-case.js';
import window from 'global/window';
/**
* The button component for toggling and selecting chapters
@ -24,9 +22,9 @@ import window from 'global/window';
*/
class ChaptersButton extends TextTrackButton {
constructor(player, options, ready){
constructor(player, options, ready) {
super(player, options, ready);
this.el_.setAttribute('aria-label','Chapters Menu');
this.el_.setAttribute('aria-label', 'Chapters Menu');
}
/**
@ -46,20 +44,18 @@ class ChaptersButton extends TextTrackButton {
* @method createItems
*/
createItems() {
let items = [];
let tracks = this.player_.textTracks();
const items = [];
const tracks = this.player_.textTracks();
if (!tracks) {
return items;
}
for (let i = 0; i < tracks.length; i++) {
let track = tracks[i];
if (track['kind'] === this.kind_) {
items.push(new TextTrackMenuItem(this.player_, {
'track': track
}));
const track = tracks[i];
if (track.kind === this.kind_) {
items.push(new TextTrackMenuItem(this.player_, {track}));
}
}
@ -73,16 +69,16 @@ class ChaptersButton extends TextTrackButton {
* @method createMenu
*/
createMenu() {
let tracks = this.player_.textTracks() || [];
const tracks = this.player_.textTracks() || [];
let chaptersTrack;
let items = this.items || [];
for (let i = tracks.length - 1; i >= 0; i--) {
// We will always choose the last track as our chaptersTrack
let track = tracks[i];
const track = tracks[i];
if (track['kind'] === this.kind_) {
if (track.kind === this.kind_) {
chaptersTrack = track;
break;
@ -90,27 +86,30 @@ class ChaptersButton extends TextTrackButton {
}
let menu = this.menu;
if (menu === undefined) {
menu = new Menu(this.player_);
let title = Dom.createEl('li', {
const title = Dom.createEl('li', {
className: 'vjs-menu-title',
innerHTML: toTitleCase(this.kind_),
tabIndex: -1
});
menu.children_.unshift(title);
Dom.insertElFirst(title, menu.contentEl());
} else {
// We will empty out the menu children each time because we want a
// fresh new menu child list each time
items.forEach(item => menu.removeChild(item));
// Empty out the ChaptersButton menu items because we no longer need them
items = [];
// We will empty out the menu children each time because we want a
// fresh new menu child list each time
items.forEach(item => menu.removeChild(item));
// Empty out the ChaptersButton menu items because we no longer need them
items = [];
}
if (chaptersTrack && chaptersTrack.cues == null) {
chaptersTrack['mode'] = 'hidden';
if (chaptersTrack && (chaptersTrack.cues === null || chaptersTrack.cues === undefined)) {
chaptersTrack.mode = 'hidden';
let remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(chaptersTrack);
const remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(chaptersTrack);
if (remoteTextTrackEl) {
remoteTextTrackEl.addEventListener('load', (event) => this.update());
@ -118,14 +117,14 @@ class ChaptersButton extends TextTrackButton {
}
if (chaptersTrack && chaptersTrack.cues && chaptersTrack.cues.length > 0) {
let cues = chaptersTrack['cues'], cue;
const cues = chaptersTrack.cues;
for (let i = 0, l = cues.length; i < l; i++) {
cue = cues[i];
const cue = cues[i];
let mi = new ChaptersTrackMenuItem(this.player_, {
'track': chaptersTrack,
'cue': cue
const mi = new ChaptersTrackMenuItem(this.player_, {
cue,
track: chaptersTrack
});
items.push(mi);

View File

@ -15,14 +15,14 @@ import * as Fn from '../../utils/fn.js';
*/
class ChaptersTrackMenuItem extends MenuItem {
constructor(player, options){
let track = options['track'];
let cue = options['cue'];
let currentTime = player.currentTime();
constructor(player, options) {
const track = options.track;
const cue = options.cue;
const currentTime = player.currentTime();
// Modify options for parent MenuItem class's init.
options['label'] = cue.text;
options['selected'] = (cue['startTime'] <= currentTime && currentTime < cue['endTime']);
options.label = cue.text;
options.selected = (cue.startTime <= currentTime && currentTime < cue.endTime);
super(player, options);
this.track = track;
@ -47,11 +47,11 @@ class ChaptersTrackMenuItem extends MenuItem {
* @method update
*/
update() {
let cue = this.cue;
let currentTime = this.player_.currentTime();
const cue = this.cue;
const currentTime = this.player_.currentTime();
// vjs.log(currentTime, cue.startTime);
this.selected(cue['startTime'] <= currentTime && currentTime < cue['endTime']);
this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
}
}

View File

@ -16,14 +16,14 @@ import * as Fn from '../../utils/fn.js';
*/
class DescriptionsButton extends TextTrackButton {
constructor(player, options, ready){
constructor(player, options, ready) {
super(player, options, ready);
this.el_.setAttribute('aria-label', 'Descriptions Menu');
let tracks = player.textTracks();
const tracks = player.textTracks();
if (tracks) {
let changeHandler = Fn.bind(this, this.handleTracksChange);
const changeHandler = Fn.bind(this, this.handleTracksChange);
tracks.addEventListener('change', changeHandler);
this.on('dispose', function() {
@ -37,14 +37,15 @@ class DescriptionsButton extends TextTrackButton {
*
* @method handleTracksChange
*/
handleTracksChange(event){
let tracks = this.player().textTracks();
handleTracksChange(event) {
const tracks = this.player().textTracks();
let disabled = false;
// Check whether a track of a different kind is showing
for (let i = 0, l = tracks.length; i < l; i++) {
let track = tracks[i];
if (track['kind'] !== this.kind_ && track['mode'] === 'showing') {
const track = tracks[i];
if (track.kind !== this.kind_ && track.mode === 'showing') {
disabled = true;
break;
}

View File

@ -14,19 +14,19 @@ import Component from '../../component.js';
*/
class OffTextTrackMenuItem extends TextTrackMenuItem {
constructor(player, options){
constructor(player, options) {
// Create pseudo track info
// Requires options['kind']
options['track'] = {
'kind': options['kind'],
'player': player,
'label': options['kind'] + ' off',
'default': false,
'mode': 'disabled'
options.track = {
player,
kind: options.kind,
label: options.kind + ' off',
default: false,
mode: 'disabled'
};
// MenuItem is selectable
options['selectable'] = true;
options.selectable = true;
super(player, options);
this.selected(true);
@ -38,13 +38,14 @@ class OffTextTrackMenuItem extends TextTrackMenuItem {
* @param {Object} event Event object
* @method handleTracksChange
*/
handleTracksChange(event){
let tracks = this.player().textTracks();
handleTracksChange(event) {
const tracks = this.player().textTracks();
let selected = true;
for (let i = 0, l = tracks.length; i < l; i++) {
let track = tracks[i];
if (track['kind'] === this.track['kind'] && track['mode'] === 'showing') {
const track = tracks[i];
if (track.kind === this.track.kind && track.mode === 'showing') {
selected = false;
break;
}

View File

@ -15,9 +15,9 @@ import Component from '../../component.js';
*/
class SubtitlesButton extends TextTrackButton {
constructor(player, options, ready){
constructor(player, options, ready) {
super(player, options, ready);
this.el_.setAttribute('aria-label','Subtitles Menu');
this.el_.setAttribute('aria-label', 'Subtitles Menu');
}
/**

View File

@ -3,7 +3,6 @@
*/
import TrackButton from '../track-button.js';
import Component from '../../component.js';
import * as Fn from '../../utils/fn.js';
import TextTrackMenuItem from './text-track-menu-item.js';
import OffTextTrackMenuItem from './off-text-track-menu-item.js';
@ -17,7 +16,7 @@ import OffTextTrackMenuItem from './off-text-track-menu-item.js';
*/
class TextTrackButton extends TrackButton {
constructor(player, options = {}){
constructor(player, options = {}) {
options.tracks = player.textTracks();
super(player, options);
@ -29,25 +28,25 @@ class TextTrackButton extends TrackButton {
* @return {Array} Array of menu items
* @method createItems
*/
createItems(items=[]) {
createItems(items = []) {
// Add an OFF menu item to turn all tracks off
items.push(new OffTextTrackMenuItem(this.player_, { 'kind': this.kind_ }));
items.push(new OffTextTrackMenuItem(this.player_, {kind: this.kind_}));
let tracks = this.player_.textTracks();
const tracks = this.player_.textTracks();
if (!tracks) {
return items;
}
for (let i = 0; i < tracks.length; i++) {
let track = tracks[i];
const track = tracks[i];
// only add tracks that are of the appropriate kind and have a label
if (track['kind'] === this.kind_) {
if (track.kind === this.kind_) {
items.push(new TextTrackMenuItem(this.player_, {
track,
// MenuItem is selectable
'selectable': true,
'track': track
selectable: true
}));
}
}

View File

@ -17,20 +17,20 @@ import document from 'global/document';
*/
class TextTrackMenuItem extends MenuItem {
constructor(player, options){
let track = options['track'];
let tracks = player.textTracks();
constructor(player, options) {
const track = options.track;
const tracks = player.textTracks();
// Modify options for parent MenuItem class's init.
options['label'] = track['label'] || track['language'] || 'Unknown';
options['selected'] = track['default'] || track['mode'] === 'showing';
options.label = track.label || track.language || 'Unknown';
options.selected = track.default || track.mode === 'showing';
super(player, options);
this.track = track;
if (tracks) {
let changeHandler = Fn.bind(this, this.handleTracksChange);
const changeHandler = Fn.bind(this, this.handleTracksChange);
tracks.addEventListener('change', changeHandler);
this.on('dispose', function() {
@ -52,7 +52,9 @@ class TextTrackMenuItem extends MenuItem {
// Android 2.3 throws an Illegal Constructor error for window.Event
try {
event = new window.Event('change');
} catch(err){}
} catch (err) {
// continue regardless of error
}
}
if (!event) {
@ -71,24 +73,26 @@ class TextTrackMenuItem extends MenuItem {
* @method handleClick
*/
handleClick(event) {
let kind = this.track['kind'];
let tracks = this.player_.textTracks();
const kind = this.track.kind;
const tracks = this.player_.textTracks();
super.handleClick(event);
if (!tracks) return;
if (!tracks) {
return;
}
for (let i = 0; i < tracks.length; i++) {
let track = tracks[i];
const track = tracks[i];
if (track['kind'] !== kind) {
if (track.kind !== kind) {
continue;
}
if (track === this.track) {
track['mode'] = 'showing';
track.mode = 'showing';
} else {
track['mode'] = 'disabled';
track.mode = 'disabled';
}
}
}
@ -98,8 +102,8 @@ class TextTrackMenuItem extends MenuItem {
*
* @method handleTracksChange
*/
handleTracksChange(event){
this.selected(this.track['mode'] === 'showing');
handleTracksChange(event) {
this.selected(this.track.mode === 'showing');
}
}

View File

@ -15,7 +15,7 @@ import formatTime from '../../utils/format-time.js';
*/
class CurrentTimeDisplay extends Component {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.on(player, 'timeupdate', this.updateContent);
@ -28,14 +28,14 @@ class CurrentTimeDisplay extends Component {
* @method createEl
*/
createEl() {
let el = super.createEl('div', {
const el = super.createEl('div', {
className: 'vjs-current-time vjs-time-control vjs-control'
});
this.contentEl_ = Dom.createEl('div', {
className: 'vjs-current-time-display',
// label the current time for screen reader users
innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00',
innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00'
}, {
// tell screen readers not to automatically read the time as it changes
'aria-live': 'off'
@ -52,9 +52,10 @@ class CurrentTimeDisplay extends Component {
*/
updateContent() {
// Allows for smooth scrubbing, when player can't keep up.
let time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
let localizedText = this.localize('Current Time');
let formattedTime = formatTime(time, this.player_.duration());
const time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
const localizedText = this.localize('Current Time');
const formattedTime = formatTime(time, this.player_.duration());
if (formattedTime !== this.formattedTime_) {
this.formattedTime_ = formattedTime;
this.contentEl_.innerHTML = `<span class="vjs-control-text">${localizedText}</span> ${formattedTime}`;

View File

@ -15,7 +15,7 @@ import formatTime from '../../utils/format-time.js';
*/
class DurationDisplay extends Component {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.on(player, 'durationchange', this.updateContent);
@ -28,7 +28,7 @@ class DurationDisplay extends Component {
* @method createEl
*/
createEl() {
let el = super.createEl('div', {
const el = super.createEl('div', {
className: 'vjs-duration vjs-time-control vjs-control'
});
@ -51,12 +51,15 @@ class DurationDisplay extends Component {
* @method updateContent
*/
updateContent() {
let duration = this.player_.duration();
const duration = this.player_.duration();
if (duration && this.duration_ !== duration) {
this.duration_ = duration;
let localizedText = this.localize('Duration Time');
let formattedTime = formatTime(duration);
this.contentEl_.innerHTML = `<span class="vjs-control-text">${localizedText}</span> ${formattedTime}`; // label the duration time for screen reader users
const localizedText = this.localize('Duration Time');
const formattedTime = formatTime(duration);
// label the duration time for screen reader users
this.contentEl_.innerHTML = `<span class="vjs-control-text">${localizedText}</span> ${formattedTime}`;
}
}

View File

@ -15,7 +15,7 @@ import formatTime from '../../utils/format-time.js';
*/
class RemainingTimeDisplay extends Component {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.on(player, 'timeupdate', this.updateContent);
@ -29,14 +29,14 @@ class RemainingTimeDisplay extends Component {
* @method createEl
*/
createEl() {
let el = super.createEl('div', {
const el = super.createEl('div', {
className: 'vjs-remaining-time vjs-time-control vjs-control'
});
this.contentEl_ = Dom.createEl('div', {
className: 'vjs-remaining-time-display',
// label the remaining time for screen reader users
innerHTML: `<span class="vjs-control-text">${this.localize('Remaining Time')}</span> -0:00`,
innerHTML: `<span class="vjs-control-text">${this.localize('Remaining Time')}</span> -0:00`
}, {
// tell screen readers not to automatically read the time as it changes
'aria-live': 'off'
@ -55,6 +55,7 @@ class RemainingTimeDisplay extends Component {
if (this.player_.duration()) {
const localizedText = this.localize('Remaining Time');
const formattedTime = formatTime(this.player_.remainingTime());
if (formattedTime !== this.formattedTime_) {
this.formattedTime_ = formattedTime;
this.contentEl_.innerHTML = `<span class="vjs-control-text">${localizedText}</span> -${formattedTime}`;

View File

@ -15,8 +15,8 @@ import * as Fn from '../utils/fn.js';
*/
class TrackButton extends MenuButton {
constructor(player, options){
let tracks = options.tracks;
constructor(player, options) {
const tracks = options.tracks;
super(player, options);
@ -28,7 +28,8 @@ class TrackButton extends MenuButton {
return;
}
let updateHandler = Fn.bind(this, this.update);
const updateHandler = Fn.bind(this, this.update);
tracks.addEventListener('removetrack', updateHandler);
tracks.addEventListener('addtrack', updateHandler);

View File

@ -6,7 +6,7 @@ import Component from '../../component.js';
import * as Fn from '../../utils/fn.js';
// Required children
import VolumeLevel from './volume-level.js';
import './volume-level.js';
/**
* The bar that contains the volume level and can be clicked on to adjust the level
@ -18,7 +18,7 @@ import VolumeLevel from './volume-level.js';
*/
class VolumeBar extends Slider {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.on(player, 'volumechange', this.updateARIAAttributes);
player.ready(Fn.bind(this, this.updateARIAAttributes));
@ -63,9 +63,8 @@ class VolumeBar extends Slider {
getPercent() {
if (this.player_.muted()) {
return 0;
} else {
return this.player_.volume();
}
return this.player_.volume();
}
/**
@ -95,7 +94,8 @@ class VolumeBar extends Slider {
*/
updateARIAAttributes() {
// Current value of volume bar as a percentage
let volume = (this.player_.volume() * 100).toFixed(2);
const volume = (this.player_.volume() * 100).toFixed(2);
this.el_.setAttribute('aria-valuenow', volume);
this.el_.setAttribute('aria-valuetext', volume + '%');
}
@ -106,7 +106,7 @@ VolumeBar.prototype.options_ = {
children: [
'volumeLevel'
],
'barName': 'volumeLevel'
barName: 'volumeLevel'
};
VolumeBar.prototype.playerEvent = 'volumechange';

View File

@ -4,7 +4,7 @@
import Component from '../../component.js';
// Required children
import VolumeBar from './volume-bar.js';
import './volume-bar.js';
/**
* The component for controlling the volume level
@ -16,15 +16,15 @@ import VolumeBar from './volume-bar.js';
*/
class VolumeControl extends Component {
constructor(player, options){
constructor(player, options) {
super(player, options);
// hide volume controls when they're not supported by the current tech
if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
if (player.tech_ && player.tech_.featuresVolumeControl === false) {
this.addClass('vjs-hidden');
}
this.on(player, 'loadstart', function(){
if (player.tech_['featuresVolumeControl'] === false) {
this.on(player, 'loadstart', function() {
if (player.tech_.featuresVolumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');

View File

@ -18,7 +18,7 @@ import VolumeBar from './volume-control/volume-bar.js';
*/
class VolumeMenuButton extends PopupButton {
constructor(player, options={}){
constructor(player, options = {}) {
// Default to inline
if (options.inline === undefined) {
options.inline = true;
@ -48,7 +48,7 @@ class VolumeMenuButton extends PopupButton {
// hide mute toggle if the current tech doesn't support volume control
function updateVisibility() {
if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
if (player.tech_ && player.tech_.featuresVolumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');
@ -58,19 +58,19 @@ class VolumeMenuButton extends PopupButton {
updateVisibility.call(this);
this.on(player, 'loadstart', updateVisibility);
this.on(this.volumeBar, ['slideractive', 'focus'], function(){
this.on(this.volumeBar, ['slideractive', 'focus'], function() {
this.addClass('vjs-slider-active');
});
this.on(this.volumeBar, ['sliderinactive', 'blur'], function(){
this.on(this.volumeBar, ['sliderinactive', 'blur'], function() {
this.removeClass('vjs-slider-active');
});
this.on(this.volumeBar, ['focus'], function(){
this.on(this.volumeBar, ['focus'], function() {
this.addClass('vjs-lock-showing');
});
this.on(this.volumeBar, ['blur'], function(){
this.on(this.volumeBar, ['blur'], function() {
this.removeClass('vjs-lock-showing');
});
}
@ -83,7 +83,8 @@ class VolumeMenuButton extends PopupButton {
*/
buildCSSClass() {
let orientationClass = '';
if (!!this.options_.vertical) {
if (this.options_.vertical) {
orientationClass = 'vjs-volume-menu-button-vertical';
} else {
orientationClass = 'vjs-volume-menu-button-horizontal';
@ -99,11 +100,11 @@ class VolumeMenuButton extends PopupButton {
* @method createPopup
*/
createPopup() {
let popup = new Popup(this.player_, {
const popup = new Popup(this.player_, {
contentElType: 'div'
});
let vb = new VolumeBar(this.player_, this.options_.volumeBar);
const vb = new VolumeBar(this.player_, this.options_.volumeBar);
popup.addChild(vb);

View File

@ -3,8 +3,6 @@
*/
import Component from './component';
import ModalDialog from './modal-dialog';
import * as Dom from './utils/dom';
import mergeOptions from './utils/merge-options';
/**
@ -46,6 +44,7 @@ class ErrorDisplay extends ModalDialog {
*/
content() {
let error = this.player().error();
return error ? this.localize(error.message) : '';
}
}

View File

@ -3,7 +3,7 @@
*/
import * as Events from './utils/events.js';
var EventTarget = function() {};
const EventTarget = function() {};
EventTarget.prototype.allowedEvents_ = {};
@ -11,21 +11,25 @@ EventTarget.prototype.on = function(type, fn) {
// Remove the addEventListener alias before calling Events.on
// so we don't get into an infinite type loop
let ael = this.addEventListener;
this.addEventListener = () => {};
Events.on(this, type, fn);
this.addEventListener = ael;
};
EventTarget.prototype.addEventListener = EventTarget.prototype.on;
EventTarget.prototype.off = function(type, fn) {
Events.off(this, type, fn);
};
EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
EventTarget.prototype.one = function(type, fn) {
// Remove the addEventListener alias before calling Events.on
// so we don't get into an infinite type loop
let ael = this.addEventListener;
this.addEventListener = () => {};
Events.one(this, type, fn);
this.addEventListener = ael;
@ -35,9 +39,7 @@ EventTarget.prototype.trigger = function(event) {
let type = event.type || event;
if (typeof event === 'string') {
event = {
type: type
};
event = {type};
}
event = Events.fixEvent(event);
@ -47,6 +49,7 @@ EventTarget.prototype.trigger = function(event) {
Events.trigger(this, event);
};
// The standard DOM EventTarget.dispatchEvent() is aliased to trigger()
EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;

View File

@ -7,7 +7,7 @@ import log from './utils/log';
* Both work the same but node adds `super_` to the subClass
* and Bable adds the superClass as __proto__. Both seem useful.
*/
const _inherits = function (subClass, superClass) {
const _inherits = function(subClass, superClass) {
if (typeof superClass !== 'function' && superClass !== null) {
throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
}
@ -44,10 +44,11 @@ const _inherits = function (subClass, superClass) {
* });
* ```
*/
const extendFn = function(superClass, subClassMethods={}) {
const extendFn = function(superClass, subClassMethods = {}) {
let subClass = function() {
superClass.apply(this, arguments);
};
let methods = {};
if (typeof subClassMethods === 'object') {
@ -66,7 +67,7 @@ const extendFn = function(superClass, subClassMethods={}) {
_inherits(subClass, superClass);
// Extend subObj's prototype with functions and other properties from props
for (var name in methods) {
for (let name in methods) {
if (methods.hasOwnProperty(name)) {
subClass.prototype[name] = methods[name];
}

View File

@ -74,7 +74,7 @@ for (let i = 0; i < apiMap.length; i++) {
// map the browser API names to the spec API names
if (browserApi) {
for (let i=0; i<browserApi.length; i++) {
for (let i = 0; i < browserApi.length; i++) {
FullscreenApi[specApi[i]] = browserApi[i];
}
}

View File

@ -8,13 +8,13 @@ import assign from 'object.assign';
*
* @param {Number} code The media error code
*/
let MediaError = function(code){
let MediaError = function(code) {
if (typeof code === 'number') {
this.code = code;
} else if (typeof code === 'string') {
// default code is zero, so this is a custom error
this.message = code;
} else if (typeof code === 'object') { // object
} else if (typeof code === 'object') {
assign(this, code);
}
@ -52,13 +52,15 @@ MediaError.prototype.message = '';
*/
MediaError.prototype.status = null;
// These errors are indexed by the W3C standard numeric value. The order
// should not be changed!
MediaError.errorTypes = [
'MEDIA_ERR_CUSTOM', // = 0
'MEDIA_ERR_ABORTED', // = 1
'MEDIA_ERR_NETWORK', // = 2
'MEDIA_ERR_DECODE', // = 3
'MEDIA_ERR_SRC_NOT_SUPPORTED', // = 4
'MEDIA_ERR_ENCRYPTED' // = 5
'MEDIA_ERR_CUSTOM',
'MEDIA_ERR_ABORTED',
'MEDIA_ERR_NETWORK',
'MEDIA_ERR_DECODE',
'MEDIA_ERR_SRC_NOT_SUPPORTED',
'MEDIA_ERR_ENCRYPTED'
];
MediaError.defaultMessages = {
@ -71,7 +73,7 @@ MediaError.defaultMessages = {
// Add types as properties on MediaError
// e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
for (let errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
MediaError[MediaError.errorTypes[errNum]] = errNum;
// values should be accessible on both the class and instance
MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;

View File

@ -18,7 +18,7 @@ import toTitleCase from '../utils/to-title-case.js';
*/
class MenuButton extends ClickableComponent {
constructor(player, options={}){
constructor(player, options = {}) {
super(player, options);
this.update();
@ -36,7 +36,7 @@ class MenuButton extends ClickableComponent {
* @method update
*/
update() {
let menu = this.createMenu();
const menu = this.createMenu();
if (this.menu) {
this.removeChild(this.menu);
@ -68,24 +68,25 @@ class MenuButton extends ClickableComponent {
* @method createMenu
*/
createMenu() {
var menu = new Menu(this.player_);
const menu = new Menu(this.player_);
// Add a title list item to the top
if (this.options_.title) {
let title = Dom.createEl('li', {
const title = Dom.createEl('li', {
className: 'vjs-menu-title',
innerHTML: toTitleCase(this.options_.title),
tabIndex: -1
});
menu.children_.unshift(title);
Dom.insertElFirst(title, menu.contentEl());
}
this.items = this['createItems']();
this.items = this.createItems();
if (this.items) {
// Add menu items to the menu
for (var i = 0; i < this.items.length; i++) {
for (let i = 0; i < this.items.length; i++) {
menu.addItem(this.items[i]);
}
}
@ -98,7 +99,7 @@ class MenuButton extends ClickableComponent {
*
* @method createItems
*/
createItems(){}
createItems() {}
/**
* Create the component's DOM element
@ -119,7 +120,7 @@ class MenuButton extends ClickableComponent {
* @method buildCSSClass
*/
buildCSSClass() {
var menuButtonClass = 'vjs-menu-button';
let menuButtonClass = 'vjs-menu-button';
// If the inline option is passed, we want to use different styles altogether.
if (this.options_.inline === true) {
@ -141,11 +142,11 @@ class MenuButton extends ClickableComponent {
* @method handleClick
*/
handleClick() {
this.one(this.menu.contentEl(), 'mouseleave', Fn.bind(this, function(e){
this.one(this.menu.contentEl(), 'mouseleave', Fn.bind(this, function(e) {
this.unpressButton();
this.el_.blur();
}));
if (this.buttonPressed_){
if (this.buttonPressed_) {
this.unpressButton();
} else {
this.pressButton();
@ -189,8 +190,8 @@ class MenuButton extends ClickableComponent {
handleSubmenuKeyPress(event) {
// Escape (27) key or Tab (9) key unpress the 'button'
if (event.which === 27 || event.which === 9){
if (this.buttonPressed_){
if (event.which === 27 || event.which === 9) {
if (this.buttonPressed_) {
this.unpressButton();
}
// Don't preventDefault for Tab key - we still want to lose focus
@ -210,7 +211,8 @@ class MenuButton extends ClickableComponent {
this.buttonPressed_ = true;
this.menu.lockShowing();
this.el_.setAttribute('aria-expanded', 'true');
this.menu.focus(); // set the focus into the submenu
// set the focus into the submenu
this.menu.focus();
}
}
@ -224,7 +226,8 @@ class MenuButton extends ClickableComponent {
this.buttonPressed_ = false;
this.menu.unlockShowing();
this.el_.setAttribute('aria-expanded', 'false');
this.el_.focus(); // Set focus back to this menu button
// Set focus back to this menu button
this.el_.focus();
}
}

View File

@ -18,9 +18,9 @@ class MenuItem extends ClickableComponent {
constructor(player, options) {
super(player, options);
this.selectable = options['selectable'];
this.selectable = options.selectable;
this.selected(options['selected']);
this.selected(options.selected);
if (this.selectable) {
// TODO: May need to be either menuitemcheckbox or menuitemradio,
@ -42,7 +42,7 @@ class MenuItem extends ClickableComponent {
createEl(type, props, attrs) {
return super.createEl('li', assign({
className: 'vjs-menu-item',
innerHTML: this.localize(this.options_['label']),
innerHTML: this.localize(this.options_.label),
tabIndex: -1
}, props), attrs);
}
@ -66,13 +66,13 @@ class MenuItem extends ClickableComponent {
if (this.selectable) {
if (selected) {
this.addClass('vjs-selected');
this.el_.setAttribute('aria-checked','true');
this.el_.setAttribute('aria-checked', 'true');
// aria-checked isn't fully supported by browsers/screen readers,
// so indicate selected state to screen reader in the control text.
this.controlText(', selected');
} else {
this.removeClass('vjs-selected');
this.el_.setAttribute('aria-checked','false');
this.el_.setAttribute('aria-checked', 'false');
// Indicate un-selected state to screen reader
// Note that a space clears out the selected state text
this.controlText(' ');

View File

@ -15,7 +15,7 @@ import * as Events from '../utils/events.js';
*/
class Menu extends Component {
constructor (player, options) {
constructor(player, options) {
super(player, options);
this.focusedChild_ = -1;
@ -31,9 +31,9 @@ class Menu extends Component {
*/
addItem(component) {
this.addChild(component);
component.on('click', Fn.bind(this, function(){
component.on('click', Fn.bind(this, function() {
this.unlockShowing();
//TODO: Need to set keyboard focus back to the menuButton
// TODO: Need to set keyboard focus back to the menuButton
}));
}
@ -44,21 +44,25 @@ class Menu extends Component {
* @method createEl
*/
createEl() {
let contentElType = this.options_.contentElType || 'ul';
const contentElType = this.options_.contentElType || 'ul';
this.contentEl_ = Dom.createEl(contentElType, {
className: 'vjs-menu-content'
});
this.contentEl_.setAttribute('role', 'menu');
var el = super.createEl('div', {
const el = super.createEl('div', {
append: this.contentEl_,
className: 'vjs-menu'
});
el.setAttribute('role', 'presentation');
el.appendChild(this.contentEl_);
// Prevent clicks from bubbling up. Needed for Menu Buttons,
// where a click on the parent is significant
Events.on(el, 'click', function(event){
Events.on(el, 'click', function(event) {
event.preventDefault();
event.stopImmediatePropagation();
});
@ -72,11 +76,14 @@ class Menu extends Component {
* @param {Object} event Event object
* @method handleKeyPress
*/
handleKeyPress (event) {
if (event.which === 37 || event.which === 40) { // Left and Down Arrows
handleKeyPress(event) {
// Left and Down Arrows
if (event.which === 37 || event.which === 40) {
event.preventDefault();
this.stepForward();
} else if (event.which === 38 || event.which === 39) { // Up and Right Arrows
// Up and Right Arrows
} else if (event.which === 38 || event.which === 39) {
event.preventDefault();
this.stepBack();
}
@ -87,21 +94,21 @@ class Menu extends Component {
*
* @method stepForward
*/
stepForward () {
let stepChild = 0;
stepForward() {
let stepChild = 0;
if (this.focusedChild_ !== undefined) {
stepChild = this.focusedChild_ + 1;
}
this.focus(stepChild);
}
if (this.focusedChild_ !== undefined) {
stepChild = this.focusedChild_ + 1;
}
this.focus(stepChild);
}
/**
* Move to previous (higher) menu item for keyboard users
*
* @method stepBack
*/
stepBack () {
/**
* Move to previous (higher) menu item for keyboard users
*
* @method stepBack
*/
stepBack() {
let stepChild = 0;
if (this.focusedChild_ !== undefined) {
@ -116,10 +123,10 @@ class Menu extends Component {
* @param {Object|String} item Index of child item set focus on
* @method focus
*/
focus (item = 0) {
let children = this.children().slice();
let haveTitle = children.length && children[0].className &&
/vjs-menu-title/.test(children[0].className);
focus(item = 0) {
const children = this.children().slice();
const haveTitle = children.length && children[0].className &&
(/vjs-menu-title/).test(children[0].className);
if (haveTitle) {
children.shift();

View File

@ -3,10 +3,7 @@
*/
import * as Dom from './utils/dom';
import * as Fn from './utils/fn';
import log from './utils/log';
import Component from './component';
import CloseButton from './close-button';
const MODAL_CLASS_NAME = 'vjs-modal-dialog';
const ESC = 27;
@ -92,7 +89,7 @@ class ModalDialog extends Component {
'aria-describedby': `${this.id()}_description`,
'aria-hidden': 'true',
'aria-label': this.label(),
role: 'dialog'
'role': 'dialog'
});
}
@ -254,6 +251,7 @@ class ModalDialog extends Component {
// The close button should be a child of the modal - not its
// content element, so temporarily change the content element.
let temp = this.contentEl_;
this.contentEl_ = this.el_;
close = this.addChild('closeButton', {controlText: 'Close Modal Dialog'});
this.contentEl_ = temp;

View File

@ -22,23 +22,25 @@ import safeParseTuple from 'safe-json-parse/tuple';
import assign from 'object.assign';
import mergeOptions from './utils/merge-options.js';
import textTrackConverter from './tracks/text-track-list-converter.js';
import ModalDialog from './modal-dialog';
import Tech from './tech/tech.js';
import AudioTrackList from './tracks/audio-track-list.js';
import VideoTrackList from './tracks/video-track-list.js';
// Include required child components (importing also registers them)
import MediaLoader from './tech/loader.js';
import PosterImage from './poster-image.js';
import TextTrackDisplay from './tracks/text-track-display.js';
import LoadingSpinner from './loading-spinner.js';
import BigPlayButton from './big-play-button.js';
import ControlBar from './control-bar/control-bar.js';
import ErrorDisplay from './error-display.js';
import TextTrackSettings from './tracks/text-track-settings.js';
import ModalDialog from './modal-dialog';
// The following imports are used only to ensure that the corresponding modules
// are always included in the video.js package. Importing the modules will
// execute them and they will register themselves with video.js.
import './tech/loader.js';
import './poster-image.js';
import './tracks/text-track-display.js';
import './loading-spinner.js';
import './big-play-button.js';
import './control-bar/control-bar.js';
import './error-display.js';
import './tracks/text-track-settings.js';
// Require html5 tech, at least for disposing the original video tag
import Tech from './tech/tech.js';
import Html5 from './tech/html5.js';
// Import Html5 tech, at least for disposing the original video tag.
import './tech/html5.js';
/**
* An instance of the `Player` class is created when any of the Video.js setup methods are used to initialize a video.
@ -70,7 +72,7 @@ class Player extends Component {
* @param {Object=} options Player options
* @param {Function=} ready Ready callback function
*/
constructor(tag, options, ready){
constructor(tag, options, ready) {
// Make sure tag ID exists
tag.id = tag.id || `vjs_video_${Guid.newGUID()}`;
@ -96,11 +98,13 @@ class Player extends Component {
if (!options.language) {
if (typeof tag.closest === 'function') {
let closest = tag.closest('[lang]');
if (closest) {
options.language = closest.getAttribute('lang');
}
} else {
let element = tag;
while (element && element.nodeType === 1) {
if (Dom.getElAttributes(element).hasOwnProperty('lang')) {
options.language = element.getAttribute('lang');
@ -124,7 +128,8 @@ class Player extends Component {
'properties you want to override?');
}
this.tag = tag; // Store the original tag used to set options
// Store the original tag used to set options
this.tag = tag;
// Store the tag attributes used to restore html5 element
this.tagAttributes = tag && Dom.getElAttributes(tag);
@ -179,7 +184,7 @@ class Player extends Component {
if (options.plugins) {
let plugins = options.plugins;
Object.getOwnPropertyNames(plugins).forEach(function(name){
Object.getOwnPropertyNames(plugins).forEach(function(name) {
if (typeof this[name] === 'function') {
this[name](plugins[name]);
} else {
@ -264,10 +269,18 @@ class Player extends Component {
// Kill reference to this player
Player.players[this.id_] = null;
if (this.tag && this.tag.player) { this.tag.player = null; }
if (this.el_ && this.el_.player) { this.el_.player = null; }
if (this.tech_) { this.tech_.dispose(); }
if (this.tag && this.tag.player) {
this.tag.player = null;
}
if (this.el_ && this.el_.player) {
this.el_.player = null;
}
if (this.tech_) {
this.tech_.dispose();
}
super.dispose();
}
@ -290,7 +303,7 @@ class Player extends Component {
// ID will now reference player box, not the video tag
const attrs = Dom.getElAttributes(tag);
Object.getOwnPropertyNames(attrs).forEach(function(attr){
Object.getOwnPropertyNames(attrs).forEach(function(attr) {
// workaround so we don't totally break IE7
// http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
if (attr === 'class') {
@ -319,6 +332,7 @@ class Player extends Component {
this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
let defaultsStyleEl = Dom.$('.vjs-styles-defaults');
let head = Dom.$('head');
head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
}
@ -330,8 +344,10 @@ class Player extends Component {
// Hide any links within the video/audio tag, because IE doesn't hide them completely.
let links = tag.getElementsByTagName('a');
for (let i = 0; i < links.length; i++) {
let linkEl = links.item(i);
Dom.addElClass(linkEl, 'vjs-hidden');
linkEl.setAttribute('hidden', 'hidden');
}
@ -348,7 +364,9 @@ class Player extends Component {
// insert the tag as the first child of the player element
// then manually add it to the children array so that this.addChild
// will work properly for other components
Dom.insertElFirst(tag, el); // Breaks iPhone, fixed in HTML5 setup.
//
// Breaks iPhone, fixed in HTML5 setup.
Dom.insertElFirst(tag, el);
this.children_.unshift(tag);
this.el_ = el;
@ -444,7 +462,7 @@ class Player extends Component {
}
// Check for width:height format
if (!/^\d+\:\d+$/.test(ratio)) {
if (!(/^\d+\:\d+$/).test(ratio)) {
throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
}
this.aspectRatio_ = ratio;
@ -516,14 +534,14 @@ class Player extends Component {
height = this.height_;
} else {
// Otherwise calculate the height from the ratio and the width
height = width * ratioMultiplier;
height = width * ratioMultiplier;
}
// Ensure the CSS class is valid by starting with an alpha character
if (/^[^a-zA-Z]/.test(this.id())) {
idClass = 'dimensions-'+this.id();
if ((/^[^a-zA-Z]/).test(this.id())) {
idClass = 'dimensions-' + this.id();
} else {
idClass = this.id()+'-dimensions';
idClass = this.id() + '-dimensions';
}
// Ensure the right class is still on the player for the style element
@ -571,9 +589,9 @@ class Player extends Component {
this.isReady_ = false;
// Grab tech-specific options from player options and add source and parent element to use.
var techOptions = assign({
let techOptions = assign({
source,
'nativeControlsForTouch': this.options_.nativeControlsForTouch,
'source': source,
'playerId': this.id(),
'techId': `${this.id()}_${techName}_api`,
'videoTracks': this.videoTracks_,
@ -602,13 +620,14 @@ class Player extends Component {
}
// Initialize tech instance
let techComponent = Tech.getTech(techName);
let TechComponent = Tech.getTech(techName);
// Support old behavior of techs being registered as components.
// Remove once that deprecated behavior is removed.
if (!techComponent) {
techComponent = Component.getComponent(techName);
if (!TechComponent) {
TechComponent = Component.getComponent(techName);
}
this.tech_ = new techComponent(techOptions);
this.tech_ = new TechComponent(techOptions);
// player.triggerReady is always async, so don't need this to be async
this.tech_.ready(Fn.bind(this, this.handleTechReady_), true);
@ -704,6 +723,7 @@ class Player extends Component {
\`IWillNotUseThisInPlugins\` to the \`tech\` method. See
https://github.com/videojs/video.js/issues/2617 for more info.
`;
window.alert(errorText);
throw new Error(errorText);
}
@ -794,9 +814,9 @@ class Player extends Component {
// This fixes both issues. Need to wait for API, so it updates displays correctly
if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
try {
delete this.tag.poster; // Chrome Fix. Fixed in Chrome v16.
}
catch (e) {
// Chrome Fix. Fixed in Chrome v16.
delete this.tag.poster;
} catch (e) {
log('deleting tag.poster throws in some browsers', e);
}
this.play();
@ -954,9 +974,9 @@ class Player extends Component {
* @method handleTechFirstPlay_
*/
handleTechFirstPlay_() {
//If the first starttime attribute is specified
//then we will start at the given offset in seconds
if(this.options_.starttime){
// If the first starttime attribute is specified
// then we will start at the given offset in seconds
if (this.options_.starttime) {
this.currentTime(this.options_.starttime);
}
@ -1024,7 +1044,9 @@ class Player extends Component {
handleTechClick_(event) {
// We're using mousedown to detect clicks thanks to Flash, but mousedown
// will also be triggered with right-clicks, so we need to prevent that
if (event.button !== 0) return;
if (event.button !== 0) {
return;
}
// When controls are disabled a click should not toggle playback because
// the click is considered a control
@ -1065,7 +1087,7 @@ class Player extends Component {
* @method handleTechTouchMove_
*/
handleTechTouchMove_() {
if (this.userWasActive){
if (this.userWasActive) {
this.reportUserActivity();
}
}
@ -1127,6 +1149,7 @@ class Player extends Component {
*/
handleTechError_() {
let error = this.tech_.error();
this.error(error);
}
@ -1181,7 +1204,8 @@ class Player extends Component {
}
handleTechTextData_() {
var data = null;
let data = null;
if (arguments.length > 1) {
data = arguments[1];
}
@ -1259,15 +1283,17 @@ class Player extends Component {
techCall_(method, arg) {
// If it's not ready yet, call method when it is
if (this.tech_ && !this.tech_.isReady_) {
this.tech_.ready(function(){
this.tech_.ready(function() {
this[method](arg);
}, true);
// Otherwise call method now
} else {
try {
this.tech_ && this.tech_[method](arg);
} catch(e) {
if (this.tech_) {
this.tech_[method](arg);
}
} catch (e) {
log(e);
throw e;
}
@ -1290,18 +1316,17 @@ class Player extends Component {
// When that happens we'll catch the errors and inform tech that it's not ready any more.
try {
return this.tech_[method]();
} catch(e) {
} catch (e) {
// When building additional tech libs, an expected method may not be defined yet
if (this.tech_[method] === undefined) {
log(`Video.js: ${method} method not defined for ${this.techName_} playback technology.`, e);
// When a method isn't available on the object it throws a TypeError
} else if (e.name === 'TypeError') {
log(`Video.js: ${method} unavailable on ${this.techName_} playback technology element.`, e);
this.tech_.isReady_ = false;
} else {
// When a method isn't available on the object it throws a TypeError
if (e.name === 'TypeError') {
log(`Video.js: ${method} unavailable on ${this.techName_} playback technology element.`, e);
this.tech_.isReady_ = false;
} else {
log(e);
}
log(e);
}
throw e;
}
@ -1414,7 +1439,8 @@ class Player extends Component {
// currentTime when scrubbing, but may not provide much performance benefit afterall.
// Should be tested. Also something has to read the actual current time or the cache will
// never get updated.
return this.cache_.currentTime = (this.techGet_('currentTime') || 0);
this.cache_.currentTime = (this.techGet_('currentTime') || 0);
return this.cache_.currentTime;
}
/**
@ -1495,10 +1521,10 @@ class Player extends Component {
* @method buffered
*/
buffered() {
var buffered = this.techGet_('buffered');
let buffered = this.techGet_('buffered');
if (!buffered || !buffered.length) {
buffered = createTimeRange(0,0);
buffered = createTimeRange(0, 0);
}
return buffered;
@ -1527,9 +1553,9 @@ class Player extends Component {
* @method bufferedEnd
*/
bufferedEnd() {
var buffered = this.buffered(),
duration = this.duration(),
end = buffered.end(buffered.length-1);
let buffered = this.buffered();
let duration = this.duration();
let end = buffered.end(buffered.length - 1);
if (end > duration) {
end = duration;
@ -1557,7 +1583,8 @@ class Player extends Component {
let vol;
if (percentAsDecimal !== undefined) {
vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1
// Force value to between 0 and 1
vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
this.cache_.volume = vol;
this.techCall_('setVolume', vol);
@ -1569,7 +1596,6 @@ class Player extends Component {
return (isNaN(vol)) ? 1 : vol;
}
/**
* Get the current muted state, or turn mute on or off
* ```js
@ -1589,7 +1615,7 @@ class Player extends Component {
this.techCall_('setMuted', muted);
return this;
}
return this.techGet_('muted') || false; // Default to false
return this.techGet_('muted') || false;
}
// Check if current tech can support native fullscreen
@ -1645,7 +1671,7 @@ class Player extends Component {
* @method requestFullscreen
*/
requestFullscreen() {
var fsApi = FullscreenApi;
let fsApi = FullscreenApi;
this.isFullscreen(true);
@ -1658,7 +1684,7 @@ class Player extends Component {
// when canceling fullscreen. Otherwise if there's multiple
// players on a page, they would all be reacting to the same fullscreen
// events
Events.on(document, fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e){
Events.on(document, fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) {
this.isFullscreen(document[fsApi.fullscreenElement]);
// If cancelling fullscreen, remove event listener.
@ -1695,17 +1721,18 @@ class Player extends Component {
* @method exitFullscreen
*/
exitFullscreen() {
var fsApi = FullscreenApi;
let fsApi = FullscreenApi;
this.isFullscreen(false);
// Check for browser element fullscreen support
if (fsApi.requestFullscreen) {
document[fsApi.exitFullscreen]();
} else if (this.tech_.supportsFullScreen()) {
this.techCall_('exitFullScreen');
this.techCall_('exitFullScreen');
} else {
this.exitFullWindow();
this.trigger('fullscreenchange');
this.exitFullWindow();
this.trigger('fullscreenchange');
}
return this;
@ -1845,7 +1872,7 @@ class Player extends Component {
// Iterate over each `innerArray` element once per `outerArray` element and execute
// `tester` with both. If `tester` returns a non-falsy value, exit early and return
// that value.
let findFirstPassingTechSourcePair = function (outerArray, innerArray, tester) {
let findFirstPassingTechSourcePair = function(outerArray, innerArray, tester) {
let found;
outerArray.some((outerChoice) => {
@ -1865,7 +1892,7 @@ class Player extends Component {
let flip = (fn) => (a, b) => fn(b, a);
let finder = ([techName, tech], source) => {
if (tech.canPlaySource(source, this.options_[techName.toLowerCase()])) {
return {source: source, tech: techName};
return {source, tech: techName};
}
};
@ -1920,6 +1947,7 @@ class Player extends Component {
}
let currentTech = Tech.getTech(this.techName_);
// Support old behavior of techs being registered as components.
// Remove once that deprecated behavior is removed.
if (!currentTech) {
@ -1948,7 +1976,7 @@ class Player extends Component {
this.currentType_ = source.type || '';
// wait until the tech is ready to set the source
this.ready(function(){
this.ready(function() {
// The setSource tech method was added with source handlers
// so older techs won't support it
@ -1984,7 +2012,7 @@ class Player extends Component {
* @method sourceList_
*/
sourceList_(sources) {
var sourceTech = this.selectSource(sources);
let sourceTech = this.selectSource(sources);
if (sourceTech) {
if (sourceTech.tech === this.techName_) {
@ -1996,7 +2024,7 @@ class Player extends Component {
}
} else {
// We need to wrap this in a timeout to give folks a chance to add error event handlers
this.setTimeout( function() {
this.setTimeout(function() {
this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
}, 0);
@ -2098,7 +2126,7 @@ class Player extends Component {
loop(value) {
if (value !== undefined) {
this.techCall_('setLoop', value);
this.options_['loop'] = value;
this.options_.loop = value;
return this;
}
return this.techGet_('loop');
@ -2172,7 +2200,8 @@ class Player extends Component {
*/
controls(bool) {
if (bool !== undefined) {
bool = !!bool; // force boolean
bool = !!bool;
// Don't trigger a change event unless it actually changed
if (this.controls_ !== bool) {
this.controls_ = bool;
@ -2218,7 +2247,8 @@ class Player extends Component {
*/
usingNativeControls(bool) {
if (bool !== undefined) {
bool = !!bool; // force boolean
bool = !!bool;
// Don't trigger a change event unless it actually changed
if (this.usingNativeControls_ !== bool) {
this.usingNativeControls_ = bool;
@ -2302,7 +2332,9 @@ class Player extends Component {
* @return {Boolean} True if the player is in the ended state, false if not.
* @method ended
*/
ended() { return this.techGet_('ended'); }
ended() {
return this.techGet_('ended');
}
/**
* Returns whether or not the player is in the "seeking" state.
@ -2310,7 +2342,9 @@ class Player extends Component {
* @return {Boolean} True if the player is in the seeking state, false if not.
* @method seeking
*/
seeking() { return this.techGet_('seeking'); }
seeking() {
return this.techGet_('seeking');
}
/**
* Returns the TimeRanges of the media that are currently available
@ -2319,7 +2353,9 @@ class Player extends Component {
* @return {TimeRanges} the seekable intervals of the media timeline
* @method seekable
*/
seekable() { return this.techGet_('seekable'); }
seekable() {
return this.techGet_('seekable');
}
/**
* Report user activity
@ -2363,8 +2399,8 @@ class Player extends Component {
//
// When this gets resolved in ALL browsers it can be removed
// https://code.google.com/p/chromium/issues/detail?id=103041
if(this.tech_) {
this.tech_.one('mousemove', function(e){
if (this.tech_) {
this.tech_.one('mousemove', function(e) {
e.stopPropagation();
e.preventDefault();
});
@ -2387,14 +2423,15 @@ class Player extends Component {
* @method listenForUserActivity_
*/
listenForUserActivity_() {
let mouseInProgress, lastMoveX, lastMoveY;
let mouseInProgress;
let lastMoveX;
let lastMoveY;
let handleActivity = Fn.bind(this, this.reportUserActivity);
let handleMouseMove = function(e) {
// #1068 - Prevent mousemove spamming
// Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
if(e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
lastMoveX = e.screenX;
lastMoveY = e.screenY;
handleActivity();
@ -2435,7 +2472,8 @@ class Player extends Component {
// then gets picked up by this loop
// http://ejohn.org/blog/learning-from-twitter/
let inactivityTimeout;
let activityCheck = this.setInterval(function() {
this.setInterval(function() {
// Check to see if mouse/touch activity has happened
if (this.userActivity_) {
// Reset the activity tracker
@ -2447,16 +2485,17 @@ class Player extends Component {
// Clear any existing inactivity timeout to start the timer over
this.clearTimeout(inactivityTimeout);
var timeout = this.options_['inactivityTimeout'];
let timeout = this.options_.inactivityTimeout;
if (timeout > 0) {
// In <timeout> milliseconds, if no more activity has occurred the
// user will be considered inactive
inactivityTimeout = this.setTimeout(function () {
inactivityTimeout = this.setTimeout(function() {
// Protect against the case where the inactivityTimeout can trigger just
// before the next user activity is picked up by the activityCheck loop
// before the next user activity is picked up by the activity check loop
// causing a flicker
if (!this.userActivity_) {
this.userActive(false);
this.userActive(false);
}
}, timeout);
}
@ -2481,11 +2520,10 @@ class Player extends Component {
return this;
}
if (this.tech_ && this.tech_['featuresPlaybackRate']) {
if (this.tech_ && this.tech_.featuresPlaybackRate) {
return this.techGet_('playbackRate');
} else {
return 1.0;
}
return 1.0;
}
/**
@ -2611,7 +2649,9 @@ class Player extends Component {
textTracks() {
// cannot use techGet_ directly because it checks to see whether the tech is ready.
// Flash is unlikely to be ready in time but textTracks should still work.
return this.tech_ && this.tech_['textTracks']();
if (this.tech_) {
return this.tech_.textTracks();
}
}
/**
@ -2621,7 +2661,9 @@ class Player extends Component {
* @method remoteTextTracks
*/
remoteTextTracks() {
return this.tech_ && this.tech_['remoteTextTracks']();
if (this.tech_) {
return this.tech_.remoteTextTracks();
}
}
/**
@ -2631,7 +2673,9 @@ class Player extends Component {
* @method remoteTextTrackEls
*/
remoteTextTrackEls() {
return this.tech_ && this.tech_['remoteTextTrackEls']();
if (this.tech_) {
return this.tech_.remoteTextTrackEls();
}
}
/**
@ -2645,7 +2689,9 @@ class Player extends Component {
* @method addTextTrack
*/
addTextTrack(kind, label, language) {
return this.tech_ && this.tech_['addTextTrack'](kind, label, language);
if (this.tech_) {
return this.tech_.addTextTrack(kind, label, language);
}
}
/**
@ -2655,7 +2701,9 @@ class Player extends Component {
* @method addRemoteTextTrack
*/
addRemoteTextTrack(options) {
return this.tech_ && this.tech_['addRemoteTextTrack'](options);
if (this.tech_) {
return this.tech_.addRemoteTextTrack(options);
}
}
/**
@ -2666,8 +2714,10 @@ class Player extends Component {
*/
// destructure the input into an object with a track argument, defaulting to arguments[0]
// default the whole argument to an empty object if nothing was passed in
removeRemoteTextTrack({track = arguments[0]} = {}) { // jshint ignore:line
this.tech_ && this.tech_['removeRemoteTextTrack'](track);
removeRemoteTextTrack({track = arguments[0]} = {}) {
if (this.tech_) {
return this.tech_.removeRemoteTextTrack(track);
}
}
/**
@ -2691,11 +2741,11 @@ class Player extends Component {
}
// Methods to add support for
// initialTime: function(){ return this.techCall_('initialTime'); },
// startOffsetTime: function(){ return this.techCall_('startOffsetTime'); },
// played: function(){ return this.techCall_('played'); },
// defaultPlaybackRate: function(){ return this.techCall_('defaultPlaybackRate'); },
// defaultMuted: function(){ return this.techCall_('defaultMuted'); }
// initialTime: function() { return this.techCall_('initialTime'); },
// startOffsetTime: function() { return this.techCall_('startOffsetTime'); },
// played: function() { return this.techCall_('played'); },
// defaultPlaybackRate: function() { return this.techCall_('defaultPlaybackRate'); },
// defaultMuted: function() { return this.techCall_('defaultMuted'); }
/**
* The player's language code
@ -2713,7 +2763,7 @@ class Player extends Component {
return this.language_;
}
this.language_ = (''+code).toLowerCase();
this.language_ = String(code).toLowerCase();
return this;
}
@ -2726,7 +2776,7 @@ class Player extends Component {
* @method languages
*/
languages() {
return mergeOptions(Player.prototype.options_.languages, this.languages_);
return mergeOptions(Player.prototype.options_.languages, this.languages_);
}
/**
@ -2770,16 +2820,14 @@ class Player extends Component {
* @return {ModalDialog}
*/
createModal(content, options) {
let player = this;
options = options || {};
options.content = content || '';
let modal = new ModalDialog(player, options);
let modal = new ModalDialog(this, options);
player.addChild(modal);
modal.on('dispose', function() {
player.removeChild(modal);
this.addChild(modal);
modal.on('dispose', () => {
this.removeChild(modal);
});
return modal.open();
@ -2795,18 +2843,19 @@ class Player extends Component {
*/
static getTagSettings(tag) {
let baseOptions = {
'sources': [],
'tracks': []
sources: [],
tracks: []
};
const tagOptions = Dom.getElAttributes(tag);
const dataSetup = tagOptions['data-setup'];
// Check if data-setup attr exists.
if (dataSetup !== null){
if (dataSetup !== null) {
// Parse options JSON
// If empty string, make it a parsable json object.
const [err, data] = safeParseTuple(dataSetup || '{}');
if (err) {
log.error(err);
}
@ -2819,10 +2868,11 @@ class Player extends Component {
if (tag.hasChildNodes()) {
const children = tag.childNodes;
for (let i=0, j=children.length; i<j; i++) {
for (let i = 0, j = children.length; i < j; i++) {
const child = children[i];
// Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
const childName = child.nodeName.toLowerCase();
if (childName === 'source') {
baseOptions.sources.push(Dom.getElAttributes(child));
} else if (childName === 'track') {
@ -2844,6 +2894,7 @@ class Player extends Component {
Player.players = {};
let navigator = window.navigator;
/*
* Player instance options, surfaced using options
* options = Player.prototype.options_
@ -2854,14 +2905,14 @@ let navigator = window.navigator;
*/
Player.prototype.options_ = {
// Default order of fallback technology
techOrder: ['html5','flash'],
techOrder: ['html5', 'flash'],
// techOrder: ['flash','html5'],
html5: {},
flash: {},
// defaultVolume: 0.85,
defaultVolume: 0.00, // The freakin seaguls are driving me crazy!
defaultVolume: 0.00,
// default inactivity timeout
inactivityTimeout: 2000,
@ -2892,47 +2943,49 @@ Player.prototype.options_ = {
notSupportedMessage: 'No compatible source was found for this media.'
};
// The following no-op expressions are here only for purposes of documentation.
/**
* Fired when the user agent begins looking for media data
*
* @event loadstart
*/
Player.prototype.handleTechLoadStart_;
Player.prototype.handleTechLoadStart_; // eslint-disable-line
/**
* Fired when the player has initial duration and dimension information
*
* @event loadedmetadata
*/
Player.prototype.handleLoadedMetaData_;
Player.prototype.handleLoadedMetaData_; // eslint-disable-line
/**
* Fired when the player receives text data
*
* @event textdata
*/
Player.prototype.handleTextData_;
Player.prototype.handleTextData_; // eslint-disable-line
/**
* Fired when the player has downloaded data at the current playback position
*
* @event loadeddata
*/
Player.prototype.handleLoadedData_;
Player.prototype.handleLoadedData_; // eslint-disable-line
/**
* Fired when the user is active, e.g. moves the mouse over the player
*
* @event useractive
*/
Player.prototype.handleUserActive_;
Player.prototype.handleUserActive_; // eslint-disable-line
/**
* Fired when the user is inactive, e.g. a short delay after the last mouse move or control interaction
*
* @event userinactive
*/
Player.prototype.handleUserInactive_;
Player.prototype.handleUserInactive_; // eslint-disable-line
/**
* Fired when the current playback position has changed *
@ -2941,31 +2994,31 @@ Player.prototype.handleUserInactive_;
*
* @event timeupdate
*/
Player.prototype.handleTimeUpdate_;
Player.prototype.handleTimeUpdate_; // eslint-disable-line
/**
* Fired when video playback ends
*
* @event ended
*/
Player.prototype.handleTechEnded_;
Player.prototype.handleTechEnded_; // eslint-disable-line
/**
* Fired when the volume changes
*
* @event volumechange
*/
Player.prototype.handleVolumeChange_;
Player.prototype.handleVolumeChange_; // eslint-disable-line
/**
* Fired when an error occurs
*
* @event error
*/
Player.prototype.handleError_;
Player.prototype.handleError_ = Player.prototype.handleError_;
Player.prototype.flexNotSupported_ = function() {
var elem = document.createElement('i');
let elem = document.createElement('i');
// Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
// common flex features that we can rely on when checking for flex support.
@ -2973,7 +3026,8 @@ Player.prototype.flexNotSupported_ = function() {
'webkitFlexBasis' in elem.style ||
'mozFlexBasis' in elem.style ||
'msFlexBasis' in elem.style ||
'msFlexOrder' in elem.style /* IE10-specific (2012 flex spec) */);
// IE10-specific (2012 flex spec)
'msFlexOrder' in elem.style);
};
Component.registerComponent('Player', Player);

View File

@ -10,7 +10,7 @@ import Player from './player.js';
* @param {Function} init The function that is run when the player inits
* @method plugin
*/
var plugin = function(name, init){
const plugin = function(name, init) {
Player.prototype[name] = init;
};

View File

@ -3,10 +3,6 @@
*/
import ClickableComponent from '../clickable-component.js';
import Component from '../component.js';
import Popup from './popup.js';
import * as Dom from '../utils/dom.js';
import * as Fn from '../utils/fn.js';
import toTitleCase from '../utils/to-title-case.js';
/**
* A button class with a popup control
@ -18,7 +14,7 @@ import toTitleCase from '../utils/to-title-case.js';
*/
class PopupButton extends ClickableComponent {
constructor(player, options={}){
constructor(player, options = {}) {
super(player, options);
this.update();
@ -30,7 +26,7 @@ class PopupButton extends ClickableComponent {
* @method update
*/
update() {
let popup = this.createPopup();
const popup = this.createPopup();
if (this.popup) {
this.removeChild(this.popup);
@ -73,7 +69,7 @@ class PopupButton extends ClickableComponent {
* @method buildCSSClass
*/
buildCSSClass() {
var menuButtonClass = 'vjs-menu-button';
let menuButtonClass = 'vjs-menu-button';
// If the inline option is passed, we want to use different styles altogether.
if (this.options_.inline === true) {

View File

@ -22,7 +22,7 @@ class Popup extends Component {
*/
addItem(component) {
this.addChild(component);
component.on('click', Fn.bind(this, function(){
component.on('click', Fn.bind(this, function() {
this.unlockShowing();
}));
}
@ -34,19 +34,22 @@ class Popup extends Component {
* @method createEl
*/
createEl() {
let contentElType = this.options_.contentElType || 'ul';
const contentElType = this.options_.contentElType || 'ul';
this.contentEl_ = Dom.createEl(contentElType, {
className: 'vjs-menu-content'
});
var el = super.createEl('div', {
const el = super.createEl('div', {
append: this.contentEl_,
className: 'vjs-menu'
});
el.appendChild(this.contentEl_);
// Prevent clicks from bubbling up. Needed for Popup Buttons,
// where a click on the parent is significant
Events.on(el, 'click', function(event){
Events.on(el, 'click', function(event) {
event.preventDefault();
event.stopImmediatePropagation();
});

View File

@ -17,7 +17,7 @@ import * as browser from './utils/browser.js';
*/
class PosterImage extends ClickableComponent {
constructor(player, options){
constructor(player, options) {
super(player, options);
this.update();
@ -90,6 +90,7 @@ class PosterImage extends ClickableComponent {
this.fallbackImg_.src = url;
} else {
let backgroundImage = '';
// Any falsey values should stay as an empty string, otherwise
// this will throw an extra error
if (url) {

View File

@ -10,27 +10,29 @@ import window from 'global/window';
let _windowLoaded = false;
let videojs;
let autoSetupTimeout;
// Automatically set up any tags that have a data-setup attribute
var autoSetup = function(){
const autoSetup = function() {
// One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack*
// var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
// var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
// var mediaEls = vids.concat(audios);
// Because IE8 doesn't support calling slice on a node list, we need to loop through each list of elements
// to build up a new, combined list of elements.
var vids = document.getElementsByTagName('video');
var audios = document.getElementsByTagName('audio');
var mediaEls = [];
// Because IE8 doesn't support calling slice on a node list, we need to loop
// through each list of elements to build up a new, combined list of elements.
const vids = document.getElementsByTagName('video');
const audios = document.getElementsByTagName('audio');
const mediaEls = [];
if (vids && vids.length > 0) {
for(let i=0, e=vids.length; i<e; i++) {
for (let i = 0, e = vids.length; i < e; i++) {
mediaEls.push(vids[i]);
}
}
if (audios && audios.length > 0) {
for(let i=0, e=audios.length; i<e; i++) {
for (let i = 0, e = audios.length; i < e; i++) {
mediaEls.push(audios[i]);
}
}
@ -38,22 +40,23 @@ var autoSetup = function(){
// Check if any media elements exist
if (mediaEls && mediaEls.length > 0) {
for (let i=0, e=mediaEls.length; i<e; i++) {
for (let i = 0, e = mediaEls.length; i < e; i++) {
let mediaEl = mediaEls[i];
// Check if element exists, has getAttribute func.
// IE seems to consider typeof el.getAttribute == 'object' instead of 'function' like expected, at least when loading the player immediately.
// IE seems to consider typeof el.getAttribute == 'object' instead of
// 'function' like expected, at least when loading the player immediately.
if (mediaEl && mediaEl.getAttribute) {
// Make sure this player hasn't already been set up.
if (mediaEl['player'] === undefined) {
if (mediaEl.player === undefined) {
let options = mediaEl.getAttribute('data-setup');
// Check if data-setup attr exists.
// We only auto-setup if they've added the data-setup attr.
if (options !== null) {
// Create new video.js instance.
let player = videojs(mediaEl);
videojs(mediaEl);
}
}
@ -71,7 +74,7 @@ var autoSetup = function(){
};
// Pause to let the DOM keep processing
var autoSetupTimeout = function(wait, vjs){
autoSetupTimeout = function(wait, vjs) {
if (vjs) {
videojs = vjs;
}
@ -82,12 +85,12 @@ var autoSetupTimeout = function(wait, vjs){
if (document.readyState === 'complete') {
_windowLoaded = true;
} else {
Events.one(window, 'load', function(){
Events.one(window, 'load', function() {
_windowLoaded = true;
});
}
var hasLoaded = function() {
const hasLoaded = function() {
return _windowLoaded;
};

View File

@ -42,7 +42,7 @@ class Slider extends Component {
* @return {Element}
* @method createEl
*/
createEl(type, props={}, attributes={}) {
createEl(type, props = {}, attributes = {}) {
// Add the slider element class to all sub classes
props.className = props.className + ' vjs-slider';
props = assign({
@ -54,7 +54,7 @@ class Slider extends Component {
'aria-valuenow': 0,
'aria-valuemin': 0,
'aria-valuemax': 100,
tabIndex: 0
'tabIndex': 0
}, attributes);
return super.createEl(type, props, attributes);
@ -67,7 +67,7 @@ class Slider extends Component {
* @method handleMouseDown
*/
handleMouseDown(event) {
let doc = this.bar.el_.ownerDocument;
const doc = this.bar.el_.ownerDocument;
event.preventDefault();
Dom.blockTextSelection();
@ -96,7 +96,7 @@ class Slider extends Component {
* @method handleMouseUp
*/
handleMouseUp() {
let doc = this.bar.el_.ownerDocument;
const doc = this.bar.el_.ownerDocument;
Dom.unblockTextSelection();
@ -119,27 +119,31 @@ class Slider extends Component {
update() {
// In VolumeBar init we have a setTimeout for update that pops and update to the end of the
// execution stack. The player is destroyed before then update will cause an error
if (!this.el_) return;
if (!this.el_) {
return;
}
// If scrubbing, we could use a cached value to make the handle keep up with the user's mouse.
// On HTML5 browsers scrubbing is really smooth, but some flash players are slow, so we might want to utilize this later.
// var progress = (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
let progress = this.getPercent();
let bar = this.bar;
const bar = this.bar;
// If there's no bar...
if (!bar) return;
if (!bar) {
return;
}
// Protect against no duration and other division issues
if (typeof progress !== 'number' ||
progress !== progress ||
progress < 0 ||
progress === Infinity) {
progress = 0;
progress = 0;
}
// Convert to a percentage for setting
let percentage = (progress * 100).toFixed(2) + '%';
const percentage = (progress * 100).toFixed(2) + '%';
// Set the new bar width or height
if (this.vertical()) {
@ -155,8 +159,9 @@ class Slider extends Component {
* @param {Object} event Event object
* @method calculateDistance
*/
calculateDistance(event){
let position = Dom.getPointerPosition(this.el_, event);
calculateDistance(event) {
const position = Dom.getPointerPosition(this.el_, event);
if (this.vertical()) {
return position.y;
}
@ -179,10 +184,13 @@ class Slider extends Component {
* @method handleKeyPress
*/
handleKeyPress(event) {
if (event.which === 37 || event.which === 40) { // Left and Down Arrows
// Left and Down Arrows
if (event.which === 37 || event.which === 40) {
event.preventDefault();
this.stepBack();
} else if (event.which === 38 || event.which === 39) { // Up and Right Arrows
// Up and Right Arrows
} else if (event.which === 38 || event.which === 39) {
event.preventDefault();
this.stepForward();
}

View File

@ -17,17 +17,19 @@ function FlashRtmpDecorator(Flash) {
stream: ''
};
if (!src) return parts;
if (!src) {
return parts;
}
// Look for the normal URL separator we expect, '&'.
// If found, we split the URL into two pieces around the
// first '&'.
let connEnd = src.search(/&(?!\w+=)/);
let streamBegin;
if (connEnd !== -1) {
streamBegin = connEnd + 1;
}
else {
} else {
// If there's not a '&', we use the last '/' as the delimiter.
connEnd = streamBegin = src.lastIndexOf('/') + 1;
if (connEnd === 0) {
@ -35,6 +37,7 @@ function FlashRtmpDecorator(Flash) {
connEnd = streamBegin = src.length;
}
}
parts.connection = src.substring(0, connEnd);
parts.stream = src.substring(streamBegin, src.length);
@ -64,7 +67,7 @@ function FlashRtmpDecorator(Flash) {
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Flash.rtmpSourceHandler.canPlayType = function(type){
Flash.rtmpSourceHandler.canPlayType = function(type) {
if (Flash.isStreamingType(type)) {
return 'maybe';
}
@ -78,7 +81,7 @@ function FlashRtmpDecorator(Flash) {
* @param {Object} options The options passed to the tech
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Flash.rtmpSourceHandler.canHandleSource = function(source, options){
Flash.rtmpSourceHandler.canHandleSource = function(source, options) {
let can = Flash.rtmpSourceHandler.canPlayType(source.type);
if (can) {
@ -100,11 +103,11 @@ function FlashRtmpDecorator(Flash) {
* @param {Flash} tech The instance of the Flash tech
* @param {Object} options The options to pass to the source
*/
Flash.rtmpSourceHandler.handleSource = function(source, tech, options){
Flash.rtmpSourceHandler.handleSource = function(source, tech, options) {
let srcParts = Flash.streamToParts(source.src);
tech['setRtmpConnection'](srcParts.connection);
tech['setRtmpStream'](srcParts.stream);
tech.setRtmpConnection(srcParts.connection);
tech.setRtmpStream(srcParts.stream);
};
// Register the native source handler

View File

@ -15,6 +15,7 @@ import window from 'global/window';
import assign from 'object.assign';
let navigator = window.navigator;
/**
* Flash Media Controller - Wrapper for fallback SWF API
*
@ -25,12 +26,12 @@ let navigator = window.navigator;
*/
class Flash extends Tech {
constructor(options, ready){
constructor(options, ready) {
super(options, ready);
// Set the source when ready
if (options.source) {
this.ready(function(){
this.ready(function() {
this.setSource(options.source);
}, true);
}
@ -38,7 +39,7 @@ class Flash extends Tech {
// Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
// This allows resetting the playhead when we catch the reload
if (options.startTime) {
this.ready(function(){
this.ready(function() {
this.load();
this.play();
this.currentTime(options.startTime);
@ -84,29 +85,32 @@ class Flash extends Tech {
let flashVars = assign({
// SWF Callback Functions
'readyFunction': 'videojs.Flash.onReady',
'eventProxyFunction': 'videojs.Flash.onEvent',
'errorEventProxyFunction': 'videojs.Flash.onError',
readyFunction: 'videojs.Flash.onReady',
eventProxyFunction: 'videojs.Flash.onEvent',
errorEventProxyFunction: 'videojs.Flash.onError',
// Player Settings
'autoplay': options.autoplay,
'preload': options.preload,
'loop': options.loop,
'muted': options.muted
autoplay: options.autoplay,
preload: options.preload,
loop: options.loop,
muted: options.muted
}, options.flashVars);
// Merge default parames with ones passed in
let params = assign({
'wmode': 'opaque', // Opaque is needed to overlay controls, but can affect playback performance
'bgcolor': '#000000' // Using bgcolor prevents a white flash when the object is loading
// Opaque is needed to overlay controls, but can affect playback performance
wmode: 'opaque',
// Using bgcolor prevents a white flash when the object is loading
bgcolor: '#000000'
}, options.params);
// Merge default attributes with ones passed in
let attributes = assign({
'id': objId,
'name': objId, // Both ID and Name needed or swf to identify itself
'class': 'vjs-tech'
// Both ID and Name needed or swf to identify itself
id: objId,
name: objId,
class: 'vjs-tech'
}, options.attributes);
this.el_ = Flash.embed(options.swf, flashVars, params, attributes);
@ -167,8 +171,7 @@ class Flash extends Tech {
// Currently the SWF doesn't autoplay if you load a source later.
// e.g. Load player w/ no source, wait 2s, set src.
if (this.autoplay()) {
var tech = this;
this.setTimeout(function(){ tech.play(); }, 0);
this.setTimeout(() => this.play(), 0);
}
}
@ -188,6 +191,7 @@ class Flash extends Tech {
*/
setCurrentTime(time) {
let seekable = this.seekable();
if (seekable.length) {
// clamp to the current seekable range
time = time > seekable.start(0) ? time : seekable.start(0);
@ -224,9 +228,8 @@ class Flash extends Tech {
currentSrc() {
if (this.currentSource_) {
return this.currentSource_.src;
} else {
return this.el_.vjs_getProperty('currentSrc');
}
return this.el_.vjs_getProperty('currentSrc');
}
/**
@ -237,10 +240,10 @@ class Flash extends Tech {
duration() {
if (this.readyState() === 0) {
return NaN;
} else {
let duration = this.el_.vjs_getProperty('duration');
return duration >= 0 ? duration : Infinity;
}
let duration = this.el_.vjs_getProperty('duration');
return duration >= 0 ? duration : Infinity;
}
/**
@ -276,6 +279,7 @@ class Flash extends Tech {
*/
seekable() {
const duration = this.duration();
if (duration === 0) {
return createTimeRange();
}
@ -290,6 +294,7 @@ class Flash extends Tech {
*/
buffered() {
let ranges = this.el_.vjs_getProperty('buffered');
if (ranges.length === 0) {
return createTimeRange();
}
@ -305,7 +310,8 @@ class Flash extends Tech {
* @method supportsFullScreen
*/
supportsFullScreen() {
return false; // Flash does not allow fullscreen through javascript
// Flash does not allow fullscreen through javascript
return false;
}
/**
@ -322,18 +328,23 @@ class Flash extends Tech {
}
// Create setters and getters for attributes
const _api = Flash.prototype;
const _readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(',');
const _readOnly = 'networkState,readyState,initialTime,startOffsetTime,paused,ended,videoWidth,videoHeight'.split(',');
function _createSetter(attr){
var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
_api['set'+attrUpper] = function(val){ return this.el_.vjs_setProperty(attr, val); };
function _createSetter(attr) {
let attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
_api['set' + attrUpper] = function(val) {
return this.el_.vjs_setProperty(attr, val);
};
}
function _createGetter(attr) {
_api[attr] = function(){ return this.el_.vjs_getProperty(attr); };
_api[attr] = function() {
return this.el_.vjs_getProperty(attr);
};
}
// Create getter and setters for all read/write attributes
@ -349,7 +360,7 @@ for (let i = 0; i < _readOnly.length; i++) {
/* Flash Support Testing -------------------------------------------------------- */
Flash.isSupported = function(){
Flash.isSupported = function() {
return Flash.version()[0] >= 10;
// return swfobject.hasFlashPlayerVersion('10');
};
@ -371,7 +382,7 @@ Flash.nativeSourceHandler = {};
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Flash.nativeSourceHandler.canPlayType = function(type){
Flash.nativeSourceHandler.canPlayType = function(type) {
if (type in Flash.formats) {
return 'maybe';
}
@ -386,11 +397,12 @@ Flash.nativeSourceHandler.canPlayType = function(type){
* @param {Object} options The options passed to the tech
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Flash.nativeSourceHandler.canHandleSource = function(source, options){
var type;
Flash.nativeSourceHandler.canHandleSource = function(source, options) {
let type;
function guessMimeType(src) {
var ext = Url.getFileExtension(src);
let ext = Url.getFileExtension(src);
if (ext) {
return `video/${ext}`;
}
@ -416,7 +428,7 @@ Flash.nativeSourceHandler.canHandleSource = function(source, options){
* @param {Flash} tech The instance of the Flash tech
* @param {Object} options The options to pass to the source
*/
Flash.nativeSourceHandler.handleSource = function(source, tech, options){
Flash.nativeSourceHandler.handleSource = function(source, tech, options) {
tech.setSrc(source.src);
};
@ -424,7 +436,7 @@ Flash.nativeSourceHandler.handleSource = function(source, tech, options){
* Clean up the source handler when disposing the player or switching sources..
* (no cleanup is needed when supporting the format natively)
*/
Flash.nativeSourceHandler.dispose = function(){};
Flash.nativeSourceHandler.dispose = function() {};
// Register the native source handler
Flash.registerSourceHandler(Flash.nativeSourceHandler);
@ -436,7 +448,7 @@ Flash.formats = {
'video/m4v': 'MP4'
};
Flash.onReady = function(currSwf){
Flash.onReady = function(currSwf) {
let el = Dom.getEl(currSwf);
let tech = el && el.tech;
@ -450,7 +462,7 @@ Flash.onReady = function(currSwf){
// The SWF isn't always ready when it says it is. Sometimes the API functions still need to be added to the object.
// If it's not ready, we set a timeout to check again shortly.
Flash.checkReady = function(tech){
Flash.checkReady = function(tech) {
// stop worrying if the tech has been disposed
if (!tech.el()) {
return;
@ -462,20 +474,21 @@ Flash.checkReady = function(tech){
tech.triggerReady();
} else {
// wait longer
this.setTimeout(function(){
Flash['checkReady'](tech);
this.setTimeout(function() {
Flash.checkReady(tech);
}, 50);
}
};
// Trigger events from the swf on the player
Flash.onEvent = function(swfID, eventName){
Flash.onEvent = function(swfID, eventName) {
let tech = Dom.getEl(swfID).tech;
tech.trigger(eventName, Array.prototype.slice.call(arguments, 2));
};
// Log errors from the swf
Flash.onError = function(swfID, err){
Flash.onError = function(swfID, err) {
const tech = Dom.getEl(swfID).tech;
// trigger MEDIA_ERR_SRC_NOT_SUPPORTED
@ -488,7 +501,7 @@ Flash.onError = function(swfID, err){
};
// Flash Version Check
Flash.version = function(){
Flash.version = function() {
let version = '0,0,0';
// IE
@ -496,18 +509,20 @@ Flash.version = function(){
version = new window.ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
// other browsers
} catch(e) {
} catch (e) {
try {
if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin){
if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin) {
version = (navigator.plugins['Shockwave Flash 2.0'] || navigator.plugins['Shockwave Flash']).description.replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
}
} catch(err) {}
} catch (err) {
// satisfy linter
}
}
return version.split(',');
};
// Flash embedding method. Only used in non-iframe mode
Flash.embed = function(swf, flashVars, params, attributes){
Flash.embed = function(swf, flashVars, params, attributes) {
const code = Flash.getEmbedCode(swf, flashVars, params, attributes);
// Get element by embedding code and retrieving created element
@ -516,7 +531,7 @@ Flash.embed = function(swf, flashVars, params, attributes){
return obj;
};
Flash.getEmbedCode = function(swf, flashVars, params, attributes){
Flash.getEmbedCode = function(swf, flashVars, params, attributes) {
const objTag = '<object type="application/x-shockwave-flash" ';
let flashVarsString = '';
let paramsString = '';
@ -524,36 +539,38 @@ Flash.getEmbedCode = function(swf, flashVars, params, attributes){
// Convert flash vars to string
if (flashVars) {
Object.getOwnPropertyNames(flashVars).forEach(function(key){
Object.getOwnPropertyNames(flashVars).forEach(function(key) {
flashVarsString += `${key}=${flashVars[key]}&amp;`;
});
}
// Add swf, flashVars, and other default params
params = assign({
'movie': swf,
'flashvars': flashVarsString,
'allowScriptAccess': 'always', // Required to talk to swf
'allowNetworking': 'all' // All should be default, but having security issues.
movie: swf,
flashvars: flashVarsString,
// Required to talk to swf
allowScriptAccess: 'always',
// All should be default, but having security issues.
allowNetworking: 'all'
}, params);
// Create param tags string
Object.getOwnPropertyNames(params).forEach(function(key){
Object.getOwnPropertyNames(params).forEach(function(key) {
paramsString += `<param name="${key}" value="${params[key]}" />`;
});
attributes = assign({
// Add swf to attributes (need both for IE and Others to work)
'data': swf,
data: swf,
// Default to 100% width/height
'width': '100%',
'height': '100%'
width: '100%',
height: '100%'
}, attributes);
// Create Attributes string
Object.getOwnPropertyNames(attributes).forEach(function(key){
Object.getOwnPropertyNames(attributes).forEach(function(key) {
attrsString += `${key}="${attributes[key]}" `;
});

View File

@ -10,7 +10,6 @@ import * as Url from '../utils/url.js';
import * as Fn from '../utils/fn.js';
import log from '../utils/log.js';
import tsml from 'tsml';
import TextTrack from '../../../src/js/tracks/text-track.js';
import * as browser from '../utils/browser.js';
import document from 'global/document';
import window from 'global/window';
@ -28,7 +27,7 @@ import toTitleCase from '../utils/to-title-case.js';
*/
class Html5 extends Tech {
constructor(options, ready){
constructor(options, ready) {
super(options, ready);
const source = options.source;
@ -74,7 +73,7 @@ class Html5 extends Tech {
}
}
for (let i=0; i<removeNodes.length; i++) {
for (let i = 0; i < removeNodes.length; i++) {
this.el_.removeChild(removeNodes[i]);
}
}
@ -102,7 +101,7 @@ class Html5 extends Tech {
if (this.featuresNativeTextTracks) {
if (crossoriginTracks) {
log.warn(tsml`Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.
log.warn(tsml`Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.
This may prevent text tracks from loading.`);
}
@ -140,7 +139,7 @@ class Html5 extends Tech {
tl.removeEventListener('addtrack', this[`handle${capitalType}TrackAdd_`]);
tl.removeEventListener('removetrack', this[`handle${capitalType}TrackRemove_`]);
}
// Stop removing old text tracks
if (tl) {
this.off('loadstart', this[`removeOld${capitalType}Tracks_`]);
@ -164,11 +163,12 @@ class Html5 extends Tech {
// Check if this browser supports moving the element into the box.
// On the iPhone video will break if you move the element,
// So we have to create a brand new element.
if (!el || this['movingMediaElementInDOM'] === false) {
if (!el || this.movingMediaElementInDOM === false) {
// If the original tag is still there, clone and remove it.
if (el) {
const clone = el.cloneNode(true);
el.parentNode.insertBefore(clone, el);
Html5.disposeMediaElement(el);
el = clone;
@ -178,6 +178,7 @@ class Html5 extends Tech {
// determine if native controls should be used
let tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag);
let attributes = mergeOptions({}, tagAttributes);
if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
delete attributes.controls;
}
@ -192,10 +193,12 @@ class Html5 extends Tech {
}
// Update specific tag settings, in case they were overridden
let settingsAttrs = ['autoplay','preload','loop','muted'];
let settingsAttrs = ['autoplay', 'preload', 'loop', 'muted'];
for (let i = settingsAttrs.length - 1; i >= 0; i--) {
const attr = settingsAttrs[i];
let overwriteAttrs = {};
if (typeof this.options_[attr] !== 'undefined') {
overwriteAttrs[attr] = this.options_[attr];
}
@ -232,6 +235,7 @@ class Html5 extends Tech {
let setLoadstartFired = function() {
loadstartFired = true;
};
this.on('loadstart', setLoadstartFired);
let triggerLoadstart = function() {
@ -241,9 +245,10 @@ class Html5 extends Tech {
this.trigger('loadstart');
}
};
this.on('loadedmetadata', triggerLoadstart);
this.ready(function(){
this.ready(function() {
this.off('loadstart', setLoadstartFired);
this.off('loadedmetadata', triggerLoadstart);
@ -281,8 +286,8 @@ class Html5 extends Tech {
}
// We still need to give the player time to add event listeners
this.ready(function(){
eventsToTrigger.forEach(function(type){
this.ready(function() {
eventsToTrigger.forEach(function(type) {
this.trigger(type);
}, this);
});
@ -303,7 +308,7 @@ class Html5 extends Tech {
tt.addEventListener('addtrack', this.handleTextTrackAdd_);
tt.addEventListener('removetrack', this.handleTextTrackRemove_);
}
// Remove (native) texttracks that are not used anymore
this.on('loadstart', this.removeOldTextTracks_);
}
@ -311,6 +316,7 @@ class Html5 extends Tech {
handleTextTrackChange(e) {
let tt = this.textTracks();
this.textTracks().trigger({
type: 'change',
target: tt,
@ -329,6 +335,7 @@ class Html5 extends Tech {
handleVideoTrackChange_(e) {
let vt = this.videoTracks();
this.videoTracks().trigger({
type: 'change',
target: vt,
@ -347,6 +354,7 @@ class Html5 extends Tech {
handleAudioTrackChange_(e) {
let audioTrackList = this.audioTracks();
this.audioTracks().trigger({
type: 'change',
target: audioTrackList,
@ -374,14 +382,15 @@ class Html5 extends Tech {
// This will loop over the techTracks and check if they are still used by the HTML5 video element
// If not, they will be removed from the emulated list
let removeTracks = [];
if (!elTracks) {
return;
}
for (let i = 0; i < techTracks.length; i++) {
let techTrack = techTracks[i];
let found = false;
for (let j = 0; j < elTracks.length; j++) {
if (elTracks[j] === techTrack) {
found = true;
@ -396,6 +405,7 @@ class Html5 extends Tech {
for (let i = 0; i < removeTracks.length; i++) {
const track = removeTracks[i];
techTracks.removeTrack_(track);
}
}
@ -403,18 +413,21 @@ class Html5 extends Tech {
removeOldTextTracks_() {
const techTracks = this.textTracks();
const elTracks = this.el().textTracks;
this.removeOldTracks_(techTracks, elTracks);
}
removeOldAudioTracks_() {
const techTracks = this.audioTracks();
const elTracks = this.el().audioTracks;
this.removeOldTracks_(techTracks, elTracks);
}
removeOldVideoTracks_() {
const techTracks = this.videoTracks();
const elTracks = this.el().videoTracks;
this.removeOldTracks_(techTracks, elTracks);
}
@ -423,14 +436,18 @@ class Html5 extends Tech {
*
* @method play
*/
play() { this.el_.play(); }
play() {
this.el_.play();
}
/**
* Pause for html5 tech
*
* @method pause
*/
pause() { this.el_.pause(); }
pause() {
this.el_.pause();
}
/**
* Paused for html5 tech
@ -438,7 +455,9 @@ class Html5 extends Tech {
* @return {Boolean}
* @method paused
*/
paused() { return this.el_.paused; }
paused() {
return this.el_.paused;
}
/**
* Get current time
@ -446,7 +465,9 @@ class Html5 extends Tech {
* @return {Number}
* @method currentTime
*/
currentTime() { return this.el_.currentTime; }
currentTime() {
return this.el_.currentTime;
}
/**
* Set current time
@ -457,7 +478,7 @@ class Html5 extends Tech {
setCurrentTime(seconds) {
try {
this.el_.currentTime = seconds;
} catch(e) {
} catch (e) {
log(e, 'Video is not ready. (Video.js)');
// this.warning(VideoJS.warnings.videoNotReady);
}
@ -469,7 +490,9 @@ class Html5 extends Tech {
* @return {Number}
* @method duration
*/
duration() { return this.el_.duration || 0; }
duration() {
return this.el_.duration || 0;
}
/**
* Get a TimeRange object that represents the intersection
@ -479,7 +502,9 @@ class Html5 extends Tech {
* @return {TimeRangeObject}
* @method buffered
*/
buffered() { return this.el_.buffered; }
buffered() {
return this.el_.buffered;
}
/**
* Get volume level
@ -487,7 +512,9 @@ class Html5 extends Tech {
* @return {Number}
* @method volume
*/
volume() { return this.el_.volume; }
volume() {
return this.el_.volume;
}
/**
* Set volume level
@ -495,7 +522,9 @@ class Html5 extends Tech {
* @param {Number} percentAsDecimal Volume percent as a decimal
* @method setVolume
*/
setVolume(percentAsDecimal) { this.el_.volume = percentAsDecimal; }
setVolume(percentAsDecimal) {
this.el_.volume = percentAsDecimal;
}
/**
* Get if muted
@ -503,7 +532,9 @@ class Html5 extends Tech {
* @return {Boolean}
* @method muted
*/
muted() { return this.el_.muted; }
muted() {
return this.el_.muted;
}
/**
* Set muted
@ -511,7 +542,9 @@ class Html5 extends Tech {
* @param {Boolean} If player is to be muted or note
* @method setMuted
*/
setMuted(muted) { this.el_.muted = muted; }
setMuted(muted) {
this.el_.muted = muted;
}
/**
* Get player width
@ -519,7 +552,9 @@ class Html5 extends Tech {
* @return {Number}
* @method width
*/
width() { return this.el_.offsetWidth; }
width() {
return this.el_.offsetWidth;
}
/**
* Get player height
@ -527,7 +562,9 @@ class Html5 extends Tech {
* @return {Number}
* @method height
*/
height() { return this.el_.offsetHeight; }
height() {
return this.el_.offsetHeight;
}
/**
* Get if there is fullscreen support
@ -538,8 +575,9 @@ class Html5 extends Tech {
supportsFullScreen() {
if (typeof this.el_.webkitEnterFullScreen === 'function') {
let userAgent = window.navigator.userAgent;
// Seems to be broken in Chromium/Chrome && Safari in Leopard
if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
if ((/Android/).test(userAgent) || !(/Chrome|Mac OS X 10.5/).test(userAgent)) {
return true;
}
}
@ -552,7 +590,7 @@ class Html5 extends Tech {
* @method enterFullScreen
*/
enterFullScreen() {
var video = this.el_;
let video = this.el_;
if ('webkitDisplayingFullscreen' in video) {
this.one('webkitbeginfullscreen', function() {
@ -571,7 +609,7 @@ class Html5 extends Tech {
// playing and pausing synchronously during the transition to fullscreen
// can get iOS ~6.1 devices into a play/pause loop
this.setTimeout(function(){
this.setTimeout(function() {
video.pause();
video.webkitEnterFullScreen();
}, 0);
@ -599,10 +637,10 @@ class Html5 extends Tech {
src(src) {
if (src === undefined) {
return this.el_.src;
} else {
// Setting src through `src` instead of `setSrc` will be deprecated
this.setSrc(src);
}
// Setting src through `src` instead of `setSrc` will be deprecated
this.setSrc(src);
}
/**
@ -621,7 +659,7 @@ class Html5 extends Tech {
*
* @method load
*/
load(){
load() {
this.el_.load();
}
@ -643,9 +681,8 @@ class Html5 extends Tech {
currentSrc() {
if (this.currentSource_) {
return this.currentSource_.src;
} else {
return this.el_.currentSrc;
}
return this.el_.currentSrc;
}
/**
@ -654,7 +691,9 @@ class Html5 extends Tech {
* @return {String}
* @method poster
*/
poster() { return this.el_.poster; }
poster() {
return this.el_.poster;
}
/**
* Set poster
@ -662,7 +701,9 @@ class Html5 extends Tech {
* @param {String} val URL to poster image
* @method
*/
setPoster(val) { this.el_.poster = val; }
setPoster(val) {
this.el_.poster = val;
}
/**
* Get preload attribute
@ -670,7 +711,9 @@ class Html5 extends Tech {
* @return {String}
* @method preload
*/
preload() { return this.el_.preload; }
preload() {
return this.el_.preload;
}
/**
* Set preload attribute
@ -678,7 +721,9 @@ class Html5 extends Tech {
* @param {String} val Value for preload attribute
* @method setPreload
*/
setPreload(val) { this.el_.preload = val; }
setPreload(val) {
this.el_.preload = val;
}
/**
* Get autoplay attribute
@ -686,7 +731,9 @@ class Html5 extends Tech {
* @return {String}
* @method autoplay
*/
autoplay() { return this.el_.autoplay; }
autoplay() {
return this.el_.autoplay;
}
/**
* Set autoplay attribute
@ -694,7 +741,9 @@ class Html5 extends Tech {
* @param {String} val Value for preload attribute
* @method setAutoplay
*/
setAutoplay(val) { this.el_.autoplay = val; }
setAutoplay(val) {
this.el_.autoplay = val;
}
/**
* Get controls attribute
@ -702,7 +751,9 @@ class Html5 extends Tech {
* @return {String}
* @method controls
*/
controls() { return this.el_.controls; }
controls() {
return this.el_.controls;
}
/**
* Set controls attribute
@ -710,7 +761,9 @@ class Html5 extends Tech {
* @param {String} val Value for controls attribute
* @method setControls
*/
setControls(val) { this.el_.controls = !!val; }
setControls(val) {
this.el_.controls = !!val;
}
/**
* Get loop attribute
@ -718,7 +771,9 @@ class Html5 extends Tech {
* @return {String}
* @method loop
*/
loop() { return this.el_.loop; }
loop() {
return this.el_.loop;
}
/**
* Set loop attribute
@ -726,7 +781,9 @@ class Html5 extends Tech {
* @param {String} val Value for loop attribute
* @method setLoop
*/
setLoop(val) { this.el_.loop = val; }
setLoop(val) {
this.el_.loop = val;
}
/**
* Get error value
@ -734,7 +791,9 @@ class Html5 extends Tech {
* @return {String}
* @method error
*/
error() { return this.el_.error; }
error() {
return this.el_.error;
}
/**
* Get whether or not the player is in the "seeking" state
@ -742,7 +801,9 @@ class Html5 extends Tech {
* @return {Boolean}
* @method seeking
*/
seeking() { return this.el_.seeking; }
seeking() {
return this.el_.seeking;
}
/**
* Get a TimeRanges object that represents the
@ -752,7 +813,9 @@ class Html5 extends Tech {
* @return {TimeRangeObject}
* @method seekable
*/
seekable() { return this.el_.seekable; }
seekable() {
return this.el_.seekable;
}
/**
* Get if video ended
@ -760,7 +823,9 @@ class Html5 extends Tech {
* @return {Boolean}
* @method ended
*/
ended() { return this.el_.ended; }
ended() {
return this.el_.ended;
}
/**
* Get the value of the muted content attribute
@ -770,7 +835,9 @@ class Html5 extends Tech {
* @return {Boolean}
* @method defaultMuted
*/
defaultMuted() { return this.el_.defaultMuted; }
defaultMuted() {
return this.el_.defaultMuted;
}
/**
* Get desired speed at which the media resource is to play
@ -778,7 +845,9 @@ class Html5 extends Tech {
* @return {Number}
* @method playbackRate
*/
playbackRate() { return this.el_.playbackRate; }
playbackRate() {
return this.el_.playbackRate;
}
/**
* Returns a TimeRanges object that represents the ranges of the
@ -787,7 +856,9 @@ class Html5 extends Tech {
* timeline that has been reached through normal playback
* @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-played
*/
played() { return this.el_.played; }
played() {
return this.el_.played;
}
/**
* Set desired speed at which the media resource is to play
@ -795,7 +866,9 @@ class Html5 extends Tech {
* @param {Number} val Speed at which the media resource is to play
* @method setPlaybackRate
*/
setPlaybackRate(val) { this.el_.playbackRate = val; }
setPlaybackRate(val) {
this.el_.playbackRate = val;
}
/**
* Get the current state of network activity for the element, from
@ -808,7 +881,9 @@ class Html5 extends Tech {
* @return {Number}
* @method networkState
*/
networkState() { return this.el_.networkState; }
networkState() {
return this.el_.networkState;
}
/**
* Get a value that expresses the current state of the element
@ -823,7 +898,9 @@ class Html5 extends Tech {
* @return {Number}
* @method readyState
*/
readyState() { return this.el_.readyState; }
readyState() {
return this.el_.readyState;
}
/**
* Get width of video
@ -831,7 +908,9 @@ class Html5 extends Tech {
* @return {Number}
* @method videoWidth
*/
videoWidth() { return this.el_.videoWidth; }
videoWidth() {
return this.el_.videoWidth;
}
/**
* Get height of video
@ -839,7 +918,9 @@ class Html5 extends Tech {
* @return {Number}
* @method videoHeight
*/
videoHeight() { return this.el_.videoHeight; }
videoHeight() {
return this.el_.videoHeight;
}
/**
* Get text tracks
@ -862,7 +943,7 @@ class Html5 extends Tech {
* @method addTextTrack
*/
addTextTrack(kind, label, language) {
if (!this['featuresNativeTextTracks']) {
if (!this.featuresNativeTextTracks) {
return super.addTextTrack(kind, label, language);
}
@ -877,8 +958,8 @@ class Html5 extends Tech {
* @return {HTMLTrackElement}
* @method addRemoteTextTrack
*/
addRemoteTextTrack(options={}) {
if (!this['featuresNativeTextTracks']) {
addRemoteTextTrack(options = {}) {
if (!this.featuresNativeTextTracks) {
return super.addRemoteTextTrack(options);
}
@ -919,12 +1000,11 @@ class Html5 extends Tech {
* @method removeRemoteTextTrack
*/
removeRemoteTextTrack(track) {
if (!this['featuresNativeTextTracks']) {
if (!this.featuresNativeTextTracks) {
return super.removeRemoteTextTrack(track);
}
let tracks, i;
let tracks;
let trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
// remove HTMLTrackElement and TextTrack from remote list
@ -933,7 +1013,8 @@ class Html5 extends Tech {
tracks = this.$$('track');
i = tracks.length;
let i = tracks.length;
while (i--) {
if (track === tracks[i] || track === tracks[i].track) {
this.el().removeChild(tracks[i]);
@ -943,7 +1024,6 @@ class Html5 extends Tech {
}
/* HTML5 Support Testing ---------------------------------------------------- */
/*
@ -955,6 +1035,7 @@ class Html5 extends Tech {
*/
Html5.TEST_VID = document.createElement('video');
let track = document.createElement('track');
track.kind = 'captions';
track.srclang = 'en';
track.label = 'English';
@ -965,10 +1046,10 @@ Html5.TEST_VID.appendChild(track);
*
* @return {Boolean}
*/
Html5.isSupported = function(){
Html5.isSupported = function() {
// IE9 with no Media Player is a LIAR! (#984)
try {
Html5.TEST_VID['volume'] = 0.5;
Html5.TEST_VID.volume = 0.5;
} catch (e) {
return false;
}
@ -994,12 +1075,12 @@ Html5.nativeSourceHandler = {};
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Html5.nativeSourceHandler.canPlayType = function(type){
Html5.nativeSourceHandler.canPlayType = function(type) {
// IE9 on Windows 7 without MediaPlayer throws an error here
// https://github.com/videojs/video.js/issues/519
try {
return Html5.TEST_VID.canPlayType(type);
} catch(e) {
} catch (e) {
return '';
}
};
@ -1011,15 +1092,15 @@ Html5.nativeSourceHandler.canPlayType = function(type){
* @param {Object} options The options passed to the tech
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Html5.nativeSourceHandler.canHandleSource = function(source, options){
var match, ext;
Html5.nativeSourceHandler.canHandleSource = function(source, options) {
// If a type was provided we should rely on that
if (source.type) {
return Html5.nativeSourceHandler.canPlayType(source.type);
// If no type, fall back to checking 'video/[EXTENSION]'
} else if (source.src) {
// If no type, fall back to checking 'video/[EXTENSION]'
ext = Url.getFileExtension(source.src);
let ext = Url.getFileExtension(source.src);
return Html5.nativeSourceHandler.canPlayType(`video/${ext}`);
}
@ -1036,7 +1117,7 @@ Html5.nativeSourceHandler.canHandleSource = function(source, options){
* @param {Html5} tech The instance of the Html5 tech
* @param {Object} options The options to pass to the source
*/
Html5.nativeSourceHandler.handleSource = function(source, tech, options){
Html5.nativeSourceHandler.handleSource = function(source, tech, options) {
tech.setSrc(source.src);
};
@ -1044,7 +1125,7 @@ Html5.nativeSourceHandler.handleSource = function(source, tech, options){
* Clean up the source handler when disposing the player or switching sources..
* (no cleanup is needed when supporting the format natively)
*/
Html5.nativeSourceHandler.dispose = function(){};
Html5.nativeSourceHandler.dispose = function() {};
// Register the native source handler
Html5.registerSourceHandler(Html5.nativeSourceHandler);
@ -1056,13 +1137,14 @@ Html5.registerSourceHandler(Html5.nativeSourceHandler);
*
* @return {Boolean}
*/
Html5.canControlVolume = function(){
Html5.canControlVolume = function() {
// IE will error if Windows Media Player not installed #3315
try {
var volume = Html5.TEST_VID.volume;
let volume = Html5.TEST_VID.volume;
Html5.TEST_VID.volume = (volume / 2) + 0.1;
return volume !== Html5.TEST_VID.volume;
} catch(e) {
} catch (e) {
return false;
}
};
@ -1072,7 +1154,7 @@ Html5.canControlVolume = function(){
*
* @return {Boolean}
*/
Html5.canControlPlaybackRate = function(){
Html5.canControlPlaybackRate = function() {
// Playback rate API is implemented in Android Chrome, but doesn't do anything
// https://github.com/videojs/video.js/issues/3180
if (browser.IS_ANDROID && browser.IS_CHROME) {
@ -1080,10 +1162,11 @@ Html5.canControlPlaybackRate = function(){
}
// IE will error if Windows Media Player not installed #3315
try {
var playbackRate = Html5.TEST_VID.playbackRate;
let playbackRate = Html5.TEST_VID.playbackRate;
Html5.TEST_VID.playbackRate = (playbackRate / 2) + 0.1;
return playbackRate !== Html5.TEST_VID.playbackRate;
} catch(e) {
} catch (e) {
return false;
}
};
@ -1094,7 +1177,7 @@ Html5.canControlPlaybackRate = function(){
* @return {Boolean}
*/
Html5.supportsNativeTextTracks = function() {
var supportsTextTracks;
let supportsTextTracks;
// Figure out native text track support
// If mode is a number, we cannot change it because it'll disappear from view.
@ -1103,7 +1186,7 @@ Html5.supportsNativeTextTracks = function() {
// TODO: Investigate firefox: https://github.com/videojs/video.js/issues/1862
supportsTextTracks = !!Html5.TEST_VID.textTracks;
if (supportsTextTracks && Html5.TEST_VID.textTracks.length > 0) {
supportsTextTracks = typeof Html5.TEST_VID.textTracks[0]['mode'] !== 'number';
supportsTextTracks = typeof Html5.TEST_VID.textTracks[0].mode !== 'number';
}
if (supportsTextTracks && browser.IS_FIREFOX) {
supportsTextTracks = false;
@ -1122,6 +1205,7 @@ Html5.supportsNativeTextTracks = function() {
*/
Html5.supportsNativeVideoTracks = function() {
let supportsVideoTracks = !!Html5.TEST_VID.videoTracks;
return supportsVideoTracks;
};
@ -1132,10 +1216,10 @@ Html5.supportsNativeVideoTracks = function() {
*/
Html5.supportsNativeAudioTracks = function() {
let supportsAudioTracks = !!Html5.TEST_VID.audioTracks;
return supportsAudioTracks;
};
/**
* An array of events available on the Html5 tech.
*
@ -1172,14 +1256,14 @@ Html5.Events = [
*
* @type {Boolean}
*/
Html5.prototype['featuresVolumeControl'] = Html5.canControlVolume();
Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
/*
* Set the tech's playbackRate support status
*
* @type {Boolean}
*/
Html5.prototype['featuresPlaybackRate'] = Html5.canControlPlaybackRate();
Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
/*
* Set the tech's status on moving the video element.
@ -1187,41 +1271,41 @@ Html5.prototype['featuresPlaybackRate'] = Html5.canControlPlaybackRate();
*
* @type {Boolean}
*/
Html5.prototype['movingMediaElementInDOM'] = !browser.IS_IOS;
Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS;
/*
* Set the the tech's fullscreen resize support status.
* HTML video is able to automatically resize when going to fullscreen.
* (No longer appears to be used. Can probably be removed.)
*/
Html5.prototype['featuresFullscreenResize'] = true;
Html5.prototype.featuresFullscreenResize = true;
/*
* Set the tech's progress event support status
* (this disables the manual progress events of the Tech)
*/
Html5.prototype['featuresProgressEvents'] = true;
Html5.prototype.featuresProgressEvents = true;
/*
* Sets the tech's status on native text track support
*
* @type {Boolean}
*/
Html5.prototype['featuresNativeTextTracks'] = Html5.supportsNativeTextTracks();
Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
/**
* Sets the tech's status on native text track support
*
* @type {Boolean}
*/
Html5.prototype['featuresNativeVideoTracks'] = Html5.supportsNativeVideoTracks();
Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
/**
* Sets the tech's status on native audio track support
*
* @type {Boolean}
*/
Html5.prototype['featuresNativeAudioTracks'] = Html5.supportsNativeAudioTracks();
Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
// HTML5 Feature detection and Device Fixes --------------------------------- //
let canPlayType;
@ -1249,7 +1333,7 @@ Html5.patchCanPlayType = function() {
canPlayType = Html5.TEST_VID.constructor.prototype.canPlayType;
}
Html5.TEST_VID.constructor.prototype.canPlayType = function(type){
Html5.TEST_VID.constructor.prototype.canPlayType = function(type) {
if (type && mp4RE.test(type)) {
return 'maybe';
}
@ -1259,7 +1343,8 @@ Html5.patchCanPlayType = function() {
};
Html5.unpatchCanPlayType = function() {
var r = Html5.TEST_VID.constructor.prototype.canPlayType;
let r = Html5.TEST_VID.constructor.prototype.canPlayType;
Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
canPlayType = null;
return r;
@ -1268,15 +1353,17 @@ Html5.unpatchCanPlayType = function() {
// by default, patch the video element
Html5.patchCanPlayType();
Html5.disposeMediaElement = function(el){
if (!el) { return; }
Html5.disposeMediaElement = function(el) {
if (!el) {
return;
}
if (el.parentNode) {
el.parentNode.removeChild(el);
}
// remove any child track or source nodes to prevent their loading
while(el.hasChildNodes()) {
while (el.hasChildNodes()) {
el.removeChild(el.firstChild);
}
@ -1294,15 +1381,18 @@ Html5.disposeMediaElement = function(el){
} catch (e) {
// not supported
}
})();
}());
}
};
Html5.resetMediaElement = function(el){
if (!el) { return; }
Html5.resetMediaElement = function(el) {
if (!el) {
return;
}
let sources = el.querySelectorAll('source');
let i = sources.length;
while (i--) {
el.removeChild(sources[i]);
}
@ -1316,8 +1406,10 @@ Html5.resetMediaElement = function(el){
(function() {
try {
el.load();
} catch (e) {}
})();
} catch (e) {
// satisfy linter
}
}());
}
};

View File

@ -3,7 +3,6 @@
*/
import Component from '../component.js';
import Tech from './tech.js';
import window from 'global/window';
import toTitleCase from '../utils/to-title-case.js';
/**
@ -18,16 +17,17 @@ import toTitleCase from '../utils/to-title-case.js';
*/
class MediaLoader extends Component {
constructor(player, options, ready){
constructor(player, options, ready) {
super(player, options, ready);
// If there are no sources when the player is initialized,
// load the first supported playback technology.
if (!options.playerOptions['sources'] || options.playerOptions['sources'].length === 0) {
for (let i=0, j=options.playerOptions['techOrder']; i<j.length; i++) {
if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
for (let i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
let techName = toTitleCase(j[i]);
let tech = Tech.getTech(techName);
// Support old behavior of techs being registered as components.
// Remove once that deprecated behavior is removed.
if (!techName) {
@ -41,11 +41,11 @@ class MediaLoader extends Component {
}
}
} else {
// // Loop through playback technologies (HTML5, Flash) and check for support.
// // Then load the best source.
// // A few assumptions here:
// // All playback technologies respect preload false.
player.src(options.playerOptions['sources']);
// Loop through playback technologies (HTML5, Flash) and check for support.
// Then load the best source.
// A few assumptions here:
// All playback technologies respect preload false.
player.src(options.playerOptions.sources);
}
}
}

View File

@ -10,10 +10,8 @@ import HTMLTrackElementList from '../tracks/html-track-element-list';
import mergeOptions from '../utils/merge-options.js';
import TextTrack from '../tracks/text-track';
import TextTrackList from '../tracks/text-track-list';
import VideoTrack from '../tracks/video-track';
import VideoTrackList from '../tracks/video-track-list';
import AudioTrackList from '../tracks/audio-track-list';
import AudioTrack from '../tracks/audio-track';
import * as Fn from '../utils/fn.js';
import log from '../utils/log.js';
import { createTimeRange } from '../utils/time-ranges.js';
@ -22,6 +20,26 @@ import MediaError from '../media-error.js';
import window from 'global/window';
import document from 'global/document';
function createTrackHelper(self, kind, label, language, options = {}) {
let tracks = self.textTracks();
options.kind = kind;
if (label) {
options.label = label;
}
if (language) {
options.language = language;
}
options.tech = self;
let track = new TextTrack(options);
tracks.addTrack_(track);
return track;
}
/**
* Base class for media (HTML5 Video, Flash) controllers
*
@ -32,7 +50,7 @@ import document from 'global/document';
*/
class Tech extends Component {
constructor(options={}, ready=function(){}){
constructor(options = {}, ready = function() {}) {
// we don't want the tech to report user activity automatically.
// This is done manually in addControlsListeners
options.reportTouchActivity = false;
@ -114,7 +132,7 @@ class Tech extends Component {
*/
trackProgress() {
this.stopTrackingProgress();
this.progressInterval = this.setInterval(Fn.bind(this, function(){
this.progressInterval = this.setInterval(Fn.bind(this, function() {
// Don't trigger unless buffered amount is greater than last time
let numBufferedPercent = this.bufferedPercent();
@ -169,7 +187,6 @@ class Tech extends Component {
this.clearInterval(this.progressInterval);
}
/*! Time Tracking -------------------------------------------------------------- */
/**
* Set event listeners for on play and pause and tracking current time
*
@ -200,10 +217,14 @@ class Tech extends Component {
* @method trackCurrentTime
*/
trackCurrentTime() {
if (this.currentTimeInterval) { this.stopTrackingCurrentTime(); }
this.currentTimeInterval = this.setInterval(function(){
if (this.currentTimeInterval) {
this.stopTrackingCurrentTime();
}
this.currentTimeInterval = this.setInterval(function() {
this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
}, 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
// 42 = 24 fps // 250 is what Webkit uses // FF uses 15
}, 250);
}
/**
@ -230,9 +251,13 @@ class Tech extends Component {
this.clearTracks(['audio', 'video', 'text']);
// Turn off any manual progress or timeupdate tracking
if (this.manualProgress) { this.manualProgressOff(); }
if (this.manualProgress) {
this.manualProgressOff();
}
if (this.manualTimeUpdates) { this.manualTimeUpdatesOff(); }
if (this.manualTimeUpdates) {
this.manualTimeUpdatesOff();
}
super.dispose();
}
@ -254,8 +279,10 @@ class Tech extends Component {
types.forEach((type) => {
let list = this[`${type}Tracks`]() || [];
let i = list.length;
while (i--) {
let track = list[i];
if (type === 'text') {
this.removeRemoteTextTrack(track);
}
@ -315,7 +342,9 @@ class Tech extends Component {
*/
setCurrentTime() {
// improve the accuracy of manual timeupdates
if (this.manualTimeUpdates) { this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); }
if (this.manualTimeUpdates) {
this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
}
}
/**
@ -330,7 +359,9 @@ class Tech extends Component {
let tracks = this.textTracks();
if (!tracks) return;
if (!tracks) {
return;
}
tracks.addEventListener('removetrack', textTrackListChanges);
tracks.addEventListener('addtrack', textTrackListChanges);
@ -341,7 +372,6 @@ class Tech extends Component {
}));
}
/**
* Initialize audio and video track listeners
*
@ -374,12 +404,14 @@ class Tech extends Component {
*/
emulateTextTracks() {
let tracks = this.textTracks();
if (!tracks) {
return;
}
if (!window['WebVTT'] && this.el().parentNode != null) {
if (!window.WebVTT && this.el().parentNode != null) {
let script = document.createElement('script');
script.src = this.options_['vtt.js'] || '../node_modules/videojs-vtt.js/dist/vtt.js';
script.onload = () => {
this.trigger('vttjsloaded');
@ -393,7 +425,7 @@ class Tech extends Component {
});
// but have not loaded yet and we set it to true before the inject so that
// we don't overwrite the injected window.WebVTT if it loads right away
window['WebVTT'] = true;
window.WebVTT = true;
this.el().parentNode.appendChild(script);
}
@ -403,6 +435,7 @@ class Tech extends Component {
for (let i = 0; i < tracks.length; i++) {
let track = tracks[i];
track.removeEventListener('cuechange', updateDisplay);
if (track.mode === 'showing') {
track.addEventListener('cuechange', updateDisplay);
@ -620,7 +653,7 @@ class Tech extends Component {
* @type {TextTrackList}
* @private
*/
Tech.prototype.textTracks_;
Tech.prototype.textTracks_; // eslint-disable-line
/**
* List of associated audio tracks
@ -628,7 +661,7 @@ Tech.prototype.textTracks_;
* @type {AudioTrackList}
* @private
*/
Tech.prototype.audioTracks_;
Tech.prototype.audioTracks_; // eslint-disable-line
/**
* List of associated video tracks
@ -636,27 +669,7 @@ Tech.prototype.audioTracks_;
* @type {VideoTrackList}
* @private
*/
Tech.prototype.videoTracks_;
var createTrackHelper = function(self, kind, label, language, options={}) {
let tracks = self.textTracks();
options.kind = kind;
if (label) {
options.label = label;
}
if (language) {
options.language = language;
}
options.tech = self;
let track = new TextTrack(options);
tracks.addTrack_(track);
return track;
};
Tech.prototype.videoTracks_; // eslint-disable-line
Tech.prototype.featuresVolumeControl = true;
@ -671,7 +684,7 @@ Tech.prototype.featuresTimeupdateEvents = false;
Tech.prototype.featuresNativeTextTracks = false;
/*
/**
* A functional mixin for techs that want to use the Source Handler pattern.
*
* ##### EXAMPLE:
@ -679,16 +692,17 @@ Tech.prototype.featuresNativeTextTracks = false;
* Tech.withSourceHandlers.call(MyTech);
*
*/
Tech.withSourceHandlers = function(_Tech){
/*
* Register a source handler
* Source handlers are scripts for handling specific formats.
* The source handler pattern is used for adaptive formats (HLS, DASH) that
* manually load video data and feed it into a Source Buffer (Media Source Extensions)
* @param {Function} handler The source handler
* @param {Boolean} first Register it before any existing handlers
*/
_Tech.registerSourceHandler = function(handler, index){
Tech.withSourceHandlers = function(_Tech) {
/**
* Register a source handler
* Source handlers are scripts for handling specific formats.
* The source handler pattern is used for adaptive formats (HLS, DASH) that
* manually load video data and feed it into a Source Buffer (Media Source Extensions)
* @param {Function} handler The source handler
* @param {Boolean} first Register it before any existing handlers
*/
_Tech.registerSourceHandler = function(handler, index) {
let handlers = _Tech.sourceHandlers;
if (!handlers) {
@ -703,12 +717,12 @@ Tech.withSourceHandlers = function(_Tech){
handlers.splice(index, 0, handler);
};
/*
/**
* Check if the tech can support the given type
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
_Tech.canPlayType = function(type){
_Tech.canPlayType = function(type) {
let handlers = _Tech.sourceHandlers || [];
let can;
@ -723,15 +737,15 @@ Tech.withSourceHandlers = function(_Tech){
return '';
};
/*
* Return the first source handler that supports the source
* TODO: Answer question: should 'probably' be prioritized over 'maybe'
* @param {Object} source The source object
* @param {Object} options The options passed to the tech
* @returns {Object} The first source handler that supports the source
* @returns {null} Null if no source handler is found
*/
_Tech.selectSourceHandler = function(source, options){
/**
* Return the first source handler that supports the source
* TODO: Answer question: should 'probably' be prioritized over 'maybe'
* @param {Object} source The source object
* @param {Object} options The options passed to the tech
* @returns {Object} The first source handler that supports the source
* @returns {null} Null if no source handler is found
*/
_Tech.selectSourceHandler = function(source, options) {
let handlers = _Tech.sourceHandlers || [];
let can;
@ -746,13 +760,13 @@ Tech.withSourceHandlers = function(_Tech){
return null;
};
/*
/**
* Check if the tech can support the given source
* @param {Object} srcObj The source object
* @param {Object} options The options passed to the tech
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
_Tech.canPlaySource = function(srcObj, options){
_Tech.canPlaySource = function(srcObj, options) {
let sh = _Tech.selectSourceHandler(srcObj, options);
if (sh) {
@ -762,16 +776,16 @@ Tech.withSourceHandlers = function(_Tech){
return '';
};
/*
/**
* When using a source handler, prefer its implementation of
* any function normally provided by the tech.
*/
let deferrable = [
'seekable',
'duration'
];
'seekable',
'duration'
];
deferrable.forEach(function (fnName) {
deferrable.forEach(function(fnName) {
let originalFn = this[fnName];
if (typeof originalFn !== 'function') {
@ -786,14 +800,14 @@ Tech.withSourceHandlers = function(_Tech){
};
}, _Tech.prototype);
/*
* Create a function for setting the source using a source object
* and source handlers.
* Should never be called unless a source handler was found.
* @param {Object} source A source object with src and type keys
* @return {Tech} self
*/
_Tech.prototype.setSource = function(source){
/**
* Create a function for setting the source using a source object
* and source handlers.
* Should never be called unless a source handler was found.
* @param {Object} source A source object with src and type keys
* @return {Tech} self
*/
_Tech.prototype.setSource = function(source) {
let sh = _Tech.selectSourceHandler(source, this.options_);
if (!sh) {

View File

@ -21,6 +21,7 @@ const disableOthers = function(list, track) {
list[i].enabled = false;
}
};
/**
* A list of possible audio tracks. All functionality is in the
* base class Tracklist and the spec for AudioTrackList is located at:

View File

@ -37,7 +37,9 @@ class AudioTrack extends Track {
}
Object.defineProperty(track, 'enabled', {
get() { return enabled; },
get() {
return enabled;
},
set(newEnabled) {
// an invalid or unchanged value
if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {

View File

@ -7,7 +7,7 @@ import document from 'global/document';
class HtmlTrackElementList {
constructor(trackElements = []) {
let list = this;
let list = this; // eslint-disable-line
if (browser.IS_IE8) {
list = document.createElement('custom');

View File

@ -39,8 +39,8 @@ class HTMLTrackElement extends EventTarget {
constructor(options = {}) {
super();
let readyState,
trackElement = this;
let readyState;
let trackElement = this; // eslint-disable-line
if (browser.IS_IE8) {
trackElement = document.createElement('custom');

View File

@ -20,7 +20,7 @@ import document from 'global/document';
class TextTrackCueList {
constructor(cues) {
let list = this;
let list = this; // eslint-disable-line
if (browser.IS_IE8) {
list = document.createElement('custom');

View File

@ -2,28 +2,60 @@
* @file text-track-display.js
*/
import Component from '../component';
import Menu from '../menu/menu.js';
import MenuItem from '../menu/menu-item.js';
import MenuButton from '../menu/menu-button.js';
import * as Fn from '../utils/fn.js';
import document from 'global/document';
import window from 'global/window';
const darkGray = '#222';
const lightGray = '#ccc';
const fontMap = {
monospace: 'monospace',
sansSerif: 'sans-serif',
serif: 'serif',
monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
monospaceSerif: '"Courier New", monospace',
monospace: 'monospace',
sansSerif: 'sans-serif',
serif: 'serif',
monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
monospaceSerif: '"Courier New", monospace',
proportionalSansSerif: 'sans-serif',
proportionalSerif: 'serif',
casual: '"Comic Sans MS", Impact, fantasy',
script: '"Monotype Corsiva", cursive',
smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
proportionalSerif: 'serif',
casual: '"Comic Sans MS", Impact, fantasy',
script: '"Monotype Corsiva", cursive',
smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
};
/**
* Add cue HTML to display
*
* @param {Number} color Hex number for color, like #f0e
* @param {Number} opacity Value for opacity,0.0 - 1.0
* @return {RGBAColor} In the form 'rgba(255, 0, 0, 0.3)'
* @method constructColor
*/
function constructColor(color, opacity) {
return 'rgba(' +
// color looks like "#f0e"
parseInt(color[1] + color[1], 16) + ',' +
parseInt(color[2] + color[2], 16) + ',' +
parseInt(color[3] + color[3], 16) + ',' +
opacity + ')';
}
/**
* Try to update style
* Some style changes will throw an error, particularly in IE8. Those should be noops.
*
* @param {Element} el The element to be styles
* @param {CSSProperty} style The CSS property to be styled
* @param {CSSStyle} rule The actual style to be applied to the property
* @method tryUpdateStyle
*/
function tryUpdateStyle(el, style, rule) {
try {
el.style[style] = rule;
} catch (e) {
// Satisfies linter.
return;
}
}
/**
* The component for displaying text track cues
*
@ -35,7 +67,7 @@ const fontMap = {
*/
class TextTrackDisplay extends Component {
constructor(player, options, ready){
constructor(player, options, ready) {
super(player, options, ready);
player.on('loadstart', Fn.bind(this, this.toggleDisplay));
@ -46,20 +78,20 @@ class TextTrackDisplay extends Component {
// Should probably be moved to an external track loader when we support
// tracks that don't need a display.
player.ready(Fn.bind(this, function() {
if (player.tech_ && player.tech_['featuresNativeTextTracks']) {
if (player.tech_ && player.tech_.featuresNativeTextTracks) {
this.hide();
return;
}
player.on('fullscreenchange', Fn.bind(this, this.updateDisplay));
let tracks = this.options_.playerOptions['tracks'] || [];
let tracks = this.options_.playerOptions.tracks || [];
for (let i = 0; i < tracks.length; i++) {
let track = tracks[i];
this.player_.addRemoteTextTrack(track);
this.player_.addRemoteTextTrack(tracks[i]);
}
let modes = {'captions': 1, 'subtitles': 1};
let modes = {captions: 1, subtitles: 1};
let trackList = this.player_.textTracks();
let firstDesc;
let firstCaptions;
@ -67,6 +99,7 @@ class TextTrackDisplay extends Component {
if (trackList) {
for (let i = 0; i < trackList.length; i++) {
let track = trackList[i];
if (track.default) {
if (track.kind === 'descriptions' && !firstDesc) {
firstDesc = track;
@ -95,7 +128,7 @@ class TextTrackDisplay extends Component {
* @method toggleDisplay
*/
toggleDisplay() {
if (this.player_.tech_ && this.player_.tech_['featuresNativeTextTracks']) {
if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
this.hide();
} else {
this.show();
@ -123,8 +156,8 @@ class TextTrackDisplay extends Component {
* @method clearDisplay
*/
clearDisplay() {
if (typeof window['WebVTT'] === 'function') {
window['WebVTT']['processCues'](window, [], this.el_);
if (typeof window.WebVTT === 'function') {
window.WebVTT.processCues(window, [], this.el_);
}
}
@ -134,7 +167,7 @@ class TextTrackDisplay extends Component {
* @method updateDisplay
*/
updateDisplay() {
var tracks = this.player_.textTracks();
let tracks = this.player_.textTracks();
this.clearDisplay();
@ -150,10 +183,12 @@ class TextTrackDisplay extends Component {
let captionsSubtitlesTrack = null;
let i = tracks.length;
while (i--) {
let track = tracks[i];
if (track['mode'] === 'showing') {
if (track['kind'] === 'descriptions') {
if (track.mode === 'showing') {
if (track.kind === 'descriptions') {
descriptionsTrack = track;
} else {
captionsSubtitlesTrack = track;
@ -175,27 +210,30 @@ class TextTrackDisplay extends Component {
* @method updateForTrack
*/
updateForTrack(track) {
if (typeof window['WebVTT'] !== 'function' || !track['activeCues']) {
if (typeof window.WebVTT !== 'function' || !track.activeCues) {
return;
}
let overrides = this.player_['textTrackSettings'].getValues();
let overrides = this.player_.textTrackSettings.getValues();
let cues = [];
for (let i = 0; i < track['activeCues'].length; i++) {
cues.push(track['activeCues'][i]);
for (let i = 0; i < track.activeCues.length; i++) {
cues.push(track.activeCues[i]);
}
window['WebVTT']['processCues'](window, cues, this.el_);
window.WebVTT.processCues(window, cues, this.el_);
let i = cues.length;
while (i--) {
let cue = cues[i];
if (!cue) {
continue;
}
let cueDiv = cue.displayState;
if (overrides.color) {
cueDiv.firstChild.style.color = overrides.color;
}
@ -236,6 +274,7 @@ class TextTrackDisplay extends Component {
}
if (overrides.fontPercent && overrides.fontPercent !== 1) {
const fontSize = window.parseFloat(cueDiv.style.fontSize);
cueDiv.style.fontSize = (fontSize * overrides.fontPercent) + 'px';
cueDiv.style.height = 'auto';
cueDiv.style.top = 'auto';
@ -253,38 +292,5 @@ class TextTrackDisplay extends Component {
}
/**
* Add cue HTML to display
*
* @param {Number} color Hex number for color, like #f0e
* @param {Number} opacity Value for opacity,0.0 - 1.0
* @return {RGBAColor} In the form 'rgba(255, 0, 0, 0.3)'
* @method constructColor
*/
function constructColor(color, opacity) {
return 'rgba(' +
// color looks like "#f0e"
parseInt(color[1] + color[1], 16) + ',' +
parseInt(color[2] + color[2], 16) + ',' +
parseInt(color[3] + color[3], 16) + ',' +
opacity + ')';
}
/**
* Try to update style
* Some style changes will throw an error, particularly in IE8. Those should be noops.
*
* @param {Element} el The element to be styles
* @param {CSSProperty} style The CSS property to be styled
* @param {CSSStyle} rule The actual style to be applied to the property
* @method tryUpdateStyle
*/
function tryUpdateStyle(el, style, rule) {
//
try {
el.style[style] = rule;
} catch (e) {}
}
Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
export default TextTrackDisplay;

View File

@ -13,13 +13,15 @@
* @private
*/
let trackToJson_ = function(track) {
let ret = ['kind', 'label', 'language', 'id',
'inBandMetadataTrackDispatchType',
'mode', 'src'].reduce((acc, prop, i) => {
let ret = [
'kind', 'label', 'language', 'id',
'inBandMetadataTrackDispatchType', 'mode', 'src'
].reduce((acc, prop, i) => {
if (track[prop]) {
acc[prop] = track[prop];
}
return acc;
}, {
cues: track.cues && Array.prototype.map.call(track.cues, function(cue) {
@ -50,6 +52,7 @@ let textTracksToJson = function(tech) {
let trackObjs = Array.prototype.map.call(trackEls, (t) => t.track);
let tracks = Array.prototype.map.call(trackEls, function(trackEl) {
let json = trackToJson_(trackEl.track);
if (trackEl.src) {
json.src = trackEl.src;
}
@ -72,6 +75,7 @@ let textTracksToJson = function(tech) {
let jsonToTextTracks = function(json, tech) {
json.forEach(function(track) {
let addedTrack = tech.addRemoteTextTrack(track).track;
if (!track.src && track.cues) {
track.cues.forEach((cue) => addedTrack.addCue(cue));
}

View File

@ -8,250 +8,7 @@ import log from '../utils/log.js';
import safeParseTuple from 'safe-json-parse/tuple';
import window from 'global/window';
/**
* Manipulate settings of texttracks
*
* @param {Object} player Main Player
* @param {Object=} options Object of option names and values
* @extends Component
* @class TextTrackSettings
*/
class TextTrackSettings extends Component {
constructor(player, options) {
super(player, options);
this.hide();
// Grab `persistTextTrackSettings` from the player options if not passed in child options
if (options.persistTextTrackSettings === undefined) {
this.options_.persistTextTrackSettings = this.options_.playerOptions.persistTextTrackSettings;
}
Events.on(this.$('.vjs-done-button'), 'click', Fn.bind(this, function() {
this.saveSettings();
this.hide();
}));
Events.on(this.$('.vjs-default-button'), 'click', Fn.bind(this, function() {
this.$('.vjs-fg-color > select').selectedIndex = 0;
this.$('.vjs-bg-color > select').selectedIndex = 0;
this.$('.window-color > select').selectedIndex = 0;
this.$('.vjs-text-opacity > select').selectedIndex = 0;
this.$('.vjs-bg-opacity > select').selectedIndex = 0;
this.$('.vjs-window-opacity > select').selectedIndex = 0;
this.$('.vjs-edge-style select').selectedIndex = 0;
this.$('.vjs-font-family select').selectedIndex = 0;
this.$('.vjs-font-percent select').selectedIndex = 2;
this.updateDisplay();
}));
Events.on(this.$('.vjs-fg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-bg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.window-color > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-text-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-bg-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-window-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-font-percent select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-edge-style select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-font-family select'), 'change', Fn.bind(this, this.updateDisplay));
if (this.options_.persistTextTrackSettings) {
this.restoreSettings();
}
}
/**
* Create the component's DOM element
*
* @return {Element}
* @method createEl
*/
createEl() {
let uniqueId = this.id_;
let dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId;
let dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId;
return super.createEl('div', {
className: 'vjs-caption-settings vjs-modal-overlay',
innerHTML: captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId),
tabIndex: -1
}, {
role: 'dialog',
'aria-labelledby': dialogLabelId,
'aria-describedby': dialogDescriptionId
});
}
/**
* Get texttrack settings
* Settings are
* .vjs-edge-style
* .vjs-font-family
* .vjs-fg-color
* .vjs-text-opacity
* .vjs-bg-color
* .vjs-bg-opacity
* .window-color
* .vjs-window-opacity
*
* @return {Object}
* @method getValues
*/
getValues() {
const textEdge = getSelectedOptionValue(this.$('.vjs-edge-style select'));
const fontFamily = getSelectedOptionValue(this.$('.vjs-font-family select'));
const fgColor = getSelectedOptionValue(this.$('.vjs-fg-color > select'));
const textOpacity = getSelectedOptionValue(this.$('.vjs-text-opacity > select'));
const bgColor = getSelectedOptionValue(this.$('.vjs-bg-color > select'));
const bgOpacity = getSelectedOptionValue(this.$('.vjs-bg-opacity > select'));
const windowColor = getSelectedOptionValue(this.$('.window-color > select'));
const windowOpacity = getSelectedOptionValue(this.$('.vjs-window-opacity > select'));
const fontPercent = window['parseFloat'](getSelectedOptionValue(this.$('.vjs-font-percent > select')));
let result = {
'backgroundOpacity': bgOpacity,
'textOpacity': textOpacity,
'windowOpacity': windowOpacity,
'edgeStyle': textEdge,
'fontFamily': fontFamily,
'color': fgColor,
'backgroundColor': bgColor,
'windowColor': windowColor,
'fontPercent': fontPercent
};
for (let name in result) {
if (result[name] === '' || result[name] === 'none' || (name === 'fontPercent' && result[name] === 1.00)) {
delete result[name];
}
}
return result;
}
/**
* Set texttrack settings
* Settings are
* .vjs-edge-style
* .vjs-font-family
* .vjs-fg-color
* .vjs-text-opacity
* .vjs-bg-color
* .vjs-bg-opacity
* .window-color
* .vjs-window-opacity
*
* @param {Object} values Object with texttrack setting values
* @method setValues
*/
setValues(values) {
setSelectedOption(this.$('.vjs-edge-style select'), values.edgeStyle);
setSelectedOption(this.$('.vjs-font-family select'), values.fontFamily);
setSelectedOption(this.$('.vjs-fg-color > select'), values.color);
setSelectedOption(this.$('.vjs-text-opacity > select'), values.textOpacity);
setSelectedOption(this.$('.vjs-bg-color > select'), values.backgroundColor);
setSelectedOption(this.$('.vjs-bg-opacity > select'), values.backgroundOpacity);
setSelectedOption(this.$('.window-color > select'), values.windowColor);
setSelectedOption(this.$('.vjs-window-opacity > select'), values.windowOpacity);
let fontPercent = values.fontPercent;
if (fontPercent) {
fontPercent = fontPercent.toFixed(2);
}
setSelectedOption(this.$('.vjs-font-percent > select'), fontPercent);
}
/**
* Restore texttrack settings
*
* @method restoreSettings
*/
restoreSettings() {
let err, values;
try {
[err, values] = safeParseTuple(window.localStorage.getItem('vjs-text-track-settings'));
if (err) {
log.error(err);
}
} catch (e) {
log.warn(e);
}
if (values) {
this.setValues(values);
}
}
/**
* Save texttrack settings to local storage
*
* @method saveSettings
*/
saveSettings() {
if (!this.options_.persistTextTrackSettings) {
return;
}
let values = this.getValues();
try {
if (Object.getOwnPropertyNames(values).length > 0) {
window.localStorage.setItem('vjs-text-track-settings', JSON.stringify(values));
} else {
window.localStorage.removeItem('vjs-text-track-settings');
}
} catch (e) {
log.warn(e);
}
}
/**
* Update display of texttrack settings
*
* @method updateDisplay
*/
updateDisplay() {
let ttDisplay = this.player_.getChild('textTrackDisplay');
if (ttDisplay) {
ttDisplay.updateDisplay();
}
}
}
Component.registerComponent('TextTrackSettings', TextTrackSettings);
function getSelectedOptionValue(target) {
let selectedOption;
// not all browsers support selectedOptions, so, fallback to options
if (target.selectedOptions) {
selectedOption = target.selectedOptions[0];
} else if (target.options) {
selectedOption = target.options[target.options.selectedIndex];
}
return selectedOption.value;
}
function setSelectedOption(target, value) {
if (!value) {
return;
}
let i;
for (i = 0; i < target.options.length; i++) {
const option = target.options[i];
if (option.value === value) {
break;
}
}
target.selectedIndex = i;
}
function captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId) {
let template = `
<div role="document">
<div role="heading" aria-level="1" id="${dialogLabelId}" class="vjs-control-text">Captions Settings Dialog</div>
@ -367,9 +124,259 @@ function captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId
<button class="vjs-done-button">Done</button>
</div>
</div> <!-- vjs-tracksettings -->
</div> <!-- role="document" -->`;
</div> <!-- role="document" -->
`;
return template;
return template;
}
function getSelectedOptionValue(target) {
let selectedOption;
// not all browsers support selectedOptions, so, fallback to options
if (target.selectedOptions) {
selectedOption = target.selectedOptions[0];
} else if (target.options) {
selectedOption = target.options[target.options.selectedIndex];
}
return selectedOption.value;
}
function setSelectedOption(target, value) {
if (!value) {
return;
}
let i;
for (i = 0; i < target.options.length; i++) {
const option = target.options[i];
if (option.value === value) {
break;
}
}
target.selectedIndex = i;
}
/**
* Manipulate settings of texttracks
*
* @param {Object} player Main Player
* @param {Object=} options Object of option names and values
* @extends Component
* @class TextTrackSettings
*/
class TextTrackSettings extends Component {
constructor(player, options) {
super(player, options);
this.hide();
// Grab `persistTextTrackSettings` from the player options if not passed in child options
if (options.persistTextTrackSettings === undefined) {
this.options_.persistTextTrackSettings = this.options_.playerOptions.persistTextTrackSettings;
}
Events.on(this.$('.vjs-done-button'), 'click', Fn.bind(this, function() {
this.saveSettings();
this.hide();
}));
Events.on(this.$('.vjs-default-button'), 'click', Fn.bind(this, function() {
this.$('.vjs-fg-color > select').selectedIndex = 0;
this.$('.vjs-bg-color > select').selectedIndex = 0;
this.$('.window-color > select').selectedIndex = 0;
this.$('.vjs-text-opacity > select').selectedIndex = 0;
this.$('.vjs-bg-opacity > select').selectedIndex = 0;
this.$('.vjs-window-opacity > select').selectedIndex = 0;
this.$('.vjs-edge-style select').selectedIndex = 0;
this.$('.vjs-font-family select').selectedIndex = 0;
this.$('.vjs-font-percent select').selectedIndex = 2;
this.updateDisplay();
}));
Events.on(this.$('.vjs-fg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-bg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.window-color > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-text-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-bg-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-window-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-font-percent select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-edge-style select'), 'change', Fn.bind(this, this.updateDisplay));
Events.on(this.$('.vjs-font-family select'), 'change', Fn.bind(this, this.updateDisplay));
if (this.options_.persistTextTrackSettings) {
this.restoreSettings();
}
}
/**
* Create the component's DOM element
*
* @return {Element}
* @method createEl
*/
createEl() {
let uniqueId = this.id_;
let dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId;
let dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId;
return super.createEl('div', {
className: 'vjs-caption-settings vjs-modal-overlay',
innerHTML: captionOptionsMenuTemplate(uniqueId, dialogLabelId, dialogDescriptionId),
tabIndex: -1
}, {
'role': 'dialog',
'aria-labelledby': dialogLabelId,
'aria-describedby': dialogDescriptionId
});
}
/**
* Get texttrack settings
* Settings are
* .vjs-edge-style
* .vjs-font-family
* .vjs-fg-color
* .vjs-text-opacity
* .vjs-bg-color
* .vjs-bg-opacity
* .window-color
* .vjs-window-opacity
*
* @return {Object}
* @method getValues
*/
getValues() {
const textEdge = getSelectedOptionValue(this.$('.vjs-edge-style select'));
const fontFamily = getSelectedOptionValue(this.$('.vjs-font-family select'));
const fgColor = getSelectedOptionValue(this.$('.vjs-fg-color > select'));
const textOpacity = getSelectedOptionValue(this.$('.vjs-text-opacity > select'));
const bgColor = getSelectedOptionValue(this.$('.vjs-bg-color > select'));
const bgOpacity = getSelectedOptionValue(this.$('.vjs-bg-opacity > select'));
const windowColor = getSelectedOptionValue(this.$('.window-color > select'));
const windowOpacity = getSelectedOptionValue(this.$('.vjs-window-opacity > select'));
const fontPercent = window.parseFloat(getSelectedOptionValue(this.$('.vjs-font-percent > select')));
let result = {
fontPercent,
fontFamily,
textOpacity,
windowColor,
windowOpacity,
backgroundOpacity: bgOpacity,
edgeStyle: textEdge,
color: fgColor,
backgroundColor: bgColor
};
for (let name in result) {
if (result[name] === '' || result[name] === 'none' || (name === 'fontPercent' && result[name] === 1.00)) {
delete result[name];
}
}
return result;
}
/**
* Set texttrack settings
* Settings are
* .vjs-edge-style
* .vjs-font-family
* .vjs-fg-color
* .vjs-text-opacity
* .vjs-bg-color
* .vjs-bg-opacity
* .window-color
* .vjs-window-opacity
*
* @param {Object} values Object with texttrack setting values
* @method setValues
*/
setValues(values) {
setSelectedOption(this.$('.vjs-edge-style select'), values.edgeStyle);
setSelectedOption(this.$('.vjs-font-family select'), values.fontFamily);
setSelectedOption(this.$('.vjs-fg-color > select'), values.color);
setSelectedOption(this.$('.vjs-text-opacity > select'), values.textOpacity);
setSelectedOption(this.$('.vjs-bg-color > select'), values.backgroundColor);
setSelectedOption(this.$('.vjs-bg-opacity > select'), values.backgroundOpacity);
setSelectedOption(this.$('.window-color > select'), values.windowColor);
setSelectedOption(this.$('.vjs-window-opacity > select'), values.windowOpacity);
let fontPercent = values.fontPercent;
if (fontPercent) {
fontPercent = fontPercent.toFixed(2);
}
setSelectedOption(this.$('.vjs-font-percent > select'), fontPercent);
}
/**
* Restore texttrack settings
*
* @method restoreSettings
*/
restoreSettings() {
let err;
let values;
try {
[err, values] = safeParseTuple(window.localStorage.getItem('vjs-text-track-settings'));
if (err) {
log.error(err);
}
} catch (e) {
log.warn(e);
}
if (values) {
this.setValues(values);
}
}
/**
* Save texttrack settings to local storage
*
* @method saveSettings
*/
saveSettings() {
if (!this.options_.persistTextTrackSettings) {
return;
}
let values = this.getValues();
try {
if (Object.getOwnPropertyNames(values).length > 0) {
window.localStorage.setItem('vjs-text-track-settings', JSON.stringify(values));
} else {
window.localStorage.removeItem('vjs-text-track-settings');
}
} catch (e) {
log.warn(e);
}
}
/**
* Update display of texttrack settings
*
* @method updateDisplay
*/
updateDisplay() {
let ttDisplay = this.player_.getChild('textTrackDisplay');
if (ttDisplay) {
ttDisplay.updateDisplay();
}
}
}
Component.registerComponent('TextTrackSettings', TextTrackSettings);
export default TextTrackSettings;

View File

@ -5,7 +5,6 @@ import TextTrackCueList from './text-track-cue-list';
import * as Fn from '../utils/fn.js';
import {TextTrackKind, TextTrackMode} from './track-enums';
import log from '../utils/log.js';
import document from 'global/document';
import window from 'global/window';
import Track from './track.js';
import { isCrossOrigin } from '../utils/url.js';
@ -42,12 +41,12 @@ const parseCues = function(srcContent, track) {
parser.parse(srcContent);
if (errors.length > 0) {
if (console.groupCollapsed) {
console.groupCollapsed(`Text Track parsing errors for ${track.src}`);
if (window.console && window.console.groupCollapsed) {
window.console.groupCollapsed(`Text Track parsing errors for ${track.src}`);
}
errors.forEach((error) => log.error(error));
if (console.groupEnd) {
console.groupEnd();
if (window.console && window.console.groupEnd) {
window.console.groupEnd();
}
}
@ -82,6 +81,7 @@ const loadTrack = function(src, track) {
if (typeof window.WebVTT !== 'function') {
if (track.tech_) {
let loadHandler = () => parseCues(responseBody, track);
track.tech_.on('vttjsloaded', loadHandler);
track.tech_.on('vttjserror', () => {
log.error(`vttjs failed to load, stopping trying to process ${track.src}`);
@ -142,6 +142,7 @@ class TextTrack extends Track {
// on IE8 this will be a document element
// for every other browser this will be a normal object
let tt = super(settings);
tt.tech_ = settings.tech;
if (browser.IS_IE8) {
@ -159,6 +160,10 @@ class TextTrack extends Track {
let activeCues = new TextTrackCueList(tt.activeCues_);
let changed = false;
let timeupdateHandler = Fn.bind(tt, function() {
// Accessing this.activeCues for the side-effects of updating itself
// due to it's nature as a getter function. Do not remove or cues will
// stop updating!
this.activeCues;
if (changed) {
this.trigger('cuechange');

View File

@ -21,7 +21,7 @@ const VideoTrackKind = {
main: 'main',
sign: 'sign',
subtitles: 'subtitles',
commentary: 'commentary',
commentary: 'commentary'
};
/**
@ -38,12 +38,12 @@ const VideoTrackKind = {
* };
*/
const AudioTrackKind = {
alternative: 'alternative',
descriptions: 'descriptions',
main: 'main',
'alternative': 'alternative',
'descriptions': 'descriptions',
'main': 'main',
'main-desc': 'main-desc',
translation: 'translation',
commentary: 'commentary',
'translation': 'translation',
'commentary': 'commentary'
};
/**
@ -65,8 +65,6 @@ const TextTrackKind = {
metadata: 'metadata'
};
/**
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
*
@ -78,9 +76,4 @@ const TextTrackMode = {
showing: 'showing'
};
/* jshint ignore:start */
// we ignore jshint here because it does not see
// AudioTrackKind as defined here
export default { VideoTrackKind, AudioTrackKind, TextTrackKind, TextTrackMode };
/* jshint ignore:end */

View File

@ -2,7 +2,6 @@
* @file track-list.js
*/
import EventTarget from '../event-target';
import * as Fn from '../utils/fn.js';
import * as browser from '../utils/browser.js';
import document from 'global/document';
@ -20,7 +19,7 @@ class TrackList extends EventTarget {
constructor(tracks = [], list = null) {
super();
if (!list) {
list = this;
list = this; // eslint-disable-line
if (browser.IS_IE8) {
list = document.createElement('custom');
for (let prop in TrackList.prototype) {
@ -119,6 +118,7 @@ class TrackList extends EventTarget {
for (let i = 0, l = this.length; i < l; i++) {
let track = this[i];
if (track.id === id) {
result = track;
break;

View File

@ -19,7 +19,8 @@ class Track extends EventTarget {
constructor(options = {}) {
super();
let track = this;
let track = this; // eslint-disable-line
if (browser.IS_IE8) {
track = document.createElement('custom');
for (let prop in Track.prototype) {
@ -38,7 +39,9 @@ class Track extends EventTarget {
for (let key in trackProps) {
Object.defineProperty(track, key, {
get() { return trackProps[key]; },
get() {
return trackProps[key];
},
set() {}
});
}

View File

@ -38,7 +38,9 @@ class VideoTrack extends Track {
}
Object.defineProperty(track, 'selected', {
get() { return selected; },
get() {
return selected;
},
set(newSelected) {
// an invalid or unchanged value
if (typeof newSelected !== 'boolean' || newSelected === selected) {

View File

@ -24,18 +24,21 @@ export const IS_IPHONE = (/iPhone/i).test(USER_AGENT) && !IS_IPAD;
export const IS_IPOD = (/iPod/i).test(USER_AGENT);
export const IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
export const IOS_VERSION = (function(){
var match = USER_AGENT.match(/OS (\d+)_/i);
if (match && match[1]) { return match[1]; }
})();
export const IOS_VERSION = (function() {
let match = USER_AGENT.match(/OS (\d+)_/i);
if (match && match[1]) {
return match[1];
}
}());
export const IS_ANDROID = (/Android/i).test(USER_AGENT);
export const ANDROID_VERSION = (function() {
// This matches Android Major.Minor.Patch versions
// ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i),
major,
minor;
let match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
let major;
let minor;
if (!match) {
return null;
@ -48,10 +51,10 @@ export const ANDROID_VERSION = (function() {
return parseFloat(match[1] + '.' + match[2]);
} else if (major) {
return major;
} else {
return null;
}
})();
return null;
}());
// Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser
export const IS_OLD_ANDROID = IS_ANDROID && (/webkit/i).test(USER_AGENT) && ANDROID_VERSION < 2.3;
export const IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
@ -60,9 +63,9 @@ export const IS_FIREFOX = (/Firefox/i).test(USER_AGENT);
export const IS_EDGE = (/Edge/i).test(USER_AGENT);
export const IS_CHROME = !IS_EDGE && (/Chrome/i).test(USER_AGENT);
export const IS_IE8 = (/MSIE\s8\.0/).test(USER_AGENT);
export const IE_VERSION = (function(result){
export const IE_VERSION = (function(result) {
return result && parseFloat(result[1]);
})((/MSIE\s(\d+)\.\d/).exec(USER_AGENT));
}((/MSIE\s(\d+)\.\d/).exec(USER_AGENT)));
export const TOUCH_ENABLED = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch);
export const BACKGROUND_SIZE_SUPPORTED = 'backgroundSize' in document.createElement('video').style;

View File

@ -13,8 +13,9 @@ import { createTimeRange } from './time-ranges.js';
* @function bufferedPercent
*/
export function bufferedPercent(buffered, duration) {
var bufferedDuration = 0,
start, end;
let bufferedDuration = 0;
let start;
let end;
if (!duration) {
return 0;
@ -24,9 +25,9 @@ export function bufferedPercent(buffered, duration) {
buffered = createTimeRange(0, 0);
}
for (let i = 0; i < buffered.length; i++){
for (let i = 0; i < buffered.length; i++) {
start = buffered.start(i);
end = buffered.end(i);
end = buffered.end(i);
// buffered end can be bigger than duration by a very small fraction
if (end > duration) {

View File

@ -3,7 +3,7 @@
*/
import document from 'global/document';
import window from 'global/window';
import * as Guid from './guid.js';
import * as Guid from './guid.js';
import log from './log.js';
import tsml from 'tsml';
@ -14,7 +14,7 @@ import tsml from 'tsml';
* @return {Boolean}
*/
function isNonBlankString(str) {
return typeof str === 'string' && /\S/.test(str);
return typeof str === 'string' && (/\S/).test(str);
}
/**
@ -25,7 +25,7 @@ function isNonBlankString(str) {
* @return {Boolean}
*/
function throwIfWhitespace(str) {
if (/\s/.test(str)) {
if ((/\s/).test(str)) {
throw new Error('class has illegal whitespace characters');
}
}
@ -40,6 +40,17 @@ function classRegExp(className) {
return new RegExp('(^|\\s)' + className + '($|\\s)');
}
/**
* Determines, via duck typing, whether or not a value is a DOM element.
*
* @function isEl
* @param {Mixed} value
* @return {Boolean}
*/
export function isEl(value) {
return !!value && typeof value === 'object' && value.nodeType === 1;
}
/**
* Creates functions to query the DOM using a given method.
*
@ -49,7 +60,7 @@ function classRegExp(className) {
* @return {Function}
*/
function createQuerier(method) {
return function (selector, context) {
return function(selector, context) {
if (!isNonBlankString(selector)) {
return document[method](null);
}
@ -68,7 +79,7 @@ function createQuerier(method) {
* @return {Element} Element with supplied ID
* @function getEl
*/
export function getEl(id){
export function getEl(id) {
if (id.indexOf('#') === 0) {
id = id.slice(1);
}
@ -85,10 +96,10 @@ export function getEl(id){
* @return {Element}
* @function createEl
*/
export function createEl(tagName='div', properties={}, attributes={}){
export function createEl(tagName = 'div', properties = {}, attributes = {}) {
let el = document.createElement(tagName);
Object.getOwnPropertyNames(properties).forEach(function(propName){
Object.getOwnPropertyNames(properties).forEach(function(propName) {
let val = properties[propName];
// See #2176
@ -104,8 +115,7 @@ export function createEl(tagName='div', properties={}, attributes={}){
}
});
Object.getOwnPropertyNames(attributes).forEach(function(attrName){
let val = attributes[attrName];
Object.getOwnPropertyNames(attributes).forEach(function(attrName) {
el.setAttribute(attrName, attributes[attrName]);
});
@ -136,7 +146,7 @@ export function textContent(el, text) {
* @private
* @function insertElFirst
*/
export function insertElFirst(child, parent){
export function insertElFirst(child, parent) {
if (parent.firstChild) {
parent.insertBefore(child, parent.firstChild);
} else {
@ -222,7 +232,7 @@ export function removeElData(el) {
// Remove the elIdAttr property from the DOM node
try {
delete el[elIdAttr];
} catch(e) {
} catch (e) {
if (el.removeAttribute) {
el.removeAttribute(elIdAttr);
} else {
@ -242,10 +252,9 @@ export function removeElData(el) {
export function hasElClass(element, classToCheck) {
if (element.classList) {
return element.classList.contains(classToCheck);
} else {
throwIfWhitespace(classToCheck);
return classRegExp(classToCheck).test(element.className);
}
throwIfWhitespace(classToCheck);
return classRegExp(classToCheck).test(element.className);
}
/**
@ -339,7 +348,7 @@ export function toggleElClass(element, classToToggle, predicate) {
* @function setElAttributes
*/
export function setElAttributes(el, attributes) {
Object.getOwnPropertyNames(attributes).forEach(function(attrName){
Object.getOwnPropertyNames(attributes).forEach(function(attrName) {
let attrValue = attributes[attrName];
if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
@ -362,25 +371,29 @@ export function setElAttributes(el, attributes) {
* @function getElAttributes
*/
export function getElAttributes(tag) {
var obj, knownBooleans, attrs, attrName, attrVal;
let obj;
let knownBooleans;
let attrs;
let attrName;
let attrVal;
obj = {};
// known boolean attributes
// we can check for matching boolean properties, but older browsers
// won't know about HTML5 boolean attributes that we still read from
knownBooleans = ','+'autoplay,controls,loop,muted,default'+',';
knownBooleans = ',' + 'autoplay,controls,loop,muted,default' + ',';
if (tag && tag.attributes && tag.attributes.length > 0) {
attrs = tag.attributes;
for (var i = attrs.length - 1; i >= 0; i--) {
for (let i = attrs.length - 1; i >= 0; i--) {
attrName = attrs[i].name;
attrVal = attrs[i].value;
// check for known booleans
// the matching element property will return a value for typeof
if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(','+attrName+',') !== -1) {
if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
// the value of an included boolean attribute is typically an empty
// string ('') which would equal false if we just check for a false value.
// we also don't want support bad code like autoplay='false'
@ -492,17 +505,6 @@ export function getPointerPosition(el, event) {
return position;
}
/**
* Determines, via duck typing, whether or not a value is a DOM element.
*
* @function isEl
* @param {Mixed} value
* @return {Boolean}
*/
export function isEl(value) {
return !!value && typeof value === 'object' && value.nodeType === 1;
}
/**
* Determines, via duck typing, whether or not a value is a text node.
*
@ -577,7 +579,7 @@ export function normalizeContent(content) {
return value;
}
if (typeof value === 'string' && /\S/.test(value)) {
if (typeof value === 'string' && (/\S/).test(value)) {
return document.createTextNode(value);
}
}).filter(value => value);

View File

@ -7,321 +7,11 @@
* robust as jquery's, so there's probably some differences.
*/
import * as Dom from './dom.js';
import * as Guid from './guid.js';
import * as Dom from './dom.js';
import * as Guid from './guid.js';
import window from 'global/window';
import document from 'global/document';
/**
* Add an event listener to element
* It stores the handler function in a separate cache object
* and adds a generic handler to the element's event,
* along with a unique id (guid) to the element.
*
* @param {Element|Object} elem Element or object to bind listeners to
* @param {String|Array} type Type of event to bind to.
* @param {Function} fn Event listener.
* @method on
*/
export function on(elem, type, fn){
if (Array.isArray(type)) {
return _handleMultipleEvents(on, elem, type, fn);
}
let data = Dom.getElData(elem);
// We need a place to store all our handler data
if (!data.handlers) data.handlers = {};
if (!data.handlers[type]) data.handlers[type] = [];
if (!fn.guid) fn.guid = Guid.newGUID();
data.handlers[type].push(fn);
if (!data.dispatcher) {
data.disabled = false;
data.dispatcher = function (event, hash){
if (data.disabled) return;
event = fixEvent(event);
var handlers = data.handlers[event.type];
if (handlers) {
// Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
var handlersCopy = handlers.slice(0);
for (var m = 0, n = handlersCopy.length; m < n; m++) {
if (event.isImmediatePropagationStopped()) {
break;
} else {
handlersCopy[m].call(elem, event, hash);
}
}
}
};
}
if (data.handlers[type].length === 1) {
if (elem.addEventListener) {
elem.addEventListener(type, data.dispatcher, false);
} else if (elem.attachEvent) {
elem.attachEvent('on' + type, data.dispatcher);
}
}
}
/**
* Removes event listeners from an element
*
* @param {Element|Object} elem Object to remove listeners from
* @param {String|Array=} type Type of listener to remove. Don't include to remove all events from element.
* @param {Function} fn Specific listener to remove. Don't include to remove listeners for an event type.
* @method off
*/
export function off(elem, type, fn) {
// Don't want to add a cache object through getElData if not needed
if (!Dom.hasElData(elem)) return;
let data = Dom.getElData(elem);
// If no events exist, nothing to unbind
if (!data.handlers) { return; }
if (Array.isArray(type)) {
return _handleMultipleEvents(off, elem, type, fn);
}
// Utility function
var removeType = function(t){
data.handlers[t] = [];
_cleanUpEvents(elem,t);
};
// Are we removing all bound events?
if (!type) {
for (let t in data.handlers) removeType(t);
return;
}
var handlers = data.handlers[type];
// If no handlers exist, nothing to unbind
if (!handlers) return;
// If no listener was provided, remove all listeners for type
if (!fn) {
removeType(type);
return;
}
// We're only removing a single handler
if (fn.guid) {
for (let n = 0; n < handlers.length; n++) {
if (handlers[n].guid === fn.guid) {
handlers.splice(n--, 1);
}
}
}
_cleanUpEvents(elem, type);
}
/**
* Trigger an event for an element
*
* @param {Element|Object} elem Element to trigger an event on
* @param {Event|Object|String} event A string (the type) or an event object with a type attribute
* @param {Object} [hash] data hash to pass along with the event
* @return {Boolean=} Returned only if default was prevented
* @method trigger
*/
export function trigger(elem, event, hash) {
// Fetches element data and a reference to the parent (for bubbling).
// Don't want to add a data object to cache for every parent,
// so checking hasElData first.
var elemData = (Dom.hasElData(elem)) ? Dom.getElData(elem) : {};
var parent = elem.parentNode || elem.ownerDocument;
// type = event.type || event,
// handler;
// If an event name was passed as a string, creates an event out of it
if (typeof event === 'string') {
event = { type:event, target:elem };
}
// Normalizes the event properties.
event = fixEvent(event);
// If the passed element has a dispatcher, executes the established handlers.
if (elemData.dispatcher) {
elemData.dispatcher.call(elem, event, hash);
}
// Unless explicitly stopped or the event does not bubble (e.g. media events)
// recursively calls this function to bubble the event up the DOM.
if (parent && !event.isPropagationStopped() && event.bubbles === true) {
trigger.call(null, parent, event, hash);
// If at the top of the DOM, triggers the default action unless disabled.
} else if (!parent && !event.defaultPrevented) {
var targetData = Dom.getElData(event.target);
// Checks if the target has a default action for this event.
if (event.target[event.type]) {
// Temporarily disables event dispatching on the target as we have already executed the handler.
targetData.disabled = true;
// Executes the default action.
if (typeof event.target[event.type] === 'function') {
event.target[event.type]();
}
// Re-enables event dispatching.
targetData.disabled = false;
}
}
// Inform the triggerer if the default was prevented by returning false
return !event.defaultPrevented;
}
/**
* Trigger a listener only once for an event
*
* @param {Element|Object} elem Element or object to
* @param {String|Array} type Name/type of event
* @param {Function} fn Event handler function
* @method one
*/
export function one(elem, type, fn) {
if (Array.isArray(type)) {
return _handleMultipleEvents(one, elem, type, fn);
}
var func = function(){
off(elem, type, func);
fn.apply(this, arguments);
};
// copy the guid to the new function so it can removed using the original function's ID
func.guid = fn.guid = fn.guid || Guid.newGUID();
on(elem, type, func);
}
/**
* Fix a native event to have standard property values
*
* @param {Object} event Event object to fix
* @return {Object}
* @private
* @method fixEvent
*/
export function fixEvent(event) {
function returnTrue() { return true; }
function returnFalse() { return false; }
// Test if fixing up is needed
// Used to check if !event.stopPropagation instead of isPropagationStopped
// But native events return true for stopPropagation, but don't have
// other expected methods like isPropagationStopped. Seems to be a problem
// with the Javascript Ninja code. So we're just overriding all events now.
if (!event || !event.isPropagationStopped) {
var old = event || window.event;
event = {};
// Clone the old object so that we can modify the values event = {};
// IE8 Doesn't like when you mess with native event properties
// Firefox returns false for event.hasOwnProperty('type') and other props
// which makes copying more difficult.
// TODO: Probably best to create a whitelist of event props
for (var key in old) {
// Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
// Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
// and webkitMovementX/Y
if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' &&
key !== 'webkitMovementX' && key !== 'webkitMovementY') {
// Chrome 32+ warns if you try to copy deprecated returnValue, but
// we still want to if preventDefault isn't supported (IE8).
if (!(key === 'returnValue' && old.preventDefault)) {
event[key] = old[key];
}
}
}
// The event occurred on this element
if (!event.target) {
event.target = event.srcElement || document;
}
// Handle which other element the event is related to
if (!event.relatedTarget) {
event.relatedTarget = event.fromElement === event.target ?
event.toElement :
event.fromElement;
}
// Stop the default browser action
event.preventDefault = function () {
if (old.preventDefault) {
old.preventDefault();
}
event.returnValue = false;
old.returnValue = false;
event.defaultPrevented = true;
};
event.defaultPrevented = false;
// Stop the event from bubbling
event.stopPropagation = function () {
if (old.stopPropagation) {
old.stopPropagation();
}
event.cancelBubble = true;
old.cancelBubble = true;
event.isPropagationStopped = returnTrue;
};
event.isPropagationStopped = returnFalse;
// Stop the event from bubbling and executing other handlers
event.stopImmediatePropagation = function () {
if (old.stopImmediatePropagation) {
old.stopImmediatePropagation();
}
event.isImmediatePropagationStopped = returnTrue;
event.stopPropagation();
};
event.isImmediatePropagationStopped = returnFalse;
// Handle mouse position
if (event.clientX != null) {
var doc = document.documentElement, body = document.body;
event.pageX = event.clientX +
(doc && doc.scrollLeft || body && body.scrollLeft || 0) -
(doc && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY +
(doc && doc.scrollTop || body && body.scrollTop || 0) -
(doc && doc.clientTop || body && body.clientTop || 0);
}
// Handle key presses
event.which = event.charCode || event.keyCode;
// Fix button for mouse clicks:
// 0 == left; 1 == middle; 2 == right
if (event.button != null) {
event.button = (event.button & 1 ? 0 :
(event.button & 4 ? 1 :
(event.button & 2 ? 2 : 0)));
}
}
// Returns fixed-up instance
return event;
}
/**
* Clean up the listener cache and dispatchers
*
@ -331,7 +21,7 @@ export function fixEvent(event) {
* @method _cleanUpEvents
*/
function _cleanUpEvents(elem, type) {
var data = Dom.getElData(elem);
let data = Dom.getElData(elem);
// Remove the events of a particular type if there are none left
if (data.handlers[type].length === 0) {
@ -372,7 +62,346 @@ function _cleanUpEvents(elem, type) {
*/
function _handleMultipleEvents(fn, elem, types, callback) {
types.forEach(function(type) {
//Call the event method for each one of the types
// Call the event method for each one of the types
fn(elem, type, callback);
});
}
/**
* Fix a native event to have standard property values
*
* @param {Object} event Event object to fix
* @return {Object}
* @private
* @method fixEvent
*/
export function fixEvent(event) {
function returnTrue() {
return true;
}
function returnFalse() {
return false;
}
// Test if fixing up is needed
// Used to check if !event.stopPropagation instead of isPropagationStopped
// But native events return true for stopPropagation, but don't have
// other expected methods like isPropagationStopped. Seems to be a problem
// with the Javascript Ninja code. So we're just overriding all events now.
if (!event || !event.isPropagationStopped) {
let old = event || window.event;
event = {};
// Clone the old object so that we can modify the values event = {};
// IE8 Doesn't like when you mess with native event properties
// Firefox returns false for event.hasOwnProperty('type') and other props
// which makes copying more difficult.
// TODO: Probably best to create a whitelist of event props
for (let key in old) {
// Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
// Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
// and webkitMovementX/Y
if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' &&
key !== 'webkitMovementX' && key !== 'webkitMovementY') {
// Chrome 32+ warns if you try to copy deprecated returnValue, but
// we still want to if preventDefault isn't supported (IE8).
if (!(key === 'returnValue' && old.preventDefault)) {
event[key] = old[key];
}
}
}
// The event occurred on this element
if (!event.target) {
event.target = event.srcElement || document;
}
// Handle which other element the event is related to
if (!event.relatedTarget) {
event.relatedTarget = event.fromElement === event.target ?
event.toElement :
event.fromElement;
}
// Stop the default browser action
event.preventDefault = function() {
if (old.preventDefault) {
old.preventDefault();
}
event.returnValue = false;
old.returnValue = false;
event.defaultPrevented = true;
};
event.defaultPrevented = false;
// Stop the event from bubbling
event.stopPropagation = function() {
if (old.stopPropagation) {
old.stopPropagation();
}
event.cancelBubble = true;
old.cancelBubble = true;
event.isPropagationStopped = returnTrue;
};
event.isPropagationStopped = returnFalse;
// Stop the event from bubbling and executing other handlers
event.stopImmediatePropagation = function() {
if (old.stopImmediatePropagation) {
old.stopImmediatePropagation();
}
event.isImmediatePropagationStopped = returnTrue;
event.stopPropagation();
};
event.isImmediatePropagationStopped = returnFalse;
// Handle mouse position
if (event.clientX != null) {
let doc = document.documentElement;
let body = document.body;
event.pageX = event.clientX +
(doc && doc.scrollLeft || body && body.scrollLeft || 0) -
(doc && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY +
(doc && doc.scrollTop || body && body.scrollTop || 0) -
(doc && doc.clientTop || body && body.clientTop || 0);
}
// Handle key presses
event.which = event.charCode || event.keyCode;
// Fix button for mouse clicks:
// 0 == left; 1 == middle; 2 == right
if (event.button != null) {
// The following is disabled because it does not pass videojs-standard
// and... yikes.
/* eslint-disable */
event.button = (event.button & 1 ? 0 :
(event.button & 4 ? 1 :
(event.button & 2 ? 2 : 0)));
/* eslint-enable */
}
}
// Returns fixed-up instance
return event;
}
/**
* Add an event listener to element
* It stores the handler function in a separate cache object
* and adds a generic handler to the element's event,
* along with a unique id (guid) to the element.
*
* @param {Element|Object} elem Element or object to bind listeners to
* @param {String|Array} type Type of event to bind to.
* @param {Function} fn Event listener.
* @method on
*/
export function on(elem, type, fn) {
if (Array.isArray(type)) {
return _handleMultipleEvents(on, elem, type, fn);
}
let data = Dom.getElData(elem);
// We need a place to store all our handler data
if (!data.handlers) {
data.handlers = {};
}
if (!data.handlers[type]) {
data.handlers[type] = [];
}
if (!fn.guid) {
fn.guid = Guid.newGUID();
}
data.handlers[type].push(fn);
if (!data.dispatcher) {
data.disabled = false;
data.dispatcher = function(event, hash) {
if (data.disabled) {
return;
}
event = fixEvent(event);
let handlers = data.handlers[event.type];
if (handlers) {
// Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
let handlersCopy = handlers.slice(0);
for (let m = 0, n = handlersCopy.length; m < n; m++) {
if (event.isImmediatePropagationStopped()) {
break;
} else {
handlersCopy[m].call(elem, event, hash);
}
}
}
};
}
if (data.handlers[type].length === 1) {
if (elem.addEventListener) {
elem.addEventListener(type, data.dispatcher, false);
} else if (elem.attachEvent) {
elem.attachEvent('on' + type, data.dispatcher);
}
}
}
/**
* Removes event listeners from an element
*
* @param {Element|Object} elem Object to remove listeners from
* @param {String|Array=} type Type of listener to remove. Don't include to remove all events from element.
* @param {Function} fn Specific listener to remove. Don't include to remove listeners for an event type.
* @method off
*/
export function off(elem, type, fn) {
// Don't want to add a cache object through getElData if not needed
if (!Dom.hasElData(elem)) {
return;
}
let data = Dom.getElData(elem);
// If no events exist, nothing to unbind
if (!data.handlers) {
return;
}
if (Array.isArray(type)) {
return _handleMultipleEvents(off, elem, type, fn);
}
// Utility function
let removeType = function(t) {
data.handlers[t] = [];
_cleanUpEvents(elem, t);
};
// Are we removing all bound events?
if (!type) {
for (let t in data.handlers) {
removeType(t);
}
return;
}
let handlers = data.handlers[type];
// If no handlers exist, nothing to unbind
if (!handlers) {
return;
}
// If no listener was provided, remove all listeners for type
if (!fn) {
removeType(type);
return;
}
// We're only removing a single handler
if (fn.guid) {
for (let n = 0; n < handlers.length; n++) {
if (handlers[n].guid === fn.guid) {
handlers.splice(n--, 1);
}
}
}
_cleanUpEvents(elem, type);
}
/**
* Trigger an event for an element
*
* @param {Element|Object} elem Element to trigger an event on
* @param {Event|Object|String} event A string (the type) or an event object with a type attribute
* @param {Object} [hash] data hash to pass along with the event
* @return {Boolean=} Returned only if default was prevented
* @method trigger
*/
export function trigger(elem, event, hash) {
// Fetches element data and a reference to the parent (for bubbling).
// Don't want to add a data object to cache for every parent,
// so checking hasElData first.
let elemData = (Dom.hasElData(elem)) ? Dom.getElData(elem) : {};
let parent = elem.parentNode || elem.ownerDocument;
// type = event.type || event,
// handler;
// If an event name was passed as a string, creates an event out of it
if (typeof event === 'string') {
event = {type: event, target: elem};
}
// Normalizes the event properties.
event = fixEvent(event);
// If the passed element has a dispatcher, executes the established handlers.
if (elemData.dispatcher) {
elemData.dispatcher.call(elem, event, hash);
}
// Unless explicitly stopped or the event does not bubble (e.g. media events)
// recursively calls this function to bubble the event up the DOM.
if (parent && !event.isPropagationStopped() && event.bubbles === true) {
trigger.call(null, parent, event, hash);
// If at the top of the DOM, triggers the default action unless disabled.
} else if (!parent && !event.defaultPrevented) {
let targetData = Dom.getElData(event.target);
// Checks if the target has a default action for this event.
if (event.target[event.type]) {
// Temporarily disables event dispatching on the target as we have already executed the handler.
targetData.disabled = true;
// Executes the default action.
if (typeof event.target[event.type] === 'function') {
event.target[event.type]();
}
// Re-enables event dispatching.
targetData.disabled = false;
}
}
// Inform the triggerer if the default was prevented by returning false
return !event.defaultPrevented;
}
/**
* Trigger a listener only once for an event
*
* @param {Element|Object} elem Element or object to
* @param {String|Array} type Name/type of event
* @param {Function} fn Event handler function
* @method one
*/
export function one(elem, type, fn) {
if (Array.isArray(type)) {
return _handleMultipleEvents(one, elem, type, fn);
}
let func = function() {
off(elem, type, func);
fn.apply(this, arguments);
};
// copy the guid to the new function so it can removed using the original function's ID
func.guid = fn.guid = fn.guid || Guid.newGUID();
on(elem, type, func);
}

View File

@ -16,7 +16,9 @@ import { newGUID } from './guid.js';
*/
export const bind = function(context, fn, uid) {
// Make sure the function has a unique ID
if (!fn.guid) { fn.guid = newGUID(); }
if (!fn.guid) {
fn.guid = newGUID();
}
// Create the new function that changes the context
let ret = function() {

View File

@ -11,7 +11,7 @@
* @private
* @function formatTime
*/
function formatTime(seconds, guide=seconds) {
function formatTime(seconds, guide = seconds) {
seconds = seconds < 0 ? 0 : seconds;
let s = Math.floor(seconds % 60);
let m = Math.floor(seconds / 60 % 60);

View File

@ -10,7 +10,7 @@ let _guid = 1;
/**
* Get the next unique ID
*
* @return {String}
* @return {String}
* @function newGUID
*/
export function newGUID() {

View File

@ -4,6 +4,8 @@
import window from 'global/window';
import {IE_VERSION} from './browser';
let log;
/**
* Log messages to the console and history based on the type of message
*
@ -16,7 +18,6 @@ import {IE_VERSION} from './browser';
* but this is exposed as a parameter to facilitate testing.
*/
export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 11) => {
const console = window.console;
// If there's no console then don't try to output messages, but they will
// still be stored in `log.history`.
@ -24,7 +25,7 @@ export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 1
// Was setting these once outside of this function, but containing them
// in the function makes it easier to test cases where console doesn't exist
// when the module is executed.
const fn = console && console[type] || function(){};
const fn = window.console && window.console[type] || function() {};
if (type !== 'log') {
@ -45,7 +46,9 @@ export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 1
if (a && typeof a === 'object' || Array.isArray(a)) {
try {
return JSON.stringify(a);
} catch (x) {}
} catch (x) {
return String(a);
}
}
// Cast to string before joining, so we get null and undefined explicitly
@ -68,9 +71,9 @@ export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 1
*
* @function log
*/
function log(...args) {
log = function(...args) {
logByType('log', args);
}
};
/**
* Keep a history of log messages
@ -93,5 +96,4 @@ log.error = (...args) => logByType('error', args);
*/
log.warn = (...args) => logByType('warn', args);
export default log;

View File

@ -4,12 +4,14 @@
import merge from 'lodash-compat/object/merge';
function isPlain(obj) {
return !!obj
&& typeof obj === 'object'
&& obj.toString() === '[object Object]'
&& obj.constructor === Object;
return !!obj &&
typeof obj === 'object' &&
obj.toString() === '[object Object]' &&
obj.constructor === Object;
}
let mergeOptions;
/**
* Merge customizer. video.js simply overwrites non-simple objects
* (like arrays) instead of attempting to overlay them.
@ -41,7 +43,7 @@ const customizer = function(destination, source) {
* provided objects
* @function mergeOptions
*/
export default function mergeOptions() {
mergeOptions = function() {
// contruct the call dynamically to handle the variable number of
// objects to merge
let args = Array.prototype.slice.call(arguments);
@ -57,4 +59,6 @@ export default function mergeOptions() {
// return the mutated result object
return args[0];
}
};
export default mergeOptions;

View File

@ -2,6 +2,7 @@ import document from 'global/document';
export let createStyleElement = function(className) {
let style = document.createElement('style');
style.className = className;
return style;

View File

@ -1,5 +1,39 @@
import log from './log.js';
function rangeCheck(fnName, index, maxIndex) {
if (index < 0 || index > maxIndex) {
throw new Error(`Failed to execute '${fnName}' on 'TimeRanges': The index provided (${index}) is greater than or equal to the maximum bound (${maxIndex}).`);
}
}
function getRange(fnName, valueIndex, ranges, rangeIndex) {
if (rangeIndex === undefined) {
log.warn(`DEPRECATED: Function '${fnName}' on 'TimeRanges' called without an index argument.`);
rangeIndex = 0;
}
rangeCheck(fnName, rangeIndex, ranges.length - 1);
return ranges[rangeIndex][valueIndex];
}
function createTimeRangesObj(ranges) {
if (ranges === undefined || ranges.length === 0) {
return {
length: 0,
start() {
throw new Error('This TimeRanges object is empty');
},
end() {
throw new Error('This TimeRanges object is empty');
}
};
}
return {
length: ranges.length,
start: getRange.bind(null, 'start', 0, ranges),
end: getRange.bind(null, 'end', 1, ranges)
};
}
/**
* @file time-ranges.js
*
@ -13,7 +47,7 @@ import log from './log.js';
* @private
* @method createTimeRanges
*/
export function createTimeRanges(start, end){
export function createTimeRanges(start, end) {
if (Array.isArray(start)) {
return createTimeRangesObj(start);
} else if (start === undefined || end === undefined) {
@ -23,37 +57,3 @@ export function createTimeRanges(start, end){
}
export { createTimeRanges as createTimeRange };
function createTimeRangesObj(ranges){
if (ranges === undefined || ranges.length === 0) {
return {
length: 0,
start: function() {
throw new Error('This TimeRanges object is empty');
},
end: function() {
throw new Error('This TimeRanges object is empty');
}
};
}
return {
length: ranges.length,
start: getRange.bind(null, 'start', 0, ranges),
end: getRange.bind(null, 'end', 1, ranges)
};
}
function getRange(fnName, valueIndex, ranges, rangeIndex){
if (rangeIndex === undefined) {
log.warn(`DEPRECATED: Function '${fnName}' on 'TimeRanges' called without an index argument.`);
rangeIndex = 0;
}
rangeCheck(fnName, rangeIndex, ranges.length - 1);
return ranges[rangeIndex][valueIndex];
}
function rangeCheck(fnName, index, maxIndex){
if (index < 0 || index > maxIndex) {
throw new Error(`Failed to execute '${fnName}' on 'TimeRanges': The index provided (${index}) is greater than or equal to the maximum bound (${maxIndex}).`);
}
}

View File

@ -8,7 +8,7 @@
* @private
* @method toTitleCase
*/
function toTitleCase(string){
function toTitleCase(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

View File

@ -16,6 +16,7 @@ export const parseUrl = function(url) {
// add the url to an anchor and let the browser parse the URL
let a = document.createElement('a');
a.href = url;
// IE8 (and 9?) Fix
@ -23,6 +24,7 @@ export const parseUrl = function(url) {
// added to the body, and an innerHTML is needed to trigger the parsing
let addToBody = (a.host === '' && a.protocol !== 'file:');
let div;
if (addToBody) {
div = document.createElement('div');
div.innerHTML = `<a href="${url}"></a>`;
@ -36,7 +38,8 @@ export const parseUrl = function(url) {
// This is also needed for IE8 because the anchor loses its
// properties when it's removed from the dom
let details = {};
for (var i = 0; i < props.length; i++) {
for (let i = 0; i < props.length; i++) {
details[props[i]] = a[props[i]];
}
@ -45,6 +48,7 @@ export const parseUrl = function(url) {
if (details.protocol === 'http:') {
details.host = details.host.replace(/:80$/, '');
}
if (details.protocol === 'https:') {
details.host = details.host.replace(/:443$/, '');
}
@ -65,11 +69,12 @@ export const parseUrl = function(url) {
* @private
* @method getAbsoluteURL
*/
export const getAbsoluteURL = function(url){
export const getAbsoluteURL = function(url) {
// Check if absolute URL
if (!url.match(/^https?:\/\//)) {
// Convert to absolute URL. Flash hosted off-site needs an absolute URL.
let div = document.createElement('div');
div.innerHTML = `<a href="${url}">x</a>`;
url = div.firstChild.href;
}
@ -85,7 +90,7 @@ export const getAbsoluteURL = function(url){
* @method getFileExtension
*/
export const getFileExtension = function(path) {
if(typeof path === 'string'){
if (typeof path === 'string') {
let splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
let pathParts = splitPathRe.exec(path);

View File

@ -1,6 +1,9 @@
/**
* @file video.js
*/
/* global define */
import window from 'global/window';
import document from 'global/document';
import * as setup from './setup';
@ -28,8 +31,6 @@ import xhr from 'xhr';
// Include the built-in techs
import Tech from './tech/tech.js';
import Html5 from './tech/html5.js';
import Flash from './tech/flash.js';
// HTML5 Element Shim for IE8
if (typeof HTMLVideoElement === 'undefined') {
@ -53,8 +54,8 @@ if (typeof HTMLVideoElement === 'undefined') {
* @mixes videojs
* @method videojs
*/
function videojs(id, options, ready){
let tag; // Element of ID
function videojs(id, options, ready) {
let tag;
// Allow for element or ID to be passed in
// String ID
@ -78,11 +79,10 @@ function videojs(id, options, ready){
}
return videojs.getPlayers()[id];
}
// Otherwise get element for ID
} else {
tag = Dom.getEl(id);
}
tag = Dom.getEl(id);
// ID is a media element
} else {
@ -90,13 +90,14 @@ function videojs(id, options, ready){
}
// Check for a useable element
if (!tag || !tag.nodeName) { // re: nodeName, could be a box div also
throw new TypeError('The element or ID supplied is not valid. (videojs)'); // Returns
// re: nodeName, could be a box div also
if (!tag || !tag.nodeName) {
throw new TypeError('The element or ID supplied is not valid. (videojs)');
}
// Element may have a player attr referring to an already created player instance.
// If not, set up a new player and return the instance.
return tag['player'] || Player.players[tag.playerId] || new Player(tag, options, ready);
return tag.player || Player.players[tag.playerId] || new Player(tag, options, ready);
}
// Add default styles
@ -106,6 +107,7 @@ if (window.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
if (!style) {
style = stylesheet.createStyleElement('vjs-styles-defaults');
let head = Dom.$('head');
head.insertBefore(style, head.firstChild);
stylesheet.setTextContent(style, `
.video-js {
@ -121,7 +123,8 @@ if (window.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
}
// Run Auto-load players
// You have to wait at least once in case this script is loaded after your video in the DOM (weird behavior only with minified version)
// You have to wait at least once in case this script is loaded after your
// video in the DOM (weird behavior only with minified version)
setup.autoSetupTimeout(1, videojs);
/*
@ -269,12 +272,12 @@ videojs.TOUCH_ENABLED = browser.TOUCH_ENABLED;
* Mimics ES6 subclassing with the `extend` keyword
* ```js
* // Create a basic javascript 'class'
* function MyClass(name){
* function MyClass(name) {
* // Set a property at initialization
* this.myName = name;
* }
* // Create an instance method
* MyClass.prototype.sayMyName = function(){
* MyClass.prototype.sayMyName = function() {
* alert(this.myName);
* };
* // Subclass the exisitng class and change the name
@ -337,12 +340,12 @@ videojs.mergeOptions = mergeOptions;
/**
* Change the context (this) of a function
*
* videojs.bind(newContext, function(){
* videojs.bind(newContext, function() {
* this === newContext
* });
*
* NOTE: as of v5.0 we require an ES5 shim, so you should use the native
* `function(){}.bind(newContext);` instead of this.
* `function() {}.bind(newContext);` instead of this.
*
* @param {*} context The object to bind as scope
* @param {Function} fn The function to be bound to a scope
@ -365,7 +368,7 @@ videojs.bind = Fn.bind;
* var player = this;
* var alertText = myPluginOptions.text || 'Player is playing!'
*
* player.on('play', function(){
* player.on('play', function() {
* alert(alertText);
* });
* });
@ -410,7 +413,7 @@ videojs.plugin = plugin;
* @mixes videojs
* @method addLanguage
*/
videojs.addLanguage = function(code, data){
videojs.addLanguage = function(code, data) {
code = ('' + code).toLowerCase();
return merge(videojs.options.languages, { [code]: data })[code];
};
@ -721,12 +724,12 @@ videojs.insertContent = Dom.insertContent;
* still support requirejs and browserify. This also needs to be closure
* compiler compatible, so string keys are used.
*/
if (typeof define === 'function' && define['amd']) {
define('videojs', [], function(){ return videojs; });
if (typeof define === 'function' && define.amd) {
define('videojs', [], () => videojs);
// checking that module is an object too because of umdjs/umd#35
} else if (typeof exports === 'object' && typeof module === 'object') {
module['exports'] = videojs;
module.exports = videojs;
}
export default videojs;