2016-07-20 22:20:25 +02:00
/ * !
*
* MediaElement . js
* HTML5 < video > and < audio > shim and player
* http : //mediaelementjs.com/
*
* Creates a JavaScript object that mimics HTML5 MediaElement API
* for browsers that don 't understand HTML5 or can' t play the provided codec
* Can play MP4 ( H . 264 ) , Ogg , WebM , FLV , WMV , WMA , ACC , and MP3
*
* Copyright 2010 - 2014 , John Dyer ( http : //j.hn)
* License : MIT
*
* /
// Namespace
var mejs = mejs || { } ;
// version number
mejs . version = '2.21.2' ;
// player number (for missing, same id attr)
mejs . meIndex = 0 ;
// media types accepted by plugins
mejs . plugins = {
silverlight : [
{ version : [ 3 , 0 ] , types : [ 'video/mp4' , 'video/m4v' , 'video/mov' , 'video/wmv' , 'audio/wma' , 'audio/m4a' , 'audio/mp3' , 'audio/wav' , 'audio/mpeg' ] }
] ,
flash : [
{ version : [ 9 , 0 , 124 ] , types : [ 'video/mp4' , 'video/m4v' , 'video/mov' , 'video/flv' , 'video/rtmp' , 'video/x-flv' , 'audio/flv' , 'audio/x-flv' , 'audio/mp3' , 'audio/m4a' , 'audio/mpeg' , 'video/dailymotion' , 'video/x-dailymotion' , 'application/x-mpegURL' ] }
// 'video/youtube', 'video/x-youtube',
// ,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
] ,
youtube : [
{ version : null , types : [ 'video/youtube' , 'video/x-youtube' , 'audio/youtube' , 'audio/x-youtube' ] }
] ,
vimeo : [
{ version : null , types : [ 'video/vimeo' , 'video/x-vimeo' ] }
]
} ;
/ *
Utility methods
* /
mejs . Utility = {
encodeUrl : function ( url ) {
return encodeURIComponent ( url ) ; //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
} ,
escapeHTML : function ( s ) {
return s . toString ( ) . split ( '&' ) . join ( '&' ) . split ( '<' ) . join ( '<' ) . split ( '"' ) . join ( '"' ) ;
} ,
absolutizeUrl : function ( url ) {
var el = document . createElement ( 'div' ) ;
el . innerHTML = '<a href="' + this . escapeHTML ( url ) + '">x</a>' ;
return el . firstChild . href ;
} ,
getScriptPath : function ( scriptNames ) {
var
i = 0 ,
j ,
codePath = '' ,
testname = '' ,
slashPos ,
filenamePos ,
scriptUrl ,
scriptPath ,
scriptFilename ,
scripts = document . getElementsByTagName ( 'script' ) ,
il = scripts . length ,
jl = scriptNames . length ;
// go through all <script> tags
for ( ; i < il ; i ++ ) {
scriptUrl = scripts [ i ] . src ;
slashPos = scriptUrl . lastIndexOf ( '/' ) ;
if ( slashPos > - 1 ) {
scriptFilename = scriptUrl . substring ( slashPos + 1 ) ;
scriptPath = scriptUrl . substring ( 0 , slashPos + 1 ) ;
} else {
scriptFilename = scriptUrl ;
scriptPath = '' ;
}
// see if any <script> tags have a file name that matches the
for ( j = 0 ; j < jl ; j ++ ) {
testname = scriptNames [ j ] ;
filenamePos = scriptFilename . indexOf ( testname ) ;
if ( filenamePos > - 1 ) {
codePath = scriptPath ;
break ;
}
}
// if we found a path, then break and return it
if ( codePath !== '' ) {
break ;
}
}
// send the best path back
return codePath ;
} ,
/ *
* Calculate the time format to use . We have a default format set in the
* options but it can be imcomplete . We ajust it according to the media
* duration .
*
* We support format like 'hh:mm:ss:ff' .
* /
calculateTimeFormat : function ( time , options , fps ) {
if ( time < 0 ) {
time = 0 ;
}
if ( typeof fps == 'undefined' ) {
fps = 25 ;
}
var format = options . timeFormat ,
firstChar = format [ 0 ] ,
firstTwoPlaces = ( format [ 1 ] == format [ 0 ] ) ,
separatorIndex = firstTwoPlaces ? 2 : 1 ,
separator = ':' ,
hours = Math . floor ( time / 3600 ) % 24 ,
minutes = Math . floor ( time / 60 ) % 60 ,
seconds = Math . floor ( time % 60 ) ,
frames = Math . floor ( ( ( time % 1 ) * fps ) . toFixed ( 3 ) ) ,
lis = [
[ frames , 'f' ] ,
[ seconds , 's' ] ,
[ minutes , 'm' ] ,
[ hours , 'h' ]
] ;
// Try to get the separator from the format
if ( format . length < separatorIndex ) {
separator = format [ separatorIndex ] ;
}
var required = false ;
for ( var i = 0 , len = lis . length ; i < len ; i ++ ) {
if ( format . indexOf ( lis [ i ] [ 1 ] ) !== - 1 ) {
required = true ;
}
else if ( required ) {
var hasNextValue = false ;
for ( var j = i ; j < len ; j ++ ) {
if ( lis [ j ] [ 0 ] > 0 ) {
hasNextValue = true ;
break ;
}
}
if ( ! hasNextValue ) {
break ;
}
if ( ! firstTwoPlaces ) {
format = firstChar + format ;
}
format = lis [ i ] [ 1 ] + separator + format ;
if ( firstTwoPlaces ) {
format = lis [ i ] [ 1 ] + format ;
}
firstChar = lis [ i ] [ 1 ] ;
}
}
options . currentTimeFormat = format ;
} ,
/ *
* Prefix the given number by zero if it is lower than 10.
* /
twoDigitsString : function ( n ) {
if ( n < 10 ) {
return '0' + n ;
}
return String ( n ) ;
} ,
secondsToTimeCode : function ( time , options ) {
if ( time < 0 ) {
time = 0 ;
}
// Maintain backward compatibility with method signature before v2.18.
if ( typeof options !== 'object' ) {
var format = 'm:ss' ;
format = arguments [ 1 ] ? 'hh:mm:ss' : format ; // forceHours
format = arguments [ 2 ] ? format + ':ff' : format ; // showFrameCount
options = {
currentTimeFormat : format ,
framesPerSecond : arguments [ 3 ] || 25
} ;
}
var fps = options . framesPerSecond ;
if ( typeof fps === 'undefined' ) {
fps = 25 ;
}
var format = options . currentTimeFormat ,
hours = Math . floor ( time / 3600 ) % 24 ,
minutes = Math . floor ( time / 60 ) % 60 ,
seconds = Math . floor ( time % 60 ) ,
frames = Math . floor ( ( ( time % 1 ) * fps ) . toFixed ( 3 ) ) ;
lis = [
[ frames , 'f' ] ,
[ seconds , 's' ] ,
[ minutes , 'm' ] ,
[ hours , 'h' ]
] ;
var res = format ;
for ( i = 0 , len = lis . length ; i < len ; i ++ ) {
res = res . replace ( lis [ i ] [ 1 ] + lis [ i ] [ 1 ] , this . twoDigitsString ( lis [ i ] [ 0 ] ) ) ;
res = res . replace ( lis [ i ] [ 1 ] , lis [ i ] [ 0 ] ) ;
}
return res ;
} ,
timeCodeToSeconds : function ( hh _mm _ss _ff , forceHours , showFrameCount , fps ) {
if ( typeof showFrameCount == 'undefined' ) {
showFrameCount = false ;
} else if ( typeof fps == 'undefined' ) {
fps = 25 ;
}
var tc _array = hh _mm _ss _ff . split ( ":" ) ,
tc _hh = parseInt ( tc _array [ 0 ] , 10 ) ,
tc _mm = parseInt ( tc _array [ 1 ] , 10 ) ,
tc _ss = parseInt ( tc _array [ 2 ] , 10 ) ,
tc _ff = 0 ,
tc _in _seconds = 0 ;
if ( showFrameCount ) {
tc _ff = parseInt ( tc _array [ 3 ] ) / fps ;
}
tc _in _seconds = ( tc _hh * 3600 ) + ( tc _mm * 60 ) + tc _ss + tc _ff ;
return tc _in _seconds ;
} ,
convertSMPTEtoSeconds : function ( SMPTE ) {
if ( typeof SMPTE != 'string' )
return false ;
SMPTE = SMPTE . replace ( ',' , '.' ) ;
var secs = 0 ,
decimalLen = ( SMPTE . indexOf ( '.' ) != - 1 ) ? SMPTE . split ( '.' ) [ 1 ] . length : 0 ,
multiplier = 1 ;
SMPTE = SMPTE . split ( ':' ) . reverse ( ) ;
for ( var i = 0 ; i < SMPTE . length ; i ++ ) {
multiplier = 1 ;
if ( i > 0 ) {
multiplier = Math . pow ( 60 , i ) ;
}
secs += Number ( SMPTE [ i ] ) * multiplier ;
}
return Number ( secs . toFixed ( decimalLen ) ) ;
} ,
/* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */
removeSwf : function ( id ) {
var obj = document . getElementById ( id ) ;
if ( obj && /object|embed/i . test ( obj . nodeName ) ) {
if ( mejs . MediaFeatures . isIE ) {
obj . style . display = "none" ;
( function ( ) {
if ( obj . readyState == 4 ) {
mejs . Utility . removeObjectInIE ( id ) ;
} else {
setTimeout ( arguments . callee , 10 ) ;
}
} ) ( ) ;
} else {
obj . parentNode . removeChild ( obj ) ;
}
}
} ,
removeObjectInIE : function ( id ) {
var obj = document . getElementById ( id ) ;
if ( obj ) {
for ( var i in obj ) {
if ( typeof obj [ i ] == "function" ) {
obj [ i ] = null ;
}
}
obj . parentNode . removeChild ( obj ) ;
}
} ,
determineScheme : function ( url ) {
if ( url && url . indexOf ( "://" ) != - 1 ) {
return url . substr ( 0 , url . indexOf ( "://" ) + 3 ) ;
}
return "//" ; // let user agent figure this out
}
} ;
// Core detector, plugins are added below
mejs . PluginDetector = {
// main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
hasPluginVersion : function ( plugin , v ) {
2017-01-13 03:03:07 +01:00
/ * *
* Workaround for LBRY : Safari now reports Flash being disabled even when it ' s installed .
* So we have to pretend we know it ' s installed . ( This also makes sure that users of all
* browsers get a "missing plugin" message if they don ' t have Flash installed . )
* /
if ( plugin == 'flash' ) {
return true ;
}
2016-07-20 22:20:25 +02:00
var pv = this . plugins [ plugin ] ;
v [ 1 ] = v [ 1 ] || 0 ;
v [ 2 ] = v [ 2 ] || 0 ;
return ( pv [ 0 ] > v [ 0 ] || ( pv [ 0 ] == v [ 0 ] && pv [ 1 ] > v [ 1 ] ) || ( pv [ 0 ] == v [ 0 ] && pv [ 1 ] == v [ 1 ] && pv [ 2 ] >= v [ 2 ] ) ) ? true : false ;
} ,
// cached values
nav : window . navigator ,
ua : window . navigator . userAgent . toLowerCase ( ) ,
// stored version numbers
plugins : [ ] ,
// runs detectPlugin() and stores the version number
addPlugin : function ( p , pluginName , mimeType , activeX , axDetect ) {
this . plugins [ p ] = this . detectPlugin ( pluginName , mimeType , activeX , axDetect ) ;
} ,
// get the version number from the mimetype (all but IE) or ActiveX (IE)
detectPlugin : function ( pluginName , mimeType , activeX , axDetect ) {
var version = [ 0 , 0 , 0 ] ,
description ,
i ,
ax ;
// Firefox, Webkit, Opera
if ( typeof ( this . nav . plugins ) != 'undefined' && typeof this . nav . plugins [ pluginName ] == 'object' ) {
description = this . nav . plugins [ pluginName ] . description ;
if ( description && ! ( typeof this . nav . mimeTypes != 'undefined' && this . nav . mimeTypes [ mimeType ] && ! this . nav . mimeTypes [ mimeType ] . enabledPlugin ) ) {
version = description . replace ( pluginName , '' ) . replace ( /^\s+/ , '' ) . replace ( /\sr/gi , '.' ) . split ( '.' ) ;
for ( i = 0 ; i < version . length ; i ++ ) {
version [ i ] = parseInt ( version [ i ] . match ( /\d+/ ) , 10 ) ;
}
}
// Internet Explorer / ActiveX
} else if ( typeof ( window . ActiveXObject ) != 'undefined' ) {
try {
ax = new ActiveXObject ( activeX ) ;
if ( ax ) {
version = axDetect ( ax ) ;
}
}
catch ( e ) { }
}
return version ;
}
} ;
// Add Flash detection
mejs . PluginDetector . addPlugin ( 'flash' , 'Shockwave Flash' , 'application/x-shockwave-flash' , 'ShockwaveFlash.ShockwaveFlash' , function ( ax ) {
// adapted from SWFObject
var version = [ ] ,
d = ax . GetVariable ( "$version" ) ;
if ( d ) {
d = d . split ( " " ) [ 1 ] . split ( "," ) ;
version = [ parseInt ( d [ 0 ] , 10 ) , parseInt ( d [ 1 ] , 10 ) , parseInt ( d [ 2 ] , 10 ) ] ;
}
return version ;
} ) ;
// Add Silverlight detection
mejs . PluginDetector . addPlugin ( 'silverlight' , 'Silverlight Plug-In' , 'application/x-silverlight-2' , 'AgControl.AgControl' , function ( ax ) {
// Silverlight cannot report its version number to IE
// but it does have a isVersionSupported function, so we have to loop through it to get a version number.
// adapted from http://www.silverlightversion.com/
var v = [ 0 , 0 , 0 , 0 ] ,
loopMatch = function ( ax , v , i , n ) {
while ( ax . isVersionSupported ( v [ 0 ] + "." + v [ 1 ] + "." + v [ 2 ] + "." + v [ 3 ] ) ) {
v [ i ] += n ;
}
v [ i ] -= n ;
} ;
loopMatch ( ax , v , 0 , 1 ) ;
loopMatch ( ax , v , 1 , 1 ) ;
loopMatch ( ax , v , 2 , 10000 ) ; // the third place in the version number is usually 5 digits (4.0.xxxxx)
loopMatch ( ax , v , 2 , 1000 ) ;
loopMatch ( ax , v , 2 , 100 ) ;
loopMatch ( ax , v , 2 , 10 ) ;
loopMatch ( ax , v , 2 , 1 ) ;
loopMatch ( ax , v , 3 , 1 ) ;
return v ;
} ) ;
// add adobe acrobat
/ *
PluginDetector . addPlugin ( 'acrobat' , 'Adobe Acrobat' , 'application/pdf' , 'AcroPDF.PDF' , function ( ax ) {
var version = [ ] ,
d = ax . GetVersions ( ) . split ( ',' ) [ 0 ] . split ( '=' ) [ 1 ] . split ( '.' ) ;
if ( d ) {
version = [ parseInt ( d [ 0 ] , 10 ) , parseInt ( d [ 1 ] , 10 ) , parseInt ( d [ 2 ] , 10 ) ] ;
}
return version ;
} ) ;
* /
// necessary detection (fixes for <IE9)
mejs . MediaFeatures = {
init : function ( ) {
var
t = this ,
d = document ,
nav = mejs . PluginDetector . nav ,
ua = mejs . PluginDetector . ua . toLowerCase ( ) ,
i ,
v ,
html5Elements = [ 'source' , 'track' , 'audio' , 'video' ] ;
// detect browsers (only the ones that have some kind of quirk we need to work around)
t . isiPad = ( ua . match ( /ipad/i ) !== null ) ;
t . isiPhone = ( ua . match ( /iphone/i ) !== null ) ;
t . isiOS = t . isiPhone || t . isiPad ;
t . isAndroid = ( ua . match ( /android/i ) !== null ) ;
t . isBustedAndroid = ( ua . match ( /android 2\.[12]/ ) !== null ) ;
t . isBustedNativeHTTPS = ( location . protocol === 'https:' && ( ua . match ( /android [12]\./ ) !== null || ua . match ( /macintosh.* version.* safari/ ) !== null ) ) ;
t . isIE = ( nav . appName . toLowerCase ( ) . indexOf ( "microsoft" ) != - 1 || nav . appName . toLowerCase ( ) . match ( /trident/gi ) !== null ) ;
t . isChrome = ( ua . match ( /chrome/gi ) !== null ) ;
t . isChromium = ( ua . match ( /chromium/gi ) !== null ) ;
t . isFirefox = ( ua . match ( /firefox/gi ) !== null ) ;
t . isWebkit = ( ua . match ( /webkit/gi ) !== null ) ;
t . isGecko = ( ua . match ( /gecko/gi ) !== null ) && ! t . isWebkit && ! t . isIE ;
t . isOpera = ( ua . match ( /opera/gi ) !== null ) ;
t . hasTouch = ( 'ontouchstart' in window ) ; // && window.ontouchstart != null); // this breaks iOS 7
// Borrowed from `Modernizr.svgasimg`, sources:
// - https://github.com/Modernizr/Modernizr/issues/687
// - https://github.com/Modernizr/Modernizr/pull/1209/files
t . svgAsImg = ! ! document . implementation . hasFeature ( 'http://www.w3.org/TR/SVG11/feature#Image' , '1.1' ) ;
// create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
for ( i = 0 ; i < html5Elements . length ; i ++ ) {
v = document . createElement ( html5Elements [ i ] ) ;
}
t . supportsMediaTag = ( typeof v . canPlayType !== 'undefined' || t . isBustedAndroid ) ;
// Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer)
try {
v . canPlayType ( "video/mp4" ) ;
} catch ( e ) {
t . supportsMediaTag = false ;
}
t . supportsPointerEvents = ( function ( ) {
// TAKEN FROM MODERNIZR
var element = document . createElement ( 'x' ) ,
documentElement = document . documentElement ,
getComputedStyle = window . getComputedStyle ,
supports ;
if ( ! ( 'pointerEvents' in element . style ) ) {
return false ;
}
element . style . pointerEvents = 'auto' ;
element . style . pointerEvents = 'x' ;
documentElement . appendChild ( element ) ;
supports = getComputedStyle &&
getComputedStyle ( element , '' ) . pointerEvents === 'auto' ;
documentElement . removeChild ( element ) ;
return ! ! supports ;
} ) ( ) ;
// Older versions of Firefox can't move plugins around without it resetting,
t . hasFirefoxPluginMovingProblem = false ;
// detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
// iOS
t . hasiOSFullScreen = ( typeof v . webkitEnterFullscreen !== 'undefined' ) ;
// W3C
t . hasNativeFullscreen = ( typeof v . requestFullscreen !== 'undefined' ) ;
// webkit/firefox/IE11+
t . hasWebkitNativeFullScreen = ( typeof v . webkitRequestFullScreen !== 'undefined' ) ;
t . hasMozNativeFullScreen = ( typeof v . mozRequestFullScreen !== 'undefined' ) ;
t . hasMsNativeFullScreen = ( typeof v . msRequestFullscreen !== 'undefined' ) ;
t . hasTrueNativeFullScreen = ( t . hasWebkitNativeFullScreen || t . hasMozNativeFullScreen || t . hasMsNativeFullScreen ) ;
t . nativeFullScreenEnabled = t . hasTrueNativeFullScreen ;
// Enabled?
if ( t . hasMozNativeFullScreen ) {
t . nativeFullScreenEnabled = document . mozFullScreenEnabled ;
} else if ( t . hasMsNativeFullScreen ) {
t . nativeFullScreenEnabled = document . msFullscreenEnabled ;
}
if ( t . isChrome ) {
t . hasiOSFullScreen = false ;
}
if ( t . hasTrueNativeFullScreen ) {
t . fullScreenEventName = '' ;
if ( t . hasWebkitNativeFullScreen ) {
t . fullScreenEventName = 'webkitfullscreenchange' ;
} else if ( t . hasMozNativeFullScreen ) {
t . fullScreenEventName = 'mozfullscreenchange' ;
} else if ( t . hasMsNativeFullScreen ) {
t . fullScreenEventName = 'MSFullscreenChange' ;
}
t . isFullScreen = function ( ) {
if ( t . hasMozNativeFullScreen ) {
return d . mozFullScreen ;
} else if ( t . hasWebkitNativeFullScreen ) {
return d . webkitIsFullScreen ;
} else if ( t . hasMsNativeFullScreen ) {
return d . msFullscreenElement !== null ;
}
}
t . requestFullScreen = function ( el ) {
if ( t . hasWebkitNativeFullScreen ) {
el . webkitRequestFullScreen ( ) ;
} else if ( t . hasMozNativeFullScreen ) {
el . mozRequestFullScreen ( ) ;
} else if ( t . hasMsNativeFullScreen ) {
el . msRequestFullscreen ( ) ;
}
}
t . cancelFullScreen = function ( ) {
if ( t . hasWebkitNativeFullScreen ) {
document . webkitCancelFullScreen ( ) ;
} else if ( t . hasMozNativeFullScreen ) {
document . mozCancelFullScreen ( ) ;
} else if ( t . hasMsNativeFullScreen ) {
document . msExitFullscreen ( ) ;
}
}
}
// OS X 10.5 can't do this even if it says it can :(
if ( t . hasiOSFullScreen && ua . match ( /mac os x 10_5/i ) ) {
t . hasNativeFullScreen = false ;
t . hasiOSFullScreen = false ;
}
}
} ;
mejs . MediaFeatures . init ( ) ;
/ *
extension methods to < video > or < audio > object to bring it into parity with PluginMediaElement ( see below )
* /
mejs . HtmlMediaElement = {
pluginType : 'native' ,
isFullScreen : false ,
setCurrentTime : function ( time ) {
this . currentTime = time ;
} ,
setMuted : function ( muted ) {
this . muted = muted ;
} ,
setVolume : function ( volume ) {
this . volume = volume ;
} ,
// for parity with the plugin versions
stop : function ( ) {
this . pause ( ) ;
} ,
// This can be a url string
// or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
setSrc : function ( url ) {
// Fix for IE9 which can't set .src when there are <source> elements. Awesome, right?
var
existingSources = this . getElementsByTagName ( 'source' ) ;
while ( existingSources . length > 0 ) {
this . removeChild ( existingSources [ 0 ] ) ;
}
if ( typeof url == 'string' ) {
this . src = url ;
} else {
var i , media ;
for ( i = 0 ; i < url . length ; i ++ ) {
media = url [ i ] ;
if ( this . canPlayType ( media . type ) ) {
this . src = media . src ;
break ;
}
}
}
} ,
setVideoSize : function ( width , height ) {
this . width = width ;
this . height = height ;
}
} ;
/ *
Mimics the < video / audio > element by calling Flash ' s External Interface or Silverlights [ ScriptableMember ]
* /
mejs . PluginMediaElement = function ( pluginid , pluginType , mediaUrl ) {
this . id = pluginid ;
this . pluginType = pluginType ;
this . src = mediaUrl ;
this . events = { } ;
this . attributes = { } ;
} ;
// JavaScript values and ExternalInterface methods that match HTML5 video properties methods
// http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
mejs . PluginMediaElement . prototype = {
// special
pluginElement : null ,
pluginType : '' ,
isFullScreen : false ,
// not implemented :(
playbackRate : - 1 ,
defaultPlaybackRate : - 1 ,
seekable : [ ] ,
played : [ ] ,
// HTML5 read-only properties
paused : true ,
ended : false ,
seeking : false ,
duration : 0 ,
error : null ,
tagName : '' ,
// HTML5 get/set properties, but only set (updated by event handlers)
muted : false ,
volume : 1 ,
currentTime : 0 ,
// HTML5 methods
play : function ( ) {
if ( this . pluginApi != null ) {
if ( this . pluginType == 'youtube' || this . pluginType == 'vimeo' ) {
this . pluginApi . playVideo ( ) ;
} else {
this . pluginApi . playMedia ( ) ;
}
this . paused = false ;
}
} ,
load : function ( ) {
if ( this . pluginApi != null ) {
if ( this . pluginType == 'youtube' || this . pluginType == 'vimeo' ) {
} else {
this . pluginApi . loadMedia ( ) ;
}
this . paused = false ;
}
} ,
pause : function ( ) {
if ( this . pluginApi != null ) {
if ( this . pluginType == 'youtube' || this . pluginType == 'vimeo' ) {
if ( this . pluginApi . getPlayerState ( ) == 1 ) {
this . pluginApi . pauseVideo ( ) ;
}
} else {
this . pluginApi . pauseMedia ( ) ;
}
this . paused = true ;
}
} ,
stop : function ( ) {
if ( this . pluginApi != null ) {
if ( this . pluginType == 'youtube' || this . pluginType == 'vimeo' ) {
this . pluginApi . stopVideo ( ) ;
} else {
this . pluginApi . stopMedia ( ) ;
}
this . paused = true ;
}
} ,
canPlayType : function ( type ) {
var i ,
j ,
pluginInfo ,
pluginVersions = mejs . plugins [ this . pluginType ] ;
for ( i = 0 ; i < pluginVersions . length ; i ++ ) {
pluginInfo = pluginVersions [ i ] ;
// test if user has the correct plugin version
if ( mejs . PluginDetector . hasPluginVersion ( this . pluginType , pluginInfo . version ) ) {
// test for plugin playback types
for ( j = 0 ; j < pluginInfo . types . length ; j ++ ) {
// find plugin that can play the type
if ( type == pluginInfo . types [ j ] ) {
return 'probably' ;
}
}
}
}
return '' ;
} ,
positionFullscreenButton : function ( x , y , visibleAndAbove ) {
if ( this . pluginApi != null && this . pluginApi . positionFullscreenButton ) {
this . pluginApi . positionFullscreenButton ( Math . floor ( x ) , Math . floor ( y ) , visibleAndAbove ) ;
}
} ,
hideFullscreenButton : function ( ) {
if ( this . pluginApi != null && this . pluginApi . hideFullscreenButton ) {
this . pluginApi . hideFullscreenButton ( ) ;
}
} ,
// custom methods since not all JavaScript implementations support get/set
// This can be a url string
// or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
setSrc : function ( url ) {
if ( typeof url == 'string' ) {
this . pluginApi . setSrc ( mejs . Utility . absolutizeUrl ( url ) ) ;
this . src = mejs . Utility . absolutizeUrl ( url ) ;
} else {
var i , media ;
for ( i = 0 ; i < url . length ; i ++ ) {
media = url [ i ] ;
if ( this . canPlayType ( media . type ) ) {
this . pluginApi . setSrc ( mejs . Utility . absolutizeUrl ( media . src ) ) ;
this . src = mejs . Utility . absolutizeUrl ( media . src ) ;
break ;
}
}
}
} ,
setCurrentTime : function ( time ) {
if ( this . pluginApi != null ) {
if ( this . pluginType == 'youtube' || this . pluginType == 'vimeo' ) {
this . pluginApi . seekTo ( time ) ;
} else {
this . pluginApi . setCurrentTime ( time ) ;
}
this . currentTime = time ;
}
} ,
setVolume : function ( volume ) {
if ( this . pluginApi != null ) {
// same on YouTube and MEjs
if ( this . pluginType == 'youtube' ) {
this . pluginApi . setVolume ( volume * 100 ) ;
} else {
this . pluginApi . setVolume ( volume ) ;
}
this . volume = volume ;
}
} ,
setMuted : function ( muted ) {
if ( this . pluginApi != null ) {
if ( this . pluginType == 'youtube' ) {
if ( muted ) {
this . pluginApi . mute ( ) ;
} else {
this . pluginApi . unMute ( ) ;
}
this . muted = muted ;
this . dispatchEvent ( { type : 'volumechange' } ) ;
} else {
this . pluginApi . setMuted ( muted ) ;
}
this . muted = muted ;
}
} ,
// additional non-HTML5 methods
setVideoSize : function ( width , height ) {
//if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
if ( this . pluginElement && this . pluginElement . style ) {
this . pluginElement . style . width = width + 'px' ;
this . pluginElement . style . height = height + 'px' ;
}
if ( this . pluginApi != null && this . pluginApi . setVideoSize ) {
this . pluginApi . setVideoSize ( width , height ) ;
}
//}
} ,
setFullscreen : function ( fullscreen ) {
if ( this . pluginApi != null && this . pluginApi . setFullscreen ) {
this . pluginApi . setFullscreen ( fullscreen ) ;
}
} ,
enterFullScreen : function ( ) {
if ( this . pluginApi != null && this . pluginApi . setFullscreen ) {
this . setFullscreen ( true ) ;
}
} ,
exitFullScreen : function ( ) {
if ( this . pluginApi != null && this . pluginApi . setFullscreen ) {
this . setFullscreen ( false ) ;
}
} ,
// start: fake events
addEventListener : function ( eventName , callback , bubble ) {
this . events [ eventName ] = this . events [ eventName ] || [ ] ;
this . events [ eventName ] . push ( callback ) ;
} ,
removeEventListener : function ( eventName , callback ) {
if ( ! eventName ) { this . events = { } ; return true ; }
var callbacks = this . events [ eventName ] ;
if ( ! callbacks ) return true ;
if ( ! callback ) { this . events [ eventName ] = [ ] ; return true ; }
for ( var i = 0 ; i < callbacks . length ; i ++ ) {
if ( callbacks [ i ] === callback ) {
this . events [ eventName ] . splice ( i , 1 ) ;
return true ;
}
}
return false ;
} ,
dispatchEvent : function ( event ) {
var i ,
args ,
callbacks = this . events [ event . type ] ;
if ( callbacks ) {
for ( i = 0 ; i < callbacks . length ; i ++ ) {
callbacks [ i ] . apply ( this , [ event ] ) ;
}
}
} ,
// end: fake events
// fake DOM attribute methods
hasAttribute : function ( name ) {
return ( name in this . attributes ) ;
} ,
removeAttribute : function ( name ) {
delete this . attributes [ name ] ;
} ,
getAttribute : function ( name ) {
if ( this . hasAttribute ( name ) ) {
return this . attributes [ name ] ;
}
return '' ;
} ,
setAttribute : function ( name , value ) {
this . attributes [ name ] = value ;
} ,
remove : function ( ) {
mejs . Utility . removeSwf ( this . pluginElement . id ) ;
mejs . MediaPluginBridge . unregisterPluginElement ( this . pluginElement . id ) ;
}
} ;
/ *
Default options
* /
mejs . MediaElementDefaults = {
// allows testing on HTML5, flash, silverlight
// auto: attempts to detect what the browser can do
// auto_plugin: prefer plugins and then attempt native HTML5
// native: forces HTML5 playback
// shim: disallows HTML5, will attempt either Flash or Silverlight
// none: forces fallback view
mode : 'auto' ,
// remove or reorder to change plugin priority and availability
plugins : [ 'flash' , 'silverlight' , 'youtube' , 'vimeo' ] ,
// shows debug errors on screen
enablePluginDebug : false ,
// use plugin for browsers that have trouble with Basic Authentication on HTTPS sites
httpsBasicAuthSite : false ,
// overrides the type specified, useful for dynamic instantiation
type : '' ,
// path to Flash and Silverlight plugins
pluginPath : mejs . Utility . getScriptPath ( [ 'mediaelement.js' , 'mediaelement.min.js' , 'mediaelement-and-player.js' , 'mediaelement-and-player.min.js' ] ) ,
// name of flash file
flashName : 'flashmediaelement.swf' ,
// streamer for RTMP streaming
flashStreamer : '' ,
// set to 'always' for CDN version
flashScriptAccess : 'sameDomain' ,
// turns on the smoothing filter in Flash
enablePluginSmoothing : false ,
// enabled pseudo-streaming (seek) on .mp4 files
enablePseudoStreaming : false ,
// start query parameter sent to server for pseudo-streaming
pseudoStreamingStartQueryParam : 'start' ,
// name of silverlight file
silverlightName : 'silverlightmediaelement.xap' ,
// default if the <video width> is not specified
defaultVideoWidth : 480 ,
// default if the <video height> is not specified
defaultVideoHeight : 270 ,
// overrides <video width>
pluginWidth : - 1 ,
// overrides <video height>
pluginHeight : - 1 ,
// additional plugin variables in 'key=value' form
pluginVars : [ ] ,
// rate in milliseconds for Flash and Silverlight to fire the timeupdate event
// larger number is less accurate, but less strain on plugin->JavaScript bridge
timerRate : 250 ,
// initial volume for player
startVolume : 0.8 ,
success : function ( ) { } ,
error : function ( ) { }
} ;
/ *
Determines if a browser supports the < video > or < audio > element
and returns either the native element or a Flash / Silverlight version that
mimics HTML5 MediaElement
* /
mejs . MediaElement = function ( el , o ) {
return mejs . HtmlMediaElementShim . create ( el , o ) ;
} ;
mejs . HtmlMediaElementShim = {
create : function ( el , o ) {
var
options = { } ,
htmlMediaElement = ( typeof ( el ) == 'string' ) ? document . getElementById ( el ) : el ,
tagName = htmlMediaElement . tagName . toLowerCase ( ) ,
isMediaTag = ( tagName === 'audio' || tagName === 'video' ) ,
src = ( isMediaTag ) ? htmlMediaElement . getAttribute ( 'src' ) : htmlMediaElement . getAttribute ( 'href' ) ,
poster = htmlMediaElement . getAttribute ( 'poster' ) ,
autoplay = htmlMediaElement . getAttribute ( 'autoplay' ) ,
preload = htmlMediaElement . getAttribute ( 'preload' ) ,
controls = htmlMediaElement . getAttribute ( 'controls' ) ,
playback ,
prop ;
// extend options
for ( prop in mejs . MediaElementDefaults ) {
options [ prop ] = mejs . MediaElementDefaults [ prop ] ;
}
for ( prop in o ) {
options [ prop ] = o [ prop ] ;
}
// clean up attributes
src = ( typeof src == 'undefined' || src === null || src == '' ) ? null : src ;
poster = ( typeof poster == 'undefined' || poster === null ) ? '' : poster ;
preload = ( typeof preload == 'undefined' || preload === null || preload === 'false' ) ? 'none' : preload ;
autoplay = ! ( typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false' ) ;
controls = ! ( typeof controls == 'undefined' || controls === null || controls === 'false' ) ;
// test for HTML5 and plugin capabilities
playback = this . determinePlayback ( htmlMediaElement , options , mejs . MediaFeatures . supportsMediaTag , isMediaTag , src ) ;
playback . url = ( playback . url !== null ) ? mejs . Utility . absolutizeUrl ( playback . url ) : '' ;
playback . scheme = mejs . Utility . determineScheme ( playback . url ) ;
if ( playback . method == 'native' ) {
// second fix for android
if ( mejs . MediaFeatures . isBustedAndroid ) {
htmlMediaElement . src = playback . url ;
htmlMediaElement . addEventListener ( 'click' , function ( ) {
htmlMediaElement . play ( ) ;
} , false ) ;
}
// add methods to native HTMLMediaElement
return this . updateNative ( playback , options , autoplay , preload ) ;
} else if ( playback . method !== '' ) {
// create plugin to mimic HTMLMediaElement
return this . createPlugin ( playback , options , poster , autoplay , preload , controls ) ;
} else {
// boo, no HTML5, no Flash, no Silverlight.
this . createErrorMessage ( playback , options , poster ) ;
return this ;
}
} ,
determinePlayback : function ( htmlMediaElement , options , supportsMediaTag , isMediaTag , src ) {
var
mediaFiles = [ ] ,
i ,
j ,
k ,
l ,
n ,
type ,
result = { method : '' , url : '' , htmlMediaElement : htmlMediaElement , isVideo : ( htmlMediaElement . tagName . toLowerCase ( ) != 'audio' ) , scheme : '' } ,
pluginName ,
pluginVersions ,
pluginInfo ,
dummy ,
media ;
// STEP 1: Get URL and type from <video src> or <source src>
// supplied type overrides <video type> and <source type>
if ( typeof options . type != 'undefined' && options . type !== '' ) {
// accept either string or array of types
if ( typeof options . type == 'string' ) {
mediaFiles . push ( { type : options . type , url : src } ) ;
} else {
for ( i = 0 ; i < options . type . length ; i ++ ) {
mediaFiles . push ( { type : options . type [ i ] , url : src } ) ;
}
}
// test for src attribute first
} else if ( src !== null ) {
type = this . formatType ( src , htmlMediaElement . getAttribute ( 'type' ) ) ;
mediaFiles . push ( { type : type , url : src } ) ;
// then test for <source> elements
} else {
// test <source> types to see if they are usable
for ( i = 0 ; i < htmlMediaElement . childNodes . length ; i ++ ) {
n = htmlMediaElement . childNodes [ i ] ;
if ( n . nodeType == 1 && n . tagName . toLowerCase ( ) == 'source' ) {
src = n . getAttribute ( 'src' ) ;
type = this . formatType ( src , n . getAttribute ( 'type' ) ) ;
media = n . getAttribute ( 'media' ) ;
if ( ! media || ! window . matchMedia || ( window . matchMedia && window . matchMedia ( media ) . matches ) ) {
mediaFiles . push ( { type : type , url : src } ) ;
}
}
}
}
// in the case of dynamicly created players
// check for audio types
if ( ! isMediaTag && mediaFiles . length > 0 && mediaFiles [ 0 ] . url !== null && this . getTypeFromFile ( mediaFiles [ 0 ] . url ) . indexOf ( 'audio' ) > - 1 ) {
result . isVideo = false ;
}
// STEP 2: Test for playback method
// special case for Android which sadly doesn't implement the canPlayType function (always returns '')
if ( mejs . MediaFeatures . isBustedAndroid ) {
htmlMediaElement . canPlayType = function ( type ) {
return ( type . match ( /video\/(mp4|m4v)/gi ) !== null ) ? 'maybe' : '' ;
} ;
}
// special case for Chromium to specify natively supported video codecs (i.e. WebM and Theora)
if ( mejs . MediaFeatures . isChromium ) {
htmlMediaElement . canPlayType = function ( type ) {
return ( type . match ( /video\/(webm|ogv|ogg)/gi ) !== null ) ? 'maybe' : '' ;
} ;
}
// test for native playback first
if ( supportsMediaTag && ( options . mode === 'auto' || options . mode === 'auto_plugin' || options . mode === 'native' ) && ! ( mejs . MediaFeatures . isBustedNativeHTTPS && options . httpsBasicAuthSite === true ) ) {
if ( ! isMediaTag ) {
// create a real HTML5 Media Element
dummy = document . createElement ( result . isVideo ? 'video' : 'audio' ) ;
htmlMediaElement . parentNode . insertBefore ( dummy , htmlMediaElement ) ;
htmlMediaElement . style . display = 'none' ;
// use this one from now on
result . htmlMediaElement = htmlMediaElement = dummy ;
}
for ( i = 0 ; i < mediaFiles . length ; i ++ ) {
// normal check
if ( mediaFiles [ i ] . type == "video/m3u8" || htmlMediaElement . canPlayType ( mediaFiles [ i ] . type ) . replace ( /no/ , '' ) !== ''
// special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
|| htmlMediaElement . canPlayType ( mediaFiles [ i ] . type . replace ( /mp3/ , 'mpeg' ) ) . replace ( /no/ , '' ) !== ''
// special case for m4a supported by detecting mp4 support
|| htmlMediaElement . canPlayType ( mediaFiles [ i ] . type . replace ( /m4a/ , 'mp4' ) ) . replace ( /no/ , '' ) !== '' ) {
result . method = 'native' ;
result . url = mediaFiles [ i ] . url ;
break ;
}
}
if ( result . method === 'native' ) {
if ( result . url !== null ) {
htmlMediaElement . src = result . url ;
}
// if `auto_plugin` mode, then cache the native result but try plugins.
if ( options . mode !== 'auto_plugin' ) {
return result ;
}
}
}
// if native playback didn't work, then test plugins
if ( options . mode === 'auto' || options . mode === 'auto_plugin' || options . mode === 'shim' ) {
for ( i = 0 ; i < mediaFiles . length ; i ++ ) {
type = mediaFiles [ i ] . type ;
// test all plugins in order of preference [silverlight, flash]
for ( j = 0 ; j < options . plugins . length ; j ++ ) {
pluginName = options . plugins [ j ] ;
// test version of plugin (for future features)
pluginVersions = mejs . plugins [ pluginName ] ;
for ( k = 0 ; k < pluginVersions . length ; k ++ ) {
pluginInfo = pluginVersions [ k ] ;
// test if user has the correct plugin version
// for youtube/vimeo
if ( pluginInfo . version == null ||
mejs . PluginDetector . hasPluginVersion ( pluginName , pluginInfo . version ) ) {
// test for plugin playback types
for ( l = 0 ; l < pluginInfo . types . length ; l ++ ) {
// find plugin that can play the type
if ( type . toLowerCase ( ) == pluginInfo . types [ l ] . toLowerCase ( ) ) {
result . method = pluginName ;
result . url = mediaFiles [ i ] . url ;
return result ;
}
}
}
}
}
}
}
// at this point, being in 'auto_plugin' mode implies that we tried plugins but failed.
// if we have native support then return that.
if ( options . mode === 'auto_plugin' && result . method === 'native' ) {
return result ;
}
// what if there's nothing to play? just grab the first available
if ( result . method === '' && mediaFiles . length > 0 ) {
result . url = mediaFiles [ 0 ] . url ;
}
return result ;
} ,
formatType : function ( url , type ) {
// if no type is supplied, fake it with the extension
if ( url && ! type ) {
return this . getTypeFromFile ( url ) ;
} else {
// only return the mime part of the type in case the attribute contains the codec
// see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
// `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
if ( type && ~ type . indexOf ( ';' ) ) {
return type . substr ( 0 , type . indexOf ( ';' ) ) ;
} else {
return type ;
}
}
} ,
getTypeFromFile : function ( url ) {
url = url . split ( '?' ) [ 0 ] ;
var
ext = url . substring ( url . lastIndexOf ( '.' ) + 1 ) . toLowerCase ( ) ,
av = /(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi . test ( ext ) ? 'video/' : 'audio/' ;
return this . getTypeFromExtension ( ext , av ) ;
} ,
getTypeFromExtension : function ( ext , av ) {
av = av || '' ;
switch ( ext ) {
case 'mp4' :
case 'm4v' :
case 'm4a' :
case 'f4v' :
case 'f4a' :
return av + 'mp4' ;
case 'flv' :
return av + 'x-flv' ;
case 'webm' :
case 'webma' :
case 'webmv' :
return av + 'webm' ;
case 'ogg' :
case 'oga' :
case 'ogv' :
return av + 'ogg' ;
case 'm3u8' :
return 'application/x-mpegurl' ;
case 'ts' :
return av + 'mp2t' ;
default :
return av + ext ;
}
} ,
createErrorMessage : function ( playback , options , poster ) {
var
htmlMediaElement = playback . htmlMediaElement ,
errorContainer = document . createElement ( 'div' ) ,
errorContent = options . customError ;
errorContainer . className = 'me-cannotplay' ;
try {
errorContainer . style . width = htmlMediaElement . width + 'px' ;
errorContainer . style . height = htmlMediaElement . height + 'px' ;
} catch ( e ) { }
if ( ! errorContent ) {
errorContent = '<a href="' + playback . url + '">' ;
if ( poster !== '' ) {
errorContent += '<img src="' + poster + '" width="100%" height="100%" alt="" />' ;
}
errorContent += '<span>' + mejs . i18n . t ( 'Download File' ) + '</span></a>' ;
}
errorContainer . innerHTML = errorContent ;
htmlMediaElement . parentNode . insertBefore ( errorContainer , htmlMediaElement ) ;
htmlMediaElement . style . display = 'none' ;
options . error ( htmlMediaElement ) ;
} ,
createPlugin : function ( playback , options , poster , autoplay , preload , controls ) {
var
htmlMediaElement = playback . htmlMediaElement ,
width = 1 ,
height = 1 ,
pluginid = 'me_' + playback . method + '_' + ( mejs . meIndex ++ ) ,
pluginMediaElement = new mejs . PluginMediaElement ( pluginid , playback . method , playback . url ) ,
container = document . createElement ( 'div' ) ,
specialIEContainer ,
node ,
initVars ;
// copy tagName from html media element
pluginMediaElement . tagName = htmlMediaElement . tagName
// copy attributes from html media element to plugin media element
for ( var i = 0 ; i < htmlMediaElement . attributes . length ; i ++ ) {
var attribute = htmlMediaElement . attributes [ i ] ;
if ( attribute . specified ) {
pluginMediaElement . setAttribute ( attribute . name , attribute . value ) ;
}
}
// check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
node = htmlMediaElement . parentNode ;
while ( node !== null && node . tagName != null && node . tagName . toLowerCase ( ) !== 'body' &&
node . parentNode != null && node . parentNode . tagName != null && node . parentNode . constructor != null && node . parentNode . constructor . name === "ShadowRoot" ) {
if ( node . parentNode . tagName . toLowerCase ( ) === 'p' ) {
node . parentNode . parentNode . insertBefore ( node , node . parentNode ) ;
break ;
}
node = node . parentNode ;
}
if ( playback . isVideo ) {
width = ( options . pluginWidth > 0 ) ? options . pluginWidth : ( options . videoWidth > 0 ) ? options . videoWidth : ( htmlMediaElement . getAttribute ( 'width' ) !== null ) ? htmlMediaElement . getAttribute ( 'width' ) : options . defaultVideoWidth ;
height = ( options . pluginHeight > 0 ) ? options . pluginHeight : ( options . videoHeight > 0 ) ? options . videoHeight : ( htmlMediaElement . getAttribute ( 'height' ) !== null ) ? htmlMediaElement . getAttribute ( 'height' ) : options . defaultVideoHeight ;
// in case of '%' make sure it's encoded
width = mejs . Utility . encodeUrl ( width ) ;
height = mejs . Utility . encodeUrl ( height ) ;
} else {
if ( options . enablePluginDebug ) {
width = 320 ;
height = 240 ;
}
}
// register plugin
pluginMediaElement . success = options . success ;
// add container (must be added to DOM before inserting HTML for IE)
container . className = 'me-plugin' ;
container . id = pluginid + '_container' ;
if ( playback . isVideo ) {
htmlMediaElement . parentNode . insertBefore ( container , htmlMediaElement ) ;
} else {
document . body . insertBefore ( container , document . body . childNodes [ 0 ] ) ;
}
if ( playback . method === 'flash' || playback . method === 'silverlight' ) {
// flash/silverlight vars
initVars = [
'id=' + pluginid ,
'isvideo=' + ( ( playback . isVideo ) ? "true" : "false" ) ,
'autoplay=' + ( ( autoplay ) ? "true" : "false" ) ,
'preload=' + preload ,
'width=' + width ,
'startvolume=' + options . startVolume ,
'timerrate=' + options . timerRate ,
'flashstreamer=' + options . flashStreamer ,
'height=' + height ,
'pseudostreamstart=' + options . pseudoStreamingStartQueryParam ] ;
if ( playback . url !== null ) {
if ( playback . method == 'flash' ) {
initVars . push ( 'file=' + mejs . Utility . encodeUrl ( playback . url ) ) ;
} else {
initVars . push ( 'file=' + playback . url ) ;
}
}
if ( options . enablePluginDebug ) {
initVars . push ( 'debug=true' ) ;
}
if ( options . enablePluginSmoothing ) {
initVars . push ( 'smoothing=true' ) ;
}
if ( options . enablePseudoStreaming ) {
initVars . push ( 'pseudostreaming=true' ) ;
}
if ( controls ) {
initVars . push ( 'controls=true' ) ; // shows controls in the plugin if desired
}
if ( options . pluginVars ) {
initVars = initVars . concat ( options . pluginVars ) ;
}
// call from plugin
window [ pluginid + '_init' ] = function ( ) {
switch ( pluginMediaElement . pluginType ) {
case 'flash' :
pluginMediaElement . pluginElement = pluginMediaElement . pluginApi = document . getElementById ( pluginid ) ;
break ;
case 'silverlight' :
pluginMediaElement . pluginElement = document . getElementById ( pluginMediaElement . id ) ;
pluginMediaElement . pluginApi = pluginMediaElement . pluginElement . Content . MediaElementJS ;
break ;
}
if ( pluginMediaElement . pluginApi != null && pluginMediaElement . success ) {
pluginMediaElement . success ( pluginMediaElement , htmlMediaElement ) ;
}
}
// event call from plugin
window [ pluginid + '_event' ] = function ( eventName , values ) {
var
e ,
i ,
bufferedTime ;
// fake event object to mimic real HTML media event.
e = {
type : eventName ,
target : pluginMediaElement
} ;
// attach all values to element and event object
for ( i in values ) {
pluginMediaElement [ i ] = values [ i ] ;
e [ i ] = values [ i ] ;
}
// fake the newer W3C buffered TimeRange (loaded and total have been removed)
bufferedTime = values . bufferedTime || 0 ;
e . target . buffered = e . buffered = {
start : function ( index ) {
return 0 ;
} ,
end : function ( index ) {
return bufferedTime ;
} ,
length : 1
} ;
pluginMediaElement . dispatchEvent ( e ) ;
}
}
switch ( playback . method ) {
case 'silverlight' :
container . innerHTML =
'<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
'<param name="initParams" value="' + initVars . join ( ',' ) + '" />' +
'<param name="windowless" value="true" />' +
'<param name="background" value="black" />' +
'<param name="minRuntimeVersion" value="3.0.0.0" />' +
'<param name="autoUpgrade" value="true" />' +
'<param name="source" value="' + options . pluginPath + options . silverlightName + '" />' +
'</object>' ;
break ;
case 'flash' :
if ( mejs . MediaFeatures . isIE ) {
specialIEContainer = document . createElement ( 'div' ) ;
container . appendChild ( specialIEContainer ) ;
specialIEContainer . outerHTML =
'<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
'id="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
'<param name="movie" value="' + options . pluginPath + options . flashName + '?' + ( new Date ( ) . getTime ( ) ) + '" />' +
'<param name="flashvars" value="' + initVars . join ( '&' ) + '" />' +
'<param name="quality" value="high" />' +
'<param name="bgcolor" value="#000000" />' +
'<param name="wmode" value="transparent" />' +
'<param name="allowScriptAccess" value="' + options . flashScriptAccess + '" />' +
'<param name="allowFullScreen" value="true" />' +
'<param name="scale" value="default" />' +
'</object>' ;
} else {
container . innerHTML =
'<embed id="' + pluginid + '" name="' + pluginid + '" ' +
'play="true" ' +
'loop="false" ' +
'quality="high" ' +
'bgcolor="#000000" ' +
'wmode="transparent" ' +
'allowScriptAccess="' + options . flashScriptAccess + '" ' +
'allowFullScreen="true" ' +
'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' +
'src="' + options . pluginPath + options . flashName + '" ' +
'flashvars="' + initVars . join ( '&' ) + '" ' +
'width="' + width + '" ' +
'height="' + height + '" ' +
'scale="default"' +
'class="mejs-shim"></embed>' ;
}
break ;
case 'youtube' :
var videoId ;
// youtu.be url from share button
if ( playback . url . lastIndexOf ( "youtu.be" ) != - 1 ) {
videoId = playback . url . substr ( playback . url . lastIndexOf ( '/' ) + 1 ) ;
if ( videoId . indexOf ( '?' ) != - 1 ) {
videoId = videoId . substr ( 0 , videoId . indexOf ( '?' ) ) ;
}
}
else {
videoId = playback . url . substr ( playback . url . lastIndexOf ( '=' ) + 1 ) ;
}
youtubeSettings = {
container : container ,
containerId : container . id ,
pluginMediaElement : pluginMediaElement ,
pluginId : pluginid ,
videoId : videoId ,
height : height ,
width : width ,
scheme : playback . scheme
} ;
// favor iframe version of YouTube
if ( window . postMessage ) {
mejs . YouTubeApi . enqueueIframe ( youtubeSettings ) ;
} else if ( mejs . PluginDetector . hasPluginVersion ( 'flash' , [ 10 , 0 , 0 ] ) ) {
mejs . YouTubeApi . createFlash ( youtubeSettings , options ) ;
}
break ;
// DEMO Code. Does NOT work.
case 'vimeo' :
var player _id = pluginid + "_player" ;
pluginMediaElement . vimeoid = playback . url . substr ( playback . url . lastIndexOf ( '/' ) + 1 ) ;
container . innerHTML = '<iframe src="' + playback . scheme + 'player.vimeo.com/video/' + pluginMediaElement . vimeoid + '?api=1&portrait=0&byline=0&title=0&player_id=' + player _id + '" width="' + width + '" height="' + height + '" frameborder="0" class="mejs-shim" id="' + player _id + '" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>' ;
if ( typeof ( $f ) == 'function' ) { // froogaloop available
var player = $f ( container . childNodes [ 0 ] ) ,
playerState = - 1 ;
player . addEvent ( 'ready' , function ( ) {
player . playVideo = function ( ) {
player . api ( 'play' ) ;
}
player . stopVideo = function ( ) {
player . api ( 'unload' ) ;
}
player . pauseVideo = function ( ) {
player . api ( 'pause' ) ;
}
player . seekTo = function ( seconds ) {
player . api ( 'seekTo' , seconds ) ;
}
player . setVolume = function ( volume ) {
player . api ( 'setVolume' , volume ) ;
}
player . setMuted = function ( muted ) {
if ( muted ) {
player . lastVolume = player . api ( 'getVolume' ) ;
player . api ( 'setVolume' , 0 ) ;
} else {
player . api ( 'setVolume' , player . lastVolume ) ;
delete player . lastVolume ;
}
}
// parity with YT player
player . getPlayerState = function ( ) {
return playerState ;
}
function createEvent ( player , pluginMediaElement , eventName , e ) {
var event = {
type : eventName ,
target : pluginMediaElement
} ;
if ( eventName == 'timeupdate' ) {
pluginMediaElement . currentTime = event . currentTime = e . seconds ;
pluginMediaElement . duration = event . duration = e . duration ;
}
pluginMediaElement . dispatchEvent ( event ) ;
}
player . addEvent ( 'play' , function ( ) {
playerState = 1 ;
createEvent ( player , pluginMediaElement , 'play' ) ;
createEvent ( player , pluginMediaElement , 'playing' ) ;
} ) ;
player . addEvent ( 'pause' , function ( ) {
playerState = 2 ;
createEvent ( player , pluginMediaElement , 'pause' ) ;
} ) ;
player . addEvent ( 'finish' , function ( ) {
playerState = 0 ;
createEvent ( player , pluginMediaElement , 'ended' ) ;
} ) ;
player . addEvent ( 'playProgress' , function ( e ) {
createEvent ( player , pluginMediaElement , 'timeupdate' , e ) ;
} ) ;
player . addEvent ( 'seek' , function ( e ) {
playerState = 3 ;
createEvent ( player , pluginMediaElement , 'seeked' , e ) ;
} ) ;
player . addEvent ( 'loadProgress' , function ( e ) {
playerState = 3 ;
createEvent ( player , pluginMediaElement , 'progress' , e ) ;
} ) ;
pluginMediaElement . pluginElement = container ;
pluginMediaElement . pluginApi = player ;
pluginMediaElement . success ( pluginMediaElement , pluginMediaElement . pluginElement ) ;
} ) ;
}
else {
console . warn ( "You need to include froogaloop for vimeo to work" ) ;
}
break ;
}
// hide original element
htmlMediaElement . style . display = 'none' ;
// prevent browser from autoplaying when using a plugin
htmlMediaElement . removeAttribute ( 'autoplay' ) ;
return pluginMediaElement ;
} ,
updateNative : function ( playback , options , autoplay , preload ) {
var htmlMediaElement = playback . htmlMediaElement ,
m ;
// add methods to video object to bring it into parity with Flash Object
for ( m in mejs . HtmlMediaElement ) {
htmlMediaElement [ m ] = mejs . HtmlMediaElement [ m ] ;
}
/ *
Chrome now supports preload = "none"
if ( mejs . MediaFeatures . isChrome ) {
// special case to enforce preload attribute (Chrome doesn't respect this)
if ( preload === 'none' && ! autoplay ) {
// forces the browser to stop loading (note: fails in IE9)
htmlMediaElement . src = '' ;
htmlMediaElement . load ( ) ;
htmlMediaElement . canceledPreload = true ;
htmlMediaElement . addEventListener ( 'play' , function ( ) {
if ( htmlMediaElement . canceledPreload ) {
htmlMediaElement . src = playback . url ;
htmlMediaElement . load ( ) ;
htmlMediaElement . play ( ) ;
htmlMediaElement . canceledPreload = false ;
}
} , false ) ;
// for some reason Chrome forgets how to autoplay sometimes.
} else if ( autoplay ) {
htmlMediaElement . load ( ) ;
htmlMediaElement . play ( ) ;
}
}
* /
// fire success code
options . success ( htmlMediaElement , htmlMediaElement ) ;
return htmlMediaElement ;
}
} ;
/ *
- test on IE ( object vs . embed )
- determine when to use iframe ( Firefox , Safari , Mobile ) vs . Flash ( Chrome , IE )
- fullscreen ?
* /
// YouTube Flash and Iframe API
mejs . YouTubeApi = {
isIframeStarted : false ,
isIframeLoaded : false ,
loadIframeApi : function ( yt ) {
if ( ! this . isIframeStarted ) {
var tag = document . createElement ( 'script' ) ;
tag . src = yt . scheme + "www.youtube.com/player_api" ;
var firstScriptTag = document . getElementsByTagName ( 'script' ) [ 0 ] ;
firstScriptTag . parentNode . insertBefore ( tag , firstScriptTag ) ;
this . isIframeStarted = true ;
}
} ,
iframeQueue : [ ] ,
enqueueIframe : function ( yt ) {
if ( this . isLoaded ) {
this . createIframe ( yt ) ;
} else {
this . loadIframeApi ( yt ) ;
this . iframeQueue . push ( yt ) ;
}
} ,
createIframe : function ( settings ) {
var
pluginMediaElement = settings . pluginMediaElement ,
player = new YT . Player ( settings . containerId , {
height : settings . height ,
width : settings . width ,
videoId : settings . videoId ,
playerVars : { controls : 0 , wmode : 'transparent' } ,
events : {
'onReady' : function ( ) {
// wrapper to match
player . setVideoSize = function ( width , height ) {
player . setSize ( width , height ) ;
}
// hook up iframe object to MEjs
settings . pluginMediaElement . pluginApi = player ;
settings . pluginMediaElement . pluginElement = document . getElementById ( settings . containerId ) ;
// init mejs
pluginMediaElement . success ( pluginMediaElement , pluginMediaElement . pluginElement ) ;
// create timer
setInterval ( function ( ) {
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'timeupdate' ) ;
} , 250 ) ;
} ,
'onStateChange' : function ( e ) {
mejs . YouTubeApi . handleStateChange ( e . data , player , pluginMediaElement ) ;
}
}
} ) ;
} ,
createEvent : function ( player , pluginMediaElement , eventName ) {
var event = {
type : eventName ,
target : pluginMediaElement
} ;
if ( player && player . getDuration ) {
// time
pluginMediaElement . currentTime = event . currentTime = player . getCurrentTime ( ) ;
pluginMediaElement . duration = event . duration = player . getDuration ( ) ;
// state
event . paused = pluginMediaElement . paused ;
event . ended = pluginMediaElement . ended ;
// sound
event . muted = player . isMuted ( ) ;
event . volume = player . getVolume ( ) / 100 ;
// progress
event . bytesTotal = player . getVideoBytesTotal ( ) ;
event . bufferedBytes = player . getVideoBytesLoaded ( ) ;
// fake the W3C buffered TimeRange
var bufferedTime = event . bufferedBytes / event . bytesTotal * event . duration ;
event . target . buffered = event . buffered = {
start : function ( index ) {
return 0 ;
} ,
end : function ( index ) {
return bufferedTime ;
} ,
length : 1
} ;
}
// send event up the chain
pluginMediaElement . dispatchEvent ( event ) ;
} ,
iFrameReady : function ( ) {
this . isLoaded = true ;
this . isIframeLoaded = true ;
while ( this . iframeQueue . length > 0 ) {
var settings = this . iframeQueue . pop ( ) ;
this . createIframe ( settings ) ;
}
} ,
// FLASH!
flashPlayers : { } ,
createFlash : function ( settings ) {
this . flashPlayers [ settings . pluginId ] = settings ;
/ *
settings . container . innerHTML =
'<object type="application/x-shockwave-flash" id="' + settings . pluginId + '" data="' + settings . scheme + 'www.youtube.com/apiplayer?enablejsapi=1&playerapiid=' + settings . pluginId + '&version=3&autoplay=0&controls=0&modestbranding=1&loop=0" ' +
'width="' + settings . width + '" height="' + settings . height + '" style="visibility: visible; " class="mejs-shim">' +
'<param name="allowScriptAccess" value="sameDomain">' +
'<param name="wmode" value="transparent">' +
'</object>' ;
* /
var specialIEContainer ,
youtubeUrl = settings . scheme + 'www.youtube.com/apiplayer?enablejsapi=1&playerapiid=' + settings . pluginId + '&version=3&autoplay=0&controls=0&modestbranding=1&loop=0' ;
if ( mejs . MediaFeatures . isIE ) {
specialIEContainer = document . createElement ( 'div' ) ;
settings . container . appendChild ( specialIEContainer ) ;
specialIEContainer . outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="' + settings . scheme + 'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
'id="' + settings . pluginId + '" width="' + settings . width + '" height="' + settings . height + '" class="mejs-shim">' +
'<param name="movie" value="' + youtubeUrl + '" />' +
'<param name="wmode" value="transparent" />' +
'<param name="allowScriptAccess" value="' + options . flashScriptAccess + '" />' +
'<param name="allowFullScreen" value="true" />' +
'</object>' ;
} else {
settings . container . innerHTML =
'<object type="application/x-shockwave-flash" id="' + settings . pluginId + '" data="' + youtubeUrl + '" ' +
'width="' + settings . width + '" height="' + settings . height + '" style="visibility: visible; " class="mejs-shim">' +
'<param name="allowScriptAccess" value="' + options . flashScriptAccess + '">' +
'<param name="wmode" value="transparent">' +
'</object>' ;
}
} ,
flashReady : function ( id ) {
var
settings = this . flashPlayers [ id ] ,
player = document . getElementById ( id ) ,
pluginMediaElement = settings . pluginMediaElement ;
// hook up and return to MediaELementPlayer.success
pluginMediaElement . pluginApi =
pluginMediaElement . pluginElement = player ;
settings . success ( pluginMediaElement , pluginMediaElement . pluginElement ) ;
// load the youtube video
player . cueVideoById ( settings . videoId ) ;
var callbackName = settings . containerId + '_callback' ;
window [ callbackName ] = function ( e ) {
mejs . YouTubeApi . handleStateChange ( e , player , pluginMediaElement ) ;
}
player . addEventListener ( 'onStateChange' , callbackName ) ;
setInterval ( function ( ) {
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'timeupdate' ) ;
} , 250 ) ;
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'canplay' ) ;
} ,
handleStateChange : function ( youTubeState , player , pluginMediaElement ) {
switch ( youTubeState ) {
case - 1 : // not started
pluginMediaElement . paused = true ;
pluginMediaElement . ended = true ;
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'loadedmetadata' ) ;
//createYouTubeEvent(player, pluginMediaElement, 'loadeddata');
break ;
case 0 :
pluginMediaElement . paused = false ;
pluginMediaElement . ended = true ;
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'ended' ) ;
break ;
case 1 :
pluginMediaElement . paused = false ;
pluginMediaElement . ended = false ;
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'play' ) ;
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'playing' ) ;
break ;
case 2 :
pluginMediaElement . paused = true ;
pluginMediaElement . ended = false ;
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'pause' ) ;
break ;
case 3 : // buffering
mejs . YouTubeApi . createEvent ( player , pluginMediaElement , 'progress' ) ;
break ;
case 5 :
// cued?
break ;
}
}
}
// IFRAME
window . onYouTubePlayerAPIReady = function ( ) {
mejs . YouTubeApi . iFrameReady ( ) ;
} ;
// FLASH
window . onYouTubePlayerReady = function ( id ) {
mejs . YouTubeApi . flashReady ( id ) ;
} ;
window . mejs = mejs ;
window . MediaElement = mejs . MediaElement ;
/ *
* Adds Internationalization and localization to mediaelement .
*
* This file does not contain translations , you have to add them manually .
* The schema is always the same : me - i18n - locale - [ IETF - language - tag ] . js
*
* Examples are provided both for german and chinese translation .
*
*
* What is the concept beyond i18n ?
* http : //en.wikipedia.org/wiki/Internationalization_and_localization
*
* What langcode should i use ?
* http : //en.wikipedia.org/wiki/IETF_language_tag
* https : //tools.ietf.org/html/rfc5646
*
*
* License ?
*
* The i18n file uses methods from the Drupal project ( drupal . js ) :
* - i18n . methods . t ( ) ( modified )
* - i18n . methods . checkPlain ( ) ( full copy )
*
* The Drupal project is ( like mediaelementjs ) licensed under GPLv2 .
* - http : //drupal.org/licensing/faq/#q1
* - https : //github.com/johndyer/mediaelement
* - http : //www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
*
* @ author
* Tim Latz ( latz . tim @ gmail . com )
*
*
* @ params
* - context - document , iframe . .
* - exports - CommonJS , window . .
*
* /
; ( function ( context , exports , undefined ) {
"use strict" ;
var i18n = {
"locale" : {
// Ensure previous values aren't overwritten.
"language" : ( exports . i18n && exports . i18n . locale . language ) || '' ,
"strings" : ( exports . i18n && exports . i18n . locale . strings ) || { }
} ,
"ietf_lang_regex" : /^(x\-)?[a-z]{2,}(\-\w{2,})?(\-\w{2,})?$/ ,
"methods" : { }
} ;
// start i18n
/ * *
* Get language , fallback to browser ' s language if empty
*
* IETF : RFC 5646 , https : //tools.ietf.org/html/rfc5646
* Examples : en , zh - CN , cmn - Hans - CN , sr - Latn - RS , es - 419 , x - private
* /
i18n . getLanguage = function ( ) {
var language = i18n . locale . language || window . navigator . userLanguage || window . navigator . language ;
return i18n . ietf _lang _regex . exec ( language ) ? language : null ;
//(WAS: convert to iso 639-1 (2-letters, lower case))
//return language.substr(0, 2).toLowerCase();
} ;
// i18n fixes for compatibility with WordPress
if ( typeof mejsL10n != 'undefined' ) {
i18n . locale . language = mejsL10n . language ;
}
/ * *
* Encode special characters in a plain - text string for display as HTML .
* /
i18n . methods . checkPlain = function ( str ) {
var character , regex ,
replace = {
'&' : '&' ,
'"' : '"' ,
'<' : '<' ,
'>' : '>'
} ;
str = String ( str ) ;
for ( character in replace ) {
if ( replace . hasOwnProperty ( character ) ) {
regex = new RegExp ( character , 'g' ) ;
str = str . replace ( regex , replace [ character ] ) ;
}
}
return str ;
} ;
/ * *
* Translate strings to the page language or a given language .
*
*
* @ param str
* A string containing the English string to translate .
*
* @ param options
* - 'context' ( defaults to the default context ) : The context the source string
* belongs to .
*
* @ return
* The translated string , escaped via i18n . methods . checkPlain ( )
* /
i18n . methods . t = function ( str , options ) {
// Fetch the localized version of the string.
if ( i18n . locale . strings && i18n . locale . strings [ options . context ] && i18n . locale . strings [ options . context ] [ str ] ) {
str = i18n . locale . strings [ options . context ] [ str ] ;
}
return i18n . methods . checkPlain ( str ) ;
} ;
/ * *
* Wrapper for i18n . methods . t ( )
*
* @ see i18n . methods . t ( )
* @ throws InvalidArgumentException
* /
i18n . t = function ( str , options ) {
if ( typeof str === 'string' && str . length > 0 ) {
// check every time due language can change for
// different reasons (translation, lang switcher ..)
var language = i18n . getLanguage ( ) ;
options = options || {
"context" : language
} ;
return i18n . methods . t ( str , options ) ;
}
else {
throw {
"name" : 'InvalidArgumentException' ,
"message" : 'First argument is either not a string or empty.'
} ;
}
} ;
// end i18n
exports . i18n = i18n ;
} ( document , mejs ) ) ;
// i18n fixes for compatibility with WordPress
; ( function ( exports , undefined ) {
"use strict" ;
if ( typeof mejsL10n != 'undefined' ) {
exports [ mejsL10n . language ] = mejsL10n . strings ;
}
} ( mejs . i18n . locale . strings ) ) ;
/ * !
*
* MediaElementPlayer
* http : //mediaelementjs.com/
*
* Creates a controller bar for HTML5 < video > add < audio > tags
* using jQuery and MediaElement . js ( HTML5 Flash / Silverlight wrapper )
*
* Copyright 2010 - 2013 , John Dyer ( http : //j.hn/)
* License : MIT
*
* /
if ( typeof jQuery != 'undefined' ) {
mejs . $ = jQuery ;
} else if ( typeof Zepto != 'undefined' ) {
mejs . $ = Zepto ;
// define `outerWidth` method which has not been realized in Zepto
Zepto . fn . outerWidth = function ( includeMargin ) {
var width = $ ( this ) . width ( ) ;
if ( includeMargin ) {
width += parseInt ( $ ( this ) . css ( 'margin-right' ) , 10 ) ;
width += parseInt ( $ ( this ) . css ( 'margin-left' ) , 10 ) ;
}
return width
}
} else if ( typeof ender != 'undefined' ) {
mejs . $ = ender ;
}
( function ( $ ) {
// default player values
mejs . MepDefaults = {
// url to poster (to fix iOS 3.x)
poster : '' ,
// When the video is ended, we can show the poster.
showPosterWhenEnded : false ,
// default if the <video width> is not specified
defaultVideoWidth : 480 ,
// default if the <video height> is not specified
defaultVideoHeight : 270 ,
// if set, overrides <video width>
videoWidth : - 1 ,
// if set, overrides <video height>
videoHeight : - 1 ,
// default if the user doesn't specify
defaultAudioWidth : 400 ,
// default if the user doesn't specify
defaultAudioHeight : 30 ,
// default amount to move back when back key is pressed
defaultSeekBackwardInterval : function ( media ) {
return ( media . duration * 0.05 ) ;
} ,
// default amount to move forward when forward key is pressed
defaultSeekForwardInterval : function ( media ) {
return ( media . duration * 0.05 ) ;
} ,
// set dimensions via JS instead of CSS
setDimensions : true ,
// width of audio player
audioWidth : - 1 ,
// height of audio player
audioHeight : - 1 ,
// initial volume when the player starts (overrided by user cookie)
startVolume : 0.8 ,
// useful for <audio> player loops
loop : false ,
// rewind to beginning when media ends
autoRewind : true ,
// resize to media dimensions
enableAutosize : true ,
/ *
* Time format to use . Default : 'mm:ss'
* Supported units :
* h : hour
* m : minute
* s : second
* f : frame count
* When using 'hh' , 'mm' , 'ss' or 'ff' we always display 2 digits .
* If you use 'h' , 'm' , 's' or 'f' we display 1 digit if possible .
*
* Example to display 75 seconds :
* Format 'mm:ss' : 01 : 15
* Format 'm:ss' : 1 : 15
* Format 'm:s' : 1 : 15
* /
timeFormat : '' ,
// forces the hour marker (##:00:00)
alwaysShowHours : false ,
// show framecount in timecode (##:00:00:00)
showTimecodeFrameCount : false ,
// used when showTimecodeFrameCount is set to true
framesPerSecond : 25 ,
// automatically calculate the width of the progress bar based on the sizes of other elements
autosizeProgress : true ,
// Hide controls when playing and mouse is not over the video
alwaysShowControls : false ,
// Display the video control
hideVideoControlsOnLoad : false ,
// Enable click video element to toggle play/pause
clickToPlayPause : true ,
// force iPad's native controls
iPadUseNativeControls : false ,
// force iPhone's native controls
iPhoneUseNativeControls : false ,
// force Android's native controls
AndroidUseNativeControls : false ,
// features to show
features : [ 'playpause' , 'current' , 'progress' , 'duration' , 'tracks' , 'volume' , 'fullscreen' ] ,
// only for dynamic
isVideo : true ,
// turns keyboard support on and off for this instance
enableKeyboard : true ,
// whenthis player starts, it will pause other players
pauseOtherPlayers : true ,
// array of keyboard actions such as play pause
keyActions : [
{
keys : [
32 , // SPACE
179 // GOOGLE play/pause button
] ,
action : function ( player , media ) {
if ( media . paused || media . ended ) {
media . play ( ) ;
} else {
media . pause ( ) ;
}
}
} ,
{
keys : [ 38 ] , // UP
action : function ( player , media ) {
player . container . find ( '.mejs-volume-slider' ) . css ( 'display' , 'block' ) ;
if ( player . isVideo ) {
player . showControls ( ) ;
player . startControlsTimer ( ) ;
}
var newVolume = Math . min ( media . volume + 0.1 , 1 ) ;
media . setVolume ( newVolume ) ;
}
} ,
{
keys : [ 40 ] , // DOWN
action : function ( player , media ) {
player . container . find ( '.mejs-volume-slider' ) . css ( 'display' , 'block' ) ;
if ( player . isVideo ) {
player . showControls ( ) ;
player . startControlsTimer ( ) ;
}
var newVolume = Math . max ( media . volume - 0.1 , 0 ) ;
media . setVolume ( newVolume ) ;
}
} ,
{
keys : [
37 , // LEFT
227 // Google TV rewind
] ,
action : function ( player , media ) {
if ( ! isNaN ( media . duration ) && media . duration > 0 ) {
if ( player . isVideo ) {
player . showControls ( ) ;
player . startControlsTimer ( ) ;
}
// 5%
var newTime = Math . max ( media . currentTime - player . options . defaultSeekBackwardInterval ( media ) , 0 ) ;
media . setCurrentTime ( newTime ) ;
}
}
} ,
{
keys : [
39 , // RIGHT
228 // Google TV forward
] ,
action : function ( player , media ) {
if ( ! isNaN ( media . duration ) && media . duration > 0 ) {
if ( player . isVideo ) {
player . showControls ( ) ;
player . startControlsTimer ( ) ;
}
// 5%
var newTime = Math . min ( media . currentTime + player . options . defaultSeekForwardInterval ( media ) , media . duration ) ;
media . setCurrentTime ( newTime ) ;
}
}
} ,
{
keys : [ 70 ] , // F
action : function ( player , media ) {
if ( typeof player . enterFullScreen != 'undefined' ) {
if ( player . isFullScreen ) {
player . exitFullScreen ( ) ;
} else {
player . enterFullScreen ( ) ;
}
}
}
} ,
{
keys : [ 77 ] , // M
action : function ( player , media ) {
player . container . find ( '.mejs-volume-slider' ) . css ( 'display' , 'block' ) ;
if ( player . isVideo ) {
player . showControls ( ) ;
player . startControlsTimer ( ) ;
}
if ( player . media . muted ) {
player . setMuted ( false ) ;
} else {
player . setMuted ( true ) ;
}
}
}
]
} ;
mejs . mepIndex = 0 ;
mejs . players = { } ;
// wraps a MediaElement object in player controls
mejs . MediaElementPlayer = function ( node , o ) {
// enforce object, even without "new" (via John Resig)
if ( ! ( this instanceof mejs . MediaElementPlayer ) ) {
return new mejs . MediaElementPlayer ( node , o ) ;
}
var t = this ;
// these will be reset after the MediaElement.success fires
t . $media = t . $node = $ ( node ) ;
t . node = t . media = t . $media [ 0 ] ;
if ( ! t . node ) {
return
}
// check for existing player
if ( typeof t . node . player != 'undefined' ) {
return t . node . player ;
}
// try to get options from data-mejsoptions
if ( typeof o == 'undefined' ) {
o = t . $node . data ( 'mejsoptions' ) ;
}
// extend default options
t . options = $ . extend ( { } , mejs . MepDefaults , o ) ;
if ( ! t . options . timeFormat ) {
// Generate the time format according to options
t . options . timeFormat = 'mm:ss' ;
if ( t . options . alwaysShowHours ) {
t . options . timeFormat = 'hh:mm:ss' ;
}
if ( t . options . showTimecodeFrameCount ) {
t . options . timeFormat += ':ff' ;
}
}
mejs . Utility . calculateTimeFormat ( 0 , t . options , t . options . framesPerSecond || 25 ) ;
// unique ID
t . id = 'mep_' + mejs . mepIndex ++ ;
// add to player array (for focus events)
mejs . players [ t . id ] = t ;
// start up
t . init ( ) ;
return t ;
} ;
// actual player
mejs . MediaElementPlayer . prototype = {
hasFocus : false ,
controlsAreVisible : true ,
init : function ( ) {
var
t = this ,
mf = mejs . MediaFeatures ,
// options for MediaElement (shim)
meOptions = $ . extend ( true , { } , t . options , {
success : function ( media , domNode ) { t . meReady ( media , domNode ) ; } ,
error : function ( e ) { t . handleError ( e ) ; }
} ) ,
tagName = t . media . tagName . toLowerCase ( ) ;
t . isDynamic = ( tagName !== 'audio' && tagName !== 'video' ) ;
if ( t . isDynamic ) {
// get video from src or href?
t . isVideo = t . options . isVideo ;
} else {
t . isVideo = ( tagName !== 'audio' && t . options . isVideo ) ;
}
// use native controls in iPad, iPhone, and Android
if ( ( mf . isiPad && t . options . iPadUseNativeControls ) || ( mf . isiPhone && t . options . iPhoneUseNativeControls ) ) {
// add controls and stop
t . $media . attr ( 'controls' , 'controls' ) ;
// attempt to fix iOS 3 bug
//t.$media.removeAttr('poster');
// no Issue found on iOS3 -ttroxell
// override Apple's autoplay override for iPads
if ( mf . isiPad && t . media . getAttribute ( 'autoplay' ) !== null ) {
t . play ( ) ;
}
} else if ( mf . isAndroid && t . options . AndroidUseNativeControls ) {
// leave default player
} else {
// DESKTOP: use MediaElementPlayer controls
// remove native controls
t . $media . removeAttr ( 'controls' ) ;
var videoPlayerTitle = t . isVideo ?
mejs . i18n . t ( 'Video Player' ) : mejs . i18n . t ( 'Audio Player' ) ;
// insert description for screen readers
$ ( '<span class="mejs-offscreen">' + videoPlayerTitle + '</span>' ) . insertBefore ( t . $media ) ;
// build container
t . container =
$ ( '<div id="' + t . id + '" class="mejs-container ' + ( mejs . MediaFeatures . svgAsImg ? 'svg' : 'no-svg' ) +
'" tabindex="0" role="application" aria-label="' + videoPlayerTitle + '">' +
'<div class="mejs-inner">' +
'<div class="mejs-mediaelement"></div>' +
'<div class="mejs-layers"></div>' +
'<div class="mejs-controls"></div>' +
'<div class="mejs-clear"></div>' +
'</div>' +
'</div>' )
. addClass ( t . $media [ 0 ] . className )
. insertBefore ( t . $media )
. focus ( function ( e ) {
if ( ! t . controlsAreVisible ) {
t . showControls ( true ) ;
var playButton = t . container . find ( '.mejs-playpause-button > button' ) ;
playButton . focus ( ) ;
}
} ) ;
// add classes for user and content
t . container . addClass (
( mf . isAndroid ? 'mejs-android ' : '' ) +
( mf . isiOS ? 'mejs-ios ' : '' ) +
( mf . isiPad ? 'mejs-ipad ' : '' ) +
( mf . isiPhone ? 'mejs-iphone ' : '' ) +
( t . isVideo ? 'mejs-video ' : 'mejs-audio ' )
) ;
// move the <video/video> tag into the right spot
t . container . find ( '.mejs-mediaelement' ) . append ( t . $media ) ;
// needs to be assigned here, after iOS remap
t . node . player = t ;
// find parts
t . controls = t . container . find ( '.mejs-controls' ) ;
t . layers = t . container . find ( '.mejs-layers' ) ;
// determine the size
/ * s i z e p r i o r i t y :
( 1 ) videoWidth ( forced ) ,
( 2 ) style = "width;height;"
( 3 ) width attribute ,
( 4 ) defaultVideoWidth ( for unspecified cases )
* /
var tagType = ( t . isVideo ? 'video' : 'audio' ) ,
capsTagName = tagType . substring ( 0 , 1 ) . toUpperCase ( ) + tagType . substring ( 1 ) ;
if ( t . options [ tagType + 'Width' ] > 0 || t . options [ tagType + 'Width' ] . toString ( ) . indexOf ( '%' ) > - 1 ) {
t . width = t . options [ tagType + 'Width' ] ;
} else if ( t . media . style . width !== '' && t . media . style . width !== null ) {
t . width = t . media . style . width ;
} else if ( t . media . getAttribute ( 'width' ) !== null ) {
t . width = t . $media . attr ( 'width' ) ;
} else {
t . width = t . options [ 'default' + capsTagName + 'Width' ] ;
}
if ( t . options [ tagType + 'Height' ] > 0 || t . options [ tagType + 'Height' ] . toString ( ) . indexOf ( '%' ) > - 1 ) {
t . height = t . options [ tagType + 'Height' ] ;
} else if ( t . media . style . height !== '' && t . media . style . height !== null ) {
t . height = t . media . style . height ;
} else if ( t . $media [ 0 ] . getAttribute ( 'height' ) !== null ) {
t . height = t . $media . attr ( 'height' ) ;
} else {
t . height = t . options [ 'default' + capsTagName + 'Height' ] ;
}
// set the size, while we wait for the plugins to load below
t . setPlayerSize ( t . width , t . height ) ;
// create MediaElementShim
meOptions . pluginWidth = t . width ;
meOptions . pluginHeight = t . height ;
}
// create MediaElement shim
mejs . MediaElement ( t . $media [ 0 ] , meOptions ) ;
if ( typeof ( t . container ) != 'undefined' && t . controlsAreVisible ) {
// controls are shown when loaded
t . container . trigger ( 'controlsshown' ) ;
}
} ,
showControls : function ( doAnimation ) {
var t = this ;
doAnimation = typeof doAnimation == 'undefined' || doAnimation ;
if ( t . controlsAreVisible )
return ;
if ( doAnimation ) {
t . controls
. removeClass ( 'mejs-offscreen' )
. stop ( true , true ) . fadeIn ( 200 , function ( ) {
t . controlsAreVisible = true ;
t . container . trigger ( 'controlsshown' ) ;
} ) ;
// any additional controls people might add and want to hide
t . container . find ( '.mejs-control' )
. removeClass ( 'mejs-offscreen' )
. stop ( true , true ) . fadeIn ( 200 , function ( ) { t . controlsAreVisible = true ; } ) ;
} else {
t . controls
. removeClass ( 'mejs-offscreen' )
. css ( 'display' , 'block' ) ;
// any additional controls people might add and want to hide
t . container . find ( '.mejs-control' )
. removeClass ( 'mejs-offscreen' )
. css ( 'display' , 'block' ) ;
t . controlsAreVisible = true ;
t . container . trigger ( 'controlsshown' ) ;
}
t . setControlsSize ( ) ;
} ,
hideControls : function ( doAnimation ) {
var t = this ;
doAnimation = typeof doAnimation == 'undefined' || doAnimation ;
if ( ! t . controlsAreVisible || t . options . alwaysShowControls || t . keyboardAction )
return ;
if ( doAnimation ) {
// fade out main controls
t . controls . stop ( true , true ) . fadeOut ( 200 , function ( ) {
$ ( this )
. addClass ( 'mejs-offscreen' )
. css ( 'display' , 'block' ) ;
t . controlsAreVisible = false ;
t . container . trigger ( 'controlshidden' ) ;
} ) ;
// any additional controls people might add and want to hide
t . container . find ( '.mejs-control' ) . stop ( true , true ) . fadeOut ( 200 , function ( ) {
$ ( this )
. addClass ( 'mejs-offscreen' )
. css ( 'display' , 'block' ) ;
} ) ;
} else {
// hide main controls
t . controls
. addClass ( 'mejs-offscreen' )
. css ( 'display' , 'block' ) ;
// hide others
t . container . find ( '.mejs-control' )
. addClass ( 'mejs-offscreen' )
. css ( 'display' , 'block' ) ;
t . controlsAreVisible = false ;
t . container . trigger ( 'controlshidden' ) ;
}
} ,
controlsTimer : null ,
startControlsTimer : function ( timeout ) {
var t = this ;
timeout = typeof timeout != 'undefined' ? timeout : 1500 ;
t . killControlsTimer ( 'start' ) ;
t . controlsTimer = setTimeout ( function ( ) {
//
t . hideControls ( ) ;
t . killControlsTimer ( 'hide' ) ;
} , timeout ) ;
} ,
killControlsTimer : function ( src ) {
var t = this ;
if ( t . controlsTimer !== null ) {
clearTimeout ( t . controlsTimer ) ;
delete t . controlsTimer ;
t . controlsTimer = null ;
}
} ,
controlsEnabled : true ,
disableControls : function ( ) {
var t = this ;
t . killControlsTimer ( ) ;
t . hideControls ( false ) ;
this . controlsEnabled = false ;
} ,
enableControls : function ( ) {
var t = this ;
t . showControls ( false ) ;
t . controlsEnabled = true ;
} ,
// Sets up all controls and events
meReady : function ( media , domNode ) {
var t = this ,
mf = mejs . MediaFeatures ,
autoplayAttr = domNode . getAttribute ( 'autoplay' ) ,
autoplay = ! ( typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false' ) ,
featureIndex ,
feature ;
// make sure it can't create itself again if a plugin reloads
if ( t . created ) {
return ;
} else {
t . created = true ;
}
t . media = media ;
t . domNode = domNode ;
if ( ! ( mf . isAndroid && t . options . AndroidUseNativeControls ) && ! ( mf . isiPad && t . options . iPadUseNativeControls ) && ! ( mf . isiPhone && t . options . iPhoneUseNativeControls ) ) {
// two built in features
t . buildposter ( t , t . controls , t . layers , t . media ) ;
t . buildkeyboard ( t , t . controls , t . layers , t . media ) ;
t . buildoverlays ( t , t . controls , t . layers , t . media ) ;
// grab for use by features
t . findTracks ( ) ;
// add user-defined features/controls
for ( featureIndex in t . options . features ) {
feature = t . options . features [ featureIndex ] ;
if ( t [ 'build' + feature ] ) {
try {
t [ 'build' + feature ] ( t , t . controls , t . layers , t . media ) ;
} catch ( e ) {
// TODO: report control error
//throw e;
}
}
}
t . container . trigger ( 'controlsready' ) ;
// reset all layers and controls
t . setPlayerSize ( t . width , t . height ) ;
t . setControlsSize ( ) ;
// controls fade
if ( t . isVideo ) {
if ( mejs . MediaFeatures . hasTouch ) {
// for touch devices (iOS, Android)
// show/hide without animation on touch
t . $media . bind ( 'touchstart' , function ( ) {
// toggle controls
if ( t . controlsAreVisible ) {
t . hideControls ( false ) ;
} else {
if ( t . controlsEnabled ) {
t . showControls ( false ) ;
}
}
} ) ;
} else {
// create callback here since it needs access to current
// MediaElement object
t . clickToPlayPauseCallback = function ( ) {
//
if ( t . options . clickToPlayPause ) {
if ( t . media . paused ) {
t . play ( ) ;
} else {
t . pause ( ) ;
}
}
} ;
// click to play/pause
t . media . addEventListener ( 'click' , t . clickToPlayPauseCallback , false ) ;
// show/hide controls
t . container
. bind ( 'mouseenter' , function ( ) {
if ( t . controlsEnabled ) {
if ( ! t . options . alwaysShowControls ) {
t . killControlsTimer ( 'enter' ) ;
t . showControls ( ) ;
t . startControlsTimer ( 2500 ) ;
}
}
} )
. bind ( 'mousemove' , function ( ) {
if ( t . controlsEnabled ) {
if ( ! t . controlsAreVisible ) {
t . showControls ( ) ;
}
if ( ! t . options . alwaysShowControls ) {
t . startControlsTimer ( 2500 ) ;
}
}
} )
. bind ( 'mouseleave' , function ( ) {
if ( t . controlsEnabled ) {
if ( ! t . media . paused && ! t . options . alwaysShowControls ) {
t . startControlsTimer ( 1000 ) ;
}
}
} ) ;
}
if ( t . options . hideVideoControlsOnLoad ) {
t . hideControls ( false ) ;
}
// check for autoplay
if ( autoplay && ! t . options . alwaysShowControls ) {
t . hideControls ( ) ;
}
// resizer
if ( t . options . enableAutosize ) {
t . media . addEventListener ( 'loadedmetadata' , function ( e ) {
// if the <video height> was not set and the options.videoHeight was not set
// then resize to the real dimensions
if ( t . options . videoHeight <= 0 && t . domNode . getAttribute ( 'height' ) === null && ! isNaN ( e . target . videoHeight ) ) {
t . setPlayerSize ( e . target . videoWidth , e . target . videoHeight ) ;
t . setControlsSize ( ) ;
t . media . setVideoSize ( e . target . videoWidth , e . target . videoHeight ) ;
}
} , false ) ;
}
}
// EVENTS
// FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them)
media . addEventListener ( 'play' , function ( ) {
var playerIndex ;
// go through all other players
for ( playerIndex in mejs . players ) {
var p = mejs . players [ playerIndex ] ;
if ( p . id != t . id && t . options . pauseOtherPlayers && ! p . paused && ! p . ended ) {
p . pause ( ) ;
}
p . hasFocus = false ;
}
t . hasFocus = true ;
} , false ) ;
// ended for all
t . media . addEventListener ( 'ended' , function ( e ) {
if ( t . options . autoRewind ) {
try {
t . media . setCurrentTime ( 0 ) ;
// Fixing an Android stock browser bug, where "seeked" isn't fired correctly after ending the video and jumping to the beginning
window . setTimeout ( function ( ) {
$ ( t . container ) . find ( '.mejs-overlay-loading' ) . parent ( ) . hide ( ) ;
} , 20 ) ;
} catch ( exp ) {
}
}
t . media . pause ( ) ;
if ( t . setProgressRail ) {
t . setProgressRail ( ) ;
}
if ( t . setCurrentRail ) {
t . setCurrentRail ( ) ;
}
if ( t . options . loop ) {
t . play ( ) ;
} else if ( ! t . options . alwaysShowControls && t . controlsEnabled ) {
t . showControls ( ) ;
}
} , false ) ;
// resize on the first play
t . media . addEventListener ( 'loadedmetadata' , function ( e ) {
if ( t . updateDuration ) {
t . updateDuration ( ) ;
}
if ( t . updateCurrent ) {
t . updateCurrent ( ) ;
}
if ( ! t . isFullScreen ) {
t . setPlayerSize ( t . width , t . height ) ;
t . setControlsSize ( ) ;
}
} , false ) ;
// Only change the time format when necessary
var duration = null ;
t . media . addEventListener ( 'timeupdate' , function ( ) {
if ( duration !== this . duration ) {
duration = this . duration ;
mejs . Utility . calculateTimeFormat ( duration , t . options , t . options . framesPerSecond || 25 ) ;
// make sure to fill in and resize the controls (e.g., 00:00 => 01:13:15
if ( t . updateDuration ) {
t . updateDuration ( ) ;
}
if ( t . updateCurrent ) {
t . updateCurrent ( ) ;
}
t . setControlsSize ( ) ;
}
} , false ) ;
t . container . focusout ( function ( e ) {
if ( e . relatedTarget ) { //FF is working on supporting focusout https://bugzilla.mozilla.org/show_bug.cgi?id=687787
var $target = $ ( e . relatedTarget ) ;
if ( t . keyboardAction && $target . parents ( '.mejs-container' ) . length === 0 ) {
t . keyboardAction = false ;
t . hideControls ( true ) ;
}
}
} ) ;
// webkit has trouble doing this without a delay
setTimeout ( function ( ) {
t . setPlayerSize ( t . width , t . height ) ;
t . setControlsSize ( ) ;
} , 50 ) ;
// adjust controls whenever window sizes (used to be in fullscreen only)
t . globalBind ( 'resize' , function ( ) {
// don't resize for fullscreen mode
if ( ! ( t . isFullScreen || ( mejs . MediaFeatures . hasTrueNativeFullScreen && document . webkitIsFullScreen ) ) ) {
t . setPlayerSize ( t . width , t . height ) ;
}
// always adjust controls
t . setControlsSize ( ) ;
} ) ;
// This is a work-around for a bug in the YouTube iFrame player, which means
// we can't use the play() API for the initial playback on iOS or Android;
// user has to start playback directly by tapping on the iFrame.
if ( t . media . pluginType == 'youtube' && ( mf . isiOS || mf . isAndroid ) ) {
t . container . find ( '.mejs-overlay-play' ) . hide ( ) ;
t . container . find ( '.mejs-poster' ) . hide ( ) ;
}
}
// force autoplay for HTML5
if ( autoplay && media . pluginType == 'native' ) {
t . play ( ) ;
}
if ( t . options . success ) {
if ( typeof t . options . success == 'string' ) {
window [ t . options . success ] ( t . media , t . domNode , t ) ;
} else {
t . options . success ( t . media , t . domNode , t ) ;
}
}
} ,
handleError : function ( e ) {
var t = this ;
if ( t . controls ) {
t . controls . hide ( ) ;
}
// Tell user that the file cannot be played
if ( t . options . error ) {
t . options . error ( e ) ;
}
} ,
setPlayerSize : function ( width , height ) {
var t = this ;
if ( ! t . options . setDimensions ) {
return false ;
}
if ( typeof width != 'undefined' ) {
t . width = width ;
}
if ( typeof height != 'undefined' ) {
t . height = height ;
}
// detect 100% mode - use currentStyle for IE since css() doesn't return percentages
if ( t . height . toString ( ) . indexOf ( '%' ) > 0 || ( t . $node . css ( 'max-width' ) !== 'none' && t . $node . css ( 'max-width' ) !== 't.width' ) || ( t . $node [ 0 ] . currentStyle && t . $node [ 0 ] . currentStyle . maxWidth === '100%' ) ) {
// do we have the native dimensions yet?
var nativeWidth = ( function ( ) {
if ( t . isVideo ) {
if ( t . media . videoWidth && t . media . videoWidth > 0 ) {
return t . media . videoWidth ;
} else if ( t . media . getAttribute ( 'width' ) !== null ) {
return t . media . getAttribute ( 'width' ) ;
} else {
return t . options . defaultVideoWidth ;
}
} else {
return t . options . defaultAudioWidth ;
}
} ) ( ) ;
var nativeHeight = ( function ( ) {
if ( t . isVideo ) {
if ( t . media . videoHeight && t . media . videoHeight > 0 ) {
return t . media . videoHeight ;
} else if ( t . media . getAttribute ( 'height' ) !== null ) {
return t . media . getAttribute ( 'height' ) ;
} else {
return t . options . defaultVideoHeight ;
}
} else {
return t . options . defaultAudioHeight ;
}
} ) ( ) ;
var
parentWidth = t . container . parent ( ) . closest ( ':visible' ) . width ( ) ,
parentHeight = t . container . parent ( ) . closest ( ':visible' ) . height ( ) ,
newHeight = t . isVideo || ! t . options . autosizeProgress ? parseInt ( parentWidth * nativeHeight / nativeWidth , 10 ) : nativeHeight ;
// When we use percent, the newHeight can't be calculated so we get the container height
if ( isNaN ( newHeight ) ) {
newHeight = parentHeight ;
}
if ( t . container . parent ( ) . length > 0 && t . container . parent ( ) [ 0 ] . tagName . toLowerCase ( ) === 'body' ) { // && t.container.siblings().count == 0) {
parentWidth = $ ( window ) . width ( ) ;
newHeight = $ ( window ) . height ( ) ;
}
if ( newHeight && parentWidth ) {
// set outer container size
t . container
. width ( parentWidth )
. height ( newHeight ) ;
// set native <video> or <audio> and shims
t . $media . add ( t . container . find ( '.mejs-shim' ) )
. width ( '100%' )
. height ( '100%' ) ;
// if shim is ready, send the size to the embeded plugin
if ( t . isVideo ) {
if ( t . media . setVideoSize ) {
t . media . setVideoSize ( parentWidth , newHeight ) ;
}
}
// set the layers
t . layers . children ( '.mejs-layer' )
. width ( '100%' )
. height ( '100%' ) ;
}
} else {
t . container
. width ( t . width )
. height ( t . height ) ;
t . layers . children ( '.mejs-layer' )
. width ( t . width )
. height ( t . height ) ;
}
} ,
setControlsSize : function ( ) {
var t = this ,
usedWidth = 0 ,
railWidth = 0 ,
rail = t . controls . find ( '.mejs-time-rail' ) ,
total = t . controls . find ( '.mejs-time-total' ) ,
others = rail . siblings ( ) ,
lastControl = others . last ( ) ,
lastControlPosition = null ;
// skip calculation if hidden
if ( ! t . container . is ( ':visible' ) || ! rail . length || ! rail . is ( ':visible' ) ) {
return ;
}
// allow the size to come from custom CSS
if ( t . options && ! t . options . autosizeProgress ) {
// Also, frontends devs can be more flexible
// due the opportunity of absolute positioning.
railWidth = parseInt ( rail . css ( 'width' ) , 10 ) ;
}
// attempt to autosize
if ( railWidth === 0 || ! railWidth ) {
// find the size of all the other controls besides the rail
others . each ( function ( ) {
var $this = $ ( this ) ;
if ( $this . css ( 'position' ) != 'absolute' && $this . is ( ':visible' ) ) {
usedWidth += $ ( this ) . outerWidth ( true ) ;
}
} ) ;
// fit the rail into the remaining space
railWidth = t . controls . width ( ) - usedWidth - ( rail . outerWidth ( true ) - rail . width ( ) ) ;
}
// resize the rail,
// but then check if the last control (say, the fullscreen button) got pushed down
// this often happens when zoomed
do {
// outer area
rail . width ( railWidth ) ;
// dark space
total . width ( railWidth - ( total . outerWidth ( true ) - total . width ( ) ) ) ;
if ( lastControl . css ( 'position' ) != 'absolute' ) {
lastControlPosition = lastControl . length ? lastControl . position ( ) : null ;
railWidth -- ;
}
} while ( lastControlPosition !== null && lastControlPosition . top . toFixed ( 2 ) > 0 && railWidth > 0 ) ;
t . container . trigger ( 'controlsresize' ) ;
} ,
buildposter : function ( player , controls , layers , media ) {
var t = this ,
poster =
$ ( '<div class="mejs-poster mejs-layer">' +
'</div>' )
. appendTo ( layers ) ,
posterUrl = player . $media . attr ( 'poster' ) ;
// prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
if ( player . options . poster !== '' ) {
posterUrl = player . options . poster ;
}
// second, try the real poster
if ( posterUrl ) {
t . setPoster ( posterUrl ) ;
} else {
poster . hide ( ) ;
}
media . addEventListener ( 'play' , function ( ) {
poster . hide ( ) ;
} , false ) ;
if ( player . options . showPosterWhenEnded && player . options . autoRewind ) {
media . addEventListener ( 'ended' , function ( ) {
poster . show ( ) ;
} , false ) ;
}
} ,
setPoster : function ( url ) {
var t = this ,
posterDiv = t . container . find ( '.mejs-poster' ) ,
posterImg = posterDiv . find ( 'img' ) ;
if ( posterImg . length === 0 ) {
posterImg = $ ( '<img width="100%" height="100%" alt="" />' ) . appendTo ( posterDiv ) ;
}
posterImg . attr ( 'src' , url ) ;
posterDiv . css ( { 'background-image' : 'url(' + url + ')' } ) ;
} ,
buildoverlays : function ( player , controls , layers , media ) {
var t = this ;
if ( ! player . isVideo )
return ;
var
loading =
$ ( '<div class="mejs-overlay mejs-layer">' +
'<div class="mejs-overlay-loading"><span></span></div>' +
'</div>' )
. hide ( ) // start out hidden
. appendTo ( layers ) ,
error =
$ ( '<div class="mejs-overlay mejs-layer">' +
'<div class="mejs-overlay-error"></div>' +
'</div>' )
. hide ( ) // start out hidden
. appendTo ( layers ) ,
// this needs to come last so it's on top
bigPlay =
$ ( '<div class="mejs-overlay mejs-layer mejs-overlay-play">' +
'<div class="mejs-overlay-button"></div>' +
'</div>' )
. appendTo ( layers )
. bind ( 'click' , function ( ) { // Removed 'touchstart' due issues on Samsung Android devices where a tap on bigPlay started and immediately stopped the video
if ( t . options . clickToPlayPause ) {
if ( media . paused ) {
media . play ( ) ;
}
}
} ) ;
/ *
if ( mejs . MediaFeatures . isiOS || mejs . MediaFeatures . isAndroid ) {
bigPlay . remove ( ) ;
loading . remove ( ) ;
}
* /
// show/hide big play button
media . addEventListener ( 'play' , function ( ) {
bigPlay . hide ( ) ;
loading . hide ( ) ;
controls . find ( '.mejs-time-buffering' ) . hide ( ) ;
error . hide ( ) ;
} , false ) ;
media . addEventListener ( 'playing' , function ( ) {
bigPlay . hide ( ) ;
loading . hide ( ) ;
controls . find ( '.mejs-time-buffering' ) . hide ( ) ;
error . hide ( ) ;
} , false ) ;
media . addEventListener ( 'seeking' , function ( ) {
loading . show ( ) ;
controls . find ( '.mejs-time-buffering' ) . show ( ) ;
} , false ) ;
media . addEventListener ( 'seeked' , function ( ) {
loading . hide ( ) ;
controls . find ( '.mejs-time-buffering' ) . hide ( ) ;
} , false ) ;
media . addEventListener ( 'pause' , function ( ) {
if ( ! mejs . MediaFeatures . isiPhone ) {
bigPlay . show ( ) ;
}
} , false ) ;
media . addEventListener ( 'waiting' , function ( ) {
loading . show ( ) ;
controls . find ( '.mejs-time-buffering' ) . show ( ) ;
} , false ) ;
// show/hide loading
media . addEventListener ( 'loadeddata' , function ( ) {
// for some reason Chrome is firing this event
//if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
// return;
loading . show ( ) ;
controls . find ( '.mejs-time-buffering' ) . show ( ) ;
// Firing the 'canplay' event after a timeout which isn't getting fired on some Android 4.1 devices (https://github.com/johndyer/mediaelement/issues/1305)
if ( mejs . MediaFeatures . isAndroid ) {
media . canplayTimeout = window . setTimeout (
function ( ) {
if ( document . createEvent ) {
var evt = document . createEvent ( 'HTMLEvents' ) ;
evt . initEvent ( 'canplay' , true , true ) ;
return media . dispatchEvent ( evt ) ;
}
} , 300
) ;
}
} , false ) ;
media . addEventListener ( 'canplay' , function ( ) {
loading . hide ( ) ;
controls . find ( '.mejs-time-buffering' ) . hide ( ) ;
clearTimeout ( media . canplayTimeout ) ; // Clear timeout inside 'loadeddata' to prevent 'canplay' to fire twice
} , false ) ;
// error handling
media . addEventListener ( 'error' , function ( e ) {
t . handleError ( e ) ;
loading . hide ( ) ;
bigPlay . hide ( ) ;
error . show ( ) ;
error . find ( '.mejs-overlay-error' ) . html ( "Error loading this resource" ) ;
} , false ) ;
media . addEventListener ( 'keydown' , function ( e ) {
t . onkeydown ( player , media , e ) ;
} , false ) ;
} ,
buildkeyboard : function ( player , controls , layers , media ) {
var t = this ;
t . container . keydown ( function ( ) {
t . keyboardAction = true ;
} ) ;
// listen for key presses
t . globalBind ( 'keydown' , function ( event ) {
player . hasFocus = $ ( event . target ) . closest ( '.mejs-container' ) . length !== 0
&& $ ( event . target ) . closest ( '.mejs-container' ) . attr ( 'id' ) === player . $media . closest ( '.mejs-container' ) . attr ( 'id' ) ;
return t . onkeydown ( player , media , event ) ;
} ) ;
// check if someone clicked outside a player region, then kill its focus
t . globalBind ( 'click' , function ( event ) {
player . hasFocus = $ ( event . target ) . closest ( '.mejs-container' ) . length !== 0 ;
} ) ;
} ,
onkeydown : function ( player , media , e ) {
if ( player . hasFocus && player . options . enableKeyboard ) {
// find a matching key
for ( var i = 0 , il = player . options . keyActions . length ; i < il ; i ++ ) {
var keyAction = player . options . keyActions [ i ] ;
for ( var j = 0 , jl = keyAction . keys . length ; j < jl ; j ++ ) {
if ( e . keyCode == keyAction . keys [ j ] ) {
if ( typeof ( e . preventDefault ) == "function" ) e . preventDefault ( ) ;
keyAction . action ( player , media , e . keyCode , e ) ;
return false ;
}
}
}
}
return true ;
} ,
findTracks : function ( ) {
var t = this ,
tracktags = t . $media . find ( 'track' ) ;
// store for use by plugins
t . tracks = [ ] ;
tracktags . each ( function ( index , track ) {
track = $ ( track ) ;
t . tracks . push ( {
srclang : ( track . attr ( 'srclang' ) ) ? track . attr ( 'srclang' ) . toLowerCase ( ) : '' ,
src : track . attr ( 'src' ) ,
kind : track . attr ( 'kind' ) ,
label : track . attr ( 'label' ) || '' ,
entries : [ ] ,
isLoaded : false
} ) ;
} ) ;
} ,
changeSkin : function ( className ) {
this . container [ 0 ] . className = 'mejs-container ' + className ;
this . setPlayerSize ( this . width , this . height ) ;
this . setControlsSize ( ) ;
} ,
play : function ( ) {
this . load ( ) ;
this . media . play ( ) ;
} ,
pause : function ( ) {
try {
this . media . pause ( ) ;
} catch ( e ) { }
} ,
load : function ( ) {
if ( ! this . isLoaded ) {
this . media . load ( ) ;
}
this . isLoaded = true ;
} ,
setMuted : function ( muted ) {
this . media . setMuted ( muted ) ;
} ,
setCurrentTime : function ( time ) {
this . media . setCurrentTime ( time ) ;
} ,
getCurrentTime : function ( ) {
return this . media . currentTime ;
} ,
setVolume : function ( volume ) {
this . media . setVolume ( volume ) ;
} ,
getVolume : function ( ) {
return this . media . volume ;
} ,
setSrc : function ( src ) {
this . media . setSrc ( src ) ;
} ,
remove : function ( ) {
var t = this , featureIndex , feature ;
t . container . prev ( '.mejs-offscreen' ) . remove ( ) ;
// invoke features cleanup
for ( featureIndex in t . options . features ) {
feature = t . options . features [ featureIndex ] ;
if ( t [ 'clean' + feature ] ) {
try {
t [ 'clean' + feature ] ( t ) ;
} catch ( e ) {
// TODO: report control error
//throw e;
//
//
}
}
}
// grab video and put it back in place
if ( ! t . isDynamic ) {
t . $media . prop ( 'controls' , true ) ;
// detach events from the video
// TODO: detach event listeners better than this;
// also detach ONLY the events attached by this plugin!
t . $node . clone ( ) . insertBefore ( t . container ) . show ( ) ;
t . $node . remove ( ) ;
} else {
t . $node . insertBefore ( t . container ) ;
}
if ( t . media . pluginType !== 'native' ) {
t . media . remove ( ) ;
}
// Remove the player from the mejs.players object so that pauseOtherPlayers doesn't blow up when trying to pause a non existance flash api.
delete mejs . players [ t . id ] ;
if ( typeof t . container == 'object' ) {
t . container . remove ( ) ;
}
t . globalUnbind ( ) ;
delete t . node . player ;
} ,
rebuildtracks : function ( ) {
var t = this ;
t . findTracks ( ) ;
t . buildtracks ( t , t . controls , t . layers , t . media ) ;
} ,
resetSize : function ( ) {
var t = this ;
// webkit has trouble doing this without a delay
setTimeout ( function ( ) {
//
t . setPlayerSize ( t . width , t . height ) ;
t . setControlsSize ( ) ;
} , 50 ) ;
}
} ;
( function ( ) {
var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/ ;
function splitEvents ( events , id ) {
// add player ID as an event namespace so it's easier to unbind them all later
var ret = { d : [ ] , w : [ ] } ;
$ . each ( ( events || '' ) . split ( ' ' ) , function ( k , v ) {
var eventname = v + '.' + id ;
if ( eventname . indexOf ( '.' ) === 0 ) {
ret . d . push ( eventname ) ;
ret . w . push ( eventname ) ;
}
else {
ret [ rwindow . test ( v ) ? 'w' : 'd' ] . push ( eventname ) ;
}
} ) ;
ret . d = ret . d . join ( ' ' ) ;
ret . w = ret . w . join ( ' ' ) ;
return ret ;
}
mejs . MediaElementPlayer . prototype . globalBind = function ( events , data , callback ) {
var t = this ;
var doc = t . node ? t . node . ownerDocument : document ;
events = splitEvents ( events , t . id ) ;
if ( events . d ) $ ( doc ) . bind ( events . d , data , callback ) ;
if ( events . w ) $ ( window ) . bind ( events . w , data , callback ) ;
} ;
mejs . MediaElementPlayer . prototype . globalUnbind = function ( events , callback ) {
var t = this ;
var doc = t . node ? t . node . ownerDocument : document ;
events = splitEvents ( events , t . id ) ;
if ( events . d ) $ ( doc ) . unbind ( events . d , callback ) ;
if ( events . w ) $ ( window ) . unbind ( events . w , callback ) ;
} ;
} ) ( ) ;
// turn into jQuery plugin
if ( typeof $ != 'undefined' ) {
$ . fn . mediaelementplayer = function ( options ) {
if ( options === false ) {
this . each ( function ( ) {
var player = $ ( this ) . data ( 'mediaelementplayer' ) ;
if ( player ) {
player . remove ( ) ;
}
$ ( this ) . removeData ( 'mediaelementplayer' ) ;
} ) ;
}
else {
this . each ( function ( ) {
$ ( this ) . data ( 'mediaelementplayer' , new mejs . MediaElementPlayer ( this , options ) ) ;
} ) ;
}
return this ;
} ;
$ ( document ) . ready ( function ( ) {
// auto enable using JSON attribute
$ ( '.mejs-player' ) . mediaelementplayer ( ) ;
} ) ;
}
// push out to window
window . MediaElementPlayer = mejs . MediaElementPlayer ;
} ) ( mejs . $ ) ;
( function ( $ ) {
$ . extend ( mejs . MepDefaults , {
playText : mejs . i18n . t ( 'Play' ) ,
pauseText : mejs . i18n . t ( 'Pause' )
} ) ;
// PLAY/pause BUTTON
$ . extend ( MediaElementPlayer . prototype , {
buildplaypause : function ( player , controls , layers , media ) {
var
t = this ,
op = t . options ,
play =
$ ( '<div class="mejs-button mejs-playpause-button mejs-play" >' +
'<button type="button" aria-controls="' + t . id + '" title="' + op . playText + '" aria-label="' + op . playText + '"></button>' +
'</div>' )
. appendTo ( controls )
. click ( function ( e ) {
e . preventDefault ( ) ;
if ( media . paused ) {
media . play ( ) ;
} else {
media . pause ( ) ;
}
return false ;
} ) ,
play _btn = play . find ( 'button' ) ;
function togglePlayPause ( which ) {
if ( 'play' === which ) {
play . removeClass ( 'mejs-play' ) . addClass ( 'mejs-pause' ) ;
play _btn . attr ( {
'title' : op . pauseText ,
'aria-label' : op . pauseText
} ) ;
} else {
play . removeClass ( 'mejs-pause' ) . addClass ( 'mejs-play' ) ;
play _btn . attr ( {
'title' : op . playText ,
'aria-label' : op . playText
} ) ;
}
} ;
togglePlayPause ( 'pse' ) ;
media . addEventListener ( 'play' , function ( ) {
togglePlayPause ( 'play' ) ;
} , false ) ;
media . addEventListener ( 'playing' , function ( ) {
togglePlayPause ( 'play' ) ;
} , false ) ;
media . addEventListener ( 'pause' , function ( ) {
togglePlayPause ( 'pse' ) ;
} , false ) ;
media . addEventListener ( 'paused' , function ( ) {
togglePlayPause ( 'pse' ) ;
} , false ) ;
}
} ) ;
} ) ( mejs . $ ) ;
( function ( $ ) {
$ . extend ( mejs . MepDefaults , {
stopText : 'Stop'
} ) ;
// STOP BUTTON
$ . extend ( MediaElementPlayer . prototype , {
buildstop : function ( player , controls , layers , media ) {
var t = this ;
$ ( '<div class="mejs-button mejs-stop-button mejs-stop">' +
'<button type="button" aria-controls="' + t . id + '" title="' + t . options . stopText + '" aria-label="' + t . options . stopText + '"></button>' +
'</div>' )
. appendTo ( controls )
. click ( function ( ) {
if ( ! media . paused ) {
media . pause ( ) ;
}
if ( media . currentTime > 0 ) {
media . setCurrentTime ( 0 ) ;
media . pause ( ) ;
controls . find ( '.mejs-time-current' ) . width ( '0px' ) ;
controls . find ( '.mejs-time-handle' ) . css ( 'left' , '0px' ) ;
controls . find ( '.mejs-time-float-current' ) . html ( mejs . Utility . secondsToTimeCode ( 0 , player . options ) ) ;
controls . find ( '.mejs-currenttime' ) . html ( mejs . Utility . secondsToTimeCode ( 0 , player . options ) ) ;
layers . find ( '.mejs-poster' ) . show ( ) ;
}
} ) ;
}
} ) ;
} ) ( mejs . $ ) ;
( function ( $ ) {
$ . extend ( mejs . MepDefaults , {
progessHelpText : mejs . i18n . t (
'Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.' )
} ) ;
// progress/loaded bar
$ . extend ( MediaElementPlayer . prototype , {
buildprogress : function ( player , controls , layers , media ) {
$ ( '<div class="mejs-time-rail">' +
'<span class="mejs-time-total mejs-time-slider">' +
//'<span class="mejs-offscreen">' + this.options.progessHelpText + '</span>' +
'<span class="mejs-time-buffering"></span>' +
'<span class="mejs-time-loaded"></span>' +
'<span class="mejs-time-current"></span>' +
'<span class="mejs-time-handle"></span>' +
'<span class="mejs-time-float">' +
'<span class="mejs-time-float-current">00:00</span>' +
'<span class="mejs-time-float-corner"></span>' +
'</span>' +
'</span>' +
'</div>' )
. appendTo ( controls ) ;
controls . find ( '.mejs-time-buffering' ) . hide ( ) ;
var
t = this ,
total = controls . find ( '.mejs-time-total' ) ,
loaded = controls . find ( '.mejs-time-loaded' ) ,
current = controls . find ( '.mejs-time-current' ) ,
handle = controls . find ( '.mejs-time-handle' ) ,
timefloat = controls . find ( '.mejs-time-float' ) ,
timefloatcurrent = controls . find ( '.mejs-time-float-current' ) ,
slider = controls . find ( '.mejs-time-slider' ) ,
handleMouseMove = function ( e ) {
var offset = total . offset ( ) ,
width = total . width ( ) ,
percentage = 0 ,
newTime = 0 ,
pos = 0 ,
x ;
// mouse or touch position relative to the object
if ( e . originalEvent && e . originalEvent . changedTouches ) {
x = e . originalEvent . changedTouches [ 0 ] . pageX ;
} else if ( e . changedTouches ) { // for Zepto
x = e . changedTouches [ 0 ] . pageX ;
} else {
x = e . pageX ;
}
console . log ( 'x is' , x )
if ( media . duration ) {
if ( x < offset . left ) {
x = offset . left ;
} else if ( x > width + offset . left ) {
x = width + offset . left ;
}
pos = x - offset . left ;
percentage = ( pos / width ) ;
newTime = ( percentage <= 0.02 ) ? 0 : percentage * media . duration ;
console . log ( 'percentage is' , percentage ) ;
console . log ( 'new time is' , newTime ) ;
// seek to where the mouse is
if ( mouseIsDown && newTime !== media . currentTime ) {
media . setCurrentTime ( newTime ) ;
}
// position floating time box
if ( ! mejs . MediaFeatures . hasTouch ) {
timefloat . css ( 'left' , pos ) ;
timefloatcurrent . html ( mejs . Utility . secondsToTimeCode ( newTime , player . options ) ) ;
timefloat . show ( ) ;
}
}
} ,
mouseIsDown = false ,
mouseIsOver = false ,
lastKeyPressTime = 0 ,
startedPaused = false ,
autoRewindInitial = player . options . autoRewind ;
// Accessibility for slider
var updateSlider = function ( e ) {
var seconds = media . currentTime ,
timeSliderText = mejs . i18n . t ( 'Time Slider' ) ,
time = mejs . Utility . secondsToTimeCode ( seconds , player . options ) ,
duration = media . duration ;
slider . attr ( {
'aria-label' : timeSliderText ,
'aria-valuemin' : 0 ,
'aria-valuemax' : duration ,
'aria-valuenow' : seconds ,
'aria-valuetext' : time ,
'role' : 'slider' ,
'tabindex' : 0
} ) ;
} ;
var restartPlayer = function ( ) {
var now = new Date ( ) ;
if ( now - lastKeyPressTime >= 1000 ) {
media . play ( ) ;
}
} ;
slider . bind ( 'focus' , function ( e ) {
player . options . autoRewind = false ;
} ) ;
slider . bind ( 'blur' , function ( e ) {
player . options . autoRewind = autoRewindInitial ;
} ) ;
slider . bind ( 'keydown' , function ( e ) {
if ( ( new Date ( ) - lastKeyPressTime ) >= 1000 ) {
startedPaused = media . paused ;
}
var keyCode = e . keyCode ,
duration = media . duration ,
seekTime = media . currentTime ,
seekForward = player . options . defaultSeekForwardInterval ( duration ) ,
seekBackward = player . options . defaultSeekBackwardInterval ( duration ) ;
switch ( keyCode ) {
case 37 : // left
case 40 : // Down
seekTime -= seekBackward ;
break ;
case 39 : // Right
case 38 : // Up
seekTime += seekForward ;
break ;
case 36 : // Home
seekTime = 0 ;
break ;
case 35 : // end
seekTime = duration ;
break ;
case 32 : // space
case 13 : // enter
media . paused ? media . play ( ) : media . pause ( ) ;
return ;
default :
return ;
}
seekTime = seekTime < 0 ? 0 : ( seekTime >= duration ? duration : Math . floor ( seekTime ) ) ;
lastKeyPressTime = new Date ( ) ;
if ( ! startedPaused ) {
media . pause ( ) ;
}
if ( seekTime < media . duration && ! startedPaused ) {
setTimeout ( restartPlayer , 1100 ) ;
}
media . setCurrentTime ( seekTime ) ;
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
return false ;
} ) ;
// handle clicks
//controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
total
. bind ( 'mousedown touchstart' , function ( e ) {
// only handle left clicks or touch
if ( e . which === 1 || e . which === 0 ) {
mouseIsDown = true ;
handleMouseMove ( e ) ;
t . globalBind ( 'mousemove.dur touchmove.dur' , function ( e ) {
handleMouseMove ( e ) ;
} ) ;
t . globalBind ( 'mouseup.dur touchend.dur' , function ( e ) {
mouseIsDown = false ;
timefloat . hide ( ) ;
t . globalUnbind ( '.dur' ) ;
} ) ;
}
} )
. bind ( 'mouseenter' , function ( e ) {
mouseIsOver = true ;
t . globalBind ( 'mousemove.dur' , function ( e ) {
handleMouseMove ( e ) ;
} ) ;
if ( ! mejs . MediaFeatures . hasTouch ) {
timefloat . show ( ) ;
}
} )
. bind ( 'mouseleave' , function ( e ) {
mouseIsOver = false ;
if ( ! mouseIsDown ) {
t . globalUnbind ( '.dur' ) ;
timefloat . hide ( ) ;
}
} ) ;
// loading
media . addEventListener ( 'progress' , function ( e ) {
player . setProgressRail ( e ) ;
player . setCurrentRail ( e ) ;
} , false ) ;
// current time
media . addEventListener ( 'timeupdate' , function ( e ) {
player . setProgressRail ( e ) ;
player . setCurrentRail ( e ) ;
updateSlider ( e ) ;
} , false ) ;
t . container . on ( 'controlsresize' , function ( ) {
player . setProgressRail ( ) ;
player . setCurrentRail ( ) ;
} ) ;
// store for later use
t . loaded = loaded ;
t . total = total ;
t . current = current ;
t . handle = handle ;
} ,
setProgressRail : function ( e ) {
var
t = this ,
target = ( e !== undefined ) ? e . target : t . media ,
percent = null ;
// newest HTML5 spec has buffered array (FF4, Webkit)
if ( target && target . buffered && target . buffered . length > 0 && target . buffered . end && target . duration ) {
// account for a real array with multiple values - always read the end of the last buffer
percent = target . buffered . end ( target . buffered . length - 1 ) / target . duration ;
}
// Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
// to be anything other than 0. If the byte count is available we use this instead.
// Browsers that support the else if do not seem to have the bufferedBytes value and
// should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
else if ( target && target . bytesTotal !== undefined && target . bytesTotal > 0 && target . bufferedBytes !== undefined ) {
percent = target . bufferedBytes / target . bytesTotal ;
}
// Firefox 3 with an Ogg file seems to go this way
else if ( e && e . lengthComputable && e . total !== 0 ) {
percent = e . loaded / e . total ;
}
// finally update the progress bar
if ( percent !== null ) {
percent = Math . min ( 1 , Math . max ( 0 , percent ) ) ;
// update loaded bar
if ( t . loaded && t . total ) {
t . loaded . width ( t . total . width ( ) * percent ) ;
}
}
} ,
setCurrentRail : function ( ) {
var t = this ;
if ( t . media . currentTime !== undefined && t . media . duration ) {
// update bar and handle
if ( t . total && t . handle ) {
var
newWidth = Math . round ( t . total . width ( ) * t . media . currentTime / t . media . duration ) ,
handlePos = newWidth - Math . round ( t . handle . outerWidth ( true ) / 2 ) ;
t . current . width ( newWidth ) ;
t . handle . css ( 'left' , handlePos ) ;
}
}
}
} ) ;
} ) ( mejs . $ ) ;
( function ( $ ) {
// options
$ . extend ( mejs . MepDefaults , {
duration : - 1 ,
timeAndDurationSeparator : '<span> | </span>'
} ) ;
// current and duration 00:00 / 00:00
$ . extend ( MediaElementPlayer . prototype , {
buildcurrent : function ( player , controls , layers , media ) {
var t = this ;
$ ( '<div class="mejs-time" role="timer" aria-live="off">' +
'<span class="mejs-currenttime">' +
mejs . Utility . secondsToTimeCode ( 0 , player . options ) +
'</span>' +
'</div>' )
. appendTo ( controls ) ;
t . currenttime = t . controls . find ( '.mejs-currenttime' ) ;
media . addEventListener ( 'timeupdate' , function ( ) {
player . updateCurrent ( ) ;
} , false ) ;
} ,
buildduration : function ( player , controls , layers , media ) {
var t = this ;
if ( controls . children ( ) . last ( ) . find ( '.mejs-currenttime' ) . length > 0 ) {
$ ( t . options . timeAndDurationSeparator +
'<span class="mejs-duration">' +
mejs . Utility . secondsToTimeCode ( t . options . duration , t . options ) +
'</span>' )
. appendTo ( controls . find ( '.mejs-time' ) ) ;
} else {
// add class to current time
controls . find ( '.mejs-currenttime' ) . parent ( ) . addClass ( 'mejs-currenttime-container' ) ;
$ ( '<div class="mejs-time mejs-duration-container">' +
'<span class="mejs-duration">' +
mejs . Utility . secondsToTimeCode ( t . options . duration , t . options ) +
'</span>' +
'</div>' )
. appendTo ( controls ) ;
}
t . durationD = t . controls . find ( '.mejs-duration' ) ;
media . addEventListener ( 'timeupdate' , function ( ) {
player . updateDuration ( ) ;
} , false ) ;
} ,
updateCurrent : function ( ) {
var t = this ;
var currentTime = t . media . currentTime ;
if ( isNaN ( currentTime ) ) {
currentTime = 0 ;
}
if ( t . currenttime ) {
t . currenttime . html ( mejs . Utility . secondsToTimeCode ( currentTime , t . options ) ) ;
}
} ,
updateDuration : function ( ) {
var t = this ;
var duration = t . media . duration ;
if ( t . options . duration > 0 ) {
duration = t . options . duration ;
}
if ( isNaN ( duration ) ) {
duration = 0 ;
}
//Toggle the long video class if the video is longer than an hour.
t . container . toggleClass ( "mejs-long-video" , duration > 3600 ) ;
if ( t . durationD && duration > 0 ) {
t . durationD . html ( mejs . Utility . secondsToTimeCode ( duration , t . options ) ) ;
}
}
} ) ;
} ) ( mejs . $ ) ;
( function ( $ ) {
$ . extend ( mejs . MepDefaults , {
muteText : mejs . i18n . t ( 'Mute Toggle' ) ,
allyVolumeControlText : mejs . i18n . t ( 'Use Up/Down Arrow keys to increase or decrease volume.' ) ,
hideVolumeOnTouchDevices : true ,
audioVolume : 'horizontal' ,
videoVolume : 'vertical'
} ) ;
$ . extend ( MediaElementPlayer . prototype , {
buildvolume : function ( player , controls , layers , media ) {
// Android and iOS don't support volume controls
if ( ( mejs . MediaFeatures . isAndroid || mejs . MediaFeatures . isiOS ) && this . options . hideVolumeOnTouchDevices )
return ;
var t = this ,
mode = ( t . isVideo ) ? t . options . videoVolume : t . options . audioVolume ,
mute = ( mode == 'horizontal' ) ?
// horizontal version
$ ( '<div class="mejs-button mejs-volume-button mejs-mute">' +
'<button type="button" aria-controls="' + t . id +
'" title="' + t . options . muteText +
'" aria-label="' + t . options . muteText +
'"></button>' +
'</div>' +
'<a href="javascript:void(0);" class="mejs-horizontal-volume-slider">' + // outer background
'<span class="mejs-offscreen">' + t . options . allyVolumeControlText + '</span>' +
'<div class="mejs-horizontal-volume-total"></div>' + // line background
'<div class="mejs-horizontal-volume-current"></div>' + // current volume
'<div class="mejs-horizontal-volume-handle"></div>' + // handle
'</a>'
)
. appendTo ( controls ) :
// vertical version
$ ( '<div class="mejs-button mejs-volume-button mejs-mute">' +
'<button type="button" aria-controls="' + t . id +
'" title="' + t . options . muteText +
'" aria-label="' + t . options . muteText +
'"></button>' +
'<a href="javascript:void(0);" class="mejs-volume-slider">' + // outer background
'<span class="mejs-offscreen">' + t . options . allyVolumeControlText + '</span>' +
'<div class="mejs-volume-total"></div>' + // line background
'<div class="mejs-volume-current"></div>' + // current volume
'<div class="mejs-volume-handle"></div>' + // handle
'</a>' +
'</div>' )
. appendTo ( controls ) ,
volumeSlider = t . container . find ( '.mejs-volume-slider, .mejs-horizontal-volume-slider' ) ,
volumeTotal = t . container . find ( '.mejs-volume-total, .mejs-horizontal-volume-total' ) ,
volumeCurrent = t . container . find ( '.mejs-volume-current, .mejs-horizontal-volume-current' ) ,
volumeHandle = t . container . find ( '.mejs-volume-handle, .mejs-horizontal-volume-handle' ) ,
positionVolumeHandle = function ( volume , secondTry ) {
if ( ! volumeSlider . is ( ':visible' ) && typeof secondTry == 'undefined' ) {
volumeSlider . show ( ) ;
positionVolumeHandle ( volume , true ) ;
volumeSlider . hide ( ) ;
return ;
}
// correct to 0-1
volume = Math . max ( 0 , volume ) ;
volume = Math . min ( volume , 1 ) ;
// ajust mute button style
if ( volume === 0 ) {
mute . removeClass ( 'mejs-mute' ) . addClass ( 'mejs-unmute' ) ;
mute . children ( 'button' ) . attr ( 'title' , mejs . i18n . t ( 'Unmute' ) ) . attr ( 'aria-label' , mejs . i18n . t ( 'Unmute' ) ) ;
} else {
mute . removeClass ( 'mejs-unmute' ) . addClass ( 'mejs-mute' ) ;
mute . children ( 'button' ) . attr ( 'title' , mejs . i18n . t ( 'Mute' ) ) . attr ( 'aria-label' , mejs . i18n . t ( 'Mute' ) ) ;
}
// top/left of full size volume slider background
var totalPosition = volumeTotal . position ( ) ;
// position slider
if ( mode == 'vertical' ) {
var
// height of the full size volume slider background
totalHeight = volumeTotal . height ( ) ,
// the new top position based on the current volume
// 70% volume on 100px height == top:30px
newTop = totalHeight - ( totalHeight * volume ) ;
// handle
volumeHandle . css ( 'top' , Math . round ( totalPosition . top + newTop - ( volumeHandle . height ( ) / 2 ) ) ) ;
// show the current visibility
volumeCurrent . height ( totalHeight - newTop ) ;
volumeCurrent . css ( 'top' , totalPosition . top + newTop ) ;
} else {
var
// height of the full size volume slider background
totalWidth = volumeTotal . width ( ) ,
// the new left position based on the current volume
newLeft = totalWidth * volume ;
// handle
volumeHandle . css ( 'left' , Math . round ( totalPosition . left + newLeft - ( volumeHandle . width ( ) / 2 ) ) ) ;
// rezize the current part of the volume bar
volumeCurrent . width ( Math . round ( newLeft ) ) ;
}
} ,
handleVolumeMove = function ( e ) {
var volume = null ,
totalOffset = volumeTotal . offset ( ) ;
// calculate the new volume based on the moust position
if ( mode === 'vertical' ) {
var
railHeight = volumeTotal . height ( ) ,
newY = e . pageY - totalOffset . top ;
volume = ( railHeight - newY ) / railHeight ;
// the controls just hide themselves (usually when mouse moves too far up)
if ( totalOffset . top === 0 || totalOffset . left === 0 ) {
return ;
}
} else {
var
railWidth = volumeTotal . width ( ) ,
newX = e . pageX - totalOffset . left ;
volume = newX / railWidth ;
}
// ensure the volume isn't outside 0-1
volume = Math . max ( 0 , volume ) ;
volume = Math . min ( volume , 1 ) ;
// position the slider and handle
positionVolumeHandle ( volume ) ;
// set the media object (this will trigger the volumechanged event)
if ( volume === 0 ) {
media . setMuted ( true ) ;
} else {
media . setMuted ( false ) ;
}
media . setVolume ( volume ) ;
} ,
mouseIsDown = false ,
mouseIsOver = false ;
// SLIDER
mute
. hover ( function ( ) {
volumeSlider . show ( ) ;
mouseIsOver = true ;
} , function ( ) {
mouseIsOver = false ;
if ( ! mouseIsDown && mode == 'vertical' ) {
volumeSlider . hide ( ) ;
}
} ) ;
var updateVolumeSlider = function ( e ) {
var volume = Math . floor ( media . volume * 100 ) ;
volumeSlider . attr ( {
'aria-label' : mejs . i18n . t ( 'Volume Slider' ) ,
'aria-valuemin' : 0 ,
'aria-valuemax' : 100 ,
'aria-valuenow' : volume ,
'aria-valuetext' : volume + '%' ,
'role' : 'slider' ,
'tabindex' : 0
} ) ;
} ;
volumeSlider
. bind ( 'mouseover' , function ( ) {
mouseIsOver = true ;
} )
. bind ( 'mousedown' , function ( e ) {
handleVolumeMove ( e ) ;
t . globalBind ( 'mousemove.vol' , function ( e ) {
handleVolumeMove ( e ) ;
} ) ;
t . globalBind ( 'mouseup.vol' , function ( ) {
mouseIsDown = false ;
t . globalUnbind ( '.vol' ) ;
if ( ! mouseIsOver && mode == 'vertical' ) {
volumeSlider . hide ( ) ;
}
} ) ;
mouseIsDown = true ;
return false ;
} )
. bind ( 'keydown' , function ( e ) {
var keyCode = e . keyCode ;
var volume = media . volume ;
switch ( keyCode ) {
case 38 : // Up
volume = Math . min ( volume + 0.1 , 1 ) ;
break ;
case 40 : // Down
volume = Math . max ( 0 , volume - 0.1 ) ;
break ;
default :
return true ;
}
mouseIsDown = false ;
positionVolumeHandle ( volume ) ;
media . setVolume ( volume ) ;
return false ;
} ) ;
// MUTE button
mute . find ( 'button' ) . click ( function ( ) {
media . setMuted ( ! media . muted ) ;
} ) ;
//Keyboard input
mute . find ( 'button' ) . bind ( 'focus' , function ( ) {
volumeSlider . show ( ) ;
} ) ;
// listen for volume change events from other sources
media . addEventListener ( 'volumechange' , function ( e ) {
if ( ! mouseIsDown ) {
if ( media . muted ) {
positionVolumeHandle ( 0 ) ;
mute . removeClass ( 'mejs-mute' ) . addClass ( 'mejs-unmute' ) ;
} else {
positionVolumeHandle ( media . volume ) ;
mute . removeClass ( 'mejs-unmute' ) . addClass ( 'mejs-mute' ) ;
}
}
updateVolumeSlider ( e ) ;
} , false ) ;
// mutes the media and sets the volume icon muted if the initial volume is set to 0
if ( player . options . startVolume === 0 ) {
media . setMuted ( true ) ;
}
// shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
if ( media . pluginType === 'native' ) {
media . setVolume ( player . options . startVolume ) ;
}
t . container . on ( 'controlsresize' , function ( ) {
positionVolumeHandle ( media . volume ) ;
} ) ;
}
} ) ;
} ) ( mejs . $ ) ;
( function ( $ ) {
$ . extend ( mejs . MepDefaults , {
usePluginFullScreen : true ,
newWindowCallback : function ( ) { return '' ; } ,
fullscreenText : mejs . i18n . t ( 'Fullscreen' )
} ) ;
$ . extend ( MediaElementPlayer . prototype , {
isFullScreen : false ,
isNativeFullScreen : false ,
isInIframe : false ,
// Possible modes
// (1) 'native-native' HTML5 video + browser fullscreen (IE10+, etc.)
// (2) 'plugin-native' plugin video + browser fullscreen (fails in some versions of Firefox)
// (3) 'fullwindow' Full window (retains all UI)
// usePluginFullScreen = true
// (4) 'plugin-click' Flash 1 - click through with pointer events
// (5) 'plugin-hover' Flash 2 - hover popup in flash (IE6-8)
fullscreenMode : '' ,
buildfullscreen : function ( player , controls , layers , media ) {
if ( ! player . isVideo )
return ;
player . isInIframe = ( window . location != window . parent . location ) ;
// detect on start
media . addEventListener ( 'play' , function ( ) { player . detectFullscreenMode ( ) ; } ) ;
// build button
var t = this ,
hideTimeout = null ,
fullscreenBtn =
$ ( '<div class="mejs-button mejs-fullscreen-button">' +
'<button type="button" aria-controls="' + t . id + '" title="' + t . options . fullscreenText + '" aria-label="' + t . options . fullscreenText + '"></button>' +
'</div>' )
. appendTo ( controls )
. on ( 'click' , function ( ) {
// toggle fullscreen
var isFullScreen = ( mejs . MediaFeatures . hasTrueNativeFullScreen && mejs . MediaFeatures . isFullScreen ( ) ) || player . isFullScreen ;
if ( isFullScreen ) {
player . exitFullScreen ( ) ;
} else {
player . enterFullScreen ( ) ;
}
} )
. on ( 'mouseover' , function ( ) {
// very old browsers with a plugin
if ( t . fullscreenMode == 'plugin-hover' ) {
if ( hideTimeout !== null ) {
clearTimeout ( hideTimeout ) ;
delete hideTimeout ;
}
var buttonPos = fullscreenBtn . offset ( ) ,
containerPos = player . container . offset ( ) ;
media . positionFullscreenButton ( buttonPos . left - containerPos . left , buttonPos . top - containerPos . top , true ) ;
}
} )
. on ( 'mouseout' , function ( ) {
if ( t . fullscreenMode == 'plugin-hover' ) {
if ( hideTimeout !== null ) {
clearTimeout ( hideTimeout ) ;
delete hideTimeout ;
}
hideTimeout = setTimeout ( function ( ) {
media . hideFullscreenButton ( ) ;
} , 1500 ) ;
}
} ) ;
player . fullscreenBtn = fullscreenBtn ;
t . globalBind ( 'keydown' , function ( e ) {
if ( e . keyCode == 27 && ( ( mejs . MediaFeatures . hasTrueNativeFullScreen && mejs . MediaFeatures . isFullScreen ( ) ) || t . isFullScreen ) ) {
player . exitFullScreen ( ) ;
}
} ) ;
t . normalHeight = 0 ;
t . normalWidth = 0 ;
// setup native fullscreen event
if ( mejs . MediaFeatures . hasTrueNativeFullScreen ) {
// chrome doesn't alays fire this in an iframe
var fullscreenChanged = function ( e ) {
if ( player . isFullScreen ) {
if ( mejs . MediaFeatures . isFullScreen ( ) ) {
player . isNativeFullScreen = true ;
// reset the controls once we are fully in full screen
player . setControlsSize ( ) ;
} else {
player . isNativeFullScreen = false ;
// when a user presses ESC
// make sure to put the player back into place
player . exitFullScreen ( ) ;
}
}
} ;
player . globalBind ( mejs . MediaFeatures . fullScreenEventName , fullscreenChanged ) ;
}
} ,
detectFullscreenMode : function ( ) {
var t = this ,
mode = '' ,
features = mejs . MediaFeatures ;
if ( features . hasTrueNativeFullScreen && t . media . pluginType === 'native' ) {
mode = 'native-native' ;
} else if ( features . hasTrueNativeFullScreen && t . media . pluginType !== 'native' && ! features . hasFirefoxPluginMovingProblem ) {
mode = 'plugin-native' ;
} else if ( t . usePluginFullScreen ) {
if ( mejs . MediaFeatures . supportsPointerEvents ) {
mode = 'plugin-click' ;
// this needs some special setup
t . createPluginClickThrough ( ) ;
} else {
mode = 'plugin-hover' ;
}
} else {
mode = 'fullwindow' ;
}
t . fullscreenMode = mode ;
return mode ;
} ,
isPluginClickThroughCreated : false ,
createPluginClickThrough : function ( ) {
var t = this ;
// don't build twice
if ( t . isPluginClickThroughCreated ) {
return ;
}
// allows clicking through the fullscreen button and controls down directly to Flash
/ *
When a user puts his mouse over the fullscreen button , we disable the controls so that mouse events can go down to flash ( pointer - events )
We then put a divs over the video and on either side of the fullscreen button
to capture mouse movement and restore the controls once the mouse moves outside of the fullscreen button
* /
var fullscreenIsDisabled = false ,
restoreControls = function ( ) {
if ( fullscreenIsDisabled ) {
// hide the hovers
for ( var i in hoverDivs ) {
hoverDivs [ i ] . hide ( ) ;
}
// restore the control bar
t . fullscreenBtn . css ( 'pointer-events' , '' ) ;
t . controls . css ( 'pointer-events' , '' ) ;
// prevent clicks from pausing video
t . media . removeEventListener ( 'click' , t . clickToPlayPauseCallback ) ;
// store for later
fullscreenIsDisabled = false ;
}
} ,
hoverDivs = { } ,
hoverDivNames = [ 'top' , 'left' , 'right' , 'bottom' ] ,
i , len ,
positionHoverDivs = function ( ) {
var fullScreenBtnOffsetLeft = fullscreenBtn . offset ( ) . left - t . container . offset ( ) . left ,
fullScreenBtnOffsetTop = fullscreenBtn . offset ( ) . top - t . container . offset ( ) . top ,
fullScreenBtnWidth = fullscreenBtn . outerWidth ( true ) ,
fullScreenBtnHeight = fullscreenBtn . outerHeight ( true ) ,
containerWidth = t . container . width ( ) ,
containerHeight = t . container . height ( ) ;
for ( i in hoverDivs ) {
hoverDivs [ i ] . css ( { position : 'absolute' , top : 0 , left : 0 } ) ; //, backgroundColor: '#f00'});
}
// over video, but not controls
hoverDivs [ 'top' ]
. width ( containerWidth )
. height ( fullScreenBtnOffsetTop ) ;
// over controls, but not the fullscreen button
hoverDivs [ 'left' ]
. width ( fullScreenBtnOffsetLeft )
. height ( fullScreenBtnHeight )
. css ( { top : fullScreenBtnOffsetTop } ) ;
// after the fullscreen button
hoverDivs [ 'right' ]
. width ( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth )
. height ( fullScreenBtnHeight )
. css ( { top : fullScreenBtnOffsetTop ,
left : fullScreenBtnOffsetLeft + fullScreenBtnWidth } ) ;
// under the fullscreen button
hoverDivs [ 'bottom' ]
. width ( containerWidth )
. height ( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop )
. css ( { top : fullScreenBtnOffsetTop + fullScreenBtnHeight } ) ;
} ;
t . globalBind ( 'resize' , function ( ) {
positionHoverDivs ( ) ;
} ) ;
for ( i = 0 , len = hoverDivNames . length ; i < len ; i ++ ) {
hoverDivs [ hoverDivNames [ i ] ] = $ ( '<div class="mejs-fullscreen-hover" />' ) . appendTo ( t . container ) . mouseover ( restoreControls ) . hide ( ) ;
}
// on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
fullscreenBtn . on ( 'mouseover' , function ( ) {
if ( ! t . isFullScreen ) {
var buttonPos = fullscreenBtn . offset ( ) ,
containerPos = player . container . offset ( ) ;
// move the button in Flash into place
media . positionFullscreenButton ( buttonPos . left - containerPos . left , buttonPos . top - containerPos . top , false ) ;
// allows click through
t . fullscreenBtn . css ( 'pointer-events' , 'none' ) ;
t . controls . css ( 'pointer-events' , 'none' ) ;
// restore click-to-play
t . media . addEventListener ( 'click' , t . clickToPlayPauseCallback ) ;
// show the divs that will restore things
for ( i in hoverDivs ) {
hoverDivs [ i ] . show ( ) ;
}
positionHoverDivs ( ) ;
fullscreenIsDisabled = true ;
}
} ) ;
// restore controls anytime the user enters or leaves fullscreen
media . addEventListener ( 'fullscreenchange' , function ( e ) {
t . isFullScreen = ! t . isFullScreen ;
// don't allow plugin click to pause video - messes with
// plugin's controls
if ( t . isFullScreen ) {
t . media . removeEventListener ( 'click' , t . clickToPlayPauseCallback ) ;
} else {
t . media . addEventListener ( 'click' , t . clickToPlayPauseCallback ) ;
}
restoreControls ( ) ;
} ) ;
// the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
// so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
t . globalBind ( 'mousemove' , function ( e ) {
// if the mouse is anywhere but the fullsceen button, then restore it all
if ( fullscreenIsDisabled ) {
var fullscreenBtnPos = fullscreenBtn . offset ( ) ;
if ( e . pageY < fullscreenBtnPos . top || e . pageY > fullscreenBtnPos . top + fullscreenBtn . outerHeight ( true ) ||
e . pageX < fullscreenBtnPos . left || e . pageX > fullscreenBtnPos . left + fullscreenBtn . outerWidth ( true )
) {
fullscreenBtn . css ( 'pointer-events' , '' ) ;
t . controls . css ( 'pointer-events' , '' ) ;
fullscreenIsDisabled = false ;
}
}
} ) ;
t . isPluginClickThroughCreated = true ;
} ,
cleanfullscreen : function ( player ) {
player . exitFullScreen ( ) ;
} ,
containerSizeTimeout : null ,
enterFullScreen : function ( ) {
var t = this ;
if ( mejs . MediaFeatures . hasiOSFullScreen ) {
t . media . webkitEnterFullscreen ( ) ;
return ;
}
// set it to not show scroll bars so 100% will work
$ ( document . documentElement ) . addClass ( 'mejs-fullscreen' ) ;
// store sizing
t . normalHeight = t . container . height ( ) ;
t . normalWidth = t . container . width ( ) ;
// attempt to do true fullscreen
if ( t . fullscreenMode === 'native-native' || t . fullscreenMode === 'plugin-native' ) {
mejs . MediaFeatures . requestFullScreen ( t . container [ 0 ] ) ;
//return;
if ( t . isInIframe ) {
// sometimes exiting from fullscreen doesn't work
// notably in Chrome <iframe>. Fixed in version 17
setTimeout ( function checkFullscreen ( ) {
if ( t . isNativeFullScreen ) {
var percentErrorMargin = 0.002 , // 0.2%
windowWidth = $ ( window ) . width ( ) ,
screenWidth = screen . width ,
absDiff = Math . abs ( screenWidth - windowWidth ) ,
marginError = screenWidth * percentErrorMargin ;
// check if the video is suddenly not really fullscreen
if ( absDiff > marginError ) {
// manually exit
t . exitFullScreen ( ) ;
} else {
// test again
setTimeout ( checkFullscreen , 500 ) ;
}
}
} , 1000 ) ;
}
} else if ( t . fullscreeMode == 'fullwindow' ) {
// move into position
}
// make full size
t . container
. addClass ( 'mejs-container-fullscreen' )
. width ( '100%' )
. height ( '100%' ) ;
//.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
// Only needed for safari 5.1 native full screen, can cause display issues elsewhere
// Actually, it seems to be needed for IE8, too
//if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
t . containerSizeTimeout = setTimeout ( function ( ) {
t . container . css ( { width : '100%' , height : '100%' } ) ;
t . setControlsSize ( ) ;
} , 500 ) ;
//}
if ( t . media . pluginType === 'native' ) {
t . $media
. width ( '100%' )
. height ( '100%' ) ;
} else {
t . container . find ( '.mejs-shim' )
. width ( '100%' )
. height ( '100%' ) ;
setTimeout ( function ( ) {
var win = $ ( window ) ,
winW = win . width ( ) ,
winH = win . height ( ) ;
t . media . setVideoSize ( winW , winH ) ;
} , 500 ) ;
}
t . layers . children ( 'div' )
. width ( '100%' )
. height ( '100%' ) ;
if ( t . fullscreenBtn ) {
t . fullscreenBtn
. removeClass ( 'mejs-fullscreen' )
. addClass ( 'mejs-unfullscreen' ) ;
}
t . setControlsSize ( ) ;
t . isFullScreen = true ;
t . container . find ( '.mejs-captions-text' ) . css ( 'font-size' , screen . width / t . width * 1.00 * 100 + '%' ) ;
t . container . find ( '.mejs-captions-position' ) . css ( 'bottom' , '45px' ) ;
t . container . trigger ( 'enteredfullscreen' ) ;
} ,
exitFullScreen : function ( ) {
var t = this ;
// Prevent container from attempting to stretch a second time
clearTimeout ( t . containerSizeTimeout ) ;
// firefox can't adjust plugins
/ *
if ( t . media . pluginType !== 'native' && mejs . MediaFeatures . isFirefox ) {
t . media . setFullscreen ( false ) ;
//player.isFullScreen = false;
return ;
}
* /
// come out of native fullscreen
if ( mejs . MediaFeatures . hasTrueNativeFullScreen && ( mejs . MediaFeatures . isFullScreen ( ) || t . isFullScreen ) ) {
mejs . MediaFeatures . cancelFullScreen ( ) ;
}
// restore scroll bars to document
$ ( document . documentElement ) . removeClass ( 'mejs-fullscreen' ) ;
t . container
. removeClass ( 'mejs-container-fullscreen' )
. width ( t . normalWidth )
. height ( t . normalHeight ) ;
if ( t . media . pluginType === 'native' ) {
t . $media
. width ( t . normalWidth )
. height ( t . normalHeight ) ;
} else {
t . container . find ( '.mejs-shim' )
. width ( t . normalWidth )
. height ( t . normalHeight ) ;
t . media . setVideoSize ( t . normalWidth , t . normalHeight ) ;
}
t . layers . children ( 'div' )
. width ( t . normalWidth )
. height ( t . normalHeight ) ;
t . fullscreenBtn
. removeClass ( 'mejs-unfullscreen' )
. addClass ( 'mejs-fullscreen' ) ;
t . setControlsSize ( ) ;
t . isFullScreen = false ;
t . container . find ( '.mejs-captions-text' ) . css ( 'font-size' , '' ) ;
t . container . find ( '.mejs-captions-position' ) . css ( 'bottom' , '' ) ;
t . container . trigger ( 'exitedfullscreen' ) ;
}
} ) ;
} ) ( mejs . $ ) ;
( function ( $ ) {
// Speed
$ . extend ( mejs . MepDefaults , {
// We also support to pass object like this:
// [{name: 'Slow', value: '0.75'}, {name: 'Normal', value: '1.00'}, ...]
speeds : [ '2.00' , '1.50' , '1.25' , '1.00' , '0.75' ] ,
defaultSpeed : '1.00' ,
speedChar : 'x'
} ) ;
$ . extend ( MediaElementPlayer . prototype , {
buildspeed : function ( player , controls , layers , media ) {
var t = this ;
if ( t . media . pluginType == 'native' ) {
var
speedButton = null ,
speedSelector = null ,
playbackSpeed = null ,
inputId = null ;
var speeds = [ ] ;
var defaultInArray = false ;
for ( var i = 0 , len = t . options . speeds . length ; i < len ; i ++ ) {
var s = t . options . speeds [ i ] ;
if ( typeof ( s ) === 'string' ) {
speeds . push ( {
name : s + t . options . speedChar ,
value : s
} ) ;
if ( s === t . options . defaultSpeed ) {
defaultInArray = true ;
}
}
else {
speeds . push ( s ) ;
if ( s . value === t . options . defaultSpeed ) {
defaultInArray = true ;
}
}
}
if ( ! defaultInArray ) {
speeds . push ( {
name : t . options . defaultSpeed + t . options . speedChar ,
value : t . options . defaultSpeed
} ) ;
}
speeds . sort ( function ( a , b ) {
return parseFloat ( b . value ) - parseFloat ( a . value ) ;
} ) ;
var getSpeedNameFromValue = function ( value ) {
for ( i = 0 , len = speeds . length ; i < len ; i ++ ) {
if ( speeds [ i ] . value === value ) {
return speeds [ i ] . name ;
}
}
} ;
var html = '<div class="mejs-button mejs-speed-button">' +
'<button type="button">' + getSpeedNameFromValue ( t . options . defaultSpeed ) + '</button>' +
'<div class="mejs-speed-selector">' +
'<ul>' ;
for ( i = 0 , il = speeds . length ; i < il ; i ++ ) {
inputId = t . id + '-speed-' + speeds [ i ] . value ;
html += '<li>' +
'<input type="radio" name="speed" ' +
'value="' + speeds [ i ] . value + '" ' +
'id="' + inputId + '" ' +
( speeds [ i ] . value === t . options . defaultSpeed ? ' checked' : '' ) +
' />' +
'<label for="' + inputId + '" ' +
( speeds [ i ] . value === t . options . defaultSpeed ? ' class="mejs-speed-selected"' : '' ) +
'>' + speeds [ i ] . name + '</label>' +
'</li>' ;
}
html += '</ul></div></div>' ;
speedButton = $ ( html ) . appendTo ( controls ) ;
speedSelector = speedButton . find ( '.mejs-speed-selector' ) ;
playbackSpeed = t . options . defaultSpeed ;
media . addEventListener ( 'loadedmetadata' , function ( e ) {
if ( playbackSpeed ) {
media . playbackRate = parseFloat ( playbackSpeed ) ;
}
} , true ) ;
speedSelector
. on ( 'click' , 'input[type="radio"]' , function ( ) {
var newSpeed = $ ( this ) . attr ( 'value' ) ;
playbackSpeed = newSpeed ;
media . playbackRate = parseFloat ( newSpeed ) ;
speedButton . find ( 'button' ) . html ( getSpeedNameFromValue ( newSpeed ) ) ;
speedButton . find ( '.mejs-speed-selected' ) . removeClass ( 'mejs-speed-selected' ) ;
speedButton . find ( 'input[type="radio"]:checked' ) . next ( ) . addClass ( 'mejs-speed-selected' ) ;
} ) ;
speedButton
. one ( 'mouseenter focusin' , function ( ) {
speedSelector
. height (
speedButton . find ( '.mejs-speed-selector ul' ) . outerHeight ( true ) +
speedButton . find ( '.mejs-speed-translations' ) . outerHeight ( true ) )
. css ( 'top' , ( - 1 * speedSelector . height ( ) ) + 'px' ) ;
} ) ;
}
}
} ) ;
} ) ( mejs . $ ) ;
( function ( $ ) {
// add extra default options
$ . extend ( mejs . MepDefaults , {
// this will automatically turn on a <track>
startLanguage : '' ,
tracksText : mejs . i18n . t ( 'Captions/Subtitles' ) ,
// By default, no WAI-ARIA live region - don't make a
// screen reader speak captions over an audio track.
tracksAriaLive : false ,
// option to remove the [cc] button when no <track kind="subtitles"> are present
hideCaptionsButtonWhenEmpty : true ,
// If true and we only have one track, change captions to popup
toggleCaptionsButtonWhenOnlyOne : false ,
// #id or .class
slidesSelector : ''
} ) ;
$ . extend ( MediaElementPlayer . prototype , {
hasChapters : false ,
cleartracks : function ( player , controls , layers , media ) {
if ( player ) {
if ( player . captions ) player . captions . remove ( ) ;
if ( player . chapters ) player . chapters . remove ( ) ;
if ( player . captionsText ) player . captionsText . remove ( ) ;
if ( player . captionsButton ) player . captionsButton . remove ( ) ;
}
} ,
buildtracks : function ( player , controls , layers , media ) {
if ( player . tracks . length === 0 )
return ;
var t = this ,
attr = t . options . tracksAriaLive ?
'role="log" aria-live="assertive" aria-atomic="false"' : '' ,
i ;
if ( t . domNode . textTracks ) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide
for ( i = t . domNode . textTracks . length - 1 ; i >= 0 ; i -- ) {
t . domNode . textTracks [ i ] . mode = "hidden" ;
}
}
t . cleartracks ( player , controls , layers , media ) ;
player . chapters =
$ ( '<div class="mejs-chapters mejs-layer"></div>' )
. prependTo ( layers ) . hide ( ) ;
player . captions =
$ ( '<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" ' +
attr + '><span class="mejs-captions-text"></span></div></div>' )
. prependTo ( layers ) . hide ( ) ;
player . captionsText = player . captions . find ( '.mejs-captions-text' ) ;
player . captionsButton =
$ ( '<div class="mejs-button mejs-captions-button">' +
'<button type="button" aria-controls="' + t . id + '" title="' + t . options . tracksText + '" aria-label="' + t . options . tracksText + '"></button>' +
'<div class="mejs-captions-selector">' +
'<ul>' +
'<li>' +
'<input type="radio" name="' + player . id + '_captions" id="' + player . id + '_captions_none" value="none" checked="checked" />' +
'<label for="' + player . id + '_captions_none">' + mejs . i18n . t ( 'None' ) + '</label>' +
'</li>' +
'</ul>' +
'</div>' +
'</div>' )
. appendTo ( controls ) ;
var subtitleCount = 0 ;
for ( i = 0 ; i < player . tracks . length ; i ++ ) {
if ( player . tracks [ i ] . kind == 'subtitles' ) {
subtitleCount ++ ;
}
}
// if only one language then just make the button a toggle
if ( t . options . toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1 ) {
// click
player . captionsButton . on ( 'click' , function ( ) {
if ( player . selectedTrack === null ) {
lang = player . tracks [ 0 ] . srclang ;
} else {
lang = 'none' ;
}
player . setTrack ( lang ) ;
} ) ;
} else {
// hover or keyboard focus
player . captionsButton . on ( 'mouseenter focusin' , function ( ) {
$ ( this ) . find ( '.mejs-captions-selector' ) . removeClass ( 'mejs-offscreen' ) ;
} )
// handle clicks to the language radio buttons
. on ( 'click' , 'input[type=radio]' , function ( ) {
lang = this . value ;
player . setTrack ( lang ) ;
} ) ;
player . captionsButton . on ( 'mouseleave focusout' , function ( ) {
$ ( this ) . find ( ".mejs-captions-selector" ) . addClass ( "mejs-offscreen" ) ;
} ) ;
}
if ( ! player . options . alwaysShowControls ) {
// move with controls
player . container
. bind ( 'controlsshown' , function ( ) {
// push captions above controls
player . container . find ( '.mejs-captions-position' ) . addClass ( 'mejs-captions-position-hover' ) ;
} )
. bind ( 'controlshidden' , function ( ) {
if ( ! media . paused ) {
// move back to normal place
player . container . find ( '.mejs-captions-position' ) . removeClass ( 'mejs-captions-position-hover' ) ;
}
} ) ;
} else {
player . container . find ( '.mejs-captions-position' ) . addClass ( 'mejs-captions-position-hover' ) ;
}
player . trackToLoad = - 1 ;
player . selectedTrack = null ;
player . isLoadingTrack = false ;
// add to list
for ( i = 0 ; i < player . tracks . length ; i ++ ) {
if ( player . tracks [ i ] . kind == 'subtitles' ) {
player . addTrackButton ( player . tracks [ i ] . srclang , player . tracks [ i ] . label ) ;
}
}
// start loading tracks
player . loadNextTrack ( ) ;
media . addEventListener ( 'timeupdate' , function ( e ) {
player . displayCaptions ( ) ;
} , false ) ;
if ( player . options . slidesSelector !== '' ) {
player . slidesContainer = $ ( player . options . slidesSelector ) ;
media . addEventListener ( 'timeupdate' , function ( e ) {
player . displaySlides ( ) ;
} , false ) ;
}
media . addEventListener ( 'loadedmetadata' , function ( e ) {
player . displayChapters ( ) ;
} , false ) ;
player . container . hover (
function ( ) {
// chapters
if ( player . hasChapters ) {
player . chapters . removeClass ( 'mejs-offscreen' ) ;
player . chapters . fadeIn ( 200 ) . height ( player . chapters . find ( '.mejs-chapter' ) . outerHeight ( ) ) ;
}
} ,
function ( ) {
if ( player . hasChapters && ! media . paused ) {
player . chapters . fadeOut ( 200 , function ( ) {
$ ( this ) . addClass ( 'mejs-offscreen' ) ;
$ ( this ) . css ( 'display' , 'block' ) ;
} ) ;
}
} ) ;
t . container . on ( 'controlsresize' , function ( ) {
t . adjustLanguageBox ( ) ;
} ) ;
// check for autoplay
if ( player . node . getAttribute ( 'autoplay' ) !== null ) {
player . chapters . addClass ( 'mejs-offscreen' ) ;
}
} ,
setTrack : function ( lang ) {
var t = this ,
i ;
if ( lang == 'none' ) {
t . selectedTrack = null ;
t . captionsButton . removeClass ( 'mejs-captions-enabled' ) ;
} else {
for ( i = 0 ; i < t . tracks . length ; i ++ ) {
if ( t . tracks [ i ] . srclang == lang ) {
if ( t . selectedTrack === null )
t . captionsButton . addClass ( 'mejs-captions-enabled' ) ;
t . selectedTrack = t . tracks [ i ] ;
t . captions . attr ( 'lang' , t . selectedTrack . srclang ) ;
t . displayCaptions ( ) ;
break ;
}
}
}
} ,
loadNextTrack : function ( ) {
var t = this ;
t . trackToLoad ++ ;
if ( t . trackToLoad < t . tracks . length ) {
t . isLoadingTrack = true ;
t . loadTrack ( t . trackToLoad ) ;
} else {
// add done?
t . isLoadingTrack = false ;
t . checkForTracks ( ) ;
}
} ,
loadTrack : function ( index ) {
var
t = this ,
track = t . tracks [ index ] ,
after = function ( ) {
track . isLoaded = true ;
t . enableTrackButton ( track . srclang , track . label ) ;
t . loadNextTrack ( ) ;
} ;
$ . ajax ( {
url : track . src ,
dataType : "text" ,
success : function ( d ) {
// parse the loaded file
if ( typeof d == "string" && ( /<tt\s+xml/ig ) . exec ( d ) ) {
track . entries = mejs . TrackFormatParser . dfxp . parse ( d ) ;
} else {
track . entries = mejs . TrackFormatParser . webvtt . parse ( d ) ;
}
after ( ) ;
if ( track . kind == 'chapters' ) {
t . media . addEventListener ( 'play' , function ( e ) {
if ( t . media . duration > 0 ) {
t . displayChapters ( track ) ;
}
} , false ) ;
}
if ( track . kind == 'slides' ) {
t . setupSlides ( track ) ;
}
} ,
error : function ( ) {
t . removeTrackButton ( track . srclang ) ;
t . loadNextTrack ( ) ;
}
} ) ;
} ,
enableTrackButton : function ( lang , label ) {
var t = this ;
if ( label === '' ) {
label = mejs . language . codes [ lang ] || lang ;
}
t . captionsButton
. find ( 'input[value=' + lang + ']' )
. prop ( 'disabled' , false )
. siblings ( 'label' )
. html ( label ) ;
// auto select
if ( t . options . startLanguage == lang ) {
$ ( '#' + t . id + '_captions_' + lang ) . prop ( 'checked' , true ) . trigger ( 'click' ) ;
}
t . adjustLanguageBox ( ) ;
} ,
removeTrackButton : function ( lang ) {
var t = this ;
t . captionsButton . find ( 'input[value=' + lang + ']' ) . closest ( 'li' ) . remove ( ) ;
t . adjustLanguageBox ( ) ;
} ,
addTrackButton : function ( lang , label ) {
var t = this ;
if ( label === '' ) {
label = mejs . language . codes [ lang ] || lang ;
}
t . captionsButton . find ( 'ul' ) . append (
$ ( '<li>' +
'<input type="radio" name="' + t . id + '_captions" id="' + t . id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
'<label for="' + t . id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>' +
'</li>' )
) ;
t . adjustLanguageBox ( ) ;
// remove this from the dropdownlist (if it exists)
t . container . find ( '.mejs-captions-translations option[value=' + lang + ']' ) . remove ( ) ;
} ,
adjustLanguageBox : function ( ) {
var t = this ;
// adjust the size of the outer box
t . captionsButton . find ( '.mejs-captions-selector' ) . height (
t . captionsButton . find ( '.mejs-captions-selector ul' ) . outerHeight ( true ) +
t . captionsButton . find ( '.mejs-captions-translations' ) . outerHeight ( true )
) ;
} ,
checkForTracks : function ( ) {
var
t = this ,
hasSubtitles = false ;
// check if any subtitles
if ( t . options . hideCaptionsButtonWhenEmpty ) {
for ( i = 0 ; i < t . tracks . length ; i ++ ) {
if ( t . tracks [ i ] . kind == 'subtitles' && t . tracks [ i ] . isLoaded ) {
hasSubtitles = true ;
break ;
}
}
if ( ! hasSubtitles ) {
t . captionsButton . hide ( ) ;
t . setControlsSize ( ) ;
}
}
} ,
displayCaptions : function ( ) {
if ( typeof this . tracks == 'undefined' )
return ;
var
t = this ,
i ,
track = t . selectedTrack ;
if ( track !== null && track . isLoaded ) {
for ( i = 0 ; i < track . entries . times . length ; i ++ ) {
if ( t . media . currentTime >= track . entries . times [ i ] . start && t . media . currentTime <= track . entries . times [ i ] . stop ) {
// Set the line before the timecode as a class so the cue can be targeted if needed
t . captionsText . html ( track . entries . text [ i ] ) . attr ( 'class' , 'mejs-captions-text ' + ( track . entries . times [ i ] . identifier || '' ) ) ;
t . captions . show ( ) . height ( 0 ) ;
return ; // exit out if one is visible;
}
}
t . captions . hide ( ) ;
} else {
t . captions . hide ( ) ;
}
} ,
setupSlides : function ( track ) {
var t = this ;
t . slides = track ;
t . slides . entries . imgs = [ t . slides . entries . text . length ] ;
t . showSlide ( 0 ) ;
} ,
showSlide : function ( index ) {
if ( typeof this . tracks == 'undefined' || typeof this . slidesContainer == 'undefined' ) {
return ;
}
var t = this ,
url = t . slides . entries . text [ index ] ,
img = t . slides . entries . imgs [ index ] ;
if ( typeof img == 'undefined' || typeof img . fadeIn == 'undefined' ) {
t . slides . entries . imgs [ index ] = img = $ ( '<img src="' + url + '">' )
. on ( 'load' , function ( ) {
img . appendTo ( t . slidesContainer )
. hide ( )
. fadeIn ( )
. siblings ( ':visible' )
. fadeOut ( ) ;
} ) ;
} else {
if ( ! img . is ( ':visible' ) && ! img . is ( ':animated' ) ) {
//
img . fadeIn ( )
. siblings ( ':visible' )
. fadeOut ( ) ;
}
}
} ,
displaySlides : function ( ) {
if ( typeof this . slides == 'undefined' )
return ;
var
t = this ,
slides = t . slides ,
i ;
for ( i = 0 ; i < slides . entries . times . length ; i ++ ) {
if ( t . media . currentTime >= slides . entries . times [ i ] . start && t . media . currentTime <= slides . entries . times [ i ] . stop ) {
t . showSlide ( i ) ;
return ; // exit out if one is visible;
}
}
} ,
displayChapters : function ( ) {
var
t = this ,
i ;
for ( i = 0 ; i < t . tracks . length ; i ++ ) {
if ( t . tracks [ i ] . kind == 'chapters' && t . tracks [ i ] . isLoaded ) {
t . drawChapters ( t . tracks [ i ] ) ;
t . hasChapters = true ;
break ;
}
}
} ,
drawChapters : function ( chapters ) {
var
t = this ,
i ,
dur ,
//width,
//left,
percent = 0 ,
usedPercent = 0 ;
t . chapters . empty ( ) ;
for ( i = 0 ; i < chapters . entries . times . length ; i ++ ) {
dur = chapters . entries . times [ i ] . stop - chapters . entries . times [ i ] . start ;
percent = Math . floor ( dur / t . media . duration * 100 ) ;
if ( percent + usedPercent > 100 || // too large
i == chapters . entries . times . length - 1 && percent + usedPercent < 100 ) // not going to fill it in
{
percent = 100 - usedPercent ;
}
//width = Math.floor(t.width * dur / t.media.duration);
//left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
//if (left + width > t.width) {
// width = t.width - left;
//}
t . chapters . append ( $ (
'<div class="mejs-chapter" rel="' + chapters . entries . times [ i ] . start + '" style="left: ' + usedPercent . toString ( ) + '%;width: ' + percent . toString ( ) + '%;">' +
'<div class="mejs-chapter-block' + ( ( i == chapters . entries . times . length - 1 ) ? ' mejs-chapter-block-last' : '' ) + '">' +
'<span class="ch-title">' + chapters . entries . text [ i ] + '</span>' +
'<span class="ch-time">' + mejs . Utility . secondsToTimeCode ( chapters . entries . times [ i ] . start , t . options ) + '–' + mejs . Utility . secondsToTimeCode ( chapters . entries . times [ i ] . stop , t . options ) + '</span>' +
'</div>' +
'</div>' ) ) ;
usedPercent += percent ;
}
t . chapters . find ( 'div.mejs-chapter' ) . click ( function ( ) {
t . media . setCurrentTime ( parseFloat ( $ ( this ) . attr ( 'rel' ) ) ) ;
if ( t . media . paused ) {
t . media . play ( ) ;
}
} ) ;
t . chapters . show ( ) ;
}
} ) ;
mejs . language = {
codes : {
af : 'Afrikaans' ,
sq : 'Albanian' ,
ar : 'Arabic' ,
be : 'Belarusian' ,
bg : 'Bulgarian' ,
ca : 'Catalan' ,
zh : 'Chinese' ,
'zh-cn' : 'Chinese Simplified' ,
'zh-tw' : 'Chinese Traditional' ,
hr : 'Croatian' ,
cs : 'Czech' ,
da : 'Danish' ,
nl : 'Dutch' ,
en : 'English' ,
et : 'Estonian' ,
fl : 'Filipino' ,
fi : 'Finnish' ,
fr : 'French' ,
gl : 'Galician' ,
de : 'German' ,
el : 'Greek' ,
ht : 'Haitian Creole' ,
iw : 'Hebrew' ,
hi : 'Hindi' ,
hu : 'Hungarian' ,
is : 'Icelandic' ,
id : 'Indonesian' ,
ga : 'Irish' ,
it : 'Italian' ,
ja : 'Japanese' ,
ko : 'Korean' ,
lv : 'Latvian' ,
lt : 'Lithuanian' ,
mk : 'Macedonian' ,
ms : 'Malay' ,
mt : 'Maltese' ,
no : 'Norwegian' ,
fa : 'Persian' ,
pl : 'Polish' ,
pt : 'Portuguese' ,
// 'pt-pt':'Portuguese (Portugal)',
ro : 'Romanian' ,
ru : 'Russian' ,
sr : 'Serbian' ,
sk : 'Slovak' ,
sl : 'Slovenian' ,
es : 'Spanish' ,
sw : 'Swahili' ,
sv : 'Swedish' ,
tl : 'Tagalog' ,
th : 'Thai' ,
tr : 'Turkish' ,
uk : 'Ukrainian' ,
vi : 'Vietnamese' ,
cy : 'Welsh' ,
yi : 'Yiddish'
}
} ;
/ *
Parses WebVTT format which should be formatted as
=== === === === === === === === === === ==
WEBVTT
1
00 : 00 : 01 , 1 -- > 00 : 00 : 05 , 000
A line of text
2
00 : 01 : 15 , 1 -- > 00 : 02 : 05 , 000
A second line of text
=== === === === === === === === === === =
Adapted from : http : //www.delphiki.com/html5/playr
* /
mejs . TrackFormatParser = {
webvtt : {
pattern _timecode : /^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/ ,
parse : function ( trackText ) {
var
i = 0 ,
lines = mejs . TrackFormatParser . split2 ( trackText , /\r?\n/ ) ,
entries = { text : [ ] , times : [ ] } ,
timecode ,
text ,
identifier ;
for ( ; i < lines . length ; i ++ ) {
timecode = this . pattern _timecode . exec ( lines [ i ] ) ;
if ( timecode && i < lines . length ) {
if ( ( i - 1 ) >= 0 && lines [ i - 1 ] !== '' ) {
identifier = lines [ i - 1 ] ;
}
i ++ ;
// grab all the (possibly multi-line) text that follows
text = lines [ i ] ;
i ++ ;
while ( lines [ i ] !== '' && i < lines . length ) {
text = text + '\n' + lines [ i ] ;
i ++ ;
}
text = $ . trim ( text ) . replace ( /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig , "<a href='$1' target='_blank'>$1</a>" ) ;
// Text is in a different array so I can use .join
entries . text . push ( text ) ;
entries . times . push (
{
identifier : identifier ,
start : ( mejs . Utility . convertSMPTEtoSeconds ( timecode [ 1 ] ) === 0 ) ? 0.200 : mejs . Utility . convertSMPTEtoSeconds ( timecode [ 1 ] ) ,
stop : mejs . Utility . convertSMPTEtoSeconds ( timecode [ 3 ] ) ,
settings : timecode [ 5 ]
} ) ;
}
identifier = '' ;
}
return entries ;
}
} ,
// Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420
dfxp : {
parse : function ( trackText ) {
trackText = $ ( trackText ) . filter ( "tt" ) ;
var
i = 0 ,
container = trackText . children ( "div" ) . eq ( 0 ) ,
lines = container . find ( "p" ) ,
styleNode = trackText . find ( "#" + container . attr ( "style" ) ) ,
styles ,
text ,
entries = { text : [ ] , times : [ ] } ;
if ( styleNode . length ) {
var attributes = styleNode . removeAttr ( "id" ) . get ( 0 ) . attributes ;
if ( attributes . length ) {
styles = { } ;
for ( i = 0 ; i < attributes . length ; i ++ ) {
styles [ attributes [ i ] . name . split ( ":" ) [ 1 ] ] = attributes [ i ] . value ;
}
}
}
for ( i = 0 ; i < lines . length ; i ++ ) {
var style ;
var _temp _times = {
start : null ,
stop : null ,
style : null
} ;
if ( lines . eq ( i ) . attr ( "begin" ) ) _temp _times . start = mejs . Utility . convertSMPTEtoSeconds ( lines . eq ( i ) . attr ( "begin" ) ) ;
if ( ! _temp _times . start && lines . eq ( i - 1 ) . attr ( "end" ) ) _temp _times . start = mejs . Utility . convertSMPTEtoSeconds ( lines . eq ( i - 1 ) . attr ( "end" ) ) ;
if ( lines . eq ( i ) . attr ( "end" ) ) _temp _times . stop = mejs . Utility . convertSMPTEtoSeconds ( lines . eq ( i ) . attr ( "end" ) ) ;
if ( ! _temp _times . stop && lines . eq ( i + 1 ) . attr ( "begin" ) ) _temp _times . stop = mejs . Utility . convertSMPTEtoSeconds ( lines . eq ( i + 1 ) . attr ( "begin" ) ) ;
if ( styles ) {
style = "" ;
for ( var _style in styles ) {
style += _style + ":" + styles [ _style ] + ";" ;
}
}
if ( style ) _temp _times . style = style ;
if ( _temp _times . start === 0 ) _temp _times . start = 0.200 ;
entries . times . push ( _temp _times ) ;
text = $ . trim ( lines . eq ( i ) . html ( ) ) . replace ( /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig , "<a href='$1' target='_blank'>$1</a>" ) ;
entries . text . push ( text ) ;
if ( entries . times . start === 0 ) entries . times . start = 2 ;
}
return entries ;
}
} ,
split2 : function ( text , regex ) {
// normal version for compliant browsers
// see below for IE fix
return text . split ( regex ) ;
}
} ;
// test for browsers with bad String.split method.
if ( 'x\n\ny' . split ( /\n/gi ) . length != 3 ) {
// add super slow IE8 and below version
mejs . TrackFormatParser . split2 = function ( text , regex ) {
var
parts = [ ] ,
chunk = '' ,
i ;
for ( i = 0 ; i < text . length ; i ++ ) {
chunk += text . substring ( i , i + 1 ) ;
if ( regex . test ( chunk ) ) {
parts . push ( chunk . replace ( regex , '' ) ) ;
chunk = '' ;
}
}
parts . push ( chunk ) ;
return parts ;
} ;
}
} ) ( mejs . $ ) ;
/ *
* ContextMenu Plugin
*
*
* /
( function ( $ ) {
$ . extend ( mejs . MepDefaults ,
{ 'contextMenuItems' : [
// demo of a fullscreen option
{
render : function ( player ) {
// check for fullscreen plugin
if ( typeof player . enterFullScreen == 'undefined' )
return null ;
if ( player . isFullScreen ) {
return mejs . i18n . t ( 'Turn off Fullscreen' ) ;
} else {
return mejs . i18n . t ( 'Go Fullscreen' ) ;
}
} ,
click : function ( player ) {
if ( player . isFullScreen ) {
player . exitFullScreen ( ) ;
} else {
player . enterFullScreen ( ) ;
}
}
}
,
// demo of a mute/unmute button
{
render : function ( player ) {
if ( player . media . muted ) {
return mejs . i18n . t ( 'Unmute' ) ;
} else {
return mejs . i18n . t ( 'Mute' ) ;
}
} ,
click : function ( player ) {
if ( player . media . muted ) {
player . setMuted ( false ) ;
} else {
player . setMuted ( true ) ;
}
}
} ,
// separator
{
isSeparator : true
}
,
// demo of simple download video
{
render : function ( player ) {
return mejs . i18n . t ( 'Download Video' ) ;
} ,
click : function ( player ) {
window . location . href = player . media . currentSrc ;
}
}
] }
) ;
$ . extend ( MediaElementPlayer . prototype , {
buildcontextmenu : function ( player , controls , layers , media ) {
// create context menu
player . contextMenu = $ ( '<div class="mejs-contextmenu"></div>' )
. appendTo ( $ ( 'body' ) )
. hide ( ) ;
// create events for showing context menu
player . container . bind ( 'contextmenu' , function ( e ) {
if ( player . isContextMenuEnabled ) {
e . preventDefault ( ) ;
player . renderContextMenu ( e . clientX - 1 , e . clientY - 1 ) ;
return false ;
}
} ) ;
player . container . bind ( 'click' , function ( ) {
player . contextMenu . hide ( ) ;
} ) ;
player . contextMenu . bind ( 'mouseleave' , function ( ) {
//
player . startContextMenuTimer ( ) ;
} ) ;
} ,
cleancontextmenu : function ( player ) {
player . contextMenu . remove ( ) ;
} ,
isContextMenuEnabled : true ,
enableContextMenu : function ( ) {
this . isContextMenuEnabled = true ;
} ,
disableContextMenu : function ( ) {
this . isContextMenuEnabled = false ;
} ,
contextMenuTimeout : null ,
startContextMenuTimer : function ( ) {
//
var t = this ;
t . killContextMenuTimer ( ) ;
t . contextMenuTimer = setTimeout ( function ( ) {
t . hideContextMenu ( ) ;
t . killContextMenuTimer ( ) ;
} , 750 ) ;
} ,
killContextMenuTimer : function ( ) {
var timer = this . contextMenuTimer ;
//
if ( timer != null ) {
clearTimeout ( timer ) ;
delete timer ;
timer = null ;
}
} ,
hideContextMenu : function ( ) {
this . contextMenu . hide ( ) ;
} ,
renderContextMenu : function ( x , y ) {
// alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly
var t = this ,
html = '' ,
items = t . options . contextMenuItems ;
for ( var i = 0 , il = items . length ; i < il ; i ++ ) {
if ( items [ i ] . isSeparator ) {
html += '<div class="mejs-contextmenu-separator"></div>' ;
} else {
var rendered = items [ i ] . render ( t ) ;
// render can return null if the item doesn't need to be used at the moment
if ( rendered != null ) {
html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + ( Math . random ( ) * 1000000 ) + '">' + rendered + '</div>' ;
}
}
}
// position and show the context menu
t . contextMenu
. empty ( )
. append ( $ ( html ) )
. css ( { top : y , left : x } )
. show ( ) ;
// bind events
t . contextMenu . find ( '.mejs-contextmenu-item' ) . each ( function ( ) {
// which one is this?
var $dom = $ ( this ) ,
itemIndex = parseInt ( $dom . data ( 'itemindex' ) , 10 ) ,
item = t . options . contextMenuItems [ itemIndex ] ;
// bind extra functionality?
if ( typeof item . show != 'undefined' )
item . show ( $dom , t ) ;
// bind click action
$dom . click ( function ( ) {
// perform click action
if ( typeof item . click != 'undefined' )
item . click ( t ) ;
// close
t . contextMenu . hide ( ) ;
} ) ;
} ) ;
// stop the controls from hiding
setTimeout ( function ( ) {
t . killControlsTimer ( 'rev3' ) ;
} , 100 ) ;
}
} ) ;
} ) ( mejs . $ ) ;
( function ( $ ) {
// skip back button
$ . extend ( mejs . MepDefaults , {
skipBackInterval : 30 ,
// %1 will be replaced with skipBackInterval in this string
skipBackText : mejs . i18n . t ( 'Skip back %1 seconds' )
} ) ;
$ . extend ( MediaElementPlayer . prototype , {
buildskipback : function ( player , controls , layers , media ) {
var
t = this ,
// Replace %1 with skip back interval
backText = t . options . skipBackText . replace ( '%1' , t . options . skipBackInterval ) ,
// create the loop button
loop =
$ ( '<div class="mejs-button mejs-skip-back-button">' +
'<button type="button" aria-controls="' + t . id + '" title="' + backText + '" aria-label="' + backText + '">' + t . options . skipBackInterval + '</button>' +
'</div>' )
// append it to the toolbar
. appendTo ( controls )
// add a click toggle event
. click ( function ( ) {
media . setCurrentTime ( Math . max ( media . currentTime - t . options . skipBackInterval , 0 ) ) ;
$ ( this ) . find ( 'button' ) . blur ( ) ;
} ) ;
}
} ) ;
} ) ( mejs . $ ) ;
/ * *
* Postroll plugin
* /
( function ( $ ) {
$ . extend ( mejs . MepDefaults , {
postrollCloseText : mejs . i18n . t ( 'Close' )
} ) ;
// Postroll
$ . extend ( MediaElementPlayer . prototype , {
buildpostroll : function ( player , controls , layers , media ) {
var
t = this ,
postrollLink = t . container . find ( 'link[rel="postroll"]' ) . attr ( 'href' ) ;
if ( typeof postrollLink !== 'undefined' ) {
player . postroll =
$ ( '<div class="mejs-postroll-layer mejs-layer"><a class="mejs-postroll-close" onclick="$(this).parent().hide();return false;">' + t . options . postrollCloseText + '</a><div class="mejs-postroll-layer-content"></div></div>' ) . prependTo ( layers ) . hide ( ) ;
t . media . addEventListener ( 'ended' , function ( e ) {
$ . ajax ( {
dataType : 'html' ,
url : postrollLink ,
success : function ( data , textStatus ) {
layers . find ( '.mejs-postroll-layer-content' ) . html ( data ) ;
}
} ) ;
player . postroll . show ( ) ;
} , false ) ;
}
}
} ) ;
} ) ( mejs . $ ) ;