2011-11-29 11:40:05 -08:00
|
|
|
/* Control - Base class for all control elements
|
|
|
|
================================================================================ */
|
|
|
|
_V_.Control = _V_.Component.extend({
|
|
|
|
|
|
|
|
buildCSSClass: function(){
|
|
|
|
return "vjs-control " + this._super();
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Button - Base class for all buttons
|
|
|
|
================================================================================ */
|
|
|
|
_V_.Button = _V_.Control.extend({
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
this.addEvent("click", this.onClick);
|
|
|
|
this.addEvent("focus", this.onFocus);
|
|
|
|
this.addEvent("blur", this.onBlur);
|
2011-11-29 11:40:05 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(type, attrs){
|
|
|
|
// Add standard Aria and Tabindex info
|
|
|
|
attrs = _V_.merge({
|
2011-12-05 11:28:18 -08:00
|
|
|
className: this.buildCSSClass(),
|
|
|
|
innerHTML: '<div><span class="vjs-control-text">' + (this.buttonText || "Need Text") + '</span></div>',
|
2011-11-29 11:40:05 -08:00
|
|
|
role: "button",
|
|
|
|
tabIndex: 0
|
2011-12-05 11:28:18 -08:00
|
|
|
}, attrs);
|
2011-11-29 11:40:05 -08:00
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
return this._super(type, attrs);
|
2011-11-29 11:40:05 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// Click - Override with specific functionality for button
|
|
|
|
onClick: function(){},
|
|
|
|
|
|
|
|
// Focus - Add keyboard functionality to element
|
|
|
|
onFocus: function(){
|
|
|
|
_V_.addEvent(document, "keyup", _V_.proxy(this, this.onKeyPress));
|
|
|
|
},
|
|
|
|
|
|
|
|
// KeyPress (document level) - Trigger click when keys are pressed
|
|
|
|
onKeyPress: function(event){
|
|
|
|
// Check for space bar (32) or enter (13) keys
|
|
|
|
if (event.which == 32 || event.which == 13) {
|
|
|
|
event.preventDefault();
|
|
|
|
this.onClick();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// Blur - Remove keyboard triggers
|
|
|
|
onBlur: function(){
|
|
|
|
_V_.removeEvent(document, "keyup", _V_.proxy(this, this.onKeyPress));
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Play Button
|
|
|
|
================================================================================ */
|
|
|
|
_V_.PlayButton = _V_.Button.extend({
|
|
|
|
|
|
|
|
buttonText: "Play",
|
|
|
|
|
|
|
|
buildCSSClass: function(){
|
|
|
|
return "vjs-play-button " + this._super();
|
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function(){
|
|
|
|
this.player.play();
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Pause Button
|
|
|
|
================================================================================ */
|
|
|
|
_V_.PauseButton = _V_.Button.extend({
|
|
|
|
|
|
|
|
buttonText: "Pause",
|
|
|
|
|
|
|
|
buildCSSClass: function(){
|
|
|
|
return "vjs-pause-button " + this._super();
|
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function(){
|
|
|
|
this.player.pause();
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Play Toggle - Play or Pause Media
|
|
|
|
================================================================================ */
|
|
|
|
_V_.PlayToggle = _V_.Button.extend({
|
|
|
|
|
|
|
|
buttonText: "Play",
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
player.addEvent("play", _V_.proxy(this, this.onPlay));
|
|
|
|
player.addEvent("pause", _V_.proxy(this, this.onPause));
|
|
|
|
},
|
|
|
|
|
|
|
|
buildCSSClass: function(){
|
|
|
|
return "vjs-play-control " + this._super();
|
|
|
|
},
|
|
|
|
|
|
|
|
// OnClick - Toggle between play and pause
|
|
|
|
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
|
|
|
|
onPlay: function(){
|
|
|
|
_V_.removeClass(this.el, "vjs-paused");
|
|
|
|
_V_.addClass(this.el, "vjs-playing");
|
|
|
|
},
|
|
|
|
|
|
|
|
// OnPause - Add the vjs-paused class to the element so it can change appearance
|
|
|
|
onPause: function(){
|
|
|
|
_V_.removeClass(this.el, "vjs-playing");
|
|
|
|
_V_.addClass(this.el, "vjs-paused");
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/* Fullscreen Toggle Behaviors
|
|
|
|
================================================================================ */
|
|
|
|
_V_.FullscreenToggle = _V_.Button.extend({
|
|
|
|
|
|
|
|
buttonText: "Fullscreen",
|
|
|
|
|
|
|
|
buildCSSClass: function(){
|
|
|
|
return "vjs-fullscreen-control " + this._super();
|
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function(){
|
2012-01-27 12:22:05 -08:00
|
|
|
if (!this.player.isFullScreen) {
|
2012-01-05 23:25:09 -08:00
|
|
|
this.player.requestFullScreen();
|
2011-11-29 11:40:05 -08:00
|
|
|
} else {
|
2012-01-05 23:25:09 -08:00
|
|
|
this.player.cancelFullScreen();
|
2011-11-29 11:40:05 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Big Play Button
|
|
|
|
================================================================================ */
|
|
|
|
_V_.BigPlayButton = _V_.Button.extend({
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
player.addEvent("play", _V_.proxy(this, this.hide));
|
|
|
|
player.addEvent("ended", _V_.proxy(this, this.show));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-big-play-button",
|
|
|
|
innerHTML: "<span></span>"
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function(){
|
2012-01-02 13:02:04 -08:00
|
|
|
// Go back to the beginning if big play button is showing at the end.
|
|
|
|
// Have to check for current time otherwise it might throw a 'not ready' error.
|
|
|
|
if(this.player.currentTime()) {
|
|
|
|
this.player.currentTime(0);
|
|
|
|
}
|
2011-11-29 11:40:05 -08:00
|
|
|
this.player.play();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-12-21 17:59:36 -08:00
|
|
|
/* Loading Spinner
|
|
|
|
================================================================================ */
|
|
|
|
_V_.LoadingSpinner = _V_.Component.extend({
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
player.addEvent("canplay", _V_.proxy(this, this.hide));
|
|
|
|
player.addEvent("canplaythrough", _V_.proxy(this, this.hide));
|
|
|
|
player.addEvent("playing", _V_.proxy(this, this.hide));
|
|
|
|
|
|
|
|
player.addEvent("seeking", _V_.proxy(this, this.show));
|
|
|
|
player.addEvent("error", _V_.proxy(this, this.show));
|
2012-01-16 15:42:31 -08:00
|
|
|
|
|
|
|
// Not showing spinner on stalled any more. Browsers may stall and then not trigger any events that would remove the spinner.
|
|
|
|
// Checked in Chrome 16 and Safari 5.1.2. http://help.videojs.com/discussions/problems/883-why-is-the-download-progress-showing
|
|
|
|
// player.addEvent("stalled", _V_.proxy(this, this.show));
|
|
|
|
|
2011-12-21 17:59:36 -08:00
|
|
|
player.addEvent("waiting", _V_.proxy(this, this.show));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
|
|
|
|
var classNameSpinner, innerHtmlSpinner;
|
|
|
|
|
|
|
|
if ( typeof this.player.el.style.WebkitBorderRadius == "string"
|
|
|
|
|| typeof this.player.el.style.MozBorderRadius == "string"
|
|
|
|
|| typeof this.player.el.style.KhtmlBorderRadius == "string"
|
|
|
|
|| typeof this.player.el.style.borderRadius == "string")
|
|
|
|
{
|
|
|
|
classNameSpinner = "vjs-loading-spinner";
|
|
|
|
innerHtmlSpinner = "<div class='ball1'></div><div class='ball2'></div><div class='ball3'></div><div class='ball4'></div><div class='ball5'></div><div class='ball6'></div><div class='ball7'></div><div class='ball8'></div>";
|
|
|
|
} else {
|
|
|
|
classNameSpinner = "vjs-loading-spinner-fallback";
|
|
|
|
innerHtmlSpinner = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._super("div", {
|
|
|
|
className: classNameSpinner,
|
|
|
|
innerHTML: innerHtmlSpinner
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-11-29 11:40:05 -08:00
|
|
|
/* Control Bar
|
|
|
|
================================================================================ */
|
|
|
|
_V_.ControlBar = _V_.Component.extend({
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
2012-01-02 16:57:17 -08:00
|
|
|
player.addEvent("play", this.proxy(this.show));
|
|
|
|
|
|
|
|
player.addEvent("mouseover", this.proxy(this.reveal));
|
|
|
|
player.addEvent("mouseout", this.proxy(this.conceal));
|
2011-11-29 11:40:05 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return _V_.createElement("div", {
|
|
|
|
className: "vjs-controls"
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2012-01-02 16:57:17 -08:00
|
|
|
// Used for transitions (fading out)
|
|
|
|
reveal: function(){
|
2011-11-29 11:40:05 -08:00
|
|
|
this.el.style.opacity = 1;
|
2012-01-23 16:00:51 -08:00
|
|
|
|
|
|
|
// IE doesn't support opacity, so use display instead
|
|
|
|
if ( !('opacity' in document.body.style) ) {
|
|
|
|
this.show();
|
|
|
|
}
|
2011-11-29 11:40:05 -08:00
|
|
|
},
|
|
|
|
|
2012-01-02 16:57:17 -08:00
|
|
|
conceal: function(){
|
2011-11-29 11:40:05 -08:00
|
|
|
this.el.style.opacity = 0;
|
2012-01-23 16:00:51 -08:00
|
|
|
if ( !('opacity' in document.body.style) ) {
|
|
|
|
this.hide();
|
|
|
|
}
|
2011-11-29 11:40:05 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Time
|
|
|
|
================================================================================ */
|
|
|
|
_V_.CurrentTimeDisplay = _V_.Component.extend({
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
player.addEvent("timeupdate", _V_.proxy(this, this.updateContent));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
var el = this._super("div", {
|
|
|
|
className: "vjs-current-time vjs-time-controls vjs-control"
|
|
|
|
});
|
|
|
|
|
|
|
|
this.content = _V_.createElement("div", {
|
|
|
|
className: "vjs-current-time-display",
|
|
|
|
innerHTML: '0:00'
|
|
|
|
});
|
|
|
|
|
|
|
|
el.appendChild(_V_.createElement("div").appendChild(this.content));
|
|
|
|
return el;
|
|
|
|
},
|
|
|
|
|
|
|
|
updateContent: function(){
|
|
|
|
// Allows for smooth scrubbing, when player can't keep up.
|
|
|
|
var time = (this.player.scrubbing) ? this.player.values.currentTime : this.player.currentTime();
|
|
|
|
this.content.innerHTML = _V_.formatTime(time, this.player.duration());
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
_V_.DurationDisplay = _V_.Component.extend({
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
player.addEvent("timeupdate", _V_.proxy(this, this.updateContent));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
var el = this._super("div", {
|
|
|
|
className: "vjs-duration vjs-time-controls vjs-control"
|
|
|
|
});
|
|
|
|
|
|
|
|
this.content = _V_.createElement("div", {
|
|
|
|
className: "vjs-duration-display",
|
|
|
|
innerHTML: '0:00'
|
|
|
|
});
|
|
|
|
|
|
|
|
el.appendChild(_V_.createElement("div").appendChild(this.content));
|
|
|
|
return el;
|
|
|
|
},
|
|
|
|
|
|
|
|
updateContent: function(){
|
|
|
|
if (this.player.duration()) { this.content.innerHTML = _V_.formatTime(this.player.duration()); }
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// Time Separator (Not used in main skin, but still available, and could be used as a 'spare element')
|
|
|
|
_V_.TimeDivider = _V_.Component.extend({
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-time-divider",
|
|
|
|
innerHTML: '<div><span>/</span></div>'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
_V_.RemainingTimeDisplay = _V_.Component.extend({
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
player.addEvent("timeupdate", _V_.proxy(this, this.updateContent));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
var el = this._super("div", {
|
|
|
|
className: "vjs-remaining-time vjs-time-controls vjs-control"
|
|
|
|
});
|
|
|
|
|
|
|
|
this.content = _V_.createElement("div", {
|
|
|
|
className: "vjs-remaining-time-display",
|
|
|
|
innerHTML: '-0:00'
|
|
|
|
});
|
|
|
|
|
|
|
|
el.appendChild(_V_.createElement("div").appendChild(this.content));
|
|
|
|
return el;
|
|
|
|
},
|
|
|
|
|
|
|
|
updateContent: function(){
|
|
|
|
if (this.player.duration()) { this.content.innerHTML = "-"+_V_.formatTime(this.player.remainingTime()); }
|
|
|
|
|
|
|
|
// Allows for smooth scrubbing, when player can't keep up.
|
|
|
|
// var time = (this.player.scrubbing) ? this.player.values.currentTime : this.player.currentTime();
|
|
|
|
// this.content.innerHTML = _V_.formatTime(time, this.player.duration());
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
/* Slider - Parent for seek bar and volume slider
|
|
|
|
================================================================================ */
|
|
|
|
_V_.Slider = _V_.Component.extend({
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
_V_.each.call(this, this.components, function(comp){
|
|
|
|
if (comp instanceof _V_[this.barClass]) {
|
|
|
|
this.bar = comp;
|
|
|
|
} else if (comp instanceof _V_[this.handleClass]) {
|
|
|
|
this.handle = comp;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
player.addEvent(this.playerEvent, _V_.proxy(this, this.update));
|
|
|
|
|
|
|
|
this.addEvent("mousedown", this.onMouseDown);
|
|
|
|
this.addEvent("focus", this.onFocus);
|
|
|
|
this.addEvent("blur", this.onBlur);
|
2011-12-08 17:33:44 -08:00
|
|
|
|
|
|
|
// Update Display
|
|
|
|
// Need to wait for styles to be loaded.
|
|
|
|
// TODO - replace setTimeout with stylesReady function.
|
|
|
|
setTimeout(this.proxy(this.update), 0);
|
2011-12-05 11:28:18 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(type, attrs) {
|
|
|
|
attrs = _V_.merge({
|
2011-12-21 17:59:36 -08:00
|
|
|
role: "slider",
|
2011-12-05 11:28:18 -08:00
|
|
|
"aria-valuenow": 0,
|
|
|
|
"aria-valuemin": 0,
|
|
|
|
"aria-valuemax": 100,
|
|
|
|
tabIndex: 0
|
|
|
|
}, attrs);
|
|
|
|
|
|
|
|
return this._super(type, attrs);
|
|
|
|
},
|
|
|
|
|
|
|
|
onMouseDown: function(event){
|
|
|
|
event.preventDefault();
|
|
|
|
_V_.blockTextSelection();
|
|
|
|
|
|
|
|
_V_.addEvent(document, "mousemove", _V_.proxy(this, this.onMouseMove));
|
|
|
|
_V_.addEvent(document, "mouseup", _V_.proxy(this, this.onMouseUp));
|
|
|
|
|
|
|
|
this.onMouseMove(event);
|
|
|
|
},
|
|
|
|
|
|
|
|
onMouseUp: function(event) {
|
|
|
|
_V_.unblockTextSelection();
|
|
|
|
_V_.removeEvent(document, "mousemove", this.onMouseMove, false);
|
|
|
|
_V_.removeEvent(document, "mouseup", this.onMouseUp, false);
|
2011-12-21 17:59:36 -08:00
|
|
|
|
|
|
|
this.update();
|
2011-12-05 11:28:18 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
update: function(){
|
|
|
|
// 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.values.currentTime / this.player.duration() : this.player.currentTime() / this.player.duration();
|
|
|
|
|
2012-01-09 16:28:26 -08:00
|
|
|
var barProgress,
|
|
|
|
progress = this.getPercent();
|
2011-12-05 11:28:18 -08:00
|
|
|
handle = this.handle,
|
2012-01-09 16:28:26 -08:00
|
|
|
bar = this.bar;
|
2011-12-05 11:28:18 -08:00
|
|
|
|
|
|
|
// Protect against no duration and other division issues
|
|
|
|
if (isNaN(progress)) { progress = 0; }
|
|
|
|
|
2012-01-09 16:28:26 -08:00
|
|
|
barProgress = progress;
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
// If there is a handle, we need to account for the handle in our calculation for progress bar
|
|
|
|
// so that it doesn't fall short of or extend past the handle.
|
|
|
|
if (handle) {
|
|
|
|
var box = this.el,
|
|
|
|
boxWidth = box.offsetWidth,
|
|
|
|
|
|
|
|
// The width of the handle in percent of the containing box
|
2012-01-09 16:28:26 -08:00
|
|
|
// In IE, widths may not be ready yet causing NaN
|
|
|
|
handlePercent = (handle.el.offsetWidth) ? handle.el.offsetWidth / boxWidth : 0,
|
2011-12-05 11:28:18 -08:00
|
|
|
|
|
|
|
// Get the adjusted size of the box, considering that the handle's center never touches the left or right side.
|
|
|
|
// There is a margin of half the handle's width on both sides.
|
|
|
|
boxAdjustedPercent = 1 - handlePercent;
|
|
|
|
|
|
|
|
// Adjust the progress that we'll use to set widths to the new adjusted box width
|
|
|
|
adjustedProgress = progress * boxAdjustedPercent,
|
|
|
|
|
|
|
|
// The bar does reach the left side, so we need to account for this in the bar's width
|
|
|
|
barProgress = adjustedProgress + (handlePercent / 2);
|
|
|
|
|
|
|
|
// Move the handle from the left based on the adjected progress
|
|
|
|
handle.el.style.left = _V_.round(adjustedProgress * 100, 2) + "%";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the new bar width
|
|
|
|
bar.el.style.width = _V_.round(barProgress * 100, 2) + "%";
|
|
|
|
},
|
|
|
|
|
|
|
|
calculateDistance: function(event){
|
|
|
|
var box = this.el,
|
|
|
|
boxX = _V_.findPosX(box),
|
|
|
|
boxW = box.offsetWidth,
|
|
|
|
handle = this.handle;
|
|
|
|
|
|
|
|
if (handle) {
|
|
|
|
var handleW = handle.el.offsetWidth;
|
|
|
|
|
|
|
|
// Adjusted X and Width, so handle doesn't go outside the bar
|
|
|
|
boxX = boxX + (handleW / 2);
|
|
|
|
boxW = boxW - handleW;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Percent that the click is through the adjusted area
|
|
|
|
return Math.max(0, Math.min(1, (event.pageX - boxX) / boxW));
|
|
|
|
},
|
|
|
|
|
|
|
|
onFocus: function(event){
|
|
|
|
_V_.addEvent(document, "keyup", _V_.proxy(this, this.onKeyPress));
|
|
|
|
},
|
|
|
|
|
|
|
|
onKeyPress: function(event){
|
|
|
|
if (event.which == 37) { // Left Arrow
|
|
|
|
event.preventDefault();
|
|
|
|
this.stepBack();
|
|
|
|
} else if (event.which == 39) { // Right Arrow
|
|
|
|
event.preventDefault();
|
|
|
|
this.stepForward();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onBlur: function(event){
|
|
|
|
_V_.removeEvent(document, "keyup", _V_.proxy(this, this.onKeyPress));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2011-11-29 11:40:05 -08:00
|
|
|
/* Progress
|
|
|
|
================================================================================ */
|
|
|
|
|
|
|
|
// Progress Control: Seek, Load Progress, and Play Progress
|
|
|
|
_V_.ProgressControl = _V_.Component.extend({
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-progress-control vjs-control"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// Seek Bar and holder for the progress bars
|
2011-12-05 11:28:18 -08:00
|
|
|
_V_.SeekBar = _V_.Slider.extend({
|
|
|
|
|
|
|
|
barClass: "PlayProgressBar",
|
|
|
|
handleClass: "SeekHandle",
|
|
|
|
playerEvent: "timeupdate",
|
2011-11-29 11:40:05 -08:00
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-progress-holder"
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
getPercent: function(){
|
|
|
|
return this.player.currentTime() / this.player.duration();
|
2011-11-29 11:40:05 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
onMouseDown: function(event){
|
2011-12-05 11:28:18 -08:00
|
|
|
this._super(event);
|
2011-11-29 11:40:05 -08:00
|
|
|
|
|
|
|
this.player.scrubbing = true;
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
this.videoWasPlaying = !this.player.paused();
|
2011-11-29 11:40:05 -08:00
|
|
|
this.player.pause();
|
|
|
|
},
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
onMouseMove: function(event){
|
|
|
|
var newTime = this.calculateDistance(event) * this.player.duration();
|
2011-11-29 11:40:05 -08:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
},
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
onMouseUp: function(event){
|
|
|
|
this._super(event);
|
|
|
|
|
2011-11-29 11:40:05 -08:00
|
|
|
this.player.scrubbing = false;
|
2011-12-05 11:28:18 -08:00
|
|
|
if (this.videoWasPlaying) {
|
2011-11-29 11:40:05 -08:00
|
|
|
this.player.play();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
stepForward: function(){
|
|
|
|
this.player.currentTime(this.player.currentTime() + 1);
|
2011-11-29 11:40:05 -08:00
|
|
|
},
|
2011-12-05 11:28:18 -08:00
|
|
|
|
|
|
|
stepBack: function(){
|
|
|
|
this.player.currentTime(this.player.currentTime() - 1);
|
2011-11-29 11:40:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// Load Progress Bar
|
|
|
|
_V_.LoadProgressBar = _V_.Component.extend({
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
player.addEvent("progress", _V_.proxy(this, this.update));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-load-progress",
|
|
|
|
innerHTML: '<span class="vjs-control-text">Loaded: 0%</span>'
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function(){
|
|
|
|
if (this.el.style) { this.el.style.width = _V_.round(this.player.bufferedPercent() * 100, 2) + "%"; }
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// Play Progress Bar
|
|
|
|
_V_.PlayProgressBar = _V_.Component.extend({
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-play-progress",
|
|
|
|
innerHTML: '<span class="vjs-control-text">Progress: 0%</span>'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// Seek Handle
|
|
|
|
// SeekBar Behavior includes play progress bar, and seek handle
|
|
|
|
// Needed so it can determine seek position based on handle position/size
|
|
|
|
_V_.SeekHandle = _V_.Component.extend({
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-seek-handle",
|
2011-12-05 11:28:18 -08:00
|
|
|
innerHTML: '<span class="vjs-control-text">00:00</span>'
|
2011-11-29 11:40:05 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Volume Scrubber
|
|
|
|
================================================================================ */
|
|
|
|
// Progress Control: Seek, Load Progress, and Play Progress
|
|
|
|
_V_.VolumeControl = _V_.Component.extend({
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-volume-control vjs-control"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
_V_.VolumeBar = _V_.Slider.extend({
|
2011-11-29 11:40:05 -08:00
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
barClass: "VolumeLevel",
|
|
|
|
handleClass: "VolumeHandle",
|
|
|
|
playerEvent: "volumechange",
|
2011-11-29 11:40:05 -08:00
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-volume-bar"
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
onMouseMove: function(event) {
|
|
|
|
this.player.volume(this.calculateDistance(event));
|
2011-11-29 11:40:05 -08:00
|
|
|
},
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
getPercent: function(){
|
|
|
|
return this.player.volume();
|
2011-11-29 11:40:05 -08:00
|
|
|
},
|
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
stepForward: function(){
|
|
|
|
this.player.volume(this.player.volume() + 0.1);
|
|
|
|
},
|
2011-11-29 11:40:05 -08:00
|
|
|
|
2011-12-05 11:28:18 -08:00
|
|
|
stepBack: function(){
|
|
|
|
this.player.volume(this.player.volume() - 0.1);
|
|
|
|
}
|
2011-11-29 11:40:05 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
_V_.VolumeLevel = _V_.Component.extend({
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-volume-level",
|
|
|
|
innerHTML: '<span class="vjs-control-text"></span>'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
_V_.VolumeHandle = _V_.Component.extend({
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-volume-handle",
|
2011-12-05 11:28:18 -08:00
|
|
|
innerHTML: '<span class="vjs-control-text"></span>'
|
|
|
|
// tabindex: 0,
|
|
|
|
// role: "slider", "aria-valuenow": 0, "aria-valuemin": 0, "aria-valuemax": 100
|
2011-11-29 11:40:05 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
_V_.MuteToggle = _V_.Button.extend({
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
player.addEvent("volumechange", _V_.proxy(this, this.update));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
|
|
|
className: "vjs-mute-control vjs-control",
|
|
|
|
innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function(event){
|
|
|
|
this.player.muted( this.player.muted() ? false : true );
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function(event){
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO improve muted icon classes */
|
|
|
|
_V_.each.call(this, [0,1,2,3], function(i){
|
|
|
|
_V_.removeClass(this.el, "vjs-vol-"+i);
|
|
|
|
});
|
|
|
|
_V_.addClass(this.el, "vjs-vol-"+level);
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2012-01-02 16:57:17 -08:00
|
|
|
/* Poster Image
|
|
|
|
================================================================================ */
|
|
|
|
_V_.Poster = _V_.Button.extend({
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
2012-01-05 23:25:09 -08:00
|
|
|
if (!this.player.options.poster) {
|
|
|
|
this.hide();
|
|
|
|
}
|
|
|
|
|
2012-01-02 16:57:17 -08:00
|
|
|
player.addEvent("play", _V_.proxy(this, this.hide));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
2012-01-12 17:39:25 -08:00
|
|
|
return _V_.createElement("img", {
|
2012-01-02 16:57:17 -08:00
|
|
|
className: "vjs-poster",
|
|
|
|
src: this.player.options.poster,
|
|
|
|
|
|
|
|
// Don't want poster to be tabbable.
|
|
|
|
tabIndex: -1
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function(){
|
|
|
|
this.player.play();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2011-11-29 11:40:05 -08:00
|
|
|
/* Text Track Displays
|
|
|
|
================================================================================ */
|
|
|
|
// Create a behavior type for each text track type (subtitlesDisplay, captionsDisplay, etc.).
|
|
|
|
// Then you can easily do something like.
|
|
|
|
// player.addBehavior(myDiv, "subtitlesDisplay");
|
|
|
|
// And the myDiv's content will be updated with the text change.
|
|
|
|
|
|
|
|
// Base class for all track displays. Should not be instantiated on its own.
|
|
|
|
_V_.TextTrackDisplay = _V_.Component.extend({
|
|
|
|
|
|
|
|
init: function(player, options){
|
|
|
|
this._super(player, options);
|
|
|
|
|
|
|
|
player.addEvent(this.trackType + "update", _V_.proxy(this, this.update));
|
|
|
|
},
|
|
|
|
|
|
|
|
createElement: function(){
|
|
|
|
return this._super("div", {
|
2011-11-29 16:39:43 -08:00
|
|
|
className: "vjs-" + this.trackType
|
2011-11-29 11:40:05 -08:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function(){
|
|
|
|
this.el.innerHTML = this.player.textTrackValue(this.trackType);
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
_V_.SubtitlesDisplay = _V_.TextTrackDisplay.extend({
|
|
|
|
|
|
|
|
trackType: "subtitles"
|
|
|
|
|
|
|
|
});
|
2011-12-05 11:28:18 -08:00
|
|
|
|
2011-11-29 11:40:05 -08:00
|
|
|
_V_.CaptionsDisplay = _V_.TextTrackDisplay.extend({
|
|
|
|
|
|
|
|
trackType: "captions"
|
|
|
|
|
|
|
|
});
|
2011-12-05 11:28:18 -08:00
|
|
|
|
2011-11-29 11:40:05 -08:00
|
|
|
_V_.ChaptersDisplay = _V_.TextTrackDisplay.extend({
|
|
|
|
|
|
|
|
trackType: "chapters"
|
|
|
|
|
|
|
|
});
|
2011-12-05 11:28:18 -08:00
|
|
|
|
2011-11-29 11:40:05 -08:00
|
|
|
_V_.DescriptionsDisplay = _V_.TextTrackDisplay.extend({
|
|
|
|
|
|
|
|
trackType: "descriptions"
|
|
|
|
|
|
|
|
});
|