1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-27 02:43:45 +02:00

Broke up controls.js into multiple files.

Cleaned up media.js files also.
This commit is contained in:
Steve Heffernan 2013-05-02 19:03:29 -07:00
parent 997b327fcd
commit 05ab972a1a
16 changed files with 728 additions and 745 deletions

View File

@ -35,12 +35,12 @@ module.exports = function(grunt) {
minify: {
source:{
src: ['build/files/combined.video.js', 'build/compiler/goog.base.js', 'src/js/exports.js'],
externs: ['src/js/media.flash.externs.js'],
externs: ['src/js/media/flash.externs.js'],
dest: 'build/files/minified.video.js'
},
tests: {
src: ['build/files/combined.video.js', 'build/compiler/goog.base.js', 'src/js/exports.js', 'test/unit/*.js', '!test/unit/api.js'],
externs: ['src/js/media.flash.externs.js', 'test/qunit/qunit-externs.js'],
externs: ['src/js/media/flash.externs.js', 'test/qunit/qunit-externs.js'],
dest: 'build/files/test.minified.video.js'
}
},

View File

@ -24,14 +24,21 @@ var sourceFiles = [
"src/js/slider.js",
"src/js/menu.js",
"src/js/player.js",
"src/js/controls.js",
"src/js/control-bar/control-bar.js",
"src/js/control-bar/play-toggle.js",
"src/js/control-bar/time-display.js",
"src/js/control-bar/fullscreen-toggle.js",
"src/js/control-bar/progress-control.js",
"src/js/control-bar/volume-control.js",
"src/js/control-bar/mute-toggle.js",
"src/js/control-bar/volume-menu-button.js",
"src/js/poster.js",
"src/js/loading-spinner.js",
"src/js/big-play-button.js",
"src/js/media.js",
"src/js/media.html5.js",
"src/js/media.flash.js",
"src/js/media.loader.js",
"src/js/media/media.js",
"src/js/media/html5.js",
"src/js/media/flash.js",
"src/js/media/loader.js",
"src/js/tracks.js",
"src/js/json.js",
"src/js/setup.js",

View File

@ -0,0 +1,87 @@
/**
* Container of main controls
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.ControlBar = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
if (!player.controls()) {
this.disable();
}
player.one('play', vjs.bind(this, function(){
var touchstart,
fadeIn = vjs.bind(this, this.fadeIn),
fadeOut = vjs.bind(this, this.fadeOut);
this.fadeIn();
if ( !('ontouchstart' in window) ) {
this.player_.on('mouseover', fadeIn);
this.player_.on('mouseout', fadeOut);
this.player_.on('pause', vjs.bind(this, this.lockShowing));
this.player_.on('play', vjs.bind(this, this.unlockShowing));
}
touchstart = false;
this.player_.on('touchstart', function() {
touchstart = true;
});
this.player_.on('touchmove', function() {
touchstart = false;
});
this.player_.on('touchend', vjs.bind(this, function(event) {
var idx;
if (touchstart) {
idx = this.el().className.search('fade-in');
if (idx !== -1) {
this.fadeOut();
} else {
this.fadeIn();
}
}
touchstart = false;
if (!this.player_.paused()) {
event.preventDefault();
}
}));
}));
}
});
vjs.ControlBar.prototype.options_ = {
loadEvent: 'play',
children: {
'playToggle': {},
'currentTimeDisplay': {},
'timeDivider': {},
'durationDisplay': {},
'remainingTimeDisplay': {},
'progressControl': {},
'fullscreenToggle': {},
'volumeControl': {},
'muteToggle': {}
// 'volumeMenuButton': {}
}
};
vjs.ControlBar.prototype.createEl = function(){
return vjs.createEl('div', {
className: 'vjs-control-bar'
});
};
vjs.ControlBar.prototype.fadeIn = function(){
vjs.Component.prototype.fadeIn.call(this);
this.player_.trigger('controlsvisible');
};
vjs.ControlBar.prototype.fadeOut = function(){
vjs.Component.prototype.fadeOut.call(this);
this.player_.trigger('controlshidden');
};

View File

@ -0,0 +1,28 @@
/**
* Toggle fullscreen video
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.FullscreenToggle = vjs.Button.extend({
/** @constructor */
init: function(player, options){
vjs.Button.call(this, player, options);
}
});
vjs.FullscreenToggle.prototype.buttonText = 'Fullscreen';
vjs.FullscreenToggle.prototype.buildCSSClass = function(){
return 'vjs-fullscreen-control ' + vjs.Button.prototype.buildCSSClass.call(this);
};
vjs.FullscreenToggle.prototype.onClick = function(){
if (!this.player_.isFullScreen) {
this.player_.requestFullScreen();
this.el_.children[0].children[0].innerHTML = 'Non-Fullscreen'; // change the button text to "Non-Fullscreen"
} else {
this.player_.cancelFullScreen();
this.el_.children[0].children[0].innerHTML = 'Fullscreen'; // change the button to "Fullscreen"
}
};

