1
0
mirror of https://github.com/videojs/video.js.git synced 2024-11-28 08:58:46 +02:00

Merge branch 'master' into feature/flash-iframe

This commit is contained in:
Steve Heffernan 2012-01-28 17:27:45 -08:00
commit 372ec827a3
9 changed files with 262 additions and 142 deletions

View File

@ -1,3 +1,7 @@
* Fixed issue with volume knob position. Improved controls fading.
* Fixed ian issue with triggering fullscreen a second time.
* Fixed issue with getting attributes in Firefox 3.0
* Escaping special characters in source URL for Flash
* Added a check for if Firefox is enabled which fixes a Firefox 9 issue
* Stopped spinner from showing on 'stalled' events since browsers sometimes don't show that they've recovered.
* Fixed CDN Version which was breaking dev.html
@ -24,3 +28,6 @@ CHANGELOG
---- 3.0.7 / 2012-01-12 / fixing-ie8-poster-bug --------------------------------
* Fixed an ie8 breaking bug with the poster
---- 3.0.8 / 2012-01-23 / fix-ie-controls-hiding -------------------------------
* Fixed issue with controls not hiding in IE due to no opacity support

View File

@ -1,4 +1,4 @@
---
major: 3
patch: 7
patch: 8
minor: 0

View File

@ -48,10 +48,31 @@ body.vjs-full-window {
position: relative; width: 100%; max-height: 100%;
}
/* Subtiles Styles */
.video-js .vjs-subtitles { color: #fff; font-size: 20px; text-align: center; position: absolute; bottom: 40px; left: 0; right: 0; }
/* Fading sytles, used to fade control bar. */
.vjs-fade-in {
visibility: visible !important; /* Needed to make sure things hide in older browsers too. */
opacity: 1 !important;
-webkit-transition: visibility 0s linear 0s, opacity 0.3s linear;
-moz-transition: visibility 0s linear 0s, opacity 0.3s linear;
-ms-transition: visibility 0s linear 0s, opacity 0.3s linear;
-o-transition: visibility 0s linear 0s, opacity 0.3s linear;
transition: visibility 0s linear 0s, opacity 0.3s linear;
}
.vjs-fade-out {
visibility: hidden !important;
opacity: 0 !important;
-webkit-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
-moz-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
-ms-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
-o-transition: visibility 0s linear 1.5s,opacity 1.5s linear;
transition: visibility 0s linear 1.5s,opacity 1.5s linear;
}
/* DEFAULT SKIN (override in another file to create new skins)
================================================================================
Instead of editing this file, I recommend creating your own skin CSS file to be included after this file,
@ -62,8 +83,6 @@ so you can upgrade to newer versions easier. You can remove all these styles by
position: absolute;
bottom: 0; /* Distance from the bottom of the box/video. Keep 0. Use height to add more bottom margin. */
left: 0; right: 0; /* 100% width of div */
opacity: 0.85;
display: none; /* Start hidden */
margin: 0; padding: 0; /* Controls are absolutely position, so no padding necessary */
height: 2.6em; /* Including any margin you want above or below control items */
color: #fff; border-top: 1px solid #404040;
@ -80,12 +99,10 @@ so you can upgrade to newer versions easier. You can remove all these styles by
/*filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#242424', endColorstr='#171717',GradientType=0 );*/ /* IE6-9 */
background: linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* W3C */
/* Fade-in using CSS Transitions */
-webkit-transition: opacity 0.3s linear;
-moz-transition: opacity 0.3s linear;
-o-transition: opacity 0.3s linear;
-ms-transition: opacity 0.3s linear;
transition: opacity 0.3s linear;
/* Start hidden and with 0 opacity. Opacity is used to fade in modern browsers. */
/* Can't use display block to hide initially because widths of slider handles aren't calculated and avaialbe for positioning correctly. */
visibility: hidden;
opacity: 0;
}
/* General styles for individual controls. */

View File

