1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-25 02:42:10 +02:00

Organized API and files.

Abstracted player API from video API.
Started on Control fading through CSS transitions.
This commit is contained in:
Steve Heffernan 2010-12-29 09:19:09 -08:00
parent 54308bb616
commit 98a3d991a0
13 changed files with 359 additions and 344 deletions

View File

@ -133,6 +133,9 @@ Changelog
---------
2.0.3
- Feature: Made returning to the start at the end of the movie an option ("returnToStart").
- Feature: Added loop option to loop movie ("loop").
- Feature: Reorganized player API and listeners.
- Feature: Setup method now has a callback, so you can more easily work with the player after setup
- Changes: setupAllWhenReady is now just setupAll (backward compatible)

View File

@ -3,6 +3,7 @@
cat src/_begin.js > combined.js
cat src/main.js >> combined.js
cat src/api.js >> combined.js
cat src/html5.js >> combined.js
cat src/flash.js >> combined.js
cat src/behaviors.js >> combined.js

View File

@ -5,10 +5,13 @@
<title>HTML5 Video Player: flowplayer</title>
<!-- Include the VideoJS Library -->
<script src="../src/video.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/main.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/api.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/html5.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/flash.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/behaviors.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/lib.js" type="text/javascript" charset="utf-8"></script>
<script src="../src/video-js.jquery.js" type="text/javascript" charset="utf-8"></script>
<!-- <script type="text/javascript" src="flowplayer-3.2.4.min.js"></script> -->
<script type="text/javascript" src="video-js.flowplayer.js"></script>

View File