View File

@ -0,0 +1,69 @@
/**
* Mute the audio
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.MuteToggle = vjs.Button.extend({
/** @constructor */
init: function(player, options){
vjs.Button.call(this, player, options);
player.on('volumechange', vjs.bind(this, this.update));
// hide mute toggle if the current tech doesn't support volume control
if (player.tech && player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
}
player.on('loadstart', vjs.bind(this, function(){
if (player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');
}
}));
}
});
vjs.MuteToggle.prototype.createEl = function(){
return vjs.Button.prototype.createEl.call(this, 'div', {
className: 'vjs-mute-control vjs-control',
innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
});
};
vjs.MuteToggle.prototype.onClick = function(){
this.player_.muted( this.player_.muted() ? false : true );
};
vjs.MuteToggle.prototype.update = function(){
var vol = this.player_.volume(),
level = 3;
if (vol === 0 || this.player_.muted()) {
level = 0;
} else if (vol < 0.33) {
level = 1;
} else if (vol < 0.67) {
level = 2;
}
// 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.
if(this.player_.muted()){
if(this.el_.children[0].children[0].innerHTML!='Unmute'){
this.el_.children[0].children[0].innerHTML = 'Unmute'; // change the button text to "Unmute"
}
} else {
if(this.el_.children[0].children[0].innerHTML!='Mute'){
this.el_.children[0].children[0].innerHTML = 'Mute'; // change the button text to "Mute"
}
}
/* TODO improve muted icon classes */
for (var i = 0; i < 4; i++) {
vjs.removeClass(this.el_, 'vjs-vol-'+i);
}
vjs.addClass(this.el_, 'vjs-vol-'+level);
};

View File

@ -0,0 +1,44 @@
/**
* Button to toggle between play and pause
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.PlayToggle = vjs.Button.extend({
/** @constructor */
init: function(player, options){
vjs.Button.call(this, player, options);
player.on('play', vjs.bind(this, this.onPlay));
player.on('pause', vjs.bind(this, this.onPause));
}
});
vjs.PlayToggle.prototype.buttonText = 'Play';
vjs.PlayToggle.prototype.buildCSSClass = function(){
return 'vjs-play-control ' + vjs.Button.prototype.buildCSSClass.call(this);
};
// OnClick - Toggle between play and pause
vjs.PlayToggle.prototype.onClick = function(){
if (this.player_.paused()) {
this.player_.play();
} else {
this.player_.pause();
}
};
// OnPlay - Add the vjs-playing class to the element so it can change appearance
vjs.PlayToggle.prototype.onPlay = function(){
vjs.removeClass(this.el_, 'vjs-paused');
vjs.addClass(this.el_, 'vjs-playing');
this.el_.children[0].children[0].innerHTML = 'Pause'; // change the button text to "Pause"
};
// OnPause - Add the vjs-paused class to the element so it can change appearance
vjs.PlayToggle.prototype.onPause = function(){
vjs.removeClass(this.el_, 'vjs-playing');
vjs.addClass(this.el_, 'vjs-paused');
this.el_.children[0].children[0].innerHTML = 'Play'; // change the button text to "Play"
};

View File

