1
0
mirror of https://github.com/videojs/video.js.git synced 2025-07-17 01:42:41 +02:00

MAJOR REFACTOR: Restrcutured code into class-based components. Setup and configuring should still work the same, but controls and tech elements are now sub-classes for Component, which builds elements and applies event behaviors.

Removed demo source code from repository root. Was receiving pull requests for the release files instead of source files. This makes it more clear where to make changes.
This commit is contained in:
Steve Heffernan
2011-11-29 11:40:05 -08:00
parent a314c0a242
commit ef321a8072
87 changed files with 2315 additions and 6436 deletions

382
src/tech.js Normal file
View File

@ -0,0 +1,382 @@
/* Playback Technology - Base class for playback technologies
================================================================================ */
_V_.PlaybackTech = _V_.Component.extend({
init: function(player, options){
// this._super(player, options);
// Make playback element clickable
// _V_.addEvent(this.el, "click", _V_.proxy(this, _V_.PlayToggle.prototype.onClick));
// player.triggerEvent("techready");
},
createElement: function(){},
setupTriggers: function(){},
removeTriggers: function(){}
});
// Create placeholder methods for each that warn when a method
// isn't supported by the current playback technology
_V_.apiMethods = "play,pause,paused,currentTime,setCurrentTime,duration,buffered,volume,setVolume,muted,setMuted,width,height,supportsFullScreen,enterFullScreen,src,load,currentSrc,preload,setPreload,autoplay,setAutoplay,loop,setLoop,error,networkState,readyState,seeking,initialTime,startOffsetTime,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks,defaultPlaybackRate,playbackRate,mediaGroup,controller,controls,defaultMuted".split(",");
_V_.each(_V_.apiMethods, function(methodName){
_V_.PlaybackTech.prototype[methodName] = function(){
throw new Error("The '"+method+"' method is not available on the playback technology's API");
}
});
/* HTML5 Playback Technology - Wrapper for HTML5 Media API
================================================================================ */
_V_.HTML5 = _V_.PlaybackTech.extend({
name: "HTML5",
init: function(player, options){
this.player = player;
this.el = this.createElement();
var source = options.source;
if (this.el.currentSrc != source.src) {
this.el.src = source.src;
} else {
player.triggerEvent("loadstart");
}
// Moving video inside box breaks autoplay on Safari. This forces it to play.
// Currently triggering play in other browsers as well.
player.addEvent("techready", function(){
if (this.options.autoplay && this.paused()) {
this.play();
}
this.removeEvent("techready", arguments.callee);
});
// Trigger tech ready on player.
// TODO: Switch to component ready when available.
setTimeout(_V_.proxy(this, function(){
this.player.triggerEvent("techready");
}), 0);
},
createElement: function(){
var el = this.player.tag, // Reuse original tag for HTML5 playback technology element
html5 = _V_.HTML5,
playerOptions = this.player.options;
// 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 (html5.supports.movingElementInDOM === false) {
var newEl = _V_.createElement("video", {
id: el.id,
className: el.className
});
player.el.removeChild(el);
el = newEl;
player.el.appendChild(el);
}
// Update tag settings, in case they were overridden
_V_.each(["autoplay","preload","loop","muted","poster"], function(attr){
el[attr] = playerOptions[attr];
}, this);
return el;
},
setupTriggers: function(){
// Make video events trigger player events
// May seem verbose here, but makes other APIs possible.
// ["play", "playing", "pause", "ended", "volumechange", "error", "progress", "seeking", "timeupdate"]
var types = _V_.HTML5.events,
i;
for (i = 0;i<types.length; i++) {
_V_.addEvent(this.el, types[i], _V_.proxy(this.player, function(e){
e.stopPropagation();
this.triggerEvent(e);
}));
}
},
removeTriggers: function(){},
play: function(){ this.el.play(); },
pause: function(){ this.el.pause(); },
paused: function(){ return this.el.paused; },
currentTime: function(){ return this.el.currentTime; },
setCurrentTime: function(seconds){
try { this.el.currentTime = seconds; }
catch(e) {
_V_.log(e);
// this.warning(VideoJS.warnings.videoNotReady);
}
},
duration: function(){ return this.el.duration || 0; },
buffered: function(){ return this.el.buffered; },
volume: function(){ return this.el.volume; },
setVolume: function(percentAsDecimal){ this.el.volume = percentAsDecimal; },
muted: function(){ return this.el.muted; },
setMuted: function(muted){ this.el.muted = muted },
width: function(){ return this.el.offsetWidth; },
height: function(){ return this.el.offsetHeight; },
supportsFullScreen: function(){
if(typeof this.el.webkitEnterFullScreen == 'function') {
// Seems to be broken in Chromium/Chrome && Safari in Leopard
if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) {
return true;
}
}
return false;
},
enterFullScreen: function(){
try {
this.el.webkitEnterFullScreen();
} catch (e) {
if (e.code == 11) {
// this.warning(VideoJS.warnings.videoNotReady);
_V_.log("VideoJS: Video not ready.")
}
}
},
src: function(src){ this.el.src = src; },
load: function(){ this.el.load(); },
currentSrc: function(){ return this.el.currentSrc; },
preload: function(){ return this.el.preload; },
setPreload: function(val){ this.el.preload = val; },
autoplay: function(){ return this.el.autoplay; },
setAutoplay: function(val){ this.el.autoplay = val; },
loop: function(){ return this.el.loop; },
setLoop: function(val){ this.el.loop = val; },
error: function(){ return this.el.error; },
networkState: function(){ return this.el.networkState; },
readyState: function(){ return this.el.readyState; },
seeking: function(){ return this.el.seeking; },
initialTime: function(){ return this.el.initialTime; },
startOffsetTime: function(){ return this.el.startOffsetTime; },
played: function(){ return this.el.played; },
seekable: function(){ return this.el.seekable; },
ended: function(){ return this.el.ended; },
videoTracks: function(){ return this.el.videoTracks; },
audioTracks: function(){ return this.el.audioTracks; },
videoWidth: function(){ return this.el.videoWidth; },
videoHeight: function(){ return this.el.videoHeight; },
textTracks: function(){ return this.el.textTracks; },
defaultPlaybackRate: function(){ return this.el.defaultPlaybackRate; },
playbackRate: function(){ return this.el.playbackRate; },
mediaGroup: function(){ return this.el.mediaGroup; },
controller: function(){ return this.el.controller; },
controls: function(){ return this.player.options.controls; },
defaultMuted: function(){ return this.el.defaultMuted; }
});
/* HTML5 Support Testing -------------------------------------------------------- */
_V_.HTML5.isSupported = function(){
return !!document.createElement("video").canPlayType;
};
_V_.HTML5.canPlaySource = function(srcObj){
return !!document.createElement("video").canPlayType(srcObj.type); // Switch to global check
// TODO: Check Type
// If no Type, check ext
// Check Media Type
};
_V_.HTML5.supports = {};
// List of all HTML5 events (various uses).
_V_.HTML5.events = "loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange".split(",");
/* HTML5 Device Fixes ---------------------------------------------------------- */
// iOS
if (_V_.isIOS()) {
// If you move a video element in the DOM, it breaks video playback.
_V_.HTML5.supports.movingElementInDOM = false;
}
// Android
if (_V_.isAndroid()) {
// Override Android 2.2 and less canPlayType method which is broken
if (_V_.androidVersion() < 3) {
document.createElement("video").constructor.prototype.canPlayType = function(type){
return (type && type.toLowerCase().indexOf("video/mp4") != -1) ? "maybe" : "";
};
}
}
/* H5SWF - Custom Flash Player with HTML5-ish API
================================================================================ */
_V_.H5swf = _V_.PlaybackTech.extend({
name: "H5swf",
// swf: "flash/video-js.swf",
swf: "https://s3.amazonaws.com/video-js/3.0b/video-js.swf",
// swf: "http://video-js.zencoder.com/3.0b/video-js.swf",
init: function(player, options){
this.player = player;
// this.el = this.createElement();
var source = options.source,
placeHolder = _V_.createElement("div", { id: player.el.id + "_temp_h5swf" }),
objId = player.el.id+"_h5swf_api",
flashvars = {
readyFunction: "_V_.H5swf.onSWFReady",
eventProxyFunction: "_V_.H5swf.onSWFEvent",
errorEventProxyFunction: "_V_.H5swf.onSWFErrorEvent",
src: source.src,
autoplay: player.options.autoplay,
preload: player.options.preload,
loop: player.options.loop,
muted: player.options.muted,
poster: player.options.poster,
},
params = {
allowScriptAccess: "always",
wmode: "opaque",
bgcolor: "#000000"
},
attributes = {
id: objId,
name: objId,
'class': 'vjs-tech'
};
player.el.appendChild(placeHolder);
swfobject.embedSWF(options.swf || this.swf, placeHolder.id, "480", "270", "9.0.124", "", flashvars, params, attributes);
},
setupTriggers: function(){
// Using global onSWFEvent func to distribute events
},
play: function(){ this.el.vjs_play(); },
pause: function(){ this.el.vjs_pause(); },
src: function(src){ this.el.vjs_src(src); },
load: function(){ this.el.vjs_load(); },
poster: function(){ this.el.vjs_getProperty("poster"); },
buffered: function(){
return _V_.createTimeRange(0, this.el.vjs_getProperty("buffered"));
},
supportsFullScreen: function(){
return false; // Flash does not allow fullscreen through javascript
},
enterFullScreen: function(){
return false;
}
});
// Create setters and getters for attributes
(function(){
var api = _V_.H5swf.prototype,
readWrite = "src,preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted".split(","),
readOnly = "error,currentSrc,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks".split(","),
callOnly = "load,play,pause".split(",");
// Overridden: buffered
createSetter = function(attr){
var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
api["set"+attrUpper] = function(val){ return this.el.vjs_setProperty(attr, val); };
},
createGetter = function(attr){
api[attr] = function(){ return this.el.vjs_getProperty(attr); };
};
// Create getter and setters for all read/write attributes
_V_.each(readWrite, function(attr){
createGetter(attr);
createSetter(attr);
});
// Create getters for read-only attributes
_V_.each(readOnly, function(attr){
createGetter(attr);
});
})();
/* Flash Support Testing -------------------------------------------------------- */
_V_.H5swf.isSupported = function(){
return swfobject.hasFlashPlayerVersion("9");
};
_V_.H5swf.canPlaySource = function(srcObj){
if (srcObj.type in _V_.H5swf.supports.format) { return "maybe"; }
};
_V_.H5swf.supports = {
format: {
"video/flv": "FLV",
"video/x-flv": "FLV",
"video/mp4": "MP4",
"video/m4v": "MP4"
},
// Optional events that we can manually mimic with timers
event: {
progress: false,
timeupdate: false
}
};
_V_.H5swf.onSWFReady = function(currSwf){
// Flash seems to be catching errors, so raising them manally
try {
// Delay for real swf ready.
setTimeout(function(){
var el = _V_.el(currSwf),
// Get player from box
player = el.parentNode.player;
el.player = player;
// Update reference to playback technology element
player.techs["H5swf"].el = el;
player.ready(function(){
// this.src("http://video-js.zencoder.com/oceans-clip.mp4");
});
player.triggerEvent("techready");
},0);
} catch(err) {
_V_.log(err);
}
};
_V_.H5swf.onSWFEvent = function(swfID, eventName, other){
try {
var player = _V_.el(swfID).player;
if (player) {
player.triggerEvent(eventName);
}
} catch(err) {
_V_.log(err);
}
};
_V_.H5swf.onSWFErrorEvent = function(swfID, eventName){
_V_.log("Flash (H5SWF) Error", eventName);
};