@ -20,80 +20,43 @@ VideoJS.flashPlayers.flowplayer = {
},
api: {
play: function(){ this.flowplayer.play(); return this; },
onPlay: function(fn){
this.flowplayer.onStart(fn.context(this));
this.flowplayer.onResume(fn.context(this));
return this;
setupTriggers: function(){
this.flowplayer.onStart(function(e){ this.triggerListeners("play", e); }.context(this));
this.flowplayer.onResume(function(e){ this.triggerListeners("play", e); }.context(this));
this.flowplayer.onPause(function(e){ this.triggerListeners("pause", e); }.context(this));
this.flowplayer.onVolume(function(e){ this.triggerListeners("volumechange", e); }.context(this));
this.flowplayer.onError(function(e){ this.triggerListeners("error", e); }.context(this));
},
pause: function(){ this.flowplayer.pause(); return this; },
onPause: function(fn){
this.flowplayer.onPause(fn.context(this));
return this;
},
play: function(){ this.flowplayer.play(); },
pause: function(){ this.flowplayer.pause(); },
paused: function(){
return !this.flowplayer.isPlaying(); // More accurate than isPaused
},
currentTime: function(seconds){
if (seconds !== undefined) {
this.flowplayer.seek(seconds);
return this;
}
return this.flowplayer.getTime();
},
currentTime: function(){ return this.flowplayer.getTime(); },
setCurrentTime: function(seconds){ this.flowplayer.seek(seconds); },
duration: function(){
var clip = this.flowplayer.getClip();
if (clip) { return clip.duration; }
return (clip) ? clip.duration : 0;
},
buffered: function(){
var status = this.flowplayer.getStatus();
return [status.bufferStart, status.bufferEnd]
return this.createTimeRange(status.bufferStart, status.bufferEnd);
},
volume: function(percentAsDecimal){
if (percentAsDecimal !== undefined) {
this.values.volume = parseFloat(percentAsDecimal);
this.flowplayer.setVolume(parseInt(percentAsDecimal * 100));
this.setLocalStorage("volume", this.values.volume);
return this;
}
return this.flowplayer.getVolume() / 100;
},
onVolumeChange: function(fn){ this.flowplayer.onVolume(fn.context(this)); return this; },
width: function(width){
if (width !== undefined) {
this.flashElement.width = width;
this.box.style.width = width+"px";
this.triggerResizeListeners();
return this;
}
return parseInt(this.flashElement.getAttribute("width"));
},
height: function(height){
if (height !== undefined) {
this.flashElement.height = height;
this.box.style.height = height+"px";
this.triggerResizeListeners();
return this;
}
return parseInt(this.flashElement.getAttribute("height"));
},
volume: function(){ return this.flowplayer.getVolume() / 100; },
setVolume: function(percentAsDecimal){ this.flowplayer.setVolume(parseInt(percentAsDecimal * 100)); },
supportsFullScreen: function(){
return false; // Flash does not allow fullscreen through javascript
// Maybe at click listener, and say "click screen".
},
enterFullScreen: function(){
this.flowplayer.toggleFullscreen();
return this;
},
enterFullScreen: function(){ this.flowplayer.toggleFullscreen(); },
onError: function(fn){ this.flowplayer.onError(fn); return this; },
readError: function(eventArguments){
var errorCode = arguments[0],
errorMessage = arguments[1];

150
dev/src/api.js Normal file
View File

@ -0,0 +1,150 @@
/* Player API
================================================================================ */
VideoJS.fn.extend({
/* Listener types: play, pause, timeupdate, bufferedupdate, ended, volumechange, error */
addListener: function(type, fn){
if (!this.listeners[type]) { this.listeners[type] = []; }
this.listeners[type].push(fn);
},
triggerListeners: function(type, e){
this.each(this.listeners[type], function(listener){
listener.call(this, e);
});
},
removeListener: function(type, fn){
var listeners = this.listeners[type];
if (!listeners) { return; }
for (var i=0; i<listeners.length; i++) {
if (listeners[i] == fn) {
listeners.splice(i--, 1);
}
}
},
play: function(){ this.api.play.apply(this); return this; },
pause: function(){ this.api.pause.apply(this); return this; },
paused: function(){ return this.api.paused.apply(this); },
currentTime: function(seconds){
if (seconds !== undefined) {
this.values.currentTime = seconds; // Cache the last set value for smoother scrubbing.
this.api.setCurrentTime.call(this, seconds);
return this;
}
return this.api.currentTime.apply(this);
},
duration: function(){ return this.api.duration.apply(this); },
buffered: function(){
var buffered = this.api.buffered.apply(this),
start = 0, end = this.values.bufferEnd = this.values.bufferEnd || 0,
timeRange;
if (buffered && buffered.length > 0 && buffered.end(0) > end) {
end = buffered.end(0);
// Storing values allows them be overridden by setBufferedFromProgress
this.values.bufferEnd = end;
}
return this.createTimeRange(start, end);
},
// Mimic HTML5 TimeRange Spec.
createTimeRange: function(start, end){
return {
length: 1,
start: function() { return start; },
end: function() { return end; }
};
},
// Calculates amount of buffer is full
bufferedPercent: function(){ return (this.duration()) ? this.buffered().end(0) / this.duration() : 0; },
volume: function(percentAsDecimal){
if (percentAsDecimal !== undefined) {
var vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1
this.values.volume = vol;
this.api.setVolume.call(this, vol);
this.setLocalStorage("volume", vol);
return this;
}
// if (this.values.volume) { return this.values.volume; }
return this.api.volume.call(this);
},
width: function(width, skipListeners){
if (width !== undefined) {
this.element.width = width; // Not using style so it can be overridden on fullscreen.
this.box.style.width = width+"px";
if (!skipListeners) { this.triggerListeners("resize"); }
return this;
}
return parseInt(this.element.getAttribute("width"));
// return this.element.offsetWidth;
},
height: function(height){
if (height !== undefined) {
this.element.height = height;
this.box.style.height = height+"px";
this.triggerListeners("resize");
return this;
}
return parseInt(this.element.getAttribute("height"));
// return this.element.offsetHeight;
},
size: function(width, height){
// Skip resize listeners on width for optimization
return this.width(width, true).height(height);
},
supportsFullScreen: function(){ return this.api.supportsFullScreen.call(this); },
// Turn on fullscreen (or window) mode
enterFullScreen: function(){
if (this.supportsFullScreen()) {
this.api.enterFullScreen.call(this);
} else {
this.enterFullWindow();
}
return this;
},
exitFullScreen: function(){
if (!this.supportsFullScreen()) {
this.exitFullWindow();
}
// Otherwise Shouldn't be called since native fullscreen uses own controls.
return this;
},
enterFullWindow: function(){
this.videoIsFullScreen = true;
// Storing original doc overflow value to return to when fullscreen is off
this.docOrigOverflow = document.documentElement.style.overflow;
// Add listener for esc key to exit fullscreen
_V_.addListener(document, "keydown", this.fullscreenOnEscKey.rEvtContext(this));
// Add listener for a window resize
_V_.addListener(window, "resize", this.fullscreenOnWindowResize.rEvtContext(this));
// Hide any scroll bars
document.documentElement.style.overflow = 'hidden';
// Apply fullscreen styles
_V_.addClass(this.box, "vjs-fullscreen");
// Resize the box, controller, and poster
this.positionAll();
},
exitFullWindow: function(){
this.videoIsFullScreen = false;
document.removeEventListener("keydown", this.fullscreenOnEscKey, false);
window.removeEventListener("resize", this.fullscreenOnWindowResize, false);
// Unhide scroll bars.
document.documentElement.style.overflow = this.docOrigOverflow;
// Remove fullscreen styles
_V_.removeClass(this.box, "vjs-fullscreen");
// Resize the box, controller, and poster to original sizes
this.positionAll();
}
});

View File

@ -6,82 +6,61 @@
/* Player Behaviors - How VideoJS reacts to what the video is doing.
================================================================================ */
VideoJS.fn.newBehavior("player", function(player){
this.onError(this.playerOnVideoError);
// Listen for when the video is played
this.onPlay(this.playerOnVideoPlay);
this.onPlay(this.trackCurrentTime);
// Listen for when the video is paused
this.onPause(this.playerOnVideoPause);
this.onPause(this.stopTrackingCurrentTime);
// Listen for when the video ends
this.onEnded(this.playerOnVideoEnded);
// Set interval for load progress using buffer watching method
// this.trackCurrentTime();
this.addListener("error", this.playerOnVideoError);
this.addListener("play", this.playerOnVideoPlay);
this.addListener("play", this.trackCurrentTime);
this.addListener("pause", this.stopTrackingCurrentTime);
this.addListener("ended", this.playerOnVideoEnded);
this.trackBuffered();
// Buffer Full
this.onBufferedUpdate(this.isBufferFull);
this.addListener("bufferedupdate", this.bufferFull);
},{
playerOnVideoError: function(event){
this.log(event);
this.log(this.video.error);
},
playerOnVideoPlay: function(event){ this.hasPlayed = true; },
playerOnVideoPause: function(event){},
playerOnVideoEnded: function(event){
this.currentTime(0);
this.pause();
if (this.options.loop) {
this.currentTime(0);
this.play();
} else if (this.options.returnToStart) {
this.currentTime(0);
this.pause();
}
},
/* Load Tracking -------------------------------------------------------------- */
// Buffer watching method for load progress.
// Used for browsers that don't support the progress event
trackBuffered: function(){
this.bufferedInterval = setInterval(this.triggerBufferedListeners.context(this), 500);
this.bufferedInterval = setInterval(function(){
// Don't trigger unless
if (this.values.bufferEnd < this.buffered().end(0)) {
this.triggerListeners("bufferedupdate");
}
}.context(this), 500);
},
stopTrackingBuffered: function(){ clearInterval(this.bufferedInterval); },
bufferedListeners: [],
onBufferedUpdate: function(fn){
this.bufferedListeners.push(fn);
},
triggerBufferedListeners: function(){
this.isBufferFull();
this.each(this.bufferedListeners, function(listener){
(listener.context(this))();
});
},
isBufferFull: function(){
if (this.bufferedPercent() == 1) { this.stopTrackingBuffered(); }
bufferFull: function(){
if (this.bufferedPercent() == 1) {
this.stopTrackingBuffered();
}
},
/* Time Tracking -------------------------------------------------------------- */
trackCurrentTime: function(){
if (this.currentTimeInterval) { clearInterval(this.currentTimeInterval); }
this.currentTimeInterval = setInterval(this.triggerCurrentTimeListeners.context(this), 100); // 42 = 24 fps
this.currentTimeInterval = setInterval(function(){
this.triggerListeners("timeupdate");
}.context(this), 100); // 42 = 24 fps
this.trackingCurrentTime = true;
},
// Turn off play progress tracking (when paused or dragging)
stopTrackingCurrentTime: function(){
clearInterval(this.currentTimeInterval);
this.trackingCurrentTime = false;
},
currentTimeListeners: [],
// onCurrentTimeUpdate is in API section now
triggerCurrentTimeListeners: function(late, newTime){ // FF passes milliseconds late as the first argument
this.each(this.currentTimeListeners, function(listener){
(listener.context(this))(newTime || this.currentTime());
});
},
/* Resize Tracking -------------------------------------------------------------- */
resizeListeners: [],
onResize: function(fn){
this.resizeListeners.push(fn);
},
// Trigger anywhere the video/box size is changed.
triggerResizeListeners: function(){
this.each(this.resizeListeners, function(listener){
(listener.context(this))();
});
}
}
);
@ -116,8 +95,8 @@ VideoJS.fn.newBehavior("box", function(element){
this.positionBox();
_V_.addClass(element, "vjs-paused");
this.activateElement(element, "mouseOverVideoReporter");
this.onPlay(this.boxOnVideoPlay);
this.onPause(this.boxOnVideoPause);
this.addListener("play", this.boxOnVideoPlay);
this.addListener("pause", this.boxOnVideoPause);
},{
boxOnVideoPlay: function(){
_V_.removeClass(this.box, "vjs-paused");
@ -134,9 +113,9 @@ VideoJS.fn.newBehavior("box", function(element){
VideoJS.fn.newBehavior("poster", function(element){
this.activateElement(element, "mouseOverVideoReporter");
this.activateElement(element, "playButton");
this.onPlay(this.hidePoster);
this.onEnded(this.showPoster);
this.onResize(this.positionPoster);
this.addListener("play", this.hidePoster);
this.addListener("ended", this.showPoster);
this.addListener("resize", this.positionPoster);
},{
showPoster: function(){
if (!this.poster) { return; }
@ -168,7 +147,7 @@ VideoJS.fn.newBehavior("poster", function(element){
VideoJS.fn.newBehavior("controlBar", function(element){
if (!this.controlBars) {
this.controlBars = [];
this.onResize(this.positionControlBars);
this.addListener("resize", this.positionControlBars);
}
this.controlBars.push(element);
_V_.addListener(element, "mousemove", this.onControlBarsMouseMove.context(this));
@ -177,6 +156,7 @@ VideoJS.fn.newBehavior("controlBar", function(element){
showControlBars: function(){
if (!this.options.controlsAtStart && !this.hasPlayed) { return; }
this.each(this.controlBars, function(bar){
// bar.style.opacity = 1;
bar.style.display = "block";
});
},
@ -188,6 +168,7 @@ VideoJS.fn.newBehavior("controlBar", function(element){
hideControlBars: function(){
if (this.options.controlsHiding && !this.mouseIsOverControls) {
this.each(this.controlBars, function(bar){
// bar.style.opacity = 0;
bar.style.display = "none";
});
}
@ -205,8 +186,8 @@ VideoJS.fn.newBehavior("controlBar", function(element){
VideoJS.fn.newBehavior("playToggle", function(element){
if (!this.elements.playToggles) {
this.elements.playToggles = [];
this.onPlay(this.playTogglesOnPlay);
this.onPause(this.playTogglesOnPause);
this.addListener("play", this.playTogglesOnPlay);
this.addListener("pause", this.playTogglesOnPause);
}
this.elements.playToggles.push(element);
_V_.addListener(element, "click", this.onPlayToggleClick.context(this));
@ -249,17 +230,17 @@ VideoJS.fn.newBehavior("pauseButton", function(element){
/* Play Progress Bar Behaviors
================================================================================ */
VideoJS.fn.newBehavior("playProgressBar", function(element){
if (!this.playProgressBars) {
this.playProgressBars = [];
this.onCurrentTimeUpdate(this.updatePlayProgressBars);
if (!this.elements.playProgressBars) {
this.elements.playProgressBars = [];
this.addListener("timeupdate", this.updatePlayProgressBars);
}
this.playProgressBars.push(element);
this.elements.playProgressBars.push(element);
},{
// Ajust the play progress bar's width based on the current play time
updatePlayProgressBars: function(newTime){
var progress = (newTime !== undefined) ? newTime / this.duration() : this.currentTime() / this.duration();
var progress = (this.scrubbing) ? this.values.currentTime / this.duration() : this.currentTime() / this.duration();
if (isNaN(progress)) { progress = 0; }
this.each(this.playProgressBars, function(bar){
this.each(this.elements.playProgressBars, function(bar){
if (bar.style) { bar.style.width = _V_.round(progress * 100, 2) + "%"; }
});
}
@ -268,12 +249,12 @@ VideoJS.fn.newBehavior("playProgressBar", function(element){
/* Load Progress Bar Behaviors
================================================================================ */
VideoJS.fn.newBehavior("loadProgressBar", function(element){
if (!this.loadProgressBars) { this.loadProgressBars = []; }
this.loadProgressBars.push(element);
this.onBufferedUpdate(this.updateLoadProgressBars);
if (!this.elements.loadProgressBars) { this.elements.loadProgressBars = []; }
this.elements.loadProgressBars.push(element);
this.addListener("bufferedupdate", this.updateLoadProgressBars);
},{
updateLoadProgressBars: function(){
this.each(this.loadProgressBars, function(bar){
this.each(this.elements.loadProgressBars, function(bar){
if (bar.style) { bar.style.width = _V_.round(this.bufferedPercent() * 100, 2) + "%"; }
});
}
@ -283,18 +264,17 @@ VideoJS.fn.newBehavior("loadProgressBar", function(element){
/* Current Time Display Behaviors
================================================================================ */
VideoJS.fn.newBehavior("currentTimeDisplay", function(element){
if (!this.currentTimeDisplays) {
this.currentTimeDisplays = [];
this.onCurrentTimeUpdate(this.updateCurrentTimeDisplays);
if (!this.elements.currentTimeDisplays) {
this.elements.currentTimeDisplays = [];
this.addListener("timeupdate", this.updateCurrentTimeDisplays);
}
this.currentTimeDisplays.push(element);
this.elements.currentTimeDisplays.push(element);
},{
// Update the displayed time (00:00)
updateCurrentTimeDisplays: function(newTime){
if (!this.currentTimeDisplays) { return; }
// Allows for smooth scrubbing, when player can't keep up.
var time = (newTime) ? newTime : this.currentTime();
this.each(this.currentTimeDisplays, function(dis){
var time = (this.scrubbing) ? this.values.currentTime : this.currentTime();
this.each(this.elements.currentTimeDisplays, function(dis){
dis.innerHTML = _V_.formatTime(time);
});
}
@ -304,15 +284,14 @@ VideoJS.fn.newBehavior("currentTimeDisplay", function(element){
/* Duration Display Behaviors
================================================================================ */
VideoJS.fn.newBehavior("durationDisplay", function(element){
if (!this.durationDisplays) {
this.durationDisplays = [];
this.onCurrentTimeUpdate(this.updateDurationDisplays);
if (!this.elements.durationDisplays) {
this.elements.durationDisplays = [];
this.addListener("timeupdate", this.updateDurationDisplays);
}
this.durationDisplays.push(element);
this.elements.durationDisplays.push(element);
},{
updateDurationDisplays: function(){
if (!this.durationDisplays) { return; }
this.each(this.durationDisplays, function(dis){
this.each(this.elements.durationDisplays, function(dis){
if (this.duration()) { dis.innerHTML = _V_.formatTime(this.duration()); }
});
}
@ -330,6 +309,7 @@ VideoJS.fn.newBehavior("currentTimeScrubber", function(element){
this.currentScrubber = scrubber;
this.stopTrackingCurrentTime(); // Allows for smooth scrubbing
this.scrubbing = true;
this.videoWasPlaying = !this.paused();
this.pause();
@ -346,6 +326,7 @@ VideoJS.fn.newBehavior("currentTimeScrubber", function(element){
_V_.unblockTextSelection();
document.removeEventListener("mousemove", this.onCurrentTimeScrubberMouseMove, false);
document.removeEventListener("mouseup", this.onCurrentTimeScrubberMouseUp, false);
this.scrubbing = false;
if (this.videoWasPlaying) {
this.play();
this.trackCurrentTime();
@ -354,28 +335,28 @@ VideoJS.fn.newBehavior("currentTimeScrubber", function(element){
setCurrentTimeWithScrubber: function(event){
var newProgress = _V_.getRelativePosition(event.pageX, this.currentScrubber);
var newTime = newProgress * this.duration();
this.triggerCurrentTimeListeners(0, newTime); // Allows for smooth scrubbing
// Don't let video end while scrubbing.
if (newTime == this.duration()) { newTime = newTime - 0.1; }
this.currentTime(newTime);
this.triggerListeners("timeupdate");
}
}
);
/* Volume Display Behaviors
================================================================================ */
VideoJS.fn.newBehavior("volumeDisplay", function(element){
if (!this.volumeDisplays) {
this.volumeDisplays = [];
this.onVolumeChange(this.updateVolumeDisplays);
if (!this.elements.volumeDisplays) {
this.elements.volumeDisplays = [];
this.addListener("volumechange", this.updateVolumeDisplays);
}
this.volumeDisplays.push(element);
this.elements.volumeDisplays.push(element);
this.updateVolumeDisplay(element); // Set the display to the initial volume
},{
// Update the volume control display
// Unique to these default controls. Uses borders to create the look of bars.
updateVolumeDisplays: function(){
if (!this.volumeDisplays) { return; }
this.each(this.volumeDisplays, function(dis){
if (!this.elements.volumeDisplays) { return; }
this.each(this.elements.volumeDisplays, function(dis){
this.updateVolumeDisplay(dis);
});
},
@ -450,14 +431,12 @@ VideoJS.fn.newBehavior("fullscreenToggle", function(element){
VideoJS.fn.newBehavior("bigPlayButton", function(element){
if (!this.elements.bigPlayButtons) {
this.elements.bigPlayButtons = [];
this.onPlay(this.bigPlayButtonsOnPlay);
this.onEnded(this.bigPlayButtonsOnEnded);
this.addListener("play", this.hideBigPlayButtons);
this.addListener("ended", this.showBigPlayButtons);
}
this.elements.bigPlayButtons.push(element);
this.activateElement(element, "playButton");
},{
bigPlayButtonsOnPlay: function(event){ this.hideBigPlayButtons(); },
bigPlayButtonsOnEnded: function(event){ this.showBigPlayButtons(); },
showBigPlayButtons: function(){
this.each(this.elements.bigPlayButtons, function(element){
element.style.display = "block";
@ -475,6 +454,7 @@ VideoJS.fn.newBehavior("bigPlayButton", function(element){
VideoJS.fn.newBehavior("spinner", function(element){
if (!this.spinners) {
this.spinners = [];
this.spinnersRotated = 0;
_V_.addListener(this.video, "loadeddata", this.spinnersOnVideoLoadedData.context(this));
_V_.addListener(this.video, "loadstart", this.spinnersOnVideoLoadStart.context(this));
_V_.addListener(this.video, "seeking", this.spinnersOnVideoSeeking.context(this));
@ -502,7 +482,6 @@ VideoJS.fn.newBehavior("spinner", function(element){
});
clearInterval(this.spinnerInterval);
},
spinnersRotated: 0,
rotateSpinners: function(){
this.each(this.spinners, function(spinner){
// spinner.style.transform = 'scale(0.5) rotate('+this.spinnersRotated+'deg)';
@ -520,7 +499,7 @@ VideoJS.fn.newBehavior("spinner", function(element){
spinnersOnVideoCanPlayThrough: function(event){ this.hideSpinners(); },
spinnersOnVideoWaiting: function(event){
// Safari sometimes triggers waiting inappropriately
// Like after video has played, any you play again.
// Like after video has played, and you play again.
this.showSpinners();
},
spinnersOnVideoStalled: function(event){},
@ -535,16 +514,17 @@ VideoJS.fn.newBehavior("spinner", function(element){
/* Subtitles
================================================================================ */
VideoJS.fn.newBehavior("subtitlesDisplay", function(element){
if (!this.subtitleDisplays) {
this.subtitleDisplays = [];
this.onCurrentTimeUpdate(this.subtitleDisplaysOnVideoTimeUpdate);
this.onEnded(function() { this.lastSubtitleIndex = 0; }.context(this));
if (!this.elements.subtitleDisplays) {
this.elements.subtitleDisplays = [];
this.addListener("timeupdate", this.subtitleDisplaysOnVideoTimeUpdate);
this.addListener("ended", function() { this.lastSubtitleIndex = 0; }.context(this));
}
this.subtitleDisplays.push(element);
this.elements.subtitleDisplays.push(element);
},{
subtitleDisplaysOnVideoTimeUpdate: function(time){
subtitleDisplaysOnVideoTimeUpdate: function(){
// Assuming all subtitles are in order by time, and do not overlap
if (this.subtitles) {
var time = this.currentTime();
// If current subtitle should stay showing, don't do anything. Otherwise, find new subtitle.
if (!this.currentSubtitle || this.currentSubtitle.start >= time || this.currentSubtitle.end < time) {
var newSubIndex = false,
@ -588,7 +568,7 @@ VideoJS.fn.newBehavior("subtitlesDisplay", function(element){
}
},
updateSubtitleDisplays: function(val){
this.each(this.subtitleDisplays, function(disp){
this.each(this.elements.subtitleDisplays, function(disp){
disp.innerHTML = val;
});
}

View File

@ -12,9 +12,10 @@ VideoJS.fn.extend({
this.replaceWithFlash();
this.element = this.flashElement;
this.video.src = ""; // Stop video from downloading if HTML5 is still supported
var flashPlayerType = VideoJS.flashPlayers[this.options.flashPlayer];
this.extend(flashPlayerType.api);
(flashPlayerType.init.context(this))();
var flashPlayer = VideoJS.flashPlayers[this.options.flashPlayer];
flashPlayer.init.call(this);
this.api = flashPlayer.api;
this.api.setupTriggers.call(this);
},
// Get Flash Fallback object element from Embed Code
@ -43,32 +44,12 @@ VideoJS.fn.extend({
}
});
/* Flash Object Fallback (Flash Player Type)
/* Flash Object Fallback (Flash Player)
================================================================================ */
VideoJS.flashPlayers = {};
VideoJS.flashPlayers.htmlObject = {
flashPlayerVersion: 9,
init: function() { return true; },
api: { // No video API available with HTML Object embed method
api: {} // No video API available with HTML Object embed method
};
width: function(width){
if (width !== undefined) {
this.element.width = width;
this.box.style.width = width+"px";
this.triggerResizeListeners();
return this;
}
return this.element.width;
},
height: function(height){
if (height !== undefined) {
this.element.height = height;
this.box.style.height = height+"px";
this.triggerResizeListeners();
return this;
}
return this.element.height;
}
}
};

View File

@ -12,6 +12,8 @@ VideoJS.fn.extend({
html5Init: function(){
this.element = this.video;
this.api = this.html5API;
this.api.setupTriggers.call(this);
this.fixPreloading(); // Support old browsers that used autobuffer
this.supportProgressEvents(); // Support browsers that don't use 'buffered'
@ -104,15 +106,18 @@ VideoJS.fn.extend({
// Listen for Video Load Progress (currently does not if html file is local)
// Buffered does't work in all browsers, so watching progress as well
supportProgressEvents: function(e){
_V_.addListener(this.video, 'progress', this.playerOnVideoProgress.context(this));
},
playerOnVideoProgress: function(event){
this.setBufferedFromProgress(event);
_V_.addListener(this.video, 'progress', this.setBufferedFromProgress.context(this));
},
// playerOnVideoProgress: function(event){
// this.setBufferedFromProgress(event);
// },
setBufferedFromProgress: function(event){ // HTML5 Only
if(event.total > 0) {
if(event.total > 0 && this.duration()) {
var newBufferEnd = (event.loaded / event.total) * this.duration();
if (newBufferEnd > this.values.bufferEnd) { this.values.bufferEnd = newBufferEnd; }
if (newBufferEnd > this.values.bufferEnd) {
this.values.bufferEnd = newBufferEnd;
this.triggerListeners("bufferedupdate");
}
}
},
@ -411,153 +416,55 @@ VideoJS.fn.extend({
/* Player API - Translate functionality from player to video
================================================================================ */
html5AddVideoListener: function(type, fn){ _V_.addListener(this.video, type, fn.rEvtContext(this)); },
html5API: {
setupTriggers: function(){
// Make video events trigger player events
// May seem verbose here, but makes other APIs possible.
var types = ["play", "pause", "ended", "volumechange", "error"],
player = this, i, type;
for (i = 0; i<types.length; i++) {
type = types[i];
_V_.addListener(this.video, type, function(e){
player.triggerListeners(this, e);
}.context(type));
}
},
play: function(){
this.video.play();
return this;
},
onPlay: function(fn){ this.html5AddVideoListener("play", fn); return this; },
play: function(){ this.video.play(); },
pause: function(){ this.video.pause(); },
paused: function(){ return this.video.paused; },
pause: function(){
this.video.pause();
return this;
},
onPause: function(fn){ this.html5AddVideoListener("pause", fn); return this; },
paused: function() { return this.video.paused; },
currentTime: function(seconds){
if (seconds !== undefined) {
currentTime: function(){ return this.video.currentTime; },
setCurrentTime: function(seconds){
try { this.video.currentTime = seconds; }
catch(e) { this.warning(VideoJS.warnings.videoNotReady); }
this.values.currentTime = seconds;
return this;
}
return this.video.currentTime;
},
onCurrentTimeUpdate: function(fn){
this.currentTimeListeners.push(fn);
},
},
onEnded: function(fn){
this.html5AddVideoListener("ended", fn); return this;
},
duration: function(){ return this.video.duration; },
buffered: function(){ return this.video.buffered; },
duration: function(){
return this.video.duration;
},
volume: function(){ return this.video.volume; },
setVolume: function(percentAsDecimal){ this.video.volume = percentAsDecimal; },
buffered: function(){
// Storing values allows them be overridden by setBufferedFromProgress
if (this.values.bufferStart === undefined) {
this.values.bufferStart = 0;
this.values.bufferEnd = 0;
}
if (this.video.buffered && this.video.buffered.length > 0) {
var newEnd = this.video.buffered.end(0);
if (newEnd > this.values.bufferEnd) { this.values.bufferEnd = newEnd; }
}
return [this.values.bufferStart, this.values.bufferEnd];
},
width: function(){ return this.video.offsetWidth; },
height: function(){ return this.video.offsetHeight; },
volume: function(percentAsDecimal){
if (percentAsDecimal !== undefined) {
// Force value to between 0 and 1
this.values.volume = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
this.video.volume = this.values.volume;
this.setLocalStorage("volume", this.values.volume);
return this;
}
if (this.values.volume) { return this.values.volume; }
return this.video.volume;
},
onVolumeChange: function(fn){ _V_.addListener(this.video, 'volumechange', fn.rEvtContext(this)); },
width: function(width){
if (width !== undefined) {
this.video.width = width; // Not using style so it can be overridden on fullscreen.
this.box.style.width = width+"px";
this.triggerResizeListeners();
return this;
}
return this.video.offsetWidth;
},
height: function(height){
if (height !== undefined) {
this.video.height = height;
this.box.style.height = height+"px";
this.triggerResizeListeners();
return this;
}
return this.video.offsetHeight;
},
supportsFullScreen: function(){
if(typeof this.video.webkitEnterFullScreen == 'function') {
// Seems to be broken in Chromium/Chrome
if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) {
return true;
supportsFullScreen: function(){
if(typeof this.video.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.video.webkitEnterFullScreen();
} catch (e) {
if (e.code == 11) { this.warning(VideoJS.warnings.videoNotReady); }
}
}
return false;
},
html5EnterNativeFullScreen: function(){
try {
this.video.webkitEnterFullScreen();
} catch (e) {
if (e.code == 11) { this.warning(VideoJS.warnings.videoNotReady); }
}
return this;
},
// Turn on fullscreen (window) mode
// Real fullscreen isn't available in browsers quite yet.
enterFullScreen: function(){
if (this.supportsFullScreen()) {
this.html5EnterNativeFullScreen();
} else {
this.enterFullWindow();
}
},
exitFullScreen: function(){
if (this.supportsFullScreen()) {
// Shouldn't be called
} else {
this.exitFullWindow();
}
},
enterFullWindow: function(){
this.videoIsFullScreen = true;
// Storing original doc overflow value to return to when fullscreen is off
this.docOrigOverflow = document.documentElement.style.overflow;
// Add listener for esc key to exit fullscreen
_V_.addListener(document, "keydown", this.fullscreenOnEscKey.rEvtContext(this));
// Add listener for a window resize
_V_.addListener(window, "resize", this.fullscreenOnWindowResize.rEvtContext(this));
// Hide any scroll bars
document.documentElement.style.overflow = 'hidden';
// Apply fullscreen styles
_V_.addClass(this.box, "vjs-fullscreen");
// Resize the box, controller, and poster
this.positionAll();
},
// Turn off fullscreen (window) mode
exitFullWindow: function(){
this.videoIsFullScreen = false;
document.removeEventListener("keydown", this.fullscreenOnEscKey, false);
window.removeEventListener("resize", this.fullscreenOnWindowResize, false);
// Unhide scroll bars.
document.documentElement.style.overflow = this.docOrigOverflow;
// Remove fullscreen styles
_V_.removeClass(this.box, "vjs-fullscreen");
// Resize the box, controller, and poster to original sizes
this.positionAll();
},
onError: function(fn){ this.html5AddVideoListener("error", fn); return this; },
}
});

View File

@ -75,6 +75,9 @@ VideoJS.extend({
},
get: function(url, onSuccess){
// if (netscape.security.PrivilegeManager.enablePrivilege) {
// netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
// }
if (typeof XMLHttpRequest == "undefined") {
XMLHttpRequest = function () {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
@ -91,7 +94,11 @@ VideoJS.extend({
onSuccess(request.responseText);
}
}.context(this);
request.send();
try {
request.send();
} catch(e) {
// Can't access file.
}
},
trim: function(string){ return string.toString().replace(/^\s+/, "").replace(/\s+$/, ""); },

View File

@ -14,17 +14,24 @@ var VideoJS = JRClass.extend({
} else {
this.video = element;
}
// Store reference to player on the video element.
// So you can acess the player later: document.getElementById("video_id").player.play();
this.video.player = this;
this.video.player = this; // Store reference to player on the video element.
this.box = this.video.parentNode; // Container element for controls positioning
this.values = {}; // Cache video values.
this.elements = {}; // Store refs to controls elements.
this.api = {}; // API to video functions
this.listeners = {}; // Store video event listeners.
this.api = {}; // Current API to video functions (changes with player type)
// Hide Links. Will be shown again if "links" player is used
this.linksFallback = this.getLinksFallback();
this.hideLinksFallback();
// Default Options
this.options = {
autoplay: false,
preload: true,
loop: false,
returnToStart: true,
useBuiltInControls: false, // Use the browser's controls (iPhone)
controlsBelow: false, // Display control bar below video vs. in front of
controlsAtStart: false, // Make controls visible when page loads
@ -42,11 +49,6 @@ var VideoJS = JRClass.extend({
if (this.getPreloadAttribute() !== undefined) { this.options.preload = this.getPreloadAttribute(); }
if (this.getAutoplayAttribute() !== undefined) { this.options.autoplay = this.getAutoplayAttribute(); }
// Store reference to embed code pieces
this.box = this.video.parentNode;
this.linksFallback = this.getLinksFallback();
this.hideLinksFallback(); // Will be shown again if "links" player is used
// Loop through the player names list in options, "html5" etc.
// For each player name, initialize the player with that name under VideoJS.players
// If the player successfully initializes, we're done
@ -122,8 +124,6 @@ var VideoJS = JRClass.extend({
return true;
}
},
// Calculates amoutn of buffer is full
bufferedPercent: function(){ return (this.duration()) ? this.buffered()[1] / this.duration() : 0; },
// Each that maintains player as context
// Break if true is returned
each: function(arr, fn){
@ -132,6 +132,7 @@ var VideoJS = JRClass.extend({
if (fn.call(this, arr[i], i)) { break; }
}
},
// Add functions to the player
extend: function(obj){
for (var attrname in obj) {
if (obj.hasOwnProperty(attrname)) { this[attrname]=obj[attrname]; }

View File

@ -1,4 +1,5 @@
// jQuery Plugin
/* jQuery Plugin
================================================================================ */
if (window.jQuery) {
(function($) {

View File

@ -5,8 +5,10 @@
<title>HTML5 Video Player</title>
<!-- Include the VideoJS Library -->
<script src="src/video.js" type="text/javascript" charset="utf-8"></script>
<script src="src/main.js" type="text/javascript" charset="utf-8"></script>
<script src="src/api.js" type="text/javascript" charset="utf-8"></script>
<script src="src/html5.js" type="text/javascript" charset="utf-8"></script>
<script src="src/flash.js" type="text/javascript" charset="utf-8"></script>
<script src="src/behaviors.js" type="text/javascript" charset="utf-8"></script>
<script src="src/lib.js" type="text/javascript" charset="utf-8"></script>
<script src="src/video-js.jquery.js" type="text/javascript" charset="utf-8"></script>
@ -80,11 +82,11 @@
<div class="video-js-box">
<!-- Using the Video for Everybody Embed Code http://camendesign.com/code/video_for_everybody -->
<video data-subtitles="../demo-subtitles.srt" id="example_video_1" class="video-js" width="640" height="264" controls="controls" preload="none" poster="http://video-js.zencoder.com/oceans-clip.png">
<video id="example_video_1" class="video-js" width="640" height="264" controls="controls" preload="auto" poster="http://video-js.zencoder.com/oceans-clip.png">
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg; codecs="theora, vorbis"' />
<track kind="subtitles" src="demo-subtitles.srt" srclang="en-US" label="English"></track>
<track kind="subtitles" src="../demo-subtitles.srt" srclang="en-US" label="English"></track>
<!-- Flash Fallback. Use any flash video player here. Make sure to keep the vjs-flash-fallback class. -->
<object id="flash_fallback_1" class="vjs-flash-fallback" width="640" height="264" type="application/x-shockwave-flash"
data="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf">
@ -109,11 +111,10 @@
<div class="video-js-box">
<!-- Using the Video for Everybody Embed Code http://camendesign.com/code/video_for_everybody -->
<video data-subtitles="../demo-subtitles.srt" id="example_video_2" class="video-js" width="640" height="264" controls="controls" preload="none" poster="http://video-js.zencoder.com/oceans-clip.png">
<!-- <video id="example_video_2" class="video-jsnot" width="640" height="264" controls="controls" preload="none" poster="http://video-js.zencoder.com/oceans-clip.png"> -->
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm; codecs="vp8, vorbis"' />
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg; codecs="theora, vorbis"' />
<track kind="subtitles" src="demo-subtitles.srt" srclang="en-US" label="English"></track>
<!-- Flash Fallback. Use any flash video player here. Make sure to keep the vjs-flash-fallback class. -->
<object id="flash_fallback_1" class="vjs-flash-fallback" width="640" height="264" type="application/x-shockwave-flash"
data="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf">
@ -124,7 +125,7 @@
<img src="http://video-js.zencoder.com/oceans-clip.png" width="640" height="264" alt="Poster Image"
title="No video playback capabilities." />
</object>
</video>
<!-- </video> -->
<!-- Download links provided for devices that can't play video in the browser. -->
<p class="vjs-no-video"><strong>Download Video:</strong>
<a href="http://video-js.zencoder.com/oceans-clip.mp4">MP4</a>,

View File

@ -51,6 +51,23 @@ so you can upgrade to newer versions easier. */
bottom: 0px; /* Distance from the bottom of the box/video. Keep 0. Use height to add more bottom margin. */
height: 35px; /* Including any margin you want above or below control items */
padding: 0; /* Controls are absolutely position, so no padding necessary */
-webkit-transition: opacity 0.5s linear;
-moz-transition: opacity 0.5s linear;
-o-transition: opacity 0.5s linear;
-ms-transition: opacity 0.5s linear;
transition: opacity 0.5s linear;
}
.vjs-hide {
opacity: 0;
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; /* IE8 */
filter: alpha(opacity=0); /* IE5-7 */
}
.vjs-show {
opacity: 1;
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; /* IE8 */
filter: alpha(opacity=100); /* IE5-7 */
}
.video-js-box .vjs-controls > div { /* Direct div children of control bar */