@ -0,0 +1,171 @@
/**
* Seek, Load Progress, and Play Progress
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.ProgressControl = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.ProgressControl.prototype.options_ = {
children: {
'seekBar': {}
}
};
vjs.ProgressControl.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-progress-control vjs-control'
});
};
/**
* Seek Bar and holder for the progress bars
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.SeekBar = vjs.Slider.extend({
/** @constructor */
init: function(player, options){
vjs.Slider.call(this, player, options);
player.on('timeupdate', vjs.bind(this, this.updateARIAAttributes));
player.ready(vjs.bind(this, this.updateARIAAttributes));
}
});
vjs.SeekBar.prototype.options_ = {
children: {
'loadProgressBar': {},
'playProgressBar': {},
'seekHandle': {}
},
'barName': 'playProgressBar',
'handleName': 'seekHandle'
};
vjs.SeekBar.prototype.playerEvent = 'timeupdate';
vjs.SeekBar.prototype.createEl = function(){
return vjs.Slider.prototype.createEl.call(this, 'div', {
className: 'vjs-progress-holder',
'aria-label': 'video progress bar'
});
};
vjs.SeekBar.prototype.updateARIAAttributes = function(){
// Allows for smooth scrubbing, when player can't keep up.
var time = (this.player_.scrubbing) ? this.player_.getCache().currentTime : this.player_.currentTime();
this.el_.setAttribute('aria-valuenow',vjs.round(this.getPercent()*100, 2)); // machine readable value of progress bar (percentage complete)
this.el_.setAttribute('aria-valuetext',vjs.formatTime(time, this.player_.duration())); // human readable value of progress bar (time complete)
};
vjs.SeekBar.prototype.getPercent = function(){
return this.player_.currentTime() / this.player_.duration();
};
vjs.SeekBar.prototype.onMouseDown = function(event){
vjs.Slider.prototype.onMouseDown.call(this, event);
this.player_.scrubbing = true;
this.videoWasPlaying = !this.player_.paused();
this.player_.pause();
};
vjs.SeekBar.prototype.onMouseMove = function(event){
var newTime = this.calculateDistance(event) * this.player_.duration();
// Don't let video end while scrubbing.
if (newTime == this.player_.duration()) { newTime = newTime - 0.1; }
// Set new time (tell player to seek to new time)
this.player_.currentTime(newTime);
};
vjs.SeekBar.prototype.onMouseUp = function(event){
vjs.Slider.prototype.onMouseUp.call(this, event);
this.player_.scrubbing = false;
if (this.videoWasPlaying) {
this.player_.play();
}
};
vjs.SeekBar.prototype.stepForward = function(){
this.player_.currentTime(this.player_.currentTime() + 5); // more quickly fast forward for keyboard-only users
};
vjs.SeekBar.prototype.stepBack = function(){
this.player_.currentTime(this.player_.currentTime() - 5); // more quickly rewind for keyboard-only users
};
/**
* Shows load progres
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.LoadProgressBar = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
player.on('progress', vjs.bind(this, this.update));
}
});
vjs.LoadProgressBar.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-load-progress',
innerHTML: '<span class="vjs-control-text">Loaded: 0%</span>'
});
};
vjs.LoadProgressBar.prototype.update = function(){
if (this.el_.style) { this.el_.style.width = vjs.round(this.player_.bufferedPercent() * 100, 2) + '%'; }
};
/**
* Shows play progress
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.PlayProgressBar = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.PlayProgressBar.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-play-progress',
innerHTML: '<span class="vjs-control-text">Progress: 0%</span>'
});
};
/**
* SeekBar component includes play progress bar, and seek handle
* Needed so it can determine seek position based on handle position/size
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.SeekHandle = vjs.SliderHandle.extend();
/** @inheritDoc */
vjs.SeekHandle.prototype.defaultValue = '00:00';
/** @inheritDoc */
vjs.SeekHandle.prototype.createEl = function(){
return vjs.SliderHandle.prototype.createEl.call(this, 'div', {
className: 'vjs-seek-handle'
});
};

View File