@ -45,18 +45,18 @@ _V_.Component = _V_.Class.extend({
init: function(player, options){
this.player = player;
if (options && options.el) {
// Allow for overridding default component options
options = this.options = _V_.merge(this.options || {}, options);
// Create element if one wasn't provided in options
if (options.el) {
this.el = options.el;
} else {
this.el = this.createElement();
}
// Array of sub-components
if (options && options.components) {
_V_.each.call(this, options.components, function(comp){
this.addComponent(comp);
});
}
// Add any components in options
this.initComponents();
},
destroy: function(){},
@ -71,38 +71,47 @@ _V_.Component = _V_.Class.extend({
return "";
},
initComponents: function(){
var options = this.options;
if (options && options.components) {
// Loop through components and add them to the player
this.eachProp(options.components, function(name, opts){
// Allow waiting to add components until a specific event is called
var tempAdd = this.proxy(function(){
this.addComponent(name, opts);
});
if (opts.loadEvent) {
this.one(opts.loadEvent, tempAdd)
} else {
tempAdd();
}
});
}
},
// Add child components to this component.
// Will generate a new child component and then append child component's element to this component's element.
// Takes either the name of the UI component class, or an object that contains a name, UI Class, and options.
addComponent: function(nameORobj){
var name, componentClass, options, component;
addComponent: function(name, options){
var componentClass, component;
if (typeof nameORobj == "string") {
name = nameORobj;
// Make sure options is at least an empty object to protect against errors
options = options || {};
// Can also pass in object to define a different class than the name and add other options
} else {
name = nameORobj.name;
componentClass = nameORobj.componentClass;
options = nameORobj.options;
}
if (!componentClass) {
// Assume name of set is a lowercased name of the UI Class (PlayButton, etc.)
componentClass = _V_.capitalize(name);
}
// Assume name of set is a lowercased name of the UI Class (PlayButton, etc.)
componentClass = options.componentClass || _V_.capitalize(name);
// Create a new object & element for this controls set
// If there's no .player, this is a player
component = new _V_[componentClass](this.player || this, options);
if (this.components === undefined) {
this.components = [];
}
this.components.push(component);
// Add the UI object's element to the container div (box)
this.el.appendChild(component.el);
// Set property name on player. Could cause conflicts with other prop names, but it's worth making refs easy.
this[name] = component;
},
/* Display
@ -114,6 +123,16 @@ _V_.Component = _V_.Class.extend({
hide: function(){
this.el.style.display = "none";
},
fadeIn: function(){
this.removeClass("vjs-fade-out");
this.addClass("vjs-fade-in");
},
fadeOut: function(){
this.removeClass("vjs-fade-in");
this.addClass("vjs-fade-out");
},
addClass: function(classToAdd){
_V_.addClass(this.el, classToAdd);
@ -134,6 +153,9 @@ _V_.Component = _V_.Class.extend({
triggerEvent: function(type, e){
return _V_.triggerEvent(this.el, type, e);
},
one: function(type, fn) {
_V_.one.call(this, this.el, type, fn);
},
/* Ready - Trigger functions when component is ready
================================================================================ */
@ -174,6 +196,15 @@ _V_.Component = _V_.Class.extend({
}
},
eachProp: function(obj, fn){
if (!obj) { return; }
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
fn.call(this, name, obj[name]);
}
}
},
extend: function(obj){
for (var attrname in obj) {
if (obj.hasOwnProperty(attrname)) { this[attrname]=obj[attrname]; }

94
src/controls.js vendored
View File

@ -140,7 +140,7 @@ _V_.FullscreenToggle = _V_.Button.extend({
},
onClick: function(){
if (!this.player.videoIsFullScreen) {
if (!this.player.isFullScreen) {
this.player.requestFullScreen();
} else {
this.player.cancelFullScreen();
@ -222,13 +222,30 @@ _V_.LoadingSpinner = _V_.Component.extend({
/* Control Bar
================================================================================ */
_V_.ControlBar = _V_.Component.extend({
options: {
loadEvent: "play",
components: {
"playToggle": {},
"fullscreenToggle": {},
"currentTimeDisplay": {},
"timeDivider": {},
"durationDisplay": {},
"remainingTimeDisplay": {},
"progressControl": {},
"volumeControl": {},
"muteToggle": {}
}
},
init: function(player, options){
this._super(player, options);
player.addEvent("play", this.proxy(this.show));
player.addEvent("mouseover", this.proxy(this.reveal));
player.addEvent("mouseout", this.proxy(this.conceal));
player.addEvent("play", this.proxy(function(){
this.fadeIn();
this.player.addEvent("mouseover", this.proxy(this.fadeIn));
this.player.addEvent("mouseout", this.proxy(this.fadeOut));
}));
},
createElement: function(){
@ -237,13 +254,14 @@ _V_.ControlBar = _V_.Component.extend({
});
},
// Used for transitions (fading out)
reveal: function(){
this.el.style.opacity = 1;
fadeIn: function(){
this._super();
this.player.triggerEvent("controlsvisible");
},
conceal: function(){
this.el.style.opacity = 0;
fadeOut: function(){
this._super();
this.player.triggerEvent("controlshidden");
}
});
@ -358,24 +376,18 @@ _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);
// Update Display
// Need to wait for styles to be loaded.
// TODO - replace setTimeout with stylesReady function.
setTimeout(this.proxy(this.update), 0);
this.player.addEvent("controlsvisible", this.proxy(this.update));
// This is actually to fix the volume handle position. http://twitter.com/#!/gerritvanaaken/status/159046254519787520
// this.player.one("timeupdate", this.proxy(this.update));
this.update();
},
createElement: function(type, attrs) {
@ -426,12 +438,15 @@ _V_.Slider = _V_.Component.extend({
// 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,
handleWidth = handle.el.offsetWidth,
// The width of the handle in percent of the containing box
// In IE, widths may not be ready yet causing NaN
handlePercent = (handle.el.offsetWidth) ? handle.el.offsetWidth / boxWidth : 0,
handlePercent = (handleWidth) ? handleWidth / boxWidth : 0,
// 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.
@ -495,6 +510,12 @@ _V_.Slider = _V_.Component.extend({
// Progress Control: Seek, Load Progress, and Play Progress
_V_.ProgressControl = _V_.Component.extend({
options: {
components: {
"seekBar": {}
}
},
createElement: function(){
return this._super("div", {
className: "vjs-progress-control vjs-control"
@ -506,8 +527,16 @@ _V_.ProgressControl = _V_.Component.extend({
// Seek Bar and holder for the progress bars
_V_.SeekBar = _V_.Slider.extend({
barClass: "PlayProgressBar",
handleClass: "SeekHandle",
options: {
components: {
"loadProgressBar": {},
// Set property names to bar and handle to match with the parent Slider class is looking for
"bar": { componentClass: "PlayProgressBar" },
"handle": { componentClass: "SeekHandle" }
}
},
playerEvent: "timeupdate",
init: function(player, options){
@ -614,6 +643,12 @@ _V_.SeekHandle = _V_.Component.extend({
// Progress Control: Seek, Load Progress, and Play Progress
_V_.VolumeControl = _V_.Component.extend({
options: {
components: {
"volumeBar": {}
}
},
createElement: function(){
return this._super("div", {
className: "vjs-volume-control vjs-control"
@ -624,8 +659,13 @@ _V_.VolumeControl = _V_.Component.extend({
_V_.VolumeBar = _V_.Slider.extend({
barClass: "VolumeLevel",
handleClass: "VolumeHandle",
options: {
components: {
"bar": { componentClass: "VolumeLevel" },
"handle": { componentClass: "VolumeHandle" }
}
},
playerEvent: "volumechange",
createElement: function(){

View File

@ -52,9 +52,7 @@ VideoJS.options = {
// techOrder: ["flash","html5"],
html5: {},
flash: {
swf: "http://vjs.zencdn.net/c/video-js.swf"
},
flash: { swf: "http://vjs.zencdn.net/c/video-js.swf" },
// Default of web browser is 300x150. Should rely on source width/height.
width: "auto",
@ -64,44 +62,52 @@ VideoJS.options = {
defaultVolume: 0.00, // The freakin seaguls are driving me crazy!
// Included control sets
components: [
"poster",
"loadingSpinner",
"bigPlayButton",
{ name: "controlBar", options: {
components: [
"playToggle",
"fullscreenToggle",
"currentTimeDisplay",
"timeDivider",
"durationDisplay",
"remainingTimeDisplay",
{ name: "progressControl", options: {
components: [
{ name: "seekBar", options: {
components: [
"loadProgressBar",
"playProgressBar",
"seekHandle"
]}
}
]}
},
{ name: "volumeControl", options: {
components: [
{ name: "volumeBar", options: {
components: [
"volumeLevel",
"volumeHandle"
]}
}
]}
},
"muteToggle"
]
}},
"subtitlesDisplay"/*, "replay"*/
]
components: {
"poster": {},
"loadingSpinner": {},
"bigPlayButton": {},
"controlBar": {},
"subtitlesDisplay": {}
}
// components: [
// "poster",
// "loadingSpinner",
// "bigPlayButton",
// { name: "controlBar", options: {
// components: [
// "playToggle",
// "fullscreenToggle",
// "currentTimeDisplay",
// "timeDivider",
// "durationDisplay",
// "remainingTimeDisplay",
// { name: "progressControl", options: {
// components: [
// { name: "seekBar", options: {
// components: [
// "loadProgressBar",
// "playProgressBar",
// "seekHandle"
// ]}
// }
// ]}
// },
// { name: "volumeControl", options: {
// components: [
// { name: "volumeBar", options: {
// components: [
// "volumeLevel",
// "volumeHandle"
// ]}
// }
// ]}
// },
// "muteToggle"
// ]
// }},
// "subtitlesDisplay"/*, "replay"*/
// ]
};
// Set CDN Version of swf

View File

@ -226,6 +226,13 @@ _V_.extend({
// }
// }
// }
},
one: function(elem, type, fn) {
_V_.addEvent(elem, type, function(){
_V_.removeEvent(elem, type, arguments.callee)
fn.apply(this, arguments);
});
}
});

View File

@ -8,8 +8,8 @@ _V_.Player = _V_.Component.extend({
var el = this.el = _V_.createElement("div"), // Div to contain video and controls
options = this.options = {},
width = options.width = tag.width,
height = options.height = tag.height,
width = options.width = tag.getAttribute('width'),
height = options.height = tag.getAttribute('height'),
// Browsers default to 300x150 if there's no width/height or video size data.
initWidth = width || 300,
@ -81,9 +81,7 @@ _V_.Player = _V_.Component.extend({
// When the API is ready, loop through the components and add to the player.
if (options.controls) {
this.ready(function(){
this.each(this.options.components, function(set){
this.addComponent(set);
});
this.initComponents();
});
}
@ -127,34 +125,36 @@ _V_.Player = _V_.Component.extend({
tracks: []
};
options.src = this.tag.src;
options.src = this.tag.getAttribute("src");
options.controls = this.tag.getAttribute("controls") !== null;
options.poster = this.tag.poster;
options.preload = this.tag.preload;
options.poster = this.tag.getAttribute("poster");
options.preload = this.tag.getAttribute("preload");
options.autoplay = this.tag.getAttribute("autoplay") !== null; // hasAttribute not IE <8 compatible
options.loop = this.tag.getAttribute("loop") !== null;
options.muted = this.tag.getAttribute("muted") !== null;
for (var c,i=0,j=this.tag.children;i<j.length;i++) {
c = j[i];
if (c.nodeName == "SOURCE") {
options.sources.push({
src: c.src,
type: c.type,
media: c.media,
title: c.title
});
}
if (c.nodeName == "TRACK") {
options.tracks.push(new _V_.Track({
src: c.getAttribute("src"),
kind: c.getAttribute("kind"),
srclang: c.getAttribute("srclang"),
label: c.getAttribute("label"),
'default': c.getAttribute("default") !== null,
title: c.getAttribute("title")
}, this));
if (this.tag.hasChildNodes()) {
for (var c,i=0,j=this.tag.childNodes;i<j.length;i++) {
c = j[i];
if (c.nodeName == "SOURCE") {
options.sources.push({
src: c.getAttribute('src'),
type: c.getAttribute('type'),
media: c.getAttribute('media'),
title: c.getAttribute('title')
});
}
if (c.nodeName == "TRACK") {
options.tracks.push(new _V_.Track({
src: c.getAttribute("src"),
kind: c.getAttribute("kind"),
srclang: c.getAttribute("srclang"),
label: c.getAttribute("label"),
'default': c.getAttribute("default") !== null,
title: c.getAttribute("title")
}, this));
}
}
}
return options;
@ -487,6 +487,11 @@ _V_.Player = _V_.Component.extend({
this.el[requestFullScreen.requestFn]();
}
// In case the user presses escape to exit fullscreen, we need to update fullscreen status
_V_.addEvent(document, requestFullScreen.eventName, this.proxy(function(){
this.isFullScreen = document[requestFullScreen.isFullScreen];
}));
} else if (this.tech.supportsFullScreen()) {
this.apiCall("enterFullScreen");
@ -494,7 +499,7 @@ _V_.Player = _V_.Component.extend({
this.enterFullWindow();
}
this.videoIsFullScreen = true;
this.isFullScreen = true;
this.triggerEvent("fullscreenchange");
return this;
@ -515,7 +520,6 @@ _V_.Player = _V_.Component.extend({
_V_.addEvent(document, requestFullScreen.eventName, this.proxy(function(){
_V_.removeEvent(document, requestFullScreen.eventName, arguments.callee);
_V_.log("document fullscreeneventchange")
this.loadTech(this.techName, { src: this.values.src })
}));
@ -532,14 +536,14 @@ _V_.Player = _V_.Component.extend({
this.exitFullWindow();
}
this.videoIsFullScreen = false;
this.isFullScreen = false;
this.triggerEvent("fullscreenchange");
return this;
},
enterFullWindow: function(){
this.videoIsFullWindow = true;
this.isFullWindow = true;
// Storing original doc overflow value to return to when fullscreen is off
this.docOrigOverflow = document.documentElement.style.overflow;
@ -559,7 +563,7 @@ _V_.Player = _V_.Component.extend({
fullWindowOnEscKey: function(event){
if (event.keyCode == 27) {
if (this.videoIsFullScreen == true) {
if (this.isFullScreen == true) {
this.cancelFullScreen();
} else {
this.exitFullWindow();
@ -568,7 +572,7 @@ _V_.Player = _V_.Component.extend({
},
exitFullWindow: function(){
this.videoIsFullWindow = false;
this.isFullWindow = false;
_V_.removeEvent(document, "keydown", this.fullWindowOnEscKey);
// Unhide scroll bars.
@ -729,6 +733,8 @@ _V_.Player = _V_.Component.extend({
(function(){
var requestFn,
cancelFn,
eventName,
isFullScreen,
playerProto = _V_.Player.prototype;
// Current W3C Spec
@ -738,6 +744,7 @@ _V_.Player = _V_.Component.extend({
requestFn = "requestFullscreen";
cancelFn = "exitFullscreen";
eventName = "fullscreenchange";
isFullScreen = "fullScreen";
// Webkit (Chrome/Safari) and Mozilla (Firefox) have working implementaitons
// that use prefixes and vary slightly from the new W3C spec. Specifically, using 'exit' instead of 'cancel',
@ -753,7 +760,12 @@ _V_.Player = _V_.Component.extend({
cancelFn = prefix + "CancelFullScreen";
eventName = prefix + "fullscreenchange";
}
if (prefix == "webkit") {
isFullScreen = prefix + "IsFullScreen";
} else {
isFullScreen = prefix + "FullScreen";
}
});
}

View File

@ -288,7 +288,7 @@ _V_.flash = _V_.PlaybackTech.extend({
// If source was supplied pass as a flash var.
if (source) {
flashVars.src = source.src;
flashVars.src = encodeURIComponent(source.src);
}
// Add to box.
@ -618,4 +618,4 @@ _V_.flash.getEmbedCode = function(swf, flashVars, params, attributes){
});
return objTag + attrsString + '>' + paramsString + '</object>';
}
}