2011-11-29 11:40:05 -08:00
/ * U I C o m p o n e n t - B a s e c l a s s f o r a l l U I o b j e c t s
=== === === === === === === === === === === === === === === === === === === === === === === === === === == * /
_V _ . Player = _V _ . Component . extend ( {
init : function ( tag , addOptions , ready ) {
this . tag = tag ; // Store the original tag used to set options
var el = this . el = _V _ . createElement ( "div" ) , // Div to contain video and controls
options = this . options = { } ,
2012-01-27 09:47:52 +01:00
width = options . width = tag . getAttribute ( 'width' ) ,
height = options . height = tag . getAttribute ( 'height' ) ,
2011-11-29 11:40:05 -08:00
// Browsers default to 300x150 if there's no width/height or video size data.
initWidth = width || 300 ,
2011-12-01 15:47:12 -08:00
initHeight = height || 150 ;
2011-11-29 11:40:05 -08:00
// Make player findable on elements
tag . player = el . player = this ;
2011-12-01 15:47:12 -08:00
// Add callback to ready queue
this . ready ( ready ) ;
2011-11-29 11:40:05 -08:00
// Wrap video tag in div (el/box) container
tag . parentNode . insertBefore ( el , tag ) ;
el . appendChild ( tag ) ; // Breaks iPhone, fixed in HTML5 setup.
// Give video tag properties to box
el . id = this . id = tag . id ; // ID will now reference box, not the video tag
el . className = tag . className ;
// Update tag id/class for use as HTML5 playback tech
tag . id += "_html5_api" ;
tag . className = "vjs-tech" ;
// Make player easily findable by ID
_V _ . players [ el . id ] = this ;
// Make box use width/height of tag, or default 300x150
el . setAttribute ( "width" , initWidth ) ;
el . setAttribute ( "height" , initHeight ) ;
// Enforce with CSS since width/height attrs don't work on divs
el . style . width = initWidth + "px" ;
el . style . height = initHeight + "px" ;
// Remove width/height attrs from tag so CSS can make it 100% width/height
tag . removeAttribute ( "width" ) ;
tag . removeAttribute ( "height" ) ;
// Set Options
_V _ . merge ( options , _V _ . options ) ; // Copy Global Defaults
_V _ . merge ( options , this . getVideoTagSettings ( ) ) ; // Override with Video Tag Options
_V _ . merge ( options , addOptions ) ; // Override/extend with options from setup call
// Store controls setting, and then remove immediately so native controls don't flash.
tag . removeAttribute ( "controls" ) ;
2012-01-02 16:57:17 -08:00
// Poster will be handled by a manual <img>
tag . removeAttribute ( "poster" ) ;
2011-11-29 11:40:05 -08:00
// Empty video tag sources and tracks so the built in player doesn't use them also.
if ( tag . hasChildNodes ( ) ) {
for ( var i = 0 , j = tag . childNodes ; i < j . length ; i ++ ) {
if ( j [ i ] . nodeName == "SOURCE" || j [ i ] . nodeName == "TRACK" ) {
tag . removeChild ( j [ i ] ) ;
}
}
}
// Cache for video property values.
this . values = { } ;
this . addClass ( "vjs-paused" ) ;
this . addEvent ( "ended" , this . onEnded ) ;
this . addEvent ( "play" , this . onPlay ) ;
this . addEvent ( "pause" , this . onPause ) ;
2011-12-01 15:47:12 -08:00
this . addEvent ( "error" , this . onError ) ;
2011-11-29 11:40:05 -08:00
// When the API is ready, loop through the components and add to the player.
2011-12-07 21:29:21 -08:00
if ( options . controls ) {
2011-12-01 15:47:12 -08:00
this . ready ( function ( ) {
2012-01-27 15:57:03 -08:00
this . initComponents ( ) ;
2011-11-29 11:40:05 -08:00
} ) ;
}
2011-11-30 13:06:32 -08:00
// If there are no sources when the player is initialized,
// load the first supported playback technology.
2011-12-07 21:29:21 -08:00
if ( ! options . sources || options . sources . length == 0 ) {
for ( var i = 0 , j = options . techOrder ; i < j . length ; i ++ ) {
2011-11-30 13:06:32 -08:00
var techName = j [ i ] ,
tech = _V _ [ techName ] ;
// Check if the browser supports this technology
2011-12-07 21:29:21 -08:00
if ( tech . isSupported ( ) ) {
2011-11-30 13:06:32 -08:00
this . loadTech ( techName ) ;
break ;
}
}
} else {
2012-02-02 19:16:45 -08:00
// Loop through playback technologies (HTML5, Flash) and check for support. Then load the best source.
// A few assumptions here:
// All playback technologies respect preload false.
2011-12-07 21:29:21 -08:00
this . src ( options . sources ) ;
2011-11-30 13:06:32 -08:00
}
2011-11-29 11:40:05 -08:00
} ,
// Cache for video property values.
values : { } ,
2011-12-15 23:17:10 +02:00
destroy : function ( ) {
// Ensure that tracking progress and time progress will stop and plater deleted
this . stopTrackingProgress ( ) ;
this . stopTrackingCurrentTime ( ) ;
2012-02-03 17:53:59 -08:00
_V _ . players [ this . id ] = null ;
delete _V _ . players [ this . id ] ;
this . tech . destroy ( ) ;
this . el . parentNode . removeChild ( this . el ) ;
2011-12-15 23:17:10 +02:00
} ,
2011-11-29 11:40:05 -08:00
2012-02-02 19:16:45 -08:00
createElement : function ( type , options ) { } ,
2011-11-29 11:40:05 -08:00
getVideoTagSettings : function ( ) {
var options = {
sources : [ ] ,
tracks : [ ]
} ;
2012-01-27 09:47:52 +01:00
options . src = this . tag . getAttribute ( "src" ) ;
2011-11-29 11:40:05 -08:00
options . controls = this . tag . getAttribute ( "controls" ) !== null ;
2012-01-27 09:47:52 +01:00
options . poster = this . tag . getAttribute ( "poster" ) ;
options . preload = this . tag . getAttribute ( "preload" ) ;
2011-11-29 11:40:05 -08:00
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 ;
2012-01-25 17:32:50 +01:00
if ( this . tag . hasChildNodes ( ) ) {
2012-01-27 10:36:02 -08:00
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 ) ) ;
}
2011-11-29 11:40:05 -08:00
}
}
return options ;
} ,
/ * P L a y b a c k T e c h n o l o g y ( t e c h )
=== === === === === === === === === === === === === === === === === === === === === === === === === === == * /
// Load/Create an instance of playback technlogy including element and API methods
// And append playback element in player div.
loadTech : function ( techName , source ) {
// Pause and remove current playback technology
if ( this . tech ) {
2012-01-05 23:25:09 -08:00
this . unloadTech ( ) ;
2011-11-29 11:40:05 -08:00
// If the first time loading, HTML5 tag will exist but won't be initialized
// So we need to remove it if we're not loading HTML5
2012-01-05 23:25:09 -08:00
} else if ( techName != "html5" && this . tag ) {
2012-01-02 13:02:04 -08:00
this . el . removeChild ( this . tag ) ;
2012-01-05 23:25:09 -08:00
this . tag = false ;
2011-11-29 11:40:05 -08:00
}
this . techName = techName ;
// Turn off API access because we're loading a new tech that might load asynchronously
this . isReady = false ;
2011-12-01 15:47:12 -08:00
var techReady = function ( ) {
this . player . triggerReady ( ) ;
2011-11-29 11:40:05 -08:00
// Manually track progress in cases where the browser/flash player doesn't report it.
2012-01-05 23:25:09 -08:00
if ( ! this . support . progressEvent ) {
2011-12-07 21:29:21 -08:00
this . player . manualProgressOn ( ) ;
2011-12-01 15:47:12 -08:00
}
2011-11-29 11:40:05 -08:00
// Manually track timeudpates in cases where the browser/flash player doesn't report it.
2012-01-05 23:25:09 -08:00
if ( ! this . support . timeupdateEvent ) {
2011-12-07 21:29:21 -08:00
this . player . manualTimeUpdatesOn ( ) ;
2011-12-01 15:47:12 -08:00
}
}
2011-11-29 11:40:05 -08:00
2012-01-02 13:02:04 -08:00
// Grab tech-specific options from player options and add source and parent element to use.
var techOptions = _V _ . merge ( { source : source , parentEl : this . el } , this . options [ techName ] )
2011-11-29 11:40:05 -08:00
2012-01-05 23:25:09 -08:00
if ( source ) {
2012-01-12 16:56:38 -05:00
if ( source . src == this . values . src && this . values . currentTime > 0 ) {
techOptions . startTime = this . values . currentTime ;
}
2012-01-05 23:25:09 -08:00
this . values . src = source . src ;
}
2012-01-02 13:02:04 -08:00
// Initialize tech instance
this . tech = new _V _ [ techName ] ( this , techOptions ) ;
this . tech . ready ( techReady ) ;
2011-11-29 11:40:05 -08:00
} ,
2012-01-05 23:25:09 -08:00
unloadTech : function ( ) {
this . tech . destroy ( ) ;
// Turn off any manual progress or timeupdate tracking
if ( this . manualProgress ) { this . manualProgressOff ( ) ; }
if ( this . manualTimeUpdates ) { this . manualTimeUpdatesOff ( ) ; }
this . tech = false ;
} ,
// There's many issues around changing the size of a Flash (or other plugin) object.
// First is a plugin reload issue in Firefox that has been around for 11 years: https://bugzilla.mozilla.org/show_bug.cgi?id=90268
// Then with the new fullscreen API, Mozilla and webkit browsers will reload the flash object after going to fullscreen.
// To get around this, we're unloading the tech, caching source and currentTime values, and reloading the tech once the plugin is resized.
2012-01-20 18:20:00 -08:00
// reloadTech: function(betweenFn){
// _V_.log("unloadingTech")
// this.unloadTech();
// _V_.log("unloadedTech")
// if (betweenFn) { betweenFn.call(); }
// _V_.log("LoadingTech")
// this.loadTech(this.techName, { src: this.values.src })
// _V_.log("loadedTech")
// },
2012-01-05 23:25:09 -08:00
2011-11-29 11:40:05 -08:00
/ * F a l l b a c k s f o r u n s u p p o r t e d e v e n t t y p e s
=== === === === === === === === === === === === === === === === === === === === === === === === === === == * /
// Manually trigger progress events based on changes to the buffered amount
// Many flash players and older HTML5 browsers don't send progress or progress-like events
manualProgressOn : function ( ) {
this . manualProgress = true ;
// Trigger progress watching when a source begins loading
this . trackProgress ( ) ;
// Watch for a native progress event call on the tech element
// In HTML5, some older versions don't support the progress event
// So we're assuming they don't, and turning off manual progress if they do.
this . tech . addEvent ( "progress" , function ( ) {
// Remove this listener from the element
this . removeEvent ( "progress" , arguments . callee ) ;
// Update known progress support for this playback technology
2012-01-05 23:25:09 -08:00
this . support . progressEvent = true ;
2011-11-29 11:40:05 -08:00
// Turn off manual progress tracking
this . player . manualProgressOff ( ) ;
} ) ;
} ,
manualProgressOff : function ( ) {
this . manualProgress = false ;
this . stopTrackingProgress ( ) ;
} ,
trackProgress : function ( ) {
this . progressInterval = setInterval ( _V _ . proxy ( this , function ( ) {
// Don't trigger unless buffered amount is greater than last time
// log(this.values.bufferEnd, this.buffered().end(0), this.duration())
/* TODO: update for multiple buffered regions */
if ( this . values . bufferEnd < this . buffered ( ) . end ( 0 ) ) {
this . triggerEvent ( "progress" ) ;
} else if ( this . bufferedPercent ( ) == 1 ) {
this . stopTrackingProgress ( ) ;
this . triggerEvent ( "progress" ) ; // Last update
}
} ) , 500 ) ;
} ,
stopTrackingProgress : function ( ) { clearInterval ( this . progressInterval ) ; } ,
/* Time Tracking -------------------------------------------------------------- */
manualTimeUpdatesOn : function ( ) {
this . manualTimeUpdates = true ;
this . addEvent ( "play" , this . trackCurrentTime ) ;
this . addEvent ( "pause" , this . stopTrackingCurrentTime ) ;
// timeupdate is also called by .currentTime whenever current time is set
// Watch for native timeupdate event
this . tech . addEvent ( "timeupdate" , function ( ) {
// Remove this listener from the element
this . removeEvent ( "timeupdate" , arguments . callee ) ;
// Update known progress support for this playback technology
2012-01-05 23:25:09 -08:00
this . support . timeupdateEvent = true ;
2011-11-29 11:40:05 -08:00
// Turn off manual progress tracking
this . player . manualTimeUpdatesOff ( ) ;
} ) ;
} ,
manualTimeUpdatesOff : function ( ) {
this . manualTimeUpdates = false ;
this . stopTrackingCurrentTime ( ) ;
this . removeEvent ( "play" , this . trackCurrentTime ) ;
this . removeEvent ( "pause" , this . stopTrackingCurrentTime ) ;
} ,
trackCurrentTime : function ( ) {
if ( this . currentTimeInterval ) { this . stopTrackingCurrentTime ( ) ; }
this . currentTimeInterval = setInterval ( _V _ . proxy ( this , function ( ) {
this . triggerEvent ( "timeupdate" ) ;
} ) , 250 ) ; // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
} ,
// Turn off play progress tracking (when paused or dragging)
stopTrackingCurrentTime : function ( ) { clearInterval ( this . currentTimeInterval ) ; } ,
/ * P l a y e r e v e n t h a n d l e r s ( h o w t h e p l a y e r r e a c t s t o c e r t a i n e v e n t s )
=== === === === === === === === === === === === === === === === === === === === === === === === === === == * /
onEnded : function ( ) {
if ( this . options . loop ) {
this . currentTime ( 0 ) ;
this . play ( ) ;
} else {
2011-12-21 17:59:36 -08:00
this . pause ( ) ;
this . currentTime ( 0 ) ;
this . pause ( ) ;
2011-11-29 11:40:05 -08:00
}
} ,
onPlay : function ( ) {
_V _ . removeClass ( this . el , "vjs-paused" ) ;
_V _ . addClass ( this . el , "vjs-playing" ) ;
} ,
onPause : function ( ) {
_V _ . removeClass ( this . el , "vjs-playing" ) ;
_V _ . addClass ( this . el , "vjs-paused" ) ;
2011-12-01 15:47:12 -08:00
} ,
2011-12-07 21:29:21 -08:00
2011-12-01 15:47:12 -08:00
onError : function ( e ) {
_V _ . log ( "Video Error" , e ) ;
2012-01-05 23:25:09 -08:00
} ,
2011-11-29 11:40:05 -08:00
/ * P l a y e r A P I
=== === === === === === === === === === === === === === === === === === === === === === === === === === == * /
2012-02-02 19:16:45 -08:00
// Pass values to the playback tech
2012-02-02 14:56:47 -08:00
techCall : function ( method , arg ) {
2012-02-02 19:16:45 -08:00
// If it's not ready yet, call method when it is
if ( ! this . tech . isReady ) {
this . tech . ready ( function ( ) {
this [ method ] ( arg ) ;
} ) ;
// Otherwise call method now
} else {
2012-02-03 10:28:00 -08:00
try {
this . tech [ method ] ( arg ) ;
} catch ( e ) {
_V _ . log ( e ) ;
}
2012-02-02 19:16:45 -08:00
}
} ,
// Get calls can't wait for the tech, and sometimes don't need to.
techGet : function ( method ) {
2012-02-03 10:28:00 -08:00
// Make sure tech is ready
if ( this . tech . isReady ) {
// Flash likes to die and reload when you hide or reposition it.
// In these cases the object methods go away and we get errors.
// When that happens we'll catch the errors and inform tech that it's not ready any more.
try {
return this . tech [ method ] ( ) ;
} catch ( e ) {
// When building additional tech libs, an expected method may not be defined yet
if ( this . tech [ method ] === undefined ) {
_V _ . log ( "Video.js: " + method + " method not defined for " + this . techName + " playback technology." , e ) ;
} else {
// When a method isn't available on the object it throws a TypeError
if ( e . name == "TypeError" ) {
_V _ . log ( "Video.js: " + method + " unavailable on " + this . techName + " playback technology element." , e ) ;
this . tech . isReady = false ;
} else {
_V _ . log ( e ) ;
}
}
}
2011-11-29 11:40:05 -08:00
}
2012-02-03 10:28:00 -08:00
return ;
2011-11-29 11:40:05 -08:00
} ,
2012-02-02 19:16:45 -08:00
// Method for calling methods on the current playback technology
// techCall: function(method, arg){
//
// // if (this.isReady) {
// //
// // } else {
// // _V_.log("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]", arguments.callee.caller.arguments.callee.caller.arguments.callee.caller)
// // return false;
// // // throw new Error("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]");
// // }
// },
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-play
2011-11-29 11:40:05 -08:00
play : function ( ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "play" ) ;
return this ;
2011-11-29 11:40:05 -08:00
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-pause
2011-11-29 11:40:05 -08:00
pause : function ( ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "pause" ) ;
return this ;
2011-11-29 11:40:05 -08:00
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-paused
// The initial state of paused should be true (in Safari it's actually false)
2011-11-29 11:40:05 -08:00
paused : function ( ) {
2012-02-02 19:16:45 -08:00
return ( this . techGet ( "paused" ) === false ) ? false : true ;
2011-11-29 11:40:05 -08:00
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-currenttime
2011-11-29 11:40:05 -08:00
currentTime : function ( seconds ) {
if ( seconds !== undefined ) {
2011-12-05 11:28:18 -08:00
// Cache the last set value for smoother scrubbing.
2012-01-05 23:25:09 -08:00
this . values . lastSetCurrentTime = seconds ;
2011-12-05 11:28:18 -08:00
2012-02-02 14:56:47 -08:00
this . techCall ( "setCurrentTime" , seconds ) ;
// Improve the accuracy of manual timeupdates
if ( this . manualTimeUpdates ) { this . triggerEvent ( "timeupdate" ) ; }
2011-12-05 11:28:18 -08:00
2011-11-29 11:40:05 -08:00
return this ;
}
2012-01-05 23:25:09 -08:00
// Cache last currentTime and return
2012-02-02 14:56:47 -08:00
// Default to 0 seconds
2012-02-02 19:16:45 -08:00
return this . values . currentTime = ( this . techGet ( "currentTime" ) || 0 ) ;
2011-11-29 11:40:05 -08:00
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-duration
// Duration should return NaN if not available. ParseFloat will turn false-ish values to NaN.
2011-11-29 11:40:05 -08:00
duration : function ( ) {
2012-02-02 19:16:45 -08:00
return parseFloat ( this . techGet ( "duration" ) ) ;
2011-11-29 11:40:05 -08:00
} ,
2012-02-02 14:56:47 -08:00
// Calculates how much time is left. Not in spec, but useful.
2011-11-29 11:40:05 -08:00
remainingTime : function ( ) {
return this . duration ( ) - this . currentTime ( ) ;
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-buffered
// Buffered returns a timerange object. Kind of like an array of portions of the video that have been downloaded.
// So far no browsers return more than one range (portion)
2011-11-29 11:40:05 -08:00
buffered : function ( ) {
2012-02-02 19:16:45 -08:00
var buffered = this . techGet ( "buffered" ) ,
2012-02-02 14:56:47 -08:00
start = 0 ,
end = this . values . bufferEnd = this . values . bufferEnd || 0 , // Default end to 0 and store in values
2011-11-29 11:40:05 -08:00
timeRange ;
2012-01-02 13:02:04 -08:00
if ( buffered && buffered . length > 0 && buffered . end ( 0 ) !== end ) {
2011-11-29 11:40:05 -08:00
end = buffered . end ( 0 ) ;
// Storing values allows them be overridden by setBufferedFromProgress
this . values . bufferEnd = end ;
}
return _V _ . createTimeRange ( start , end ) ;
} ,
2012-02-02 14:56:47 -08:00
// Calculates amount of buffer is full. Not in spec but useful.
2011-11-29 11:40:05 -08:00
bufferedPercent : function ( ) {
return ( this . duration ( ) ) ? this . buffered ( ) . end ( 0 ) / this . duration ( ) : 0 ;
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-volume
2011-11-29 11:40:05 -08:00
volume : function ( percentAsDecimal ) {
2012-02-02 14:56:47 -08:00
var vol ;
2011-11-29 11:40:05 -08:00
if ( percentAsDecimal !== undefined ) {
2012-02-02 14:56:47 -08:00
vol = Math . max ( 0 , Math . min ( 1 , parseFloat ( percentAsDecimal ) ) ) ; // Force value to between 0 and 1
2011-11-29 11:40:05 -08:00
this . values . volume = vol ;
2012-02-02 14:56:47 -08:00
this . techCall ( "setVolume" , vol ) ;
2011-11-29 11:40:05 -08:00
_V _ . setLocalStorage ( "volume" , vol ) ;
return this ;
}
2012-02-02 14:56:47 -08:00
// Default to 1 when returning current volume.
2012-02-02 19:16:45 -08:00
vol = parseFloat ( this . techGet ( "volume" ) ) ;
2012-02-02 14:56:47 -08:00
return ( isNaN ( vol ) ) ? 1 : vol ;
2011-11-29 11:40:05 -08:00
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#attr-media-muted
2011-11-29 11:40:05 -08:00
muted : function ( muted ) {
if ( muted !== undefined ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "setMuted" , muted ) ;
2011-11-29 11:40:05 -08:00
return this ;
}
2012-02-02 19:16:45 -08:00
return this . techGet ( "muted" ) || false ; // Default to false
2011-11-29 11:40:05 -08:00
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/dimension-attributes.html#attr-dim-height
// Video tag width/height only work in pixels. No percents.
// We could potentially allow percents but won't for now until we can do testing around it.
2011-11-29 11:40:05 -08:00
width : function ( width , skipListeners ) {
if ( width !== undefined ) {
this . el . width = width ;
this . el . style . width = width + "px" ;
2012-02-02 14:56:47 -08:00
// skipListeners allows us to avoid triggering the resize event when setting both width and height
2011-11-29 11:40:05 -08:00
if ( ! skipListeners ) { this . triggerEvent ( "resize" ) ; }
return this ;
}
return parseInt ( this . el . getAttribute ( "width" ) ) ;
} ,
height : function ( height ) {
if ( height !== undefined ) {
this . el . height = height ;
this . el . style . height = height + "px" ;
this . triggerEvent ( "resize" ) ;
return this ;
}
return parseInt ( this . el . getAttribute ( "height" ) ) ;
} ,
2012-02-02 14:56:47 -08:00
// Set both width and height at the same time.
2011-11-29 11:40:05 -08:00
size : function ( width , height ) {
// Skip resize listeners on width for optimization
return this . width ( width , true ) . height ( height ) ;
} ,
2012-02-02 14:56:47 -08:00
// Check if current tech can support native fullscreen (e.g. with built in controls lik iOS, so not our flash swf)
2012-02-02 19:16:45 -08:00
supportsFullScreen : function ( ) { return this . techGet ( "supportsFullScreen" ) || false ; } ,
2011-11-29 11:40:05 -08:00
// Turn on fullscreen (or window) mode
2012-01-05 23:25:09 -08:00
requestFullScreen : function ( ) {
var requestFullScreen = _V _ . support . requestFullScreen ;
2012-01-29 20:03:44 -08:00
this . isFullScreen = true ;
2012-01-05 23:25:09 -08:00
// Check for browser element fullscreen support
if ( requestFullScreen ) {
2012-01-29 20:03:44 -08:00
2012-01-05 23:25:09 -08:00
// Flash and other plugins get reloaded when you take their parent to fullscreen.
// To fix that we'll remove the tech, and reload it after the resize has finished.
2012-01-20 17:34:18 -08:00
if ( this . tech . support . fullscreenResize === false && this . options . flash . iFrameMode != true ) {
2012-01-05 23:25:09 -08:00
this . pause ( ) ;
this . unloadTech ( ) ;
_V _ . addEvent ( document , requestFullScreen . eventName , this . proxy ( function ( ) {
_V _ . removeEvent ( document , requestFullScreen . eventName , arguments . callee ) ;
this . loadTech ( this . techName , { src : this . values . src } ) ;
} ) ) ;
this . el [ requestFullScreen . requestFn ] ( ) ;
} else {
this . el [ requestFullScreen . requestFn ] ( ) ;
}
2012-01-27 12:22:05 -08:00
// 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 ] ;
} ) ) ;
2012-01-05 23:25:09 -08:00
} else if ( this . tech . supportsFullScreen ( ) ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "enterFullScreen" ) ;
2012-01-05 23:25:09 -08:00
} else {
this . enterFullWindow ( ) ;
}
this . triggerEvent ( "fullscreenchange" ) ;
2011-12-01 15:24:34 -08:00
return this ;
} ,
2012-01-05 23:25:09 -08:00
cancelFullScreen : function ( ) {
var requestFullScreen = _V _ . support . requestFullScreen ;
// Check for browser element fullscreen support
if ( requestFullScreen ) {
// Flash and other plugins get reloaded when you take their parent to fullscreen.
// To fix that we'll remove the tech, and reload it after the resize has finished.
2012-01-20 17:34:18 -08:00
if ( this . tech . support . fullscreenResize === false && this . options . flash . iFrameMode != true ) {
2012-01-05 23:25:09 -08:00
this . pause ( ) ;
this . unloadTech ( ) ;
_V _ . addEvent ( document , requestFullScreen . eventName , this . proxy ( function ( ) {
_V _ . removeEvent ( document , requestFullScreen . eventName , arguments . callee ) ;
this . loadTech ( this . techName , { src : this . values . src } )
} ) ) ;
document [ requestFullScreen . cancelFn ] ( ) ;
2011-12-01 15:24:34 -08:00
} else {
2012-01-05 23:25:09 -08:00
document [ requestFullScreen . cancelFn ] ( ) ;
2011-12-01 15:24:34 -08:00
}
2011-12-01 16:48:06 -08:00
2012-01-05 23:25:09 -08:00
} else if ( this . tech . supportsFullScreen ( ) ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "exitFullScreen" ) ;
2012-01-05 23:25:09 -08:00
} else {
this . exitFullWindow ( ) ;
}
2012-01-27 12:22:05 -08:00
this . isFullScreen = false ;
2012-01-05 23:25:09 -08:00
this . triggerEvent ( "fullscreenchange" ) ;
2011-12-01 16:48:06 -08:00
return this ;
} ,
2011-11-29 11:40:05 -08:00
2012-02-02 14:56:47 -08:00
// When fullscreen isn't supported we can stretch the video container to as wide as the browser will let us.
2011-11-29 11:40:05 -08:00
enterFullWindow : function ( ) {
2012-01-27 12:22:05 -08:00
this . isFullWindow = true ;
2011-11-29 11:40:05 -08:00
// 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 _ . addEvent ( document , "keydown" , _V _ . proxy ( this , this . fullWindowOnEscKey ) ) ;
// Hide any scroll bars
document . documentElement . style . overflow = 'hidden' ;
// Apply fullscreen styles
_V _ . addClass ( document . body , "vjs-full-window" ) ;
_V _ . addClass ( this . el , "vjs-fullscreen" ) ;
this . triggerEvent ( "enterFullWindow" ) ;
} ,
fullWindowOnEscKey : function ( event ) {
if ( event . keyCode == 27 ) {
2012-01-27 12:22:05 -08:00
if ( this . isFullScreen == true ) {
2012-01-14 18:03:37 -08:00
this . cancelFullScreen ( ) ;
} else {
this . exitFullWindow ( ) ;
}
2011-11-29 11:40:05 -08:00
}
} ,
exitFullWindow : function ( ) {
2012-01-27 12:22:05 -08:00
this . isFullWindow = false ;
2011-11-29 11:40:05 -08:00
_V _ . removeEvent ( document , "keydown" , this . fullWindowOnEscKey ) ;
// Unhide scroll bars.
document . documentElement . style . overflow = this . docOrigOverflow ;
// Remove fullscreen styles
_V _ . removeClass ( document . body , "vjs-full-window" ) ;
_V _ . removeClass ( this . el , "vjs-fullscreen" ) ;
// Resize the box, controller, and poster to original sizes
// this.positionAll();
this . triggerEvent ( "exitFullWindow" ) ;
} ,
2012-02-02 14:56:47 -08:00
selectSource : function ( sources ) {
// Loop through each playback technology in the options order
for ( var i = 0 , j = this . options . techOrder ; i < j . length ; i ++ ) {
var techName = j [ i ] ,
tech = _V _ [ techName ] ;
// tech = _V_.tech[techName];
// Check if the browser supports this technology
if ( tech . isSupported ( ) ) {
// Loop through each source object
for ( var a = 0 , b = sources ; a < b . length ; a ++ ) {
var source = b [ a ] ;
// Check if source can be played with this technology
if ( tech . canPlaySource . call ( this , source ) ) {
return { source : source , tech : techName } ;
}
}
}
}
return false ;
} ,
2011-11-29 11:40:05 -08:00
// src is a pretty powerful function
// If you pass it an array of source objects, it will find the best source to play and use that object.src
// If the new source requires a new playback technology, it will switch to that.
// If you pass it an object, it will set the source to object.src
// If you pass it anything else (url string) it will set the video source to that
src : function ( source ) {
// Case: Array of source objects to choose from and pick the best to play
if ( source instanceof Array ) {
2012-02-02 14:56:47 -08:00
var sourceTech = this . selectSource ( source ) ,
source ,
techName ;
2011-11-29 11:40:05 -08:00
2012-02-02 14:56:47 -08:00
if ( sourceTech ) {
source = sourceTech . source ;
techName = sourceTech . tech ;
2011-11-29 11:40:05 -08:00
2012-02-02 14:56:47 -08:00
// If this technology is already loaded, set source
if ( techName == this . techName ) {
this . src ( source ) ; // Passing the source object
2011-11-29 11:40:05 -08:00
2012-02-02 14:56:47 -08:00
// Otherwise load this technology with chosen source
} else {
this . loadTech ( techName , source ) ;
2011-11-29 11:40:05 -08:00
}
2012-02-02 14:56:47 -08:00
} else {
_V _ . log ( "No compatible source and playback technology were found." )
2011-11-29 11:40:05 -08:00
}
// Case: Source object { src: "", type: "" ... }
} else if ( source instanceof Object ) {
2012-02-02 14:56:47 -08:00
2011-12-07 22:02:40 -08:00
if ( _V _ [ this . techName ] . canPlaySource ( source ) ) {
2011-12-01 15:47:12 -08:00
this . src ( source . src ) ;
} else {
// Send through tech loop to check for a compatible technology.
this . src ( [ source ] ) ;
}
2012-02-02 14:56:47 -08:00
2011-11-29 11:40:05 -08:00
// Case: URL String (http://myvideo...)
} else {
2012-01-05 23:25:09 -08:00
// Cache for getting last set source
this . values . src = source ;
2011-12-01 15:47:12 -08:00
if ( ! this . isReady ) {
this . ready ( function ( ) {
this . src ( source ) ;
} ) ;
} else {
2012-02-02 14:56:47 -08:00
this . techCall ( "src" , source ) ;
2011-12-01 15:47:12 -08:00
if ( this . options . preload == "auto" ) {
this . load ( ) ;
}
if ( this . options . autoplay ) {
2011-12-01 16:48:06 -08:00
this . play ( ) ;
2011-12-01 15:47:12 -08:00
}
}
2011-11-29 11:40:05 -08:00
}
return this ;
} ,
// Begin loading the src data
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-load
2011-11-29 11:40:05 -08:00
load : function ( ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "load" ) ;
2011-11-29 11:40:05 -08:00
return this ;
} ,
2012-02-02 14:56:47 -08:00
// http://dev.w3.org/html5/spec/video.html#dom-media-currentsrc
2011-11-29 11:40:05 -08:00
currentSrc : function ( ) {
2012-02-02 19:16:45 -08:00
return this . techGet ( "currentSrc" ) || this . values . src || "" ;
2011-11-29 11:40:05 -08:00
} ,
textTrackValue : function ( kind , value ) {
if ( value !== undefined ) {
this . values [ kind ] = value ;
this . triggerEvent ( kind + "update" ) ;
return this ;
}
return this . values [ kind ] ;
} ,
// Attributes/Options
preload : function ( value ) {
if ( value !== undefined ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "setPreload" , value ) ;
2011-11-29 11:40:05 -08:00
this . options . preload = value ;
return this ;
}
2012-02-02 19:16:45 -08:00
return this . techGet ( "preload" ) ;
2011-11-29 11:40:05 -08:00
} ,
autoplay : function ( value ) {
if ( value !== undefined ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "setAutoplay" , value ) ;
2011-11-29 11:40:05 -08:00
this . options . autoplay = value ;
return this ;
}
2012-02-02 19:16:45 -08:00
return this . techGet ( "autoplay" , value ) ;
2011-11-29 11:40:05 -08:00
} ,
loop : function ( value ) {
if ( value !== undefined ) {
2012-02-02 14:56:47 -08:00
this . techCall ( "setLoop" , value ) ;
2011-11-29 11:40:05 -08:00
this . options . loop = value ;
return this ;
}
2012-02-02 19:16:45 -08:00
return this . techGet ( "loop" ) ;
2011-11-29 11:40:05 -08:00
} ,
controls : function ( ) { return this . options . controls ; } ,
textTracks : function ( ) { return this . options . tracks ; } ,
2012-02-02 19:16:45 -08:00
poster : function ( ) { return this . techGet ( "poster" ) ; } ,
error : function ( ) { return this . techGet ( "error" ) ; } ,
ended : function ( ) { return this . techGet ( "ended" ) ; }
2012-02-02 14:56:47 -08:00
// Methods to add support for
// networkState: function(){ return this.techCall("networkState"); },
// readyState: function(){ return this.techCall("readyState"); },
// seeking: function(){ return this.techCall("seeking"); },
// initialTime: function(){ return this.techCall("initialTime"); },
// startOffsetTime: function(){ return this.techCall("startOffsetTime"); },
// played: function(){ return this.techCall("played"); },
// seekable: function(){ return this.techCall("seekable"); },
// videoTracks: function(){ return this.techCall("videoTracks"); },
// audioTracks: function(){ return this.techCall("audioTracks"); },
// videoWidth: function(){ return this.techCall("videoWidth"); },
// videoHeight: function(){ return this.techCall("videoHeight"); },
// defaultPlaybackRate: function(){ return this.techCall("defaultPlaybackRate"); },
// playbackRate: function(){ return this.techCall("playbackRate"); },
// mediaGroup: function(){ return this.techCall("mediaGroup"); },
// controller: function(){ return this.techCall("controller"); },
// defaultMuted: function(){ return this.techCall("defaultMuted"); }
2011-11-29 11:40:05 -08:00
} ) ;
2012-01-05 23:25:09 -08:00
// RequestFullscreen API
( function ( ) {
var requestFn ,
cancelFn ,
2012-01-27 12:22:05 -08:00
eventName ,
isFullScreen ,
2012-01-05 23:25:09 -08:00
playerProto = _V _ . Player . prototype ;
// Current W3C Spec
// http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html#api
// Mozilla Draft: https://wiki.mozilla.org/Gecko:FullScreenAPI#fullscreenchange_event
if ( document . cancelFullscreen !== undefined ) {
requestFn = "requestFullscreen" ;
cancelFn = "exitFullscreen" ;
eventName = "fullscreenchange" ;
2012-01-27 12:22:05 -08:00
isFullScreen = "fullScreen" ;
2012-01-05 23:25:09 -08:00
// 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',
// and lowercasing the 'S' in Fullscreen.
// Other browsers don't have any hints of which version they might follow yet, so not going to try to predict by loopeing through all prefixes.
} else {
_V _ . each ( [ "moz" , "webkit" ] , function ( prefix ) {
2012-01-16 15:03:38 -08:00
// https://github.com/zencoder/video-js/pull/128
2012-01-13 17:34:39 +01:00
if ( ( prefix != "moz" || document . mozFullScreenEnabled ) && document [ prefix + "CancelFullScreen" ] !== undefined ) {
2012-01-05 23:25:09 -08:00
requestFn = prefix + "RequestFullScreen" ;
cancelFn = prefix + "CancelFullScreen" ;
eventName = prefix + "fullscreenchange" ;
2012-01-29 20:03:44 -08:00
if ( prefix == "webkit" ) {
isFullScreen = prefix + "IsFullScreen" ;
} else {
_V _ . log ( "moz here" )
isFullScreen = prefix + "FullScreen" ;
}
2012-01-05 23:25:09 -08:00
}
2012-01-29 20:03:44 -08:00
2012-01-05 23:25:09 -08:00
} ) ;
}
if ( requestFn ) {
_V _ . support . requestFullScreen = {
requestFn : requestFn ,
cancelFn : cancelFn ,
2012-01-29 20:03:44 -08:00
eventName : eventName ,
isFullScreen : isFullScreen
2012-01-05 23:25:09 -08:00
} ;
}
} ) ( ) ;