@ -0,0 +1,133 @@
/**
* Displays the current time
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.CurrentTimeDisplay = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
player.on('timeupdate', vjs.bind(this, this.updateContent));
}
});
vjs.CurrentTimeDisplay.prototype.createEl = function(){
var el = vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-current-time vjs-time-controls vjs-control'
});
this.content = vjs.createEl('div', {
className: 'vjs-current-time-display',
innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00', // label the current time for screen reader users
'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
});
el.appendChild(vjs.createEl('div').appendChild(this.content));
return el;
};
vjs.CurrentTimeDisplay.prototype.updateContent = function(){
// Allows for smooth scrubbing, when player can't keep up.
var time = (this.player_.scrubbing) ? this.player_.getCache().currentTime : this.player_.currentTime();
this.content.innerHTML = '<span class="vjs-control-text">Current Time </span>' + vjs.formatTime(time, this.player_.duration());
};
/**
* Displays the duration
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.DurationDisplay = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
player.on('timeupdate', vjs.bind(this, this.updateContent)); // this might need to be changes to 'durationchange' instead of 'timeupdate' eventually, however the durationchange event fires before this.player_.duration() is set, so the value cannot be written out using this method. Once the order of durationchange and this.player_.duration() being set is figured out, this can be updated.
}
});
vjs.DurationDisplay.prototype.createEl = function(){
var el = vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-duration vjs-time-controls vjs-control'
});
this.content = vjs.createEl('div', {
className: 'vjs-duration-display',
innerHTML: '<span class="vjs-control-text">Duration Time </span>' + '0:00', // label the duration time for screen reader users
'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
});
el.appendChild(vjs.createEl('div').appendChild(this.content));
return el;
};
vjs.DurationDisplay.prototype.updateContent = function(){
if (this.player_.duration()) {
this.content.innerHTML = '<span class="vjs-control-text">Duration Time </span>' + vjs.formatTime(this.player_.duration()); // label the duration time for screen reader users
}
};
/**
* Time Separator (Not used in main skin, but still available, and could be used as a 'spare element')
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.TimeDivider = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.TimeDivider.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-time-divider',
innerHTML: '<div><span>/</span></div>'
});
};
/**
* Displays the time left in the video
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.RemainingTimeDisplay = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
player.on('timeupdate', vjs.bind(this, this.updateContent));
}
});
vjs.RemainingTimeDisplay.prototype.createEl = function(){
var el = vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-remaining-time vjs-time-controls vjs-control'
});
this.content = vjs.createEl('div', {
className: 'vjs-remaining-time-display',
innerHTML: '<span class="vjs-control-text">Remaining Time </span>' + '-0:00', // label the remaining time for screen reader users
'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
});
el.appendChild(vjs.createEl('div').appendChild(this.content));
return el;
};
vjs.RemainingTimeDisplay.prototype.updateContent = function(){
if (this.player_.duration()) {
if (this.player_.duration()) {
this.content.innerHTML = '<span class="vjs-control-text">Remaining Time </span>' + '-'+ vjs.formatTime(this.player_.remainingTime());
}
}
// Allows for smooth scrubbing, when player can't keep up.
// var time = (this.player_.scrubbing) ? this.player_.getCache().currentTime : this.player_.currentTime();
// this.content.innerHTML = vjs.formatTime(time, this.player_.duration());
};

View File

@ -0,0 +1,134 @@
/**
* Control the volume
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.VolumeControl = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
// hide volume controls when they're not supported by the current tech
if (player.tech && player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
}
player.on('loadstart', vjs.bind(this, function(){
if (player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');
}
}));
}
});
vjs.VolumeControl.prototype.options_ = {
children: {
'volumeBar': {}
}
};
vjs.VolumeControl.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-control vjs-control'
});
};
/**
* Contains volume level
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.VolumeBar = vjs.Slider.extend({
/** @constructor */
init: function(player, options){
vjs.Slider.call(this, player, options);
player.on('volumechange', vjs.bind(this, this.updateARIAAttributes));
player.ready(vjs.bind(this, this.updateARIAAttributes));
setTimeout(vjs.bind(this, this.update), 0); // update when elements is in DOM
}
});
vjs.VolumeBar.prototype.updateARIAAttributes = function(){
// Current value of volume bar as a percentage
this.el_.setAttribute('aria-valuenow',vjs.round(this.player_.volume()*100, 2));
this.el_.setAttribute('aria-valuetext',vjs.round(this.player_.volume()*100, 2)+'%');
};
vjs.VolumeBar.prototype.options_ = {
children: {
'volumeLevel': {},
'volumeHandle': {}
},
'barName': 'volumeLevel',
'handleName': 'volumeHandle'
};
vjs.VolumeBar.prototype.playerEvent = 'volumechange';
vjs.VolumeBar.prototype.createEl = function(){
return vjs.Slider.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-bar',
'aria-label': 'volume level'
});
};
vjs.VolumeBar.prototype.onMouseMove = function(event) {
this.player_.volume(this.calculateDistance(event));
};
vjs.VolumeBar.prototype.getPercent = function(){
if (this.player_.muted()) {
return 0;
} else {
return this.player_.volume();
}
};
vjs.VolumeBar.prototype.stepForward = function(){
this.player_.volume(this.player_.volume() + 0.1);
};
vjs.VolumeBar.prototype.stepBack = function(){
this.player_.volume(this.player_.volume() - 0.1);
};
/**
* Shows volume level
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.VolumeLevel = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.VolumeLevel.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-level',
innerHTML: '<span class="vjs-control-text"></span>'
});
};
/**
* Change volume level
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.VolumeHandle = vjs.SliderHandle.extend();
/** @inheritDoc */
vjs.VolumeHandle.prototype.defaultValue = '00:00';
/** @inheritDoc */
vjs.VolumeHandle.prototype.createEl = function(){
return vjs.SliderHandle.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-handle'
});
};

View File

@ -0,0 +1,48 @@
/**
* Menu button with a popup for showing the volume slider.
* @constructor
*/
vjs.VolumeMenuButton = vjs.MenuButton.extend({
/** @constructor */
init: function(player, options){
vjs.MenuButton.call(this, player, options);
// Same listeners as MuteToggle
player.on('volumechange', vjs.bind(this, this.update));
// hide mute toggle if the current tech doesn't support volume control
if (player.tech && player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
}
player.on('loadstart', vjs.bind(this, function(){
if (player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');
}
}));
this.addClass('vjs-menu-button');
}
});
vjs.VolumeMenuButton.prototype.createMenu = function(){
var menu = new vjs.Menu(this.player_, {
contentElType: 'div'
});
var vc = new vjs.VolumeBar(this.player_, vjs.obj.merge({vertical: true}, this.options_.volumeBar));
menu.addChild(vc);
return menu;
};
vjs.VolumeMenuButton.prototype.onClick = function(){
vjs.MuteToggle.prototype.onClick.call(this);
vjs.MenuButton.prototype.onClick.call(this);
};
vjs.VolumeMenuButton.prototype.createEl = function(){
return vjs.Button.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-menu-button vjs-menu-button vjs-control',
innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
});
};
vjs.VolumeMenuButton.prototype.update = vjs.MuteToggle.prototype.update;

738
src/js/controls.js vendored
View File

@ -1,738 +0,0 @@
/**
* @fileoverview Controls classes for Video.js buttons, sliders, etc.
*/
/* Control Bar
================================================================================ */
/**
* Container of main controls
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.ControlBar = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
if (!player.controls()) {
this.disable();
}
player.one('play', vjs.bind(this, function(){
var touchstart,
fadeIn = vjs.bind(this, this.fadeIn),
fadeOut = vjs.bind(this, this.fadeOut);
this.fadeIn();
if ( !('ontouchstart' in window) ) {
this.player_.on('mouseover', fadeIn);
this.player_.on('mouseout', fadeOut);
this.player_.on('pause', vjs.bind(this, this.lockShowing));
this.player_.on('play', vjs.bind(this, this.unlockShowing));
}
touchstart = false;
this.player_.on('touchstart', function() {
touchstart = true;
});
this.player_.on('touchmove', function() {
touchstart = false;
});
this.player_.on('touchend', vjs.bind(this, function(event) {
var idx;
if (touchstart) {
idx = this.el().className.search('fade-in');
if (idx !== -1) {
this.fadeOut();
} else {
this.fadeIn();
}
}
touchstart = false;
if (!this.player_.paused()) {
event.preventDefault();
}
}));
}));
}
});
vjs.ControlBar.prototype.options_ = {
loadEvent: 'play',
children: {
'playToggle': {},
'currentTimeDisplay': {},
'timeDivider': {},
'durationDisplay': {},
'remainingTimeDisplay': {},
'progressControl': {},
'fullscreenToggle': {},
'volumeControl': {},
'muteToggle': {}
// 'volumeMenuButton': {}
}
};
vjs.ControlBar.prototype.createEl = function(){
return vjs.createEl('div', {
className: 'vjs-control-bar'
});
};
vjs.ControlBar.prototype.fadeIn = function(){
vjs.Component.prototype.fadeIn.call(this);
this.player_.trigger('controlsvisible');
};
vjs.ControlBar.prototype.fadeOut = function(){
vjs.Component.prototype.fadeOut.call(this);
this.player_.trigger('controlshidden');
};
/* Play Toggle - Play or Pause Media
================================================================================ */
/**
* Button to toggle between play and pause
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.PlayToggle = vjs.Button.extend({
/** @constructor */
init: function(player, options){
vjs.Button.call(this, player, options);
player.on('play', vjs.bind(this, this.onPlay));
player.on('pause', vjs.bind(this, this.onPause));
}
});
vjs.PlayToggle.prototype.buttonText = 'Play';
vjs.PlayToggle.prototype.buildCSSClass = function(){
return 'vjs-play-control ' + vjs.Button.prototype.buildCSSClass.call(this);
};
// OnClick - Toggle between play and pause
vjs.PlayToggle.prototype.onClick = function(){
if (this.player_.paused()) {
this.player_.play();
} else {
this.player_.pause();
}
};
// OnPlay - Add the vjs-playing class to the element so it can change appearance
vjs.PlayToggle.prototype.onPlay = function(){
vjs.removeClass(this.el_, 'vjs-paused');
vjs.addClass(this.el_, 'vjs-playing');
this.el_.children[0].children[0].innerHTML = 'Pause'; // change the button text to "Pause"
};
// OnPause - Add the vjs-paused class to the element so it can change appearance
vjs.PlayToggle.prototype.onPause = function(){
vjs.removeClass(this.el_, 'vjs-playing');
vjs.addClass(this.el_, 'vjs-paused');
this.el_.children[0].children[0].innerHTML = 'Play'; // change the button text to "Play"
};
/* Fullscreen Toggle Behaviors
================================================================================ */
/**
* Toggle fullscreen video
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.FullscreenToggle = vjs.Button.extend({
/** @constructor */
init: function(player, options){
vjs.Button.call(this, player, options);
}
});
vjs.FullscreenToggle.prototype.buttonText = 'Fullscreen';
vjs.FullscreenToggle.prototype.buildCSSClass = function(){
return 'vjs-fullscreen-control ' + vjs.Button.prototype.buildCSSClass.call(this);
};
vjs.FullscreenToggle.prototype.onClick = function(){
if (!this.player_.isFullScreen) {
this.player_.requestFullScreen();
this.el_.children[0].children[0].innerHTML = 'Non-Fullscreen'; // change the button text to "Non-Fullscreen"
} else {
this.player_.cancelFullScreen();
this.el_.children[0].children[0].innerHTML = 'Fullscreen'; // change the button to "Fullscreen"
}
};
/* Time
================================================================================ */
/**
* Displays the current time
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.CurrentTimeDisplay = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
player.on('timeupdate', vjs.bind(this, this.updateContent));
}
});
vjs.CurrentTimeDisplay.prototype.createEl = function(){
var el = vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-current-time vjs-time-controls vjs-control'
});
this.content = vjs.createEl('div', {
className: 'vjs-current-time-display',
innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00', // label the current time for screen reader users
'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
});
el.appendChild(vjs.createEl('div').appendChild(this.content));
return el;
};
vjs.CurrentTimeDisplay.prototype.updateContent = function(){
// Allows for smooth scrubbing, when player can't keep up.
var time = (this.player_.scrubbing) ? this.player_.getCache().currentTime : this.player_.currentTime();
this.content.innerHTML = '<span class="vjs-control-text">Current Time </span>' + vjs.formatTime(time, this.player_.duration());
};
/**
* Displays the duration
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.DurationDisplay = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
player.on('timeupdate', vjs.bind(this, this.updateContent)); // this might need to be changes to 'durationchange' instead of 'timeupdate' eventually, however the durationchange event fires before this.player_.duration() is set, so the value cannot be written out using this method. Once the order of durationchange and this.player_.duration() being set is figured out, this can be updated.
}
});
vjs.DurationDisplay.prototype.createEl = function(){
var el = vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-duration vjs-time-controls vjs-control'
});
this.content = vjs.createEl('div', {
className: 'vjs-duration-display',
innerHTML: '<span class="vjs-control-text">Duration Time </span>' + '0:00', // label the duration time for screen reader users
'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
});
el.appendChild(vjs.createEl('div').appendChild(this.content));
return el;
};
vjs.DurationDisplay.prototype.updateContent = function(){
if (this.player_.duration()) {
this.content.innerHTML = '<span class="vjs-control-text">Duration Time </span>' + vjs.formatTime(this.player_.duration()); // label the duration time for screen reader users
}
};
/**
* Time Separator (Not used in main skin, but still available, and could be used as a 'spare element')
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.TimeDivider = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.TimeDivider.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-time-divider',
innerHTML: '<div><span>/</span></div>'
});
};
/**
* Displays the time left in the video
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.RemainingTimeDisplay = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
player.on('timeupdate', vjs.bind(this, this.updateContent));
}
});
vjs.RemainingTimeDisplay.prototype.createEl = function(){
var el = vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-remaining-time vjs-time-controls vjs-control'
});
this.content = vjs.createEl('div', {
className: 'vjs-remaining-time-display',
innerHTML: '<span class="vjs-control-text">Remaining Time </span>' + '-0:00', // label the remaining time for screen reader users
'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
});
el.appendChild(vjs.createEl('div').appendChild(this.content));
return el;
};
vjs.RemainingTimeDisplay.prototype.updateContent = function(){
if (this.player_.duration()) {
if (this.player_.duration()) {
this.content.innerHTML = '<span class="vjs-control-text">Remaining Time </span>' + '-'+ vjs.formatTime(this.player_.remainingTime());
}
}
// Allows for smooth scrubbing, when player can't keep up.
// var time = (this.player_.scrubbing) ? this.player_.getCache().currentTime : this.player_.currentTime();
// this.content.innerHTML = vjs.formatTime(time, this.player_.duration());
};
/* Progress
================================================================================ */
/**
* Seek, Load Progress, and Play Progress
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.ProgressControl = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.ProgressControl.prototype.options_ = {
children: {
'seekBar': {}
}
};
vjs.ProgressControl.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-progress-control vjs-control'
});
};
/**
* Seek Bar and holder for the progress bars
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.SeekBar = vjs.Slider.extend({
/** @constructor */
init: function(player, options){
vjs.Slider.call(this, player, options);
player.on('timeupdate', vjs.bind(this, this.updateARIAAttributes));
player.ready(vjs.bind(this, this.updateARIAAttributes));
}
});
vjs.SeekBar.prototype.options_ = {
children: {
'loadProgressBar': {},
'playProgressBar': {},
'seekHandle': {}
},
'barName': 'playProgressBar',
'handleName': 'seekHandle'
};
vjs.SeekBar.prototype.playerEvent = 'timeupdate';
vjs.SeekBar.prototype.createEl = function(){
return vjs.Slider.prototype.createEl.call(this, 'div', {
className: 'vjs-progress-holder',
'aria-label': 'video progress bar'
});
};
vjs.SeekBar.prototype.updateARIAAttributes = function(){
// Allows for smooth scrubbing, when player can't keep up.
var time = (this.player_.scrubbing) ? this.player_.getCache().currentTime : this.player_.currentTime();
this.el_.setAttribute('aria-valuenow',vjs.round(this.getPercent()*100, 2)); // machine readable value of progress bar (percentage complete)
this.el_.setAttribute('aria-valuetext',vjs.formatTime(time, this.player_.duration())); // human readable value of progress bar (time complete)
};
vjs.SeekBar.prototype.getPercent = function(){
return this.player_.currentTime() / this.player_.duration();
};
vjs.SeekBar.prototype.onMouseDown = function(event){
vjs.Slider.prototype.onMouseDown.call(this, event);
this.player_.scrubbing = true;
this.videoWasPlaying = !this.player_.paused();
this.player_.pause();
};
vjs.SeekBar.prototype.onMouseMove = function(event){
var newTime = this.calculateDistance(event) * this.player_.duration();
// Don't let video end while scrubbing.
if (newTime == this.player_.duration()) { newTime = newTime - 0.1; }
// Set new time (tell player to seek to new time)
this.player_.currentTime(newTime);
};
vjs.SeekBar.prototype.onMouseUp = function(event){
vjs.Slider.prototype.onMouseUp.call(this, event);
this.player_.scrubbing = false;
if (this.videoWasPlaying) {
this.player_.play();
}
};
vjs.SeekBar.prototype.stepForward = function(){
this.player_.currentTime(this.player_.currentTime() + 5); // more quickly fast forward for keyboard-only users
};
vjs.SeekBar.prototype.stepBack = function(){
this.player_.currentTime(this.player_.currentTime() - 5); // more quickly rewind for keyboard-only users
};
/**
* Shows load progres
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.LoadProgressBar = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
player.on('progress', vjs.bind(this, this.update));
}
});
vjs.LoadProgressBar.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-load-progress',
innerHTML: '<span class="vjs-control-text">Loaded: 0%</span>'
});
};
vjs.LoadProgressBar.prototype.update = function(){
if (this.el_.style) { this.el_.style.width = vjs.round(this.player_.bufferedPercent() * 100, 2) + '%'; }
};
/**
* Shows play progress
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.PlayProgressBar = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.PlayProgressBar.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-play-progress',
innerHTML: '<span class="vjs-control-text">Progress: 0%</span>'
});
};
/**
* SeekBar component includes play progress bar, and seek handle
* Needed so it can determine seek position based on handle position/size
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.SeekHandle = vjs.SliderHandle.extend();
/** @inheritDoc */
vjs.SeekHandle.prototype.defaultValue = '00:00';
/** @inheritDoc */
vjs.SeekHandle.prototype.createEl = function(){
return vjs.SliderHandle.prototype.createEl.call(this, 'div', {
className: 'vjs-seek-handle'
});
};
/**
* Control the volume
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.VolumeControl = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
// hide volume controls when they're not supported by the current tech
if (player.tech && player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
}
player.on('loadstart', vjs.bind(this, function(){
if (player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');
}
}));
}
});
vjs.VolumeControl.prototype.options_ = {
children: {
'volumeBar': {}
}
};
vjs.VolumeControl.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-control vjs-control'
});
};
/**
* Contains volume level
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.VolumeBar = vjs.Slider.extend({
/** @constructor */
init: function(player, options){
vjs.Slider.call(this, player, options);
player.on('volumechange', vjs.bind(this, this.updateARIAAttributes));
player.ready(vjs.bind(this, this.updateARIAAttributes));
setTimeout(vjs.bind(this, this.update), 0); // update when elements is in DOM
}
});
vjs.VolumeBar.prototype.updateARIAAttributes = function(){
// Current value of volume bar as a percentage
this.el_.setAttribute('aria-valuenow',vjs.round(this.player_.volume()*100, 2));
this.el_.setAttribute('aria-valuetext',vjs.round(this.player_.volume()*100, 2)+'%');
};
vjs.VolumeBar.prototype.options_ = {
children: {
'volumeLevel': {},
'volumeHandle': {}
},
'barName': 'volumeLevel',
'handleName': 'volumeHandle'
};
vjs.VolumeBar.prototype.playerEvent = 'volumechange';
vjs.VolumeBar.prototype.createEl = function(){
return vjs.Slider.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-bar',
'aria-label': 'volume level'
});
};
vjs.VolumeBar.prototype.onMouseMove = function(event) {
this.player_.volume(this.calculateDistance(event));
};
vjs.VolumeBar.prototype.getPercent = function(){
if (this.player_.muted()) {
return 0;
} else {
return this.player_.volume();
}
};
vjs.VolumeBar.prototype.stepForward = function(){
this.player_.volume(this.player_.volume() + 0.1);
};
vjs.VolumeBar.prototype.stepBack = function(){
this.player_.volume(this.player_.volume() - 0.1);
};
/**
* Shows volume level
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.VolumeLevel = vjs.Component.extend({
/** @constructor */
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.VolumeLevel.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-level',
innerHTML: '<span class="vjs-control-text"></span>'
});
};
/**
* Change volume level
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.VolumeHandle = vjs.SliderHandle.extend();
/** @inheritDoc */
vjs.VolumeHandle.prototype.defaultValue = '00:00';
/** @inheritDoc */
vjs.VolumeHandle.prototype.createEl = function(){
return vjs.SliderHandle.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-handle'
});
};
/**
* Mute the audio
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.MuteToggle = vjs.Button.extend({
/** @constructor */
init: function(player, options){
vjs.Button.call(this, player, options);
player.on('volumechange', vjs.bind(this, this.update));
// hide mute toggle if the current tech doesn't support volume control
if (player.tech && player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
}
player.on('loadstart', vjs.bind(this, function(){
if (player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');
}
}));
}
});
vjs.MuteToggle.prototype.createEl = function(){
return vjs.Button.prototype.createEl.call(this, 'div', {
className: 'vjs-mute-control vjs-control',
innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
});
};
vjs.MuteToggle.prototype.onClick = function(){
this.player_.muted( this.player_.muted() ? false : true );
};
vjs.MuteToggle.prototype.update = function(){
var vol = this.player_.volume(),
level = 3;
if (vol === 0 || this.player_.muted()) {
level = 0;
} else if (vol < 0.33) {
level = 1;
} else if (vol < 0.67) {
level = 2;
}
// 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.
if(this.player_.muted()){
if(this.el_.children[0].children[0].innerHTML!='Unmute'){
this.el_.children[0].children[0].innerHTML = 'Unmute'; // change the button text to "Unmute"
}
} else {
if(this.el_.children[0].children[0].innerHTML!='Mute'){
this.el_.children[0].children[0].innerHTML = 'Mute'; // change the button text to "Mute"
}
}
/* TODO improve muted icon classes */
for (var i = 0; i < 4; i++) {
vjs.removeClass(this.el_, 'vjs-vol-'+i);
}
vjs.addClass(this.el_, 'vjs-vol-'+level);
};
/**
* Menu button with a popup for showing the volume slider.
* @constructor
*/
vjs.VolumeMenuButton = vjs.MenuButton.extend({
/** @constructor */
init: function(player, options){
vjs.MenuButton.call(this, player, options);
// Same listeners as MuteToggle
player.on('volumechange', vjs.bind(this, this.update));
// hide mute toggle if the current tech doesn't support volume control
if (player.tech && player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
}
player.on('loadstart', vjs.bind(this, function(){
if (player.tech.features && player.tech.features.volumeControl === false) {
this.addClass('vjs-hidden');
} else {
this.removeClass('vjs-hidden');
}
}));
this.addClass('vjs-menu-button');
}
});
vjs.VolumeMenuButton.prototype.createMenu = function(){
var menu = new vjs.Menu(this.player_, {
contentElType: 'div'
});
var vc = new vjs.VolumeBar(this.player_, vjs.obj.merge({vertical: true}, this.options_.volumeBar));
menu.addChild(vc);
return menu;
};
vjs.VolumeMenuButton.prototype.onClick = function(){
vjs.MuteToggle.prototype.onClick.call(this);
vjs.MenuButton.prototype.onClick.call(this);
};
vjs.VolumeMenuButton.prototype.createEl = function(){
return vjs.Button.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-menu-button vjs-menu-button vjs-control',
innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
});
};
vjs.VolumeMenuButton.prototype.update = vjs.MuteToggle.prototype.update;