r82437 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r82436‎ | r82437 | r82438 >
Date:06:24, 19 February 2011
Author:dale
Status:deferred
Tags:
Comment:
* restructured resource locations for EmbedPlayer
* Moved some classes from mw.EmbedPlayer.js into their respective class name files
* improved hide show control bar support for jquery.menu selection
* basic support for stream selection control bar component
Modified paths:
  • /trunk/extensions/MwEmbedSupport/MwEmbedModules/MediaWikiSupport/resources/MediaWikiPlayerSupport.js (modified) (history)
  • /trunk/extensions/MwEmbedSupport/MwEmbedModules/MwEmbedSupport/MwEmbedSupport.php (modified) (history)
  • /trunk/extensions/MwEmbedSupport/MwEmbedModules/MwEmbedSupport/jquery.menu/jquery.menu.js (modified) (history)
  • /trunk/extensions/MwEmbedSupport/MwEmbedModules/MwEmbedSupport/mwEmbedSupport.js (modified) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/EmbedPlayer.config.php (modified) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/EmbedPlayer.loader.js (modified) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/EmbedPlayer.php (modified) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/iframeApi (deleted) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/players (deleted) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources (added) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaElement.js (added) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaPlayer.js (added) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaPlayers.js (added) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaSource.js (added) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/iframeApi (added) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayer.js (replaced) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerNative.js (modified) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedTypes.js (added) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins (added) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/mw.style.PlayerSkinKskin.css (modified) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js (replaced) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/skins (deleted) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/TimedText/TimedText.php (modified) (history)
  • /trunk/extensions/TimedMediaHandler/MwEmbedModules/TimedText/resources/mw.TimedText.js (modified) (history)
  • /trunk/extensions/TimedMediaHandler/TimedMediaTransformOutput.php (modified) (history)
  • /trunk/extensions/TimedMediaHandler/WebVideoTranscode/WebVideoTranscode.php (modified) (history)
  • /trunk/extensions/TimedMediaHandler/handlers/OggHandler/OggHandler.php (modified) (history)
  • /trunk/extensions/TimedMediaHandler/handlers/WebMHandler/WebMHandler.php (modified) (history)

Diff [purge]

Index: trunk/extensions/MwEmbedSupport/MwEmbedModules/MediaWikiSupport/resources/MediaWikiPlayerSupport.js
@@ -204,6 +204,12 @@
205205 callback();
206206 }
207207 });
 208+
 209+ $( embedPlayer ).bind('GetShareIframeCode', function(event, callback){
 210+ if( data-mwprovider )
 211+ // Check the embedPlayer title key:
 212+ iframeSrc = $( embedPlayer).attr( 'data-mwtitle')
 213+ });
208214 };
209215
210216 } )( window.mediaWiki, window.jQuery );
\ No newline at end of file
Index: trunk/extensions/MwEmbedSupport/MwEmbedModules/MwEmbedSupport/jquery.menu/jquery.menu.js
@@ -44,7 +44,7 @@
4545 );
4646 }
4747 $li.find( 'a' ).append( $('<span>').text( string ) );
48 - //mw.log(' li html: ' + $('<div>').append( $li ).html() );
 48+// mw.log(string + "\n" + ' li html: ' + $('<div>').append( $li ).html() );
4949 return $li;
5050 };
5151
@@ -127,6 +127,7 @@
128128 },
129129 showSpeed: 200, // show/hide speed in milliseconds
130130 createMenuCallback: null,
 131+ closeMenuCallback: null,
131132 callerOnState: 'ui-state-active', // class to change the appearance of the link/button when the menu is showing
132133 loadingState: 'ui-state-loading', // class added to the link/button while the menu is created
133134 linkHover: 'ui-state-hover', // class for menu option hover state
@@ -173,6 +174,10 @@
174175 container.hide();
175176 }
176177 menu.menuOpen = false;
 178+ if( typeof options.closeMenuCallback == 'function'){
 179+ options.closeMenuCallback();
 180+ }
 181+
177182 $(document).unbind('click', killAllMenus);
178183 $(document).unbind('keydown');
179184 };
@@ -382,7 +387,6 @@
383388 var allSubLists = $(this).find('ul');
384389
385390 allSubLists.css({ left: linkWidth, width: linkWidth }).hide();
386 -
387391 $(this).find('a:eq(0)').addClass('fg-menu-indicator').html(
388392 '<span>' + $(this).find('a:eq(0)').html() +
389393 '</span><span class="ui-icon '+options.nextMenuLink+'"></span>')
@@ -479,11 +483,18 @@
480484 checkMenuHeight(topList);
481485
482486 topList.find('a').each(function() {
483 - // if the link opens a child menu:
 487+
484488 if ($(this).next().is('ul')) {
485489 $(this)
486490 .addClass('fg-menu-indicator')
487 - .each(function() { $(this).html('<span>' + $(this).html() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>'); })
 491+ .each(function() {
 492+ // if the link opens a child menu:
 493+ if( !$(this).hasClass('fg-menu-link') ){
 494+ $(this)
 495+ .addClass('fg-menu-link')
 496+ .html( nextMenuLink = '<span>' + $(this).html() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>' )
 497+ }
 498+ })
488499 .click(function() { // ----- show the next menu
489500 var nextList = $(this).next();
490501 var parentUl = $(this).parents('ul:eq(0)');
Index: trunk/extensions/MwEmbedSupport/MwEmbedModules/MwEmbedSupport/MwEmbedSupport.php
@@ -32,5 +32,10 @@
3333 'styles' => 'jquery.menu/jquery.menu.css'
3434 ),
3535 "jquery.triggerQueueCallback" => array( 'scripts'=> "jquery/jquery.triggerQueueCallback.js" ),
36 - "jquery.mwEmbedUtil" => array( 'scripts' => "jquery/jquery.mwEmbedUtil.js" ),
 36+ "jquery.mwEmbedUtil" => array(
 37+ 'scripts' => "jquery/jquery.mwEmbedUtil.js",
 38+ 'dependencies' => array(
 39+ 'jquery.ui.dialog'
 40+ )
 41+ ),
3742 );
Index: trunk/extensions/MwEmbedSupport/MwEmbedModules/MwEmbedSupport/mwEmbedSupport.js
@@ -158,7 +158,8 @@
159159 }
160160 };
161161 // Else use normal mediaWiki string based gM
162 - return mediaWiki.msg( key, paramaters);
 162+
 163+ return mediaWiki.msg.apply( this, $.makeArray( arguments ) );
163164 };
164165
165166 /**
Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/WebMHandler.php
@@ -100,6 +100,16 @@
101101 return $metadata['playtime_seconds'];
102102 }
103103 }
 104+ function getFramerate( $file ){
 105+ $metadata = $this->unpackMetadata( $file->getMetadata() );
 106+ if ( !$metadata || isset( $metadata['error'] ) ) {
 107+ return 0;
 108+ } else {
 109+ print_r($metadata);
 110+ die();
 111+ return $metadata['framerate'];
 112+ }
 113+ }
104114
105115 function getShortDesc( $file ) {
106116 global $wgLang, $wgMediaAudioTypes, $wgMediaVideoTypes;
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/OggHandler.php
@@ -109,7 +109,20 @@
110110 return $metadata['length'];
111111 }
112112 }
113 -
 113+ function getFramerate( $file ){
 114+ $metadata = $this->unpackMetadata( $file->getMetadata() );
 115+ if ( !$metadata || isset( $metadata['error'] ) ) {
 116+ return 0;
 117+ } else {
 118+ // Return the first found theora stream framerate:
 119+ foreach ( $metadata['streams'] as $stream ) {
 120+ if( $stream['type'] == 'Theora' ){
 121+ return $stream['header']['FRN'] / $stream['header']['FRD'];
 122+ }
 123+ }
 124+ return 0;
 125+ }
 126+ }
114127 function getShortDesc( $file ) {
115128 global $wgLang, $wgMediaAudioTypes, $wgMediaVideoTypes;
116129
Index: trunk/extensions/TimedMediaHandler/TimedMediaTransformOutput.php
@@ -52,7 +52,7 @@
5353 return $this->getXmlTagOutput(
5454 $this->getMediaAttr(),
5555 $this->getMediaSources(),
56 - $this->getTextSources()
 56+ $this->getLocalTextSources()
5757 );
5858 }
5959 // XXX migrate this to the mediawiki XML class as 'tagSet' helper function
@@ -66,6 +66,7 @@
6767 }
6868 return $s;
6969 }
 70+
7071 /**
7172 * Call mediaWiki xml helper class to build media tag output from
7273 * supplied arrays
@@ -124,7 +125,7 @@
125126 );
126127
127128 // Add api provider:
128 - if( $this->file->getRepoName() != 'local' ){
 129+ if( !$this->file->isLocal() ){
129130 // Set the api provider name to "commons" for shared ( instant commons convention )
130131 // ( provider names should have identified the provider
131132 // instead of the provider type "shared" )
@@ -147,8 +148,15 @@
148149 return $this->sources;
149150 }
150151
151 - function getTextSources(){
 152+ function getLocalTextSources(){
152153 global $wgServer, $wgScriptPath;
 154+
 155+ // Don't do lookup if non-local path:
 156+ // TODO integrate with repo api and do remote lookup
 157+ if( !$this->file->isLocal() ){
 158+ return array();
 159+ }
 160+
153161 // Check local cache:
154162 if( $this->textTracks ){
155163 return $this->textTracks;
@@ -183,7 +191,7 @@
184192 if( !isset( $langNames[ $languageKey ] ) ){
185193 continue;
186194 }
187 - $this->textTracks[] = array(
 195+ $this->textTracks[] = array(
188196 'kind' => 'subtitles',
189197 'data-mwtitle' => $subTitle->getNsText() . ':' . $subTitle->getDBkey(),
190198 'type' => 'text/x-srt',
@@ -191,7 +199,7 @@
192200 // http://www.whatwg.org/specs/web-apps/current-work/webvtt.html
193201 'src' => $subTitle->getFullURL( array(
194202 'action' => 'raw',
195 - 'ctype' => 'text/plain'
 203+ 'ctype' => 'text/x-srt'
196204 )),
197205 'srclang' => $languageKey,
198206 'label' => wfMsg('timedmedia-subtitle-language',
Index: trunk/extensions/TimedMediaHandler/WebVideoTranscode/WebVideoTranscode.php
@@ -54,7 +54,7 @@
5555 'videoBitrate' => '160',
5656 'audioBitrate' => '32',
5757 'samplerate' => '22050',
58 - //'framerate' => '15',
 58+ 'framerate' => '18',
5959 'channels' => '1',
6060 'noUpscaling' => 'true',
6161 'twopass' => 'true',
@@ -221,21 +221,33 @@
222222 $thumbName = $file->thumbName( array() );
223223 $thumbUrl = $file->getThumbUrl( $thumbName );
224224 $thumbUrlDir = dirname( $thumbUrl );
225 -
 225+
226226 // if the source size is < $transcodeKey assume source size:
227227 if( is_file( $derivativeFile ) ){
 228+ // Estimate bandwith:
 229+ $bandwith = intval( filesize( $derivativeFile ) / $file->getLength() ) * 8;
 230+
 231+ list( $width, $height ) = WebVideoTranscode::getMaxSizeTransform(
 232+ $file,
 233+ self::$derivativeSettings[$transcodeKey]['maxSize']
 234+ );
 235+
 236+ $framerate = ( isset( self::$derivativeSettings[$transcodeKey]['framerate'] ) )?
 237+ self::$derivativeSettings[$transcodeKey]['framerate'] :
 238+ $file->getHandler()->getFramerate( $file );
228239 $sources[] = array(
229240 'src' => $thumbUrlDir . '/' .$file->getName() . '.' . $transcodeKey,
230241 'title' => wfMsg('timedmedia-derivative-desc-' . $transcodeKey ),
231242 'data-shorttitle' => wfMsg('timedmedia-derivative-' . $transcodeKey),
232 - 'data-size' => implode( 'x',
233 - WebVideoTranscode::getMaxSizeTransform(
234 - $file,
235 - self::$derivativeSettings[$transcodeKey]['maxSize']
236 - )
237 - )
 243+
 244+ // Add data attributes per emerging DASH / webTV adaptive streaming attributes
 245+ // eventually we will define a manifest xml entry point.
 246+ 'data-width' => $width,
 247+ 'data-height' => $height,
 248+ 'data-bandwith' => $bandwith,
 249+ 'data-framerate' => $framerate,
238250 );
239 - } else {
 251+ } else {
240252 self::updateJobQueue($file, $transcodeKey);
241253 }
242254 }
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/TimedText/resources/mw.TimedText.js
@@ -361,7 +361,13 @@
362362 'autoShow': autoShow,
363363 'targetMenuContainer' : _this.menuTarget,
364364 'positionOpts' : positionOpts,
365 - 'backLinkText' : gM( 'mwe-timedtext-back-btn' )
 365+ 'backLinkText' : gM( 'mwe-timedtext-back-btn' ),
 366+ 'createMenuCallback' : function(){
 367+ _this.embedPlayer.controlBuilder.showControlBar( true );
 368+ },
 369+ 'closeMenuCallback' : function(){
 370+ _this.embedPlayer.controlBuilder.hideControlBar( true );
 371+ }
366372 } );
367373 });
368374 },
@@ -394,6 +400,7 @@
395401 this.textSources = [ ];
396402 // Get local reference to all timed text sources: ( text/xml, text/x-srt etc )
397403 var inlineSources = this.embedPlayer.mediaElement.getSources( 'text' );
 404+
398405 // Add all the sources to textSources
399406 for( var i = 0 ; i < inlineSources.length ; i++ ) {
400407 // Make a new textSource:
@@ -401,9 +408,9 @@
402409 this.textSources.push( source );
403410 }
404411
405 - // If there are no inline sources check & apiTitleKey
406 - if( this.textSources.length == 0 || !this.embedPlayer.apiTitleKey ) {
407 - //no other sources just issue the callback:
 412+ // If there are inline sources or no apiTitleKey we are done loading
 413+ if( this.textSources.length != 0 || !this.embedPlayer.apiTitleKey ) {
 414+ // No other sources just issue the callback:
408415 callback();
409416 return ;
410417 }
@@ -416,6 +423,7 @@
417424 mw.log("Error: loading source without apiProvider or apiTitleKey");
418425 return ;
419426 }
 427+
420428 //For now only support mediaWikTrack provider library
421429 this.textProvider = new mw.MediaWikTrackProvider( {
422430 'provider_id' : provider_id,
@@ -444,7 +452,7 @@
445453
446454 // Add a title
447455 $( textElm ).attr('title',
448 - gM('mwe-timedtext-key-language', [textSource.srclang, mw.Language.names[ textSource.srclang ] ] )
 456+ gM('mwe-timedtext-key-language', textSource.srclang, mw.Language.names[ textSource.srclang ] )
449457 );
450458
451459 // Add the sources to the parent embedPlayer
@@ -794,13 +802,12 @@
795803 return $j.getLineItem( source.title, source_icon, function() {
796804 _this.selectTextSource( source );
797805 });
798 - }
799 -
 806+ }
800807 if( source.srclang ) {
801808 var langKey = source.srclang.toLowerCase();
802 - _this.getLanguageName ( langKey );
 809+ var cat = gM('mwe-timedtext-key-language', langKey, _this.getLanguageName ( langKey ) );
803810 return $j.getLineItem(
804 - gM('mwe-timedtext-key-language', [langKey, mw.Language.names[ source.srclang ] ] ),
 811+ gM('mwe-timedtext-key-language', langKey, _this.getLanguageName ( langKey ) ),
805812 source_icon,
806813 function() {
807814 _this.selectTextSource( source );
@@ -862,9 +869,6 @@
863870 // Update the config and redraw layout
864871 _this.config.layout = layoutMode;
865872
866 - // Update the user config:
867 - mw.setUserConfig( 'timedTextConfig', _this.config);
868 -
869873 // Update the display:
870874 _this.updateLayout();
871875 }
@@ -1074,7 +1078,7 @@
10751079 // Setup the display text div:
10761080 var layoutMode = this.getLayoutMode();
10771081 if( layoutMode == 'ontop' ) {
1078 - this.embedPlayer.controlBuilder.displayOptionsMenuFlag = false;
 1082+ this.embedPlayer.controlBuilder.keepControlBarOnScreen = false;
10791083 var $track = $('<div>')
10801084 .addClass( 'track' + ' ' + 'track_' + category )
10811085 .css( {
@@ -1100,7 +1104,7 @@
11011105 $playerTarget.append( $track );
11021106
11031107 } else if ( layoutMode == 'below') {
1104 - this.embedPlayer.controlBuilder.displayOptionsMenuFlag = true;
 1108+ this.embedPlayer.controlBuilder.keepControlBarOnScreen = true;
11051109 // Set the belowBar size to 60 pixels:
11061110 var belowBarHeight = 60;
11071111 // Append before controls:
@@ -1191,7 +1195,7 @@
11921196 }
11931197 };
11941198 _this.loaded = true;
1195 -
 1199+
11961200 // Set parser handler:
11971201 switch( this.getMIMEType() ) {
11981202 //Special mediaWiki srt format ( support wiki-text in srt's )
@@ -1212,6 +1216,7 @@
12131217 mw.log("Error: no handler for type: " + this.getMIMEType() );
12141218 return ;
12151219 }
 1220+
12161221 // Try to load src via textProvider:
12171222 if( this.textProvider && this.titleKey ) {
12181223 this.textProvider.loadTitleKey( this.titleKey, function( data ) {
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/TimedText/TimedText.php
@@ -6,8 +6,9 @@
77 'scripts' => "resources/mw.TimedText.js",
88 'styles' => "resources/mw.style.TimedText.css",
99 'dependencies' => array(
10 - 'mw.EmbedPlayer',
11 - 'mw.Language.names'
 10+ 'mw.EmbedPlayer',
 11+ 'mw.Language.names',
 12+ 'jquery.ui.dialog',
1213 ),
1314 'messageFile' => 'TimedText.i18n.php',
1415 ),
@@ -15,8 +16,7 @@
1617 'scripts' => "resources/mw.TimedTextEdit.js",
1718 'styles' => "resources/mw.style.TimedTextEdit.css",
1819 'dependencies' => array(
19 - 'mw.TimedText',
20 - 'jquery.ui.dialog',
 20+ 'mw.TimedText',
2121 'jquery.ui.tabs'
2222 )
2323 ),
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/EmbedPlayer.php
@@ -2,15 +2,33 @@
33
44 // Register all the EmbedPlayer modules
55 return array(
 6+ "MediaElement" => array( 'scripts' => 'resources/MediaElement.js' ),
 7+ "MediaPlayer" => array( 'scripts' => 'resources/MediaPlayer.js' ),
 8+ "MediaPlayers" => array(
 9+ 'scripts' => 'resources/MediaPlayers.js',
 10+ 'dependencies' => 'MediaPlayer'
 11+ ),
 12+ "MediaSource" => array( 'scripts' => 'resources/MediaSource.js' ),
 13+ "mw.EmbedTypes" => array(
 14+ 'scripts' => 'resources/mw.EmbedTypes.js',
 15+ 'dependencies' => 'MediaPlayers'
 16+ ),
 17+
618 "mw.EmbedPlayer" => array(
719 'scripts' => array(
8 - "players/mw.EmbedPlayer.js",
9 - "skins/mw.PlayerControlBuilder.js",
 20+ "resources/mw.EmbedPlayer.js",
 21+ "resources/skins/mw.PlayerControlBuilder.js",
1022 ),
1123 'dependencies' => array(
12 - // mwEmbed support:
 24+ // mwEmbed support module
1325 'mwEmbedSupport',
1426
 27+ // Sub classes:
 28+ 'MediaElement',
 29+ 'MediaPlayers',
 30+ 'MediaSource',
 31+ 'mw.EmbedTypes',
 32+
1533 // jQuery dependencies:
1634 'jquery.hoverIntent',
1735 'jquery.cookie',
@@ -18,24 +36,24 @@
1937 'jquery.menu',
2038 'jquery.ui.slider'
2139 ),
22 - 'styles' => "skins/mw.style.EmbedPlayer.css",
 40+ 'styles' => "resources/skins/mw.style.EmbedPlayer.css",
2341 'messageFile' => 'EmbedPlayer.i18n.php',
2442 ),
2543
26 - "mw.EmbedPlayerKplayer" => array( 'scripts'=> "players/mw.EmbedPlayerKplayer.js" ),
27 - "mw.EmbedPlayerGeneric" => array( 'scripts'=> "players/mw.EmbedPlayerGeneric.js" ),
28 - "mw.EmbedPlayerJava" => array( 'scripts'=> "players/mw.EmbedPlayerJava.js"),
29 - "mw.EmbedPlayerNative" => array( 'scripts'=> "players/mw.EmbedPlayerNative.js" ),
 44+ "mw.EmbedPlayerKplayer" => array( 'scripts'=> "resources/mw.EmbedPlayerKplayer.js" ),
 45+ "mw.EmbedPlayerGeneric" => array( 'scripts'=> "resources/mw.EmbedPlayerGeneric.js" ),
 46+ "mw.EmbedPlayerJava" => array( 'scripts'=> "resources/mw.EmbedPlayerJava.js"),
 47+ "mw.EmbedPlayerNative" => array( 'scripts'=> "resources/mw.EmbedPlayerNative.js" ),
3048
31 - "mw.EmbedPlayerVlc" => array( 'scripts'=> "players/mw.EmbedPlayerVlc.js" ),
 49+ "mw.EmbedPlayerVlc" => array( 'scripts'=> "resources/mw.EmbedPlayerVlc.js" ),
3250
33 - "mw.IFramePlayerApiServer" => array( 'scripts' => "iframeApi/mw.IFramePlayerApiServer.js" ),
34 - "mw.IFramePlayerApiClient" => array( 'scripts' => "iframeApi/mw.IFramePlayerApiClient.js" ),
 51+ "mw.IFramePlayerApiServer" => array( 'scripts' => "resources/iframeApi/mw.IFramePlayerApiServer.js" ),
 52+ "mw.IFramePlayerApiClient" => array( 'scripts' => "resources/iframeApi/mw.IFramePlayerApiClient.js" ),
3553
36 - "mw.PlayerSkinKskin" => array( 'scripts' => "skins/kskin/mw.PlayerSkinKskin.js",
37 - 'styles' => "skins/kskin/mw.style.PlayerSkinKskin.css"),
 54+ "mw.PlayerSkinKskin" => array( 'scripts' => "resources/skins/kskin/mw.PlayerSkinKskin.js",
 55+ 'styles' => "resources/skins/kskin/mw.style.PlayerSkinKskin.css"),
3856
39 - "mw.PlayerSkinMvpcf" => array( 'scripts'=> "skins/mvpcf/mw.PlayerSkinMvpcf.js",
40 - 'styles'=> "skins/mvpcf/mw.style.PlayerSkinMvpcf.css"),
 57+ "mw.PlayerSkinMvpcf" => array( 'scripts'=> "resources/skins/mvpcf/mw.PlayerSkinMvpcf.js",
 58+ 'styles'=> "resources/skins/mvpcf/mw.style.PlayerSkinMvpcf.css"),
4159 );
4260 ?>
\ No newline at end of file
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/EmbedPlayer.config.php
@@ -53,6 +53,9 @@
5454
5555 // If fullscreen is global enabled.
5656 "EmbedPlayer.EnableFullscreen" => true,
 57+
 58+ // If the options control bar menu item should be enabled:
 59+ 'EmbedPlayer.EnableOptionsMenu' => true,
5760
5861 // If mwEmbed should use the Native player controls
5962 // this will prevent video tag rewriting and skinning
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/EmbedPlayer.loader.js
@@ -113,9 +113,9 @@
114114 mediaWiki.loader.using( dependencySet, function(){
115115 setTimeout(function(){
116116 mw.processEmbedPlayers( playerSelect, readyCallback );
117 - },500);
118 - }, function(){
119 - throw new Error( 'Error loading EmbedPlayer dependency set' );
 117+ },1000);
 118+ }, function( e ){
 119+ //throw new Error( 'Error loading EmbedPlayer dependency set' );
120120 });
121121 };
122122
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerGeneric.js
@@ -0,0 +1,33 @@
 2+/*
 3+* Simple embed object for unknown application/ogg plugin
 4+*/
 5+( function( mw, $ ) {
 6+ mw.EmbedPlayerGeneric = {
 7+ // List of supported features of the generic plugin
 8+ supports: {
 9+ 'playHead':false,
 10+ 'pause':false,
 11+ 'stop':true,
 12+ 'fullscreen':false,
 13+ 'timeDisplay':false,
 14+ 'volumeControl':false
 15+ },
 16+
 17+ // Instance name:
 18+ instanceOf:'Generic',
 19+
 20+ /*
 21+ * Generic embed html
 22+ *
 23+ * @return {String}
 24+ * embed code for generic ogg plugin
 25+ */
 26+ doEmbedHTML: function() {
 27+ $( this ).html(
 28+ '<object type="application/ogg" ' +
 29+ 'width="' + this.getWidth() + '" height="' + this.getHeight() + '" ' +
 30+ 'data="' + this.getSrc( this.seek_time_sec ) + '"></object>'
 31+ );
 32+ }
 33+ };
 34+} )( window.mediaWiki, window.jQuery );
\ No newline at end of file
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerGeneric.js
___________________________________________________________________
Added: svn:mime-type
135 + text/plain
Added: svn:eol-style
236 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaElement.js
@@ -0,0 +1,344 @@
 2+/**
 3+ * A media element corresponding to a <video> element.
 4+ *
 5+ * It is implemented as a collection of mediaSource objects. The media sources
 6+ * will be initialized from the <video> element, its child <source> elements,
 7+ * and/or the ROE file referenced by the <video> element.
 8+ *
 9+ * @param {element}
 10+ * videoElement <video> element used for initialization.
 11+ * @constructor
 12+ */
 13+function mediaElement( element ) {
 14+ this.init( element );
 15+}
 16+
 17+mediaElement.prototype = {
 18+
 19+ // The array of mediaSource elements.
 20+ sources: null,
 21+
 22+ // flag for ROE data being added.
 23+ addedROEData: false,
 24+
 25+ // Selected mediaSource element.
 26+ selectedSource: null,
 27+
 28+ /**
 29+ * Media Element constructor
 30+ *
 31+ * Sets up a mediaElement from a provided top level "video" element adds any
 32+ * child sources that are found
 33+ *
 34+ * @param {Element}
 35+ * videoElement Element that has src attribute or has children
 36+ * source elements
 37+ */
 38+ init: function( videoElement ) {
 39+ var _this = this;
 40+ mw.log( "EmbedPlayer::mediaElement:init:" + videoElement.id );
 41+ this.sources = new Array();
 42+
 43+ // Process the videoElement as a source element:
 44+ if ( $( videoElement ).attr( "src" ) ) {
 45+ _this.tryAddSource( videoElement );
 46+ }
 47+
 48+ // Process elements source children
 49+ $( videoElement ).find( 'source,track' ).each( function( ) {
 50+ _this.tryAddSource( this );
 51+ } );
 52+ },
 53+
 54+ /**
 55+ * Updates the time request for all sources that have a standard time
 56+ * request argument (ie &t=start_time/end_time)
 57+ *
 58+ * @param {String}
 59+ * start_npt Start time in npt format
 60+ * @param {String}
 61+ * end_npt End time in npt format
 62+ */
 63+ updateSourceTimes: function( start_npt, end_npt ) {
 64+ var _this = this;
 65+ $j.each( this.sources, function( inx, mediaSource ) {
 66+ mediaSource.updateSrcTime( start_npt, end_npt );
 67+ } );
 68+ },
 69+
 70+ /**
 71+ * Check for Timed Text tracks
 72+ *
 73+ * @return {Boolean} True if text tracks exist, false if no text tracks are
 74+ * found
 75+ */
 76+ textSourceExists: function() {
 77+ for ( var i = 0; i < this.sources.length; i++ ) {
 78+ if ( this.sources[i].mimeType == 'text/cmml' ||
 79+ this.sources[i].mimeType == 'text/x-srt' )
 80+ {
 81+ return true;
 82+ }
 83+ };
 84+ return false;
 85+ },
 86+
 87+ /**
 88+ * Returns the array of mediaSources of this element.
 89+ *
 90+ * @param {String}
 91+ * [mimeFilter] Filter criteria for set of mediaSources to return
 92+ * @return {Array} mediaSource elements.
 93+ */
 94+ getSources: function( mimeFilter ) {
 95+ if ( !mimeFilter ) {
 96+ return this.sources;
 97+ }
 98+ // Apply mime filter:
 99+ var source_set = new Array();
 100+ for ( var i = 0; i < this.sources.length ; i++ ) {
 101+ if ( this.sources[i].mimeType &&
 102+ this.sources[i].mimeType.indexOf( mimeFilter ) != -1 )
 103+ {
 104+ source_set.push( this.sources[i] );
 105+ }
 106+ }
 107+ return source_set;
 108+ },
 109+
 110+ /**
 111+ * Selects a source by id
 112+ *
 113+ * @param {String}
 114+ * source_id Id of the source to select.
 115+ * @return {MediaSource} The selected mediaSource or null if not found
 116+ */
 117+ getSourceById:function( source_id ) {
 118+ for ( var i = 0; i < this.sources.length ; i++ ) {
 119+ if ( this.sources[i].id == source_id ) {
 120+ return this.sources[i];
 121+ }
 122+ }
 123+ return null;
 124+ },
 125+
 126+ /**
 127+ * Selects a particular source for playback updating the "selectedSource"
 128+ *
 129+ * @param {Number}
 130+ * index Index of source element to set as selectedSource
 131+ */
 132+ selectSource:function( index ) {
 133+ mw.log( 'EmbedPlayer::mediaElement:selectSource:' + index );
 134+ var playableSources = this.getPlayableSources();
 135+ for ( var i = 0; i < playableSources.length; i++ ) {
 136+ if ( i == index ) {
 137+ this.selectedSource = playableSources[i];
 138+ // Update the user selected format:
 139+ mw.EmbedTypes.getMediaPlayers().setFormatPreference( playableSources[i].mimeType );
 140+ break;
 141+ }
 142+ }
 143+ },
 144+
 145+ /**
 146+ * Selects the default source via cookie preference, default marked, or by
 147+ * id order
 148+ */
 149+ autoSelectSource: function() {
 150+ mw.log( 'EmbedPlayer::mediaElement::autoSelectSource' );
 151+ var _this = this;
 152+ // Select the default source
 153+ var playableSources = this.getPlayableSources();
 154+ var flash_flag = ogg_flag = false;
 155+
 156+ // Check if there are any playableSources
 157+ if( playableSources.length == 0 ){
 158+ return false;
 159+ }
 160+ var setSelectedSource = function( source ){
 161+ _this.selectedSource = source;
 162+ };
 163+
 164+ // Set via user-preference
 165+ for ( var source = 0; source < playableSources.length; source++ ) {
 166+ var mimeType = playableSources[source].mimeType;
 167+ if ( mw.EmbedTypes.getMediaPlayers().preference[ 'format_preference' ] == mimeType ) {
 168+ mw.log( 'EmbedPlayer::autoSelectSource: Set via preference: ' + playableSources[source].mimeType );
 169+ setSelectedSource( playableSources[source] );
 170+ return true;
 171+ }
 172+ }
 173+
 174+ // Set via module driven preference:
 175+ $(this).trigger( 'AutoSelectSource', [ playableSources ] );
 176+ if( _this.selectedSource ){
 177+ return true;
 178+ }
 179+
 180+ // Set via marked default:
 181+ for ( var source = 0; source < playableSources.length; source++ ) {
 182+ if ( playableSources[ source ].markedDefault ) {
 183+ mw.log( 'EmbedPlayer::autoSelectSource: Set via marked default: ' + playableSources[source].markedDefault );
 184+ setSelectedSource( playableSources[source] );
 185+ return true;
 186+ }
 187+ }
 188+
 189+ // Prefer native playback ( and prefer WebM over ogg and h.264 )
 190+ var namedSources = [];
 191+ for ( var source = 0; source < playableSources.length; source++ ) {
 192+ var mimeType = playableSources[source].mimeType;
 193+ var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
 194+ if ( player && player.library == 'Native' ) {
 195+ switch( player.id ){
 196+ case 'oggNative':
 197+ namedSources['ogg'] = playableSources[ source ];
 198+ break;
 199+ case 'webmNative':
 200+ namedSources['webm'] = playableSources[ source ];
 201+ break;
 202+ case 'h264Native':
 203+ namedSources['h264'] = playableSources[ source ];
 204+ break;
 205+ }
 206+ }
 207+ }
 208+ var codecPref =mw.getConfig( 'EmbedPlayer.CodecPreference');
 209+ for(var i =0; i < codecPref.length; i++){
 210+ var codec = codecPref[ i ];
 211+ if( namedSources[ codec ]){
 212+ setSelectedSource( namedSources[ codec ] );
 213+ return true;
 214+ }
 215+ };
 216+
 217+
 218+ // Set h264 via native or flash fallback
 219+ for ( var source = 0; source < playableSources.length; source++ ) {
 220+ var mimeType = playableSources[source].mimeType;
 221+ var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
 222+ if ( mimeType == 'video/h264'
 223+ && player
 224+ && (
 225+ player.library == 'Native'
 226+ ||
 227+ player.library == 'Kplayer'
 228+ )
 229+ ) {
 230+ mw.log('EmbedPlayer::autoSelectSource: Set h264 via native or flash fallback');
 231+ setSelectedSource( playableSources[ source ] );
 232+ return true;
 233+ }
 234+ };
 235+
 236+ // Else just select first source
 237+ if ( !this.selectedSource ) {
 238+ mw.log( 'EmbedPlayer::autoSelectSource: Set via first source:' + playableSources[0] );
 239+ setSelectedSource( playableSources[0] );
 240+ return true;
 241+ }
 242+ // No Source found so no source selected
 243+ return false;
 244+ },
 245+
 246+ /**
 247+ * check if the mime is ogg
 248+ */
 249+ isOgg: function( mimeType ){
 250+ if ( mimeType == 'video/ogg'
 251+ || mimeType == 'ogg/video'
 252+ || mimeType == 'video/annodex'
 253+ || mimeType == 'application/ogg'
 254+ ) {
 255+ return true;
 256+ }
 257+ return false;
 258+ },
 259+
 260+ /**
 261+ * Returns the thumbnail URL for the media element.
 262+ *
 263+ * @returns {String} thumbnail URL
 264+ */
 265+ getPosterSrc: function( ) {
 266+ return this.poster;
 267+ },
 268+
 269+ /**
 270+ * Checks whether there is a stream of a specified MIME type.
 271+ *
 272+ * @param {String}
 273+ * mimeType MIME type to check.
 274+ * @return {Boolean} true if sources include MIME false if not.
 275+ */
 276+ hasStreamOfMIMEType: function( mimeType )
 277+ {
 278+ for ( var i = 0; i < this.sources.length; i++ )
 279+ {
 280+ if ( this.sources[i].getMIMEType() == mimeType ){
 281+ return true;
 282+ }
 283+ }
 284+ return false;
 285+ },
 286+
 287+ /**
 288+ * Checks if media is a playable type
 289+ */
 290+ isPlayableType: function( mimeType ) {
 291+ if ( mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType ) ) {
 292+ return true;
 293+ } else {
 294+ return false;
 295+ }
 296+ },
 297+
 298+ /**
 299+ * Adds a single mediaSource using the provided element if the element has a
 300+ * 'src' attribute.
 301+ *
 302+ * @param {Element}
 303+ * element <video>, <source> or <mediaSource> <text> element.
 304+ */
 305+ tryAddSource: function( element ) {
 306+ // mw.log( 'f:tryAddSource:' + $( element ).attr( "src" ) );
 307+ var newSrc = $( element ).attr( 'src' );
 308+ if ( newSrc ) {
 309+ // make sure an existing element with the same src does not already
 310+ // exist:
 311+ for ( var i = 0; i < this.sources.length; i++ ) {
 312+ if ( this.sources[i].src == newSrc ) {
 313+ // Source already exists update any new attr:
 314+ this.sources[i].updateSource( element );
 315+ return this.sources[i];
 316+ }
 317+ }
 318+ }
 319+ // Create a new source
 320+ var source = new mediaSource( element );
 321+
 322+ this.sources.push( source );
 323+ // mw.log( 'tryAddSource: added source ::' + source + 'sl:' +
 324+ // this.sources.length );
 325+ return source;
 326+ },
 327+
 328+ /**
 329+ * Get playable sources
 330+ *
 331+ * @returns {Array} of playable sources
 332+ */
 333+ getPlayableSources: function() {
 334+ var playableSources = [];
 335+ for ( var i = 0; i < this.sources.length; i++ ) {
 336+ if ( this.isPlayableType( this.sources[i].mimeType ) ) {
 337+ playableSources.push( this.sources[i] );
 338+ } else {
 339+ mw.log( "type " + this.sources[i].mimeType + ' is not playable' );
 340+ }
 341+ };
 342+ return playableSources;
 343+ }
 344+};
 345+
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaElement.js
___________________________________________________________________
Added: svn:mime-type
1346 + text/plain
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayer.js
@@ -0,0 +1,2322 @@
 2+/**
 3+* embedPlayer is the base class for html5 video tag javascript abstraction library
 4+* embedPlayer include a few subclasses:
 5+*
 6+* mediaPlayer Media player embed system ie: java, vlc or native.
 7+* mediaElement Represents source media elements
 8+* mw.PlayerControlBuilder Handles skinning of the player controls
 9+*/
 10+
 11+( function( mw, $ ) {
 12+/**
 13+ * The base source attribute checks also see:
 14+ * http://dev.w3.org/html5/spec/Overview.html#the-source-element
 15+ */
 16+mw.mergeConfig( 'EmbedPlayer.SourceAttributes', [
 17+ // source id
 18+ 'id',
 19+
 20+ // media url
 21+ 'src',
 22+
 23+ // Title string for the source asset
 24+ 'title',
 25+
 26+ // boolean if we support temporal url requests on the source media
 27+ 'URLTimeEncoding',
 28+
 29+ // Media has a startOffset ( used for plugins that
 30+ // display ogg page time rather than presentation time
 31+ 'data-startoffset',
 32+
 33+ // A hint to the duration of the media file so that duration
 34+ // can be displayed in the player without loading the media file
 35+ 'data-durationhint',
 36+
 37+ // Source stream qualities ( will eventually be adaptive streaming )
 38+ 'data-shorttitle', // short title for stream ( usefull for stream switching control bar item)
 39+ 'data-width', // the width of the stream
 40+ 'data-height', // the height of the stream
 41+ 'data-bandwith', // the overall bitrate of the stream
 42+ 'data-framerate', // the framereate of the stream
 43+
 44+ // Media start time
 45+ 'start',
 46+
 47+ // Media end time
 48+ 'end',
 49+
 50+ // If the source is the default source
 51+ 'default'
 52+] );
 53+
 54+/**
 55+ * Merge in the default video attributes supported by embedPlayer:
 56+ */
 57+mw.mergeConfig('EmbedPlayer.Attributes', {
 58+ /*
 59+ * Base html element attributes:
 60+ */
 61+
 62+ // id: Auto-populated if unset
 63+ "id" : null,
 64+
 65+ // Width: alternate to "style" to set player width
 66+ "width" : null,
 67+
 68+ // Height: alternative to "style" to set player height
 69+ "height" : null,
 70+
 71+ /*
 72+ * Base html5 video element attributes / states also see:
 73+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
 74+ */
 75+
 76+ // Media src URI, can be relative or absolute URI
 77+ "src" : null,
 78+
 79+ // Poster attribute for displaying a place holder image before loading
 80+ // or playing the video
 81+ "poster" : null,
 82+
 83+ // Autoplay if the media should start playing
 84+ "autoplay" : false,
 85+
 86+ // Loop attribute if the media should repeat on complete
 87+ "loop" : false,
 88+
 89+ // If the player controls should be displayed
 90+ "controls" : true,
 91+
 92+ // Video starts "paused"
 93+ "paused" : true,
 94+
 95+ // ReadyState an attribute informs clients of video loading state:
 96+ // see: http://www.whatwg.org/specs/web-apps/current-work/#readystate
 97+ "readyState" : 0,
 98+
 99+ // Loading state of the video element
 100+ "networkState" : 0,
 101+
 102+ // Current playback position
 103+ "currentTime" : 0,
 104+
 105+ // Previous player set time
 106+ // Lets javascript use $('#videoId').get(0).currentTime = newTime;
 107+ "previousTime" : 0,
 108+
 109+ // Previous player set volume
 110+ // Lets javascript use $('#videoId').get(0).volume = newVolume;
 111+ "previousVolume" : 1,
 112+
 113+ // Initial player volume:
 114+ "volume" : 0.75,
 115+
 116+ // Caches the volume before a mute toggle
 117+ "preMuteVolume" : 0.75,
 118+
 119+ // Media duration: Value is populated via
 120+ // custom data-durationhint attribute or via the media file once its played
 121+ "duration" : null,
 122+
 123+ // Mute state
 124+ "muted" : false,
 125+
 126+ /**
 127+ * Custom attributes for embedPlayer player: (not part of the html5
 128+ * video spec)
 129+ */
 130+
 131+ // Default video aspect ratio
 132+ 'videoAspect' : '4:3',
 133+
 134+ // Start time of the clip
 135+ "start" : 0,
 136+
 137+ // End time of the clip
 138+ "end" : null,
 139+
 140+ // If the player controls should be overlaid
 141+ // ( Global default via config EmbedPlayer.OverlayControls in module
 142+ // loader.js)
 143+ "overlaycontrols" : true,
 144+
 145+ // Attribute to use 'native' controls
 146+ "usenativecontrols" : false,
 147+
 148+ // If the player should include an attribution button:
 149+ 'attributionbutton' : true,
 150+
 151+ // If serving an ogg_chop segment use this to offset the presentation time
 152+ // ( for some plugins that use ogg page time rather than presentation time )
 153+ "startOffset" : 0,
 154+
 155+ // If the download link should be shown
 156+ "download_link" : true,
 157+
 158+ // Content type of the media
 159+ "type" : null
 160+} );
 161+
 162+
 163+/**
 164+ * Selector based embedPlayer processing
 165+ *
 166+ * @param {Function=}
 167+ * callback Optional Function to be called once video interfaces
 168+ * are ready
 169+ *
 170+ */
 171+mw.processEmbedPlayers = function( playerSelect, callback ) {
 172+ mw.log( 'EmbedPlayer:: processEmbedPlayers' );
 173+
 174+
 175+ /**
 176+ * Adds a player element for the embedPlayer to rewrite
 177+ *
 178+ * uses embedPlayer interface on audio / video elements uses mvPlayList
 179+ * interface on playlist elements
 180+ *
 181+ * Once a player interface is established the following chain of functions
 182+ * are called;
 183+ *
 184+ * _this.checkPlayerSources()
 185+ * _this.setupSourcePlayer()
 186+ * _this.inheritEmbedPlayer()
 187+ * _this.selectedPlayer.load()
 188+ * _this.showPlayer()
 189+ *
 190+ * @param {Element}
 191+ * playerElement DOM element to be swapped
 192+ * @param {Object}
 193+ * [Optional] attributes Extra attributes to apply to the player
 194+ * interface
 195+ */
 196+ var addPlayerElement = function( playerElement ) {
 197+ var _this = this;
 198+ mw.log('EmbedPlayer:: addElement:: ' + playerElement.id );
 199+
 200+ var waitForMeta = true;
 201+
 202+ // Be sure to "stop" the target ( Firefox 3x keeps playing
 203+ // the video even though its been removed from the DOM )
 204+ if( playerElement.pause ){
 205+ playerElement.pause();
 206+ }
 207+
 208+ // Allow modules to override the wait for metadata flag:
 209+ $( mw ).trigger( 'checkPlayerWaitForMetaData', playerElement );
 210+
 211+ // Update the waitForMeta object if set to boolean false:
 212+ waitForMeta = ( playerElement.waitForMeta === false )? false : true;
 213+
 214+
 215+ // Confirm we want to wait for meta data ( if not already set to false by module )
 216+ if( waitForMeta ){
 217+ waitForMeta = waitForMetaCheck( playerElement );
 218+ }
 219+
 220+ var ranPlayerSwapFlag = false;
 221+
 222+ // Local callback to runPlayer swap once playerElement has metadata
 223+ function runPlayerSwap() {
 224+ // Don't run player swap twice
 225+ if( ranPlayerSwapFlag ){
 226+ return ;
 227+ }
 228+ ranPlayerSwapFlag = true;
 229+ mw.log("EmbedPlayer::runPlayerSwap::" + $( playerElement ).attr('id') );
 230+
 231+ var playerInterface = new mw.EmbedPlayer( playerElement );
 232+ var swapPlayer = swapEmbedPlayerElement( playerElement, playerInterface );
 233+
 234+ // Trigger the EmbedPlayerNewPlayer for embedPlayer interface
 235+ mw.log("EmbedPlayer::EmbedPlayerNewPlayer:trigger " + playerInterface.id );
 236+ $( mw ).trigger ( 'EmbedPlayerNewPlayer', $( '#' + playerInterface.id ).get(0) );
 237+
 238+ // Issue the checkPlayerSources call to the new player
 239+ // interface: make sure to use the element that is in the DOM:
 240+ $( '#' + playerInterface.id ).get(0).checkPlayerSources();
 241+ }
 242+
 243+ if( waitForMeta && mw.getConfig('EmbedPlayer.WaitForMeta' ) ) {
 244+ mw.log('EmbedPlayer::WaitForMeta ( video missing height (' +
 245+ $( playerElement ).attr('height') + '), width (' +
 246+ $( playerElement ).attr('width') + ') or duration: ' +
 247+ $( playerElement ).attr('duration')
 248+ );
 249+ $( playerElement ).bind("loadedmetadata", runPlayerSwap );
 250+
 251+ // Time-out of 5 seconds ( maybe still playable but no timely
 252+ // metadata )
 253+ setTimeout( runPlayerSwap, 5000 );
 254+ return ;
 255+ } else {
 256+ runPlayerSwap();
 257+ return ;
 258+ }
 259+ };
 260+
 261+ /**
 262+ * Check if we should wait for metadata.
 263+ *
 264+ * @return true if the size is "likely" to be updated by waiting for metadata
 265+ * false if the size has been set via an attribute or is already loaded
 266+ */
 267+ var waitForMetaCheck = function( playerElement ){
 268+ var waitForMeta = false;
 269+
 270+ // Don't wait for metadata for non html5 media elements
 271+ if( !playerElement ){
 272+ return false;
 273+ }
 274+ if( !playerElement.tagName || ( playerElement.tagName.toLowerCase() != 'audio' && playerElement.tagName.toLowerCase() != 'video' ) ){
 275+ return false;
 276+ }
 277+ // If we don't have a native player don't wait for metadata
 278+ if( !mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'oggNative') &&
 279+ !mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'webmNative') &&
 280+ !mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'h264Native' ) )
 281+ {
 282+ return false;
 283+ }
 284+
 285+
 286+ var width = $( playerElement ).css( 'width' );
 287+ var height = $( playerElement ).css( 'height' );
 288+
 289+ // Css video defaults ( firefox )
 290+ if( $( playerElement ).css( 'width' ) == '300px' &&
 291+ $( playerElement ).css( 'height' ) == '150px'
 292+ ){
 293+ waitForMeta = true;
 294+ } else {
 295+ // Check if we should wait for duration:
 296+ if( $( playerElement ).attr( 'duration') ||
 297+ $( playerElement ).attr('data-durationhint')
 298+ ){
 299+ // height, width and duration set; do not wait for meta data:
 300+ return false;
 301+ } else {
 302+ waitForMeta = true;
 303+ }
 304+ }
 305+
 306+ // Firefox ~ sometimes~ gives -1 for unloaded media
 307+ if ( $(playerElement).attr( 'width' ) == -1 || $(playerElement).attr( 'height' ) == -1 ) {
 308+ waitForMeta = true;
 309+ }
 310+
 311+ // Google Chrome / safari gives 0 width height for unloaded media
 312+ if( $(playerElement).attr( 'width' ) === 0 ||
 313+ $(playerElement).attr( 'height' ) === 0
 314+ ) {
 315+ waitForMeta = true;
 316+ }
 317+
 318+ // Firefox default width height is ~sometimes~ 150 / 300
 319+ if( this.height == 150 && this.width == 300 ){
 320+ waitForMeta = true;
 321+ }
 322+
 323+ // Make sure we have a src attribute or source child
 324+ // ( i.e not a video tag to be dynamically populated or looked up from
 325+ // xml resource description )
 326+ if( waitForMeta &&
 327+ (
 328+ $( playerElement ).attr('src') ||
 329+ $( playerElement ).find("source[src]").length !== 0
 330+ )
 331+ ) {
 332+ // Detect src type ( if no type set )
 333+ return true;
 334+ } else {
 335+ // playerElement is not likely to update its meta data ( no src )
 336+ return false;
 337+ }
 338+ };
 339+
 340+ /**
 341+ * swapEmbedPlayerElement
 342+ *
 343+ * Takes a video element as input and swaps it out with an embed player interface
 344+ *
 345+ * @param {Element}
 346+ * targetElement Element to be swapped
 347+ * @param {Object}
 348+ * playerInterface Interface to swap into the target element
 349+ */
 350+ var swapEmbedPlayerElement = function( targetElement, playerInterface ) {
 351+ mw.log( 'EmbedPlayer::swapEmbedPlayerElement: ' + targetElement.id );
 352+ // Create a new element to swap the player interface into
 353+ var swapPlayerElement = document.createElement('div');
 354+
 355+ // Add a class that identifies all embedPlayers:
 356+ $( swapPlayerElement ).addClass( 'mwEmbedPlayer' );
 357+
 358+ // Get properties / methods from playerInterface:
 359+ for ( var method in playerInterface ) {
 360+ if ( method != 'readyState' ) { // readyState crashes IE ( don't include )
 361+ swapPlayerElement[ method ] = playerInterface[ method ];
 362+ }
 363+ }
 364+ // Check if we are using native controls or Persistent player ( should keep the video embed around )
 365+ if( playerInterface.useNativePlayerControls() || playerInterface.isPersistentNativePlayer() ) {
 366+ $( targetElement )
 367+ .attr( 'id', playerInterface.pid )
 368+ .addClass( 'nativeEmbedPlayerPid' )
 369+ .show()
 370+ .after(
 371+ $( swapPlayerElement ).css( 'display', 'none' )
 372+ );
 373+
 374+ } else {
 375+ $( targetElement ).replaceWith( swapPlayerElement );
 376+ }
 377+
 378+
 379+ // Set swapPlayerElement has height / width set and set to loading:
 380+ $( swapPlayerElement ).css( {
 381+ 'width' : playerInterface.width + 'px',
 382+ 'height' : playerInterface.height + 'px'
 383+ } );
 384+
 385+ // If we don't already have a loadSpiner add one:
 386+ if( $('#loadingSpinner_' + playerInterface.id ).length == 0 ){
 387+ if( playerInterface.useNativePlayerControls() || playerInterface.isPersistentNativePlayer() ) {
 388+ var $spinner = $( targetElement )
 389+ .getAbsoluteOverlaySpinner();
 390+ }else{
 391+ var $spinner = $( swapPlayerElement ).getAbsoluteOverlaySpinner();
 392+ }
 393+ $spinner.attr('id', 'loadingSpinner_' + playerInterface.id );
 394+ }
 395+ return swapPlayerElement;
 396+ };
 397+
 398+ // Add a loader for <div> embed player rewrites:
 399+ $( playerSelect ).each( function( index, playerElement) {
 400+
 401+ // Make sure the playerElement has an id:
 402+ if( !$( playerElement ).attr('id') ){
 403+ $( playerElement ).attr( "id", 'mwe_v' + ( index ) );
 404+ }
 405+
 406+ // If we are dynamically embedding on a "div" check if we can
 407+ // add a poster image behind the loader:
 408+ if( playerElement.nodeName.toLowerCase() == 'div'
 409+ && ( attributes.poster || $(playerElement).attr( 'poster' ) ) ){
 410+ var posterSrc = ( attributes.poster ) ? attributes.poster : $(playerElement).attr( 'poster' );
 411+
 412+ // Set image size:
 413+ var width = $( playerElement ).width();
 414+ var height = $( playerElement ).height();
 415+ if( !width ){
 416+ var width = ( attributes.width ) ? attributes.width : '100%';
 417+ }
 418+ if( !height ){
 419+ var height = ( attributes.height ) ? attributes.height : '100%';
 420+ }
 421+
 422+ mw.log('EmbedPlayer:: set loading background: ' + posterSrc);
 423+ $( playerElement ).append(
 424+ $( '<img />' )
 425+ .attr( 'src', posterSrc)
 426+ .css({
 427+ 'position' : 'absolute',
 428+ 'width' : width,
 429+ 'height' : height
 430+ })
 431+ );
 432+ }
 433+ });
 434+
 435+ // Create the Global Embed Player Manager ( if not already created )
 436+ // legacy EmbedPlayerManagerReady event ( should remove )
 437+ $( mw ).trigger( 'EmbedPlayerManagerReady' );
 438+
 439+ // Make sure we have user preference setup for setting preferences on video selection
 440+ var addedToPlayerManager = false;
 441+ mw.log("EmbedPlayer:: do: " + $( playerSelect ).length + ' players ');
 442+
 443+ // Add each selected element to the player manager:
 444+ $( playerSelect ).each( function( index, playerElement) {
 445+ // Make sure the video tag was not generated by our library:
 446+ if( $( playerElement ).hasClass( 'nativeEmbedPlayerPid' ) ){
 447+ $('#loadingSpinner_' + $( playerElement ).attr('id') ).remove();
 448+ mw.log( 'EmbedPlayer::$j.embedPlayer skip embedPlayer gennerated video: ' + playerElement );
 449+ } else {
 450+ addedToPlayerManager = true;
 451+ // Add the player
 452+ addPlayerElement( playerElement );
 453+ }
 454+ });
 455+ if( addedToPlayerManager ){
 456+ if( callback ){
 457+ $( mw ).bind( "playersReadyEvent", callback );
 458+ }
 459+ } else {
 460+ // Run the callback directly if no players were added
 461+ if( callback ){
 462+ callback();
 463+ }
 464+ }
 465+};
 466+
 467+
 468+/**
 469+ * Base embedPlayer object
 470+ *
 471+ * @param {Element}
 472+ * element, the element used for initialization.
 473+ * @constructor
 474+ */
 475+mw.EmbedPlayer = function( element ) {
 476+ return this.init( element );
 477+};
 478+
 479+mw.EmbedPlayer.prototype = {
 480+
 481+ // The mediaElement object containing all mediaSource objects
 482+ 'mediaElement' : null,
 483+
 484+ // Object that describes the supported feature set of the underling plugin /
 485+ // Support list is described in PlayerControlBuilder components
 486+ 'supports': { },
 487+
 488+ // Preview mode flag,
 489+ // some plugins don't seek accurately but in preview mode we need
 490+ // accurate seeks so we do tricks like hide the image until its ready
 491+ 'previewMode' : false,
 492+
 493+ // Ready to play
 494+ // NOTE: we should switch over to setting the html5 video ready state
 495+ 'readyToPlay' : false,
 496+
 497+ // Stores the loading errors
 498+ 'loadError' : false,
 499+
 500+ // Thumbnail updating flag ( to avoid rewriting an thumbnail thats already
 501+ // being updated)
 502+ 'thumbnail_updating' : false,
 503+
 504+ // Poster display flag
 505+ 'posterDisplayed' : true,
 506+
 507+ // Local variable to hold CMML meeta data about the current clip
 508+ // for more on CMML see: http://wiki.xiph.org/CMML
 509+ 'cmmlData': null,
 510+
 511+ // Stores the seek time request, Updated by the doSeek function
 512+ 'serverSeekTime' : 0,
 513+
 514+ // If the embedPlayer is current 'seeking'
 515+ 'seeking' : false,
 516+
 517+ // Percent of the clip buffered:
 518+ 'bufferedPercent' : 0,
 519+
 520+ // Holds the timer interval function
 521+ 'monitorTimerId' : null,
 522+
 523+ // Buffer flags
 524+ 'bufferStartFlag' : false,
 525+ 'bufferEndFlag' : false,
 526+
 527+ // For supporting media fragments stores the play end time
 528+ 'pauseTime' : null,
 529+
 530+ // On done playing
 531+ 'donePlayingCount' : 0
 532+ ,
 533+ // if player events should be Propagated
 534+ '_propagateEvents': true,
 535+
 536+ // If the onDone interface should be displayed
 537+ 'onDoneInterfaceFlag': true,
 538+
 539+
 540+ /**
 541+ * embedPlayer
 542+ *
 543+ * @constructor
 544+ *
 545+ * @param {Element}
 546+ * element DOM element that we are building the player interface for.
 547+ */
 548+ init: function( element ) {
 549+ var _this = this;
 550+ mw.log('EmbedPlayer: initEmbedPlayer: ' + $(element).width() );
 551+
 552+ var playerAttributes = mw.getConfig( 'EmbedPlayer.Attributes' );
 553+
 554+ // Setup the player Interface from supported attributes:
 555+ for ( var attr in playerAttributes ) {
 556+ // We can't use $(element).attr( attr ) because we have to check for boolean attributes:
 557+ if ( element.getAttribute( attr ) != null ) {
 558+ // boolean attributes
 559+ if( element.getAttribute( attr ) == '' ){
 560+ this[ attr ] = true;
 561+ } else {
 562+ this[ attr ] = element.getAttribute( attr );
 563+ }
 564+ } else {
 565+ this[attr] = playerAttributes[attr];
 566+ }
 567+ // string -> boolean
 568+ if( this[ attr ] == "false" ) this[attr] = false;
 569+ if( this[ attr ] == "true" ) this[attr] = true;
 570+ }
 571+ // Hide "controls" if using native player controls:
 572+ if( this.useNativePlayerControls() ){
 573+ _this.controls = false;
 574+ }
 575+ if ( $( element ).attr( 'poster' ) ) {
 576+ _this.poster = $( element ).attr( 'poster' );
 577+ }
 578+
 579+ // Set the skin name from the class
 580+ var sn = $(element).attr( 'class' );
 581+
 582+ if ( sn && sn != '' ) {
 583+ var skinList = mw.getConfig('EmbedPlayer.SkinList');
 584+ for ( var n = 0; n < skinList.length; n++ ) {
 585+ if ( sn.indexOf( skinList[n].toLowerCase() ) !== -1 ) {
 586+ this.skinName = skinList[ n ];
 587+ }
 588+ }
 589+ }
 590+
 591+ // Set the default skin if unset:
 592+ if ( !this.skinName ) {
 593+ this.skinName = mw.getConfig( 'EmbedPlayer.DefaultSkin' );
 594+ }
 595+ if( !this.monitorRate ){
 596+ this.monitorRate = mw.getConfig( 'EmbedPlayer.MonitorRate' );
 597+ }
 598+
 599+ // Make sure startOffset is cast as an float:
 600+ if ( this.startOffset && this.startOffset.split( ':' ).length >= 2 ) {
 601+ this.startOffset = parseFloat( mw.npt2seconds( this.startOffset ) );
 602+ }
 603+
 604+ // Make sure offset is in float:
 605+ this.startOffset = parseFloat( this.startOffset );
 606+
 607+ // Set the source duration ( if provided in the element metaData or
 608+ // data-durationhint )
 609+ if ( $( element ).attr( 'duration' ) ) {
 610+ _this.duration = $( element ).attr( 'duration' );
 611+ }
 612+
 613+ if ( !_this.duration && $( element ).attr( 'data-durationhint' ) ) {
 614+ _this['data-durationhint'] = $( element ).attr( 'data-durationhint' );
 615+ // Convert duration hint if needed:
 616+ _this.duration = mw.npt2seconds( _this['data-durationhint'] );
 617+ }
 618+
 619+ // Make sure duration is a float:
 620+ this.duration = parseFloat( this.duration );
 621+ mw.log( 'EmbedPlayer::mediaElement:' + this.id + " duration is: " + this.duration );
 622+
 623+ // Set the player size attributes based loaded video element:
 624+ this.loadPlayerSize( element );
 625+ // Set the plugin id
 626+ this.pid = 'pid_' + this.id;
 627+
 628+ // Grab any innerHTML and set it to missing_plugin_html
 629+ // NOTE: we should strip "source" tags instead of checking and skipping
 630+ if ( element.innerHTML != '' && element.getElementsByTagName( 'source' ).length == 0 ) {
 631+ // mw.log( 'innerHTML: ' + element.innerHTML );
 632+ this.user_missing_plugin_html = element.innerHTML;
 633+ }
 634+ // Add the mediaElement object with the elements sources:
 635+ this.mediaElement = new mediaElement( element );
 636+ },
 637+ /**
 638+ * Stop events from Propagation and blocks interface updates and trigger events.
 639+ * @return
 640+ */
 641+ stopEventPropagation: function(){
 642+ this.stopMonitor();
 643+ this._propagateEvents = false;
 644+ },
 645+ /**
 646+ * Restores event propagation
 647+ * @return
 648+ */
 649+ restoreEventPropagation: function(){
 650+ this._propagateEvents = true;
 651+ this.startMonitor();
 652+ },
 653+
 654+ enableSeekBar: function(){
 655+ this.controlBuilder.enableSeekBar();
 656+ $( this ).trigger( 'onEnableSeekBar');
 657+ },
 658+ disableSeekBar: function(){
 659+ this.controlBuilder.disableSeekBar();
 660+ $( this ).trigger( 'ondisableSeekBar');
 661+ },
 662+
 663+ /**
 664+ * For plugin-players to update supported features
 665+ */
 666+ updateFeatureSupport: function(){
 667+ $( this ).trigger('updateFeatureSupportEvent', this.supports );
 668+ return ;
 669+ },
 670+
 671+ /**
 672+ * Set the width & height from css style attribute, element attribute, or by
 673+ * default value if no css or attribute is provided set a callback to
 674+ * resize.
 675+ *
 676+ * Updates this.width & this.height
 677+ *
 678+ * @param {Element}
 679+ * element Source element to grab size from
 680+ */
 681+ loadPlayerSize: function( element ) {
 682+ this.height = $(element).css( 'height' );
 683+ this.width = $(element).css( 'width' );
 684+ // Special check for chrome 100% with re-mapping to 32px
 685+ // ( hopefully no one embeds video at 32x32 )
 686+ if( this.height == '32px' || this.height =='32px' ){
 687+ this.width = '100%';
 688+ this.height = '100%';
 689+ }
 690+ mw.log('EmbedPlayer::loadPlayerSize: css size:' + this.width + ' h: ' + this.height);
 691+
 692+ // Set to parent size ( resize events will cause player size updates)
 693+ if( this.height.indexOf('100%') != -1 || this.width.indexOf('100%') != -1 ){
 694+ $relativeParent = $(element).parents().filter(function() {
 695+ // reduce to only relative position or "body" elements
 696+ return $(this).is('body') || $(this).css('position') == 'relative';
 697+ }).slice(0,1); // grab only the "first"
 698+ this.width = $relativeParent.width();
 699+ this.height = $relativeParent.height();
 700+ }
 701+ // Make sure height and width are a number
 702+ this.height = parseInt( this.height );
 703+ this.width = parseInt( this.width );
 704+
 705+ // Set via attribute if CSS is zero or NaN and we have an attribute value:
 706+ this.height = ( this.height==0 || isNaN( this.height )
 707+ && $(element).attr( 'height' ) ) ?
 708+ parseInt( $(element).attr( 'height' ) ): this.height;
 709+ this.width = ( this.width == 0 || isNaN( this.width )
 710+ && $(element).attr( 'width' ) )?
 711+ parseInt( $(element).attr( 'width' ) ): this.width;
 712+
 713+
 714+ // Special case for audio
 715+ // Firefox sets audio height to "0px" while webkit uses 32px .. force
 716+ // zero:
 717+ if( element.tagName.toLowerCase() == 'audio' && this.height == '32' ) {
 718+ this.height = 0;
 719+ }
 720+
 721+ // Use default aspect ration to get height or width ( if rewriting a
 722+ // non-audio player )
 723+ if( element.tagName.toLowerCase() != 'audio' && this.videoAspect ) {
 724+ var aspect = this.videoAspect.split( ':' );
 725+ if( this.height && !this.width ) {
 726+ this.width = parseInt( this.height * ( aspect[0] / aspect[1] ) );
 727+ }
 728+ if( this.width && !this.height ) {
 729+ var apectRatio = ( aspect[1] / aspect[0] );
 730+ this.height = parseInt( this.width * ( aspect[1] / aspect[0] ) );
 731+ }
 732+ }
 733+
 734+ // On load sometimes attr is temporally -1 as we don't have video metadata yet.
 735+ // or in IE we get NaN for width height
 736+ //
 737+ // NOTE: browsers that do support height width should set "waitForMeta" flag in addElement
 738+ if( ( isNaN( this.height )|| isNaN( this.width ) ) ||
 739+ ( this.height == -1 || this.width == -1 ) ||
 740+ // Check for firefox defaults
 741+ // Note: ideally firefox would not do random guesses at css
 742+ // values
 743+ ( (this.height == 150 || this.height == 64 ) && this.width == 300 )
 744+ ) {
 745+ var defaultSize = mw.getConfig( 'EmbedPlayer.DefaultSize' ).split( 'x' );
 746+ if( isNaN( this.width ) ){
 747+ this.width = defaultSize[0];
 748+ }
 749+
 750+ // Special height default for audio tag ( if not set )
 751+ if( element.tagName.toLowerCase() == 'audio' ) {
 752+ this.height = 0;
 753+ }else{
 754+ this.height = defaultSize[1];
 755+ }
 756+ }
 757+ },
 758+ /**
 759+ * Resize the player to a new size preserving aspect ratio Wraps the
 760+ * controlBuilder.resizePlayer function
 761+ */
 762+ resizePlayer: function( size , animate, callback){
 763+ mw.log("EmbedPlayer::resizePlayer:" + size.width + ' x ' + size.height );
 764+
 765+ // Check if we are native display then resize the playerElement directly
 766+ if( this.useNativePlayerControls() ){
 767+ if( animate ){
 768+ $( this.getPlayerElement() ).animate( size , callback);
 769+ } else {
 770+ $( this.getPlayerElement() ).css( size );
 771+ if( callback ) {
 772+ callback();
 773+ }
 774+ }
 775+ } else {
 776+ this.controlBuilder.resizePlayer( size, animate, callback);
 777+ }
 778+ $( this ).trigger( 'onResizePlayer', [size, animate] );
 779+ },
 780+
 781+ /**
 782+ * Get the player pixel width not including controls
 783+ *
 784+ * @return {Number} pixel height of the video
 785+ */
 786+ getPlayerWidth: function() {
 787+ return $( this ).width();
 788+ },
 789+
 790+ /**
 791+ * Get the player pixel height not including controls
 792+ *
 793+ * @return {Number} pixel height of the video
 794+ */
 795+ getPlayerHeight: function() {
 796+ return $( this ).height();
 797+ },
 798+
 799+ /**
 800+ * Check player for sources. If we need to get media sources form an
 801+ * external file that request is issued here
 802+ */
 803+ checkPlayerSources: function() {
 804+ mw.log( 'EmbedPlayer::checkPlayerSources: ' + this.id );
 805+ var _this = this;
 806+
 807+ // Allow plugins to block on sources lookup:
 808+ $( _this ).triggerQueueCallback( 'CheckPlayerSourcesEvent', function(){
 809+ _this.setupSourcePlayer();
 810+ });
 811+ },
 812+
 813+ /**
 814+ * Empty the player sources
 815+ */
 816+ emptySources: function(){
 817+ if( this.mediaElement ){
 818+ this.mediaElement.sources = [];
 819+ this.mediaElement.selectedSource = null;
 820+ }
 821+ },
 822+
 823+ /**
 824+ * Insert and play a video source ( useful for ads or bumper videos )
 825+ *
 826+ * Only works while video is in active play back. Only tested with native
 827+ * playback atm.
 828+ */
 829+ switchPlaySrc: function( source ){
 830+ mw.log("Error: only native playback supports insertAndPlaySource right now");
 831+ },
 832+
 833+ /**
 834+ * Set up the select source player
 835+ *
 836+ * issues autoSelectSource call
 837+ *
 838+ * Sets load error if no source is playable
 839+ */
 840+ setupSourcePlayer: function() {
 841+ mw.log("EmbedPlayer::setupSourcePlayer: " + this.id + ' sources: ' + this.mediaElement.sources.length );
 842+
 843+ // Autoseletct the media source
 844+ this.mediaElement.autoSelectSource();
 845+
 846+ // Auto select player based on default order
 847+ if ( !this.mediaElement.selectedSource ) {
 848+ mw.log( 'setupSourcePlayer:: no sources, type:' + this.type );
 849+ } else {
 850+ this.selectedPlayer = mw.EmbedTypes.getMediaPlayers().defaultPlayer( this.mediaElement.selectedSource.mimeType );
 851+ }
 852+
 853+ if ( this.selectedPlayer ) {
 854+ // Inherit the playback system of the selected player:
 855+ this.inheritEmbedPlayer();
 856+ } else {
 857+ this.showPluginMissingHTML();
 858+ }
 859+ },
 860+
 861+ /**
 862+ * Load and inherit methods from the selected player interface
 863+ *
 864+ * @param {Function}
 865+ * callback Function to be called once playback-system has been
 866+ * inherited
 867+ */
 868+ inheritEmbedPlayer: function( callback ) {
 869+ mw.log( "EmbedPlayer::inheritEmbedPlayer:duration is: " + this.getDuration() + ' p: ' + this.id );
 870+
 871+ // Clear out any non-base embedObj methods:
 872+ if ( this.instanceOf ) {
 873+ eval( 'var tmpObj = mw.EmbedPlayer' + this.instanceOf );
 874+ for ( var i in tmpObj ) { // for in loop oky for object
 875+ if ( this[ 'parent_' + i ] ) {
 876+ this[i] = this[ 'parent_' + i];
 877+ } else {
 878+ this[i] = null;
 879+ }
 880+ }
 881+ }
 882+
 883+ // Set up the new embedObj
 884+ mw.log( 'EmbedPlayer::inheritEmbedPlayer: embedding with ' + this.selectedPlayer.library );
 885+ var _this = this;
 886+
 887+ // Load the selected player
 888+ this.selectedPlayer.load( function() {
 889+ mw.log( 'EmbedPlayer::inheritEmbedPlayer ' + _this.selectedPlayer.library + " player loaded for " + _this.id );
 890+
 891+ // Get embed library player Interface
 892+ var playerInterface = mw[ 'EmbedPlayer' + _this.selectedPlayer.library ];
 893+
 894+ for ( var method in playerInterface ) {
 895+ if ( _this[method] && !_this['parent_' + method] ) {
 896+ _this['parent_' + method] = _this[method];
 897+ }
 898+ _this[ method ] = playerInterface[method];
 899+ }
 900+
 901+ // Update feature support
 902+ _this.updateFeatureSupport();
 903+
 904+ _this.getDuration();
 905+
 906+ // Run player display with timeout to avoid function stacking
 907+ setTimeout(function(){
 908+ _this.showPlayer();
 909+
 910+ // Run the callback if provided
 911+ if ( typeof callback == 'function' ){
 912+ callback();
 913+ }
 914+ },0);
 915+
 916+ } );
 917+ },
 918+
 919+ /**
 920+ * Select a player playback system
 921+ *
 922+ * @param {Object}
 923+ * player Player playback system to be selected player playback
 924+ * system include vlc, native, java etc.
 925+ */
 926+ selectPlayer: function( player ) {
 927+ var _this = this;
 928+ if ( this.selectedPlayer.id != player.id ) {
 929+ this.selectedPlayer = player;
 930+ this.inheritEmbedPlayer( function(){
 931+ // Hide / remove track container
 932+ _this.$interface.find( '.track' ).remove();
 933+ // We have to re-bind hoverIntent ( has to happen in this scope
 934+ // )
 935+ if( _this.controls && _this.controlBuilder.checkOverlayControls() ){
 936+ _this.controlBuilder.showControlBar();
 937+ _this.$interface.hoverIntent({
 938+ 'sensitivity': 4,
 939+ 'timeout' : 2000,
 940+ 'over' : function(){
 941+ _this.controlBuilder.showControlBar();
 942+ },
 943+ 'out' : function(){
 944+ _this.controlBuilder.hideControlBar();
 945+ }
 946+ });
 947+ }
 948+ });
 949+ }
 950+ },
 951+
 952+ /**
 953+ * Get a time range from the media start and end time
 954+ *
 955+ * @return start_npt and end_npt time if present
 956+ */
 957+ getTimeRange: function() {
 958+ var end_time = (this.controlBuilder.longTimeDisp)? '/' + mw.seconds2npt( this.getDuration() ) : '';
 959+ var default_time_range = '0:00' + end_time;
 960+ if ( !this.mediaElement )
 961+ return default_time_range;
 962+ if ( !this.mediaElement.selectedSource )
 963+ return default_time_range;
 964+ if ( !this.mediaElement.selectedSource.end_npt )
 965+ return default_time_range;
 966+ return this.mediaElement.selectedSource.start_npt + this.mediaElement.selectedSource.end_npt;
 967+ },
 968+
 969+ /**
 970+ * Get the duration of the embed player
 971+ */
 972+ getDuration: function() {
 973+ return this.duration;
 974+ },
 975+
 976+ /**
 977+ * Get the player height
 978+ */
 979+ getHeight: function() {
 980+ return this.height;
 981+ },
 982+
 983+ /**
 984+ * Get the player width
 985+ */
 986+ getWidth: function(){
 987+ return this.width;
 988+ },
 989+
 990+ /**
 991+ * Check if the selected source is an audio element:
 992+ */
 993+ isAudio: function(){
 994+ return ( this.mediaElement.selectedSource.mimeType.indexOf('audio/') !== -1 );
 995+ },
 996+
 997+ /**
 998+ * Get the plugin embed html ( should be implemented by embed player
 999+ * interface )
 1000+ */
 1001+ doEmbedHTML: function() {
 1002+ return 'Error: function doEmbedHTML should be implemented by embed player interface ';
 1003+ },
 1004+
 1005+ /**
 1006+ * Seek function ( should be implemented by embedPlayer interface
 1007+ * playerNative, playerKplayer etc. ) embedPlayer doSeek only handles URL
 1008+ * time seeks
 1009+ */
 1010+ doSeek: function( percent ) {
 1011+ var _this = this;
 1012+
 1013+ this.seeking = true;
 1014+
 1015+ // See if we should do a server side seek ( player independent )
 1016+ if ( this.supportsURLTimeEncoding() ) {
 1017+ mw.log( 'EmbedPlayer::doSeek:: updated serverSeekTime: ' + mw.seconds2npt ( this.serverSeekTime ) +
 1018+ ' currentTime: ' + _this.currentTime );
 1019+ // make sure we need to seek:
 1020+ if( _this.currentTime == _this.serverSeekTime ){
 1021+ return ;
 1022+ }
 1023+
 1024+ this.stop();
 1025+ this.didSeekJump = true;
 1026+ // Make sure this.serverSeekTime is up-to-date:
 1027+ this.serverSeekTime = mw.npt2seconds( this.start_npt ) + parseFloat( percent * this.getDuration() );
 1028+ // Update the slider
 1029+ this.updatePlayHead( percent );
 1030+ }
 1031+
 1032+ // Do play request in 100ms ( give the dom time to swap out the embed player )
 1033+ setTimeout( function() {
 1034+ _this.seeking = false;
 1035+ _this.play();
 1036+ _this.monitor();
 1037+ }, 100 );
 1038+
 1039+ // Run the onSeeking interface update
 1040+ // NOTE controlBuilder should really bind to html5 events rather
 1041+ // than explicitly calling it or inheriting stuff.
 1042+ this.controlBuilder.onSeek();
 1043+ },
 1044+
 1045+ /**
 1046+ * Seeks to the requested time and issues a callback when ready (should be
 1047+ * overwritten by client that supports frame serving)
 1048+ */
 1049+ setCurrentTime: function( time, callback ) {
 1050+ mw.log( 'Error: base embed setCurrentTime can not frame serve (override via plugin)' );
 1051+ },
 1052+
 1053+ /**
 1054+ * On clip done action. Called once a clip is done playing
 1055+ */
 1056+ onClipDone: function() {
 1057+ var _this = this;
 1058+ // don't run onclipdone if _propagateEvents is off
 1059+ if( !_this._propagateEvents ){
 1060+ return ;
 1061+ }
 1062+ mw.log( 'EmbedPlayer::onClipDone:' + this.id + ' doneCount:' + this.donePlayingCount + ' stop state:' +this.isStopped() );
 1063+ // Only run stopped once:
 1064+ if( !this.isStopped() ){
 1065+ // Stop the monitor and event propagation
 1066+ this.stopEventPropagation();
 1067+
 1068+ // Show the control bar:
 1069+ this.controlBuilder.showControlBar();
 1070+
 1071+ // Update the clip done playing count:
 1072+ this.donePlayingCount ++;
 1073+
 1074+ // Run the ended trigger ( allow the ended object to prevent default actions )
 1075+ mw.log("EmbedPlayer::onClipDone:Trigger ended");
 1076+
 1077+ // TOOD we should improve the end event flow
 1078+ $( this ).trigger( 'ended' );
 1079+
 1080+ // if the ended event did not trigger more timeline actions run the actual stop:
 1081+ if( this.onDoneInterfaceFlag ){
 1082+ this.stop();
 1083+ // restore event propagation
 1084+ this.restoreEventPropagation();
 1085+ // Check if we have the "loop" property set
 1086+ if( this.loop ) {
 1087+ this.play();
 1088+ return;
 1089+ }
 1090+
 1091+ // Stop the clip (load the thumbnail etc)
 1092+ this.serverSeekTime = 0;
 1093+ this.updatePlayHead( 0 );
 1094+
 1095+ // Make sure we are not in preview mode( no end clip actions in
 1096+ // preview mode)
 1097+ if ( this.previewMode ) {
 1098+ return ;
 1099+ }
 1100+
 1101+ // Do the controlBuilder onClip done interface
 1102+ this.controlBuilder.onClipDone();
 1103+ }
 1104+ }
 1105+ },
 1106+
 1107+
 1108+ /**
 1109+ * Shows the video Thumbnail, updates pause state
 1110+ */
 1111+ showThumbnail: function() {
 1112+ var _this = this;
 1113+ mw.log( 'EmbedPlayer::showThumbnail' + this.posterDisplayed );
 1114+
 1115+ // Close Menu Overlay:
 1116+ this.controlBuilder.closeMenuOverlay();
 1117+
 1118+ // update the thumbnail html:
 1119+ this.updatePosterHTML();
 1120+
 1121+ this.paused = true;
 1122+ this.posterDisplayed = true;
 1123+ // Make sure the controlBuilder bindings are up-to-date
 1124+ this.controlBuilder.addControlBindings();
 1125+
 1126+ // Once the thumbnail is shown run the mediaReady trigger (if not using native controls)
 1127+ // Native controls has a native mediaLoaded event
 1128+ if( !this.useNativePlayerControls() ){
 1129+ mw.log("mediaLoaded");
 1130+ $( this ).trigger( 'mediaLoaded' );
 1131+ }
 1132+ },
 1133+
 1134+ /**
 1135+ * Show the player
 1136+ */
 1137+ showPlayer: function () {
 1138+ mw.log( 'EmbedPlayer:: Show player: ' + this.id + ' interace: w:' + this.width + ' h:' + this.height );
 1139+ var _this = this;
 1140+ // Set-up the local controlBuilder instance:
 1141+ this.controlBuilder = new mw.PlayerControlBuilder( this );
 1142+ var _this = this;
 1143+
 1144+ // Remove loader
 1145+ $('#loadingSpinner_' + this.id ).remove();
 1146+
 1147+ // Make sure we have mwplayer_interface
 1148+ if( $( this ).parent( '.mwplayer_interface' ).length == 0 ) {
 1149+ // Select "player"
 1150+ $( this ).wrap(
 1151+ $('<div>')
 1152+ .addClass( 'mwplayer_interface ' + this.controlBuilder.playerClass )
 1153+ .css({
 1154+ 'width' : this.width + 'px',
 1155+ 'height' : this.height + 'px',
 1156+ 'position' : 'relative'
 1157+ })
 1158+ )
 1159+ // position the "player" absolute inside the relative interface
 1160+ // parent:
 1161+ .css('position', 'absolute');
 1162+ }
 1163+
 1164+ // Set up local jQuery object reference to "mwplayer_interface"
 1165+ this.$interface = $( this ).parent( '.mwplayer_interface' );
 1166+ // If a isPersistentNativePlayer ( overlay the controls )
 1167+ if( !this.useNativePlayerControls() && this.isPersistentNativePlayer() ){
 1168+ this.$interface.css({
 1169+ 'position' : 'absolute',
 1170+ 'top' : '0px',
 1171+ 'left' : '0px',
 1172+ 'background': null
 1173+ });
 1174+
 1175+ if( this.isPersistentNativePlayer() && !_this.controlBuilder.checkOverlayControls() ){
 1176+ // if Persistent native player always give it the player height
 1177+ $('#' + this.pid ).css('height', this.height - _this.controlBuilder.height );
 1178+ }
 1179+ $( this ).show();
 1180+ this.controls = true;
 1181+ };
 1182+
 1183+ if( !this.useNativePlayerControls() && !this.isPersistentNativePlayer() && !_this.controlBuilder.checkOverlayControls() ){
 1184+ // Update the video size per available control space.
 1185+ $(this).css('height', this.height - _this.controlBuilder.height );
 1186+ }
 1187+
 1188+ // Update Thumbnail for the "player"
 1189+ this.updatePosterHTML();
 1190+
 1191+ // Add controls if enabled:
 1192+ if ( this.controls ) {
 1193+ this.controlBuilder.addControls();
 1194+ } else {
 1195+ // Need to think about this some more...
 1196+ // Interface is hidden if controls are "off"
 1197+ this.$interface.hide();
 1198+ }
 1199+
 1200+ // Update temporal url if present
 1201+ this.updateTemporalUrl();
 1202+
 1203+
 1204+ if ( this.autoplay ) {
 1205+ mw.log( 'EmbedPlayer::showPlayer::activating autoplay' );
 1206+ // Issue a non-blocking play request
 1207+ setTimeout(function(){
 1208+ _this.play();
 1209+ },1);
 1210+ }
 1211+
 1212+ },
 1213+ /**
 1214+ * Media fragments handler based on:
 1215+ * http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#fragment-dimensions
 1216+ *
 1217+ * We support seconds and npt ( normal play time )
 1218+ *
 1219+ * Updates the player per fragment url info if present
 1220+ *
 1221+ */
 1222+ updateTemporalUrl: function(){
 1223+ var sourceHash = /[^\#]+$/.exec( this.getSrc() ).toString();
 1224+ if( sourceHash.indexOf('t=') === 0 ){
 1225+ // parse the times
 1226+ var times = sourceHash.substr(2).split(',');
 1227+ if( times[0] ){
 1228+ // update the current time
 1229+ this.currentTime = mw.npt2seconds( times[0].toString() );
 1230+ }
 1231+ if( times[1] ){
 1232+ this.pauseTime = mw.npt2seconds( times[1].toString() );
 1233+ // ignore invalid ranges:
 1234+ if( this.pauseTime < this.currentTime ){
 1235+ this.pauseTime = null;
 1236+ }
 1237+ }
 1238+ // Update the play head
 1239+ this.updatePlayHead( this.currentTime / this.duration );
 1240+ // Update status:
 1241+ this.controlBuilder.setStatus( mw.seconds2npt( this.currentTime ) );
 1242+ }
 1243+ },
 1244+ /**
 1245+ * Get missing plugin html (check for user included code)
 1246+ *
 1247+ * @param {String}
 1248+ * [misssingType] missing type mime
 1249+ */
 1250+ showPluginMissingHTML: function( ) {
 1251+ mw.log("EmbedPlayer::showPluginMissingHTML");
 1252+ // Control builder ( for play button )
 1253+ this.controlBuilder = new mw.PlayerControlBuilder( this );
 1254+
 1255+ this.controlBuilder.doWarningBindinng( 'EmbedPlayer.DirectFileLinkWarning',
 1256+ gM( 'mwe-embedplayer-download-warn', mw.getConfig('EmbedPlayer.FirefoxLink') )
 1257+ );
 1258+ // Hide loader
 1259+ $('#loadingSpinner_' + this.id ).remove();
 1260+ $(this).css('position', 'relative');
 1261+
 1262+ // Get mime type for un-supported formats:
 1263+ this.updatePosterHTML();
 1264+
 1265+ // Set the play button to the first available source:
 1266+ $(this).find('.play-btn-large')
 1267+ .unbind('click')
 1268+ .wrap(
 1269+ $('<a />').attr( {
 1270+ 'href': this.mediaElement.sources[0].getSrc(),
 1271+ 'title' : gM('mwe-embedplayer-play_clip')
 1272+ } )
 1273+ );
 1274+ },
 1275+
 1276+ /**
 1277+ * Update the video time request via a time request string
 1278+ *
 1279+ * @param {String}
 1280+ * time_req
 1281+ */
 1282+ updateVideoTimeReq: function( time_req ) {
 1283+ mw.log( 'EmbedPlayer::updateVideoTimeReq:' + time_req );
 1284+ var time_parts = time_req.split( '/' );
 1285+ this.updateVideoTime( time_parts[0], time_parts[1] );
 1286+ },
 1287+
 1288+ /**
 1289+ * Update Video time from provided start_npt and end_npt values
 1290+ *
 1291+ * @param {String}
 1292+ * start_npt the new start time in npt format
 1293+ * @pamra {String} end_npt the new end time in npt format
 1294+ */
 1295+ updateVideoTime: function( start_npt, end_npt ) {
 1296+ // update media
 1297+ this.mediaElement.updateSourceTimes( start_npt, end_npt );
 1298+
 1299+ // update mv_time
 1300+ this.controlBuilder.setStatus( start_npt + '/' + end_npt );
 1301+
 1302+ // reset slider
 1303+ this.updatePlayHead( 0 );
 1304+
 1305+ // reset seek_offset:
 1306+ if ( this.mediaElement.selectedSource.URLTimeEncoding ) {
 1307+ this.serverSeekTime = 0;
 1308+ } else {
 1309+ this.serverSeekTime = mw.npt2seconds( start_npt );
 1310+ }
 1311+ },
 1312+
 1313+
 1314+ /**
 1315+ * Update Thumb time with npt formated time
 1316+ *
 1317+ * @param {String}
 1318+ * time NPT formated time to update thumbnail
 1319+ */
 1320+ updateThumbTimeNPT: function( time ) {
 1321+ this.updateThumbTime( mw.npt2seconds( time ) - parseInt( this.startOffset ) );
 1322+ },
 1323+
 1324+ /**
 1325+ * Update the thumb with a new time
 1326+ *
 1327+ * @param {Float}
 1328+ * floatSeconds Time to update the thumb to
 1329+ */
 1330+ updateThumbTime:function( floatSeconds ) {
 1331+ // mw.log('updateThumbTime:'+floatSeconds);
 1332+ var _this = this;
 1333+ if ( typeof this.org_thum_src == 'undefined' ) {
 1334+ this.org_thum_src = this.poster;
 1335+ }
 1336+ if ( this.org_thum_src.indexOf( 't=' ) !== -1 ) {
 1337+ this.last_thumb_url = mw.replaceUrlParams( this.org_thum_src,
 1338+ {
 1339+ 't' : mw.seconds2npt( floatSeconds + parseInt( this.startOffset ) )
 1340+ }
 1341+ );
 1342+ if ( !this.thumbnail_updating ) {
 1343+ this.updatePoster( this.last_thumb_url , false );
 1344+ this.last_thumb_url = null;
 1345+ }
 1346+ }
 1347+ },
 1348+
 1349+ /**
 1350+ * Updates the displayed thumbnail via percent of the stream
 1351+ *
 1352+ * @param {Float}
 1353+ * percent Percent of duration to update thumb
 1354+ */
 1355+ updateThumbPerc:function( percent ) {
 1356+ return this.updateThumbTime( ( this.getDuration() * percent ) );
 1357+ },
 1358+
 1359+ /**
 1360+ * Updates the thumbnail if the thumbnail is being displayed
 1361+ *
 1362+ * @param {String}
 1363+ * src New src of thumbnail
 1364+ * @param {Boolean}
 1365+ * quick_switch true switch happens instantly false / undefined
 1366+ * animated cross fade
 1367+ */
 1368+ updatePosterSrc: function( src, quick_switch ) {
 1369+ // make sure we don't go to the same url if we are not already updating:
 1370+ if ( !this.thumbnail_updating && $( '#img_thumb_' + this.id ).attr( 'src' ) == src )
 1371+ return false;
 1372+ // if we are already updating don't issue a new update:
 1373+ if ( this.thumbnail_updating && $( '#new_img_thumb_' + this.id ).attr( 'src' ) == src )
 1374+ return false;
 1375+
 1376+ mw.log( 'update thumb: ' + src );
 1377+
 1378+ if ( quick_switch ) {
 1379+ $( '#img_thumb_' + this.id ).attr( 'src', src );
 1380+ } else {
 1381+ var _this = this;
 1382+ // if still animating remove new_img_thumb_
 1383+ if ( this.thumbnail_updating == true )
 1384+ $( '#new_img_thumb_' + this.id ).stop().remove();
 1385+
 1386+ if ( this.posterDisplayed ) {
 1387+ mw.log( 'set to thumb:' + src );
 1388+ this.thumbnail_updating = true;
 1389+ $( this ).append(
 1390+ $('<img />')
 1391+ .attr({
 1392+ 'src' : src,
 1393+ 'id' : 'new_img_thumb_' + this.id,
 1394+ 'width' : this.width,
 1395+ 'height': this.height
 1396+ })
 1397+ .css( {
 1398+ 'display' : 'none',
 1399+ 'position' : 'absolute',
 1400+ 'z-index' : 2,
 1401+ 'top' : '0px',
 1402+ 'left' : '0px'
 1403+ })
 1404+ );
 1405+ // mw.log('appended: new_img_thumb_');
 1406+ $( '#new_img_thumb_' + this.id ).fadeIn( "slow", function() {
 1407+ // once faded in remove org and rename new:
 1408+ $( '#img_thumb_' + _this.id ).remove();
 1409+ $( '#new_img_thumb_' + _this.id ).attr( 'id', 'img_thumb_' + _this.id );
 1410+ $( '#img_thumb_' + _this.id ).css( 'z-index', '1' );
 1411+ _this.thumbnail_updating = false;
 1412+ // mw.log("done fadding in "+
 1413+ // $('#img_thumb_'+_this.id).attr("src"));
 1414+
 1415+ // if we have a thumb queued update to that
 1416+ if ( _this.last_thumb_url ) {
 1417+ var src_url = _this.last_thumb_url;
 1418+ _this.last_thumb_url = null;
 1419+ _this.updatePosterSrc( src_url );
 1420+ }
 1421+ } );
 1422+ }
 1423+ }
 1424+ },
 1425+ // update the video poster:
 1426+ updatePosterSrc: function( posterSrc ){
 1427+ this.poster = posterSrc;
 1428+ },
 1429+ /**
 1430+ * Returns the HTML code for the video when it is in thumbnail mode.
 1431+ * playing, configuring the player, inline cmml display, HTML linkback,
 1432+ * download, and embed code.
 1433+ */
 1434+ updatePosterHTML: function () {
 1435+ mw.log( 'EmbedPlayer:updatePosterHTML::' + this.id );
 1436+ var thumb_html = '';
 1437+ var class_atr = '';
 1438+ var style_atr = '';
 1439+
 1440+ if( this.useNativePlayerControls() ){
 1441+ this.showNativePlayer();
 1442+ return ;
 1443+ }
 1444+
 1445+ // Set by default thumb value if not found
 1446+ var posterSrc = ( this.poster ) ? this.poster :
 1447+ mw.getConfig( 'imagesPath' ) + 'vid_default_thumb.jpg';
 1448+
 1449+ // Update PersistentNativePlayer poster:
 1450+ if( this.isPersistentNativePlayer() ){
 1451+ $( '#' + this.pid ).attr('poster', posterSrc);
 1452+ } else {
 1453+ // Poster support is not very consistent in browsers
 1454+ // use a jpg poster image:
 1455+ $( this ).html(
 1456+ $( '<img />' )
 1457+ .css({
 1458+ 'position' : 'relative',
 1459+ 'width' : '100%',
 1460+ 'height' : '100%'
 1461+ })
 1462+ .attr({
 1463+ 'id' : 'img_thumb_' + this.id,
 1464+ 'src' : posterSrc
 1465+ })
 1466+ .addClass( 'playerPoster' )
 1467+ );
 1468+ }
 1469+ if ( this.controls && this.controlBuilder
 1470+ && this.height > this.controlBuilder.getComponentHeight( 'playButtonLarge' )
 1471+ ) {
 1472+ $( this ).append(
 1473+ this.controlBuilder.getComponent( 'playButtonLarge' )
 1474+ );
 1475+ }
 1476+ },
 1477+
 1478+ /**
 1479+ * Checks if native controls should be used
 1480+ *
 1481+ * @param [player]
 1482+ * Object Optional player object to check controls attribute
 1483+ * @returns boolean true if the mwEmbed player interface should be used
 1484+ * false if the mwEmbed player interface should not be used
 1485+ */
 1486+ useNativePlayerControls: function() {
 1487+
 1488+ if( this.usenativecontrols === true ){
 1489+ return true;
 1490+ }
 1491+ if( mw.getConfig('EmbedPlayer.NativeControls') === true ) {
 1492+ return true;
 1493+ }
 1494+
 1495+ // Do some device detection devices that don't support overlays
 1496+ // and go into full screen once play is clicked:
 1497+ if( mw.isAndroid2() || mw.isIpod() || mw.isIphone() ){
 1498+ return true;
 1499+ }
 1500+ // iPad can use html controls if its a persistantPlayer in the dom before loading )
 1501+ // else it needs to use native controls:
 1502+ if( mw.isIpad() ){
 1503+ if( this.isPersistentNativePlayer() && mw.getConfig('EmbedPlayer.EnableIpadHTMLControls') ){
 1504+ return false;
 1505+ } else {
 1506+ // Set warning that your trying to do iPad controls without persistent native player:
 1507+ if( mw.getConfig('EmbedPlayer.EnableIpadHTMLControls') ){
 1508+ mw.log("Error:: Trying to set EnableIpadHTMLControls without Persistent Native Player");
 1509+ }
 1510+ return true;
 1511+ }
 1512+ }
 1513+ return false;
 1514+ },
 1515+
 1516+ isPersistentNativePlayer: function(){
 1517+ return $('#' + this.pid ).hasClass('persistentNativePlayer');
 1518+ },
 1519+
 1520+
 1521+ /**
 1522+ * Show the native player embed code
 1523+ *
 1524+ * This is for cases where the main library needs to "get out of the way"
 1525+ * since the device only supports a limited subset of the html5 and won't
 1526+ * work with an html javascirpt interface
 1527+ */
 1528+ showNativePlayer: function(){
 1529+ var _this = this;
 1530+ // Empty the player of any child nodes
 1531+ $(this).empty();
 1532+
 1533+ // Remove the player loader spinner if it exists
 1534+ $('#loadingSpinner_' + this.id ).remove();
 1535+
 1536+ // Setup the source
 1537+ var source = this.mediaElement.getSources( 'video/h264' )[0];
 1538+ // Support fake user agent
 1539+ if( !source || !source.src ){
 1540+ mw.log( 'Warning: Your probably fakeing the iPhone userAgent ( no h.264 source )' );
 1541+ source = this.mediaElement.getSources( 'video/ogg' )[0];
 1542+ }
 1543+
 1544+ // Setup videoAttribues
 1545+ var videoAttribues = {
 1546+ 'poster': _this.poster,
 1547+ 'src' : source.src,
 1548+ 'controls' : 'true'
 1549+ };
 1550+ if( this.loop ){
 1551+ videoAttribues[ 'loop' ] = 'true';
 1552+ }
 1553+ var cssStyle = {
 1554+ 'width' : _this.width,
 1555+ 'height' : _this.height
 1556+ };
 1557+
 1558+ // If not a persistentNativePlayer swap the video tag
 1559+ // completely instead of just updating properties:
 1560+ $( '#' + this.pid ).replaceWith(
 1561+ _this.getNativePlayerHtml( videoAttribues, cssStyle )
 1562+ );
 1563+
 1564+ // Bind native events:
 1565+ this.applyMediaElementBindings();
 1566+
 1567+ // Android only can play with a special play button ( no native controls
 1568+ // persistentNativePlayer has no controls:
 1569+ if( mw.isAndroid2() ){
 1570+ this.addPlayBtnLarge();
 1571+ }
 1572+ return ;
 1573+ },
 1574+ addPlayBtnLarge:function(){
 1575+ var _this = this;
 1576+ var $pid = $( '#' + _this.pid );
 1577+ $pid.siblings('.play-btn-large').remove();
 1578+ $playButton = this.controlBuilder.getComponent('playButtonLarge');
 1579+ $pid.after(
 1580+ $playButton
 1581+ .css({
 1582+ 'left' : parseInt( $pid.position().left ) + parseInt( $playButton.css('left') ),
 1583+ 'top' : parseInt( $pid.position().top ) + parseInt( $playButton.css('top') )
 1584+ })
 1585+ );
 1586+ },
 1587+ /**
 1588+ * Should be set via native embed support
 1589+ */
 1590+ getNativePlayerHtml: function(){
 1591+ return $('<div />' )
 1592+ .css( 'width', this.getWidth() )
 1593+ .html( 'Error: Trying to get native html5 player without native support for codec' );
 1594+ },
 1595+ /**
 1596+ * Should be set via native embed support
 1597+ */
 1598+ applyMediaElementBindings: function(){
 1599+ return ;
 1600+ },
 1601+
 1602+ /**
 1603+ * Gets code to embed the player remotely for "share" this player links
 1604+ */
 1605+ getEmbeddingHTML: function() {
 1606+ switch( mw.getConfig( 'EmbedPlayer.ShareEmbedMode' ) ){
 1607+ case 'iframe':
 1608+ return this.getShareIframeObject();
 1609+ break;
 1610+ case 'videojs':
 1611+ return this.getShareEmbedVideoJs();
 1612+ break;
 1613+ }
 1614+ },
 1615+
 1616+ /**
 1617+ * Get the share embed object code
 1618+ *
 1619+ * NOTE this could probably share a bit more code with getShareEmbedVideoJs
 1620+ */
 1621+ getShareIframeObject: function(){
 1622+ // allow modules to generate the iframe:
 1623+ var iframeEmbedCode ={};
 1624+ $( this ).trigger( 'GetShareIframeCode', [ iframeEmbedCode ] );
 1625+ if( iframeEmbedCode.code ){
 1626+ return frameEmbedCode.code;
 1627+ }
 1628+
 1629+ // old style embed:
 1630+ var iframeUrl = mw.getMwEmbedPath() + 'mwEmbedFrame.php?';
 1631+ var params = {'src[]':[]};
 1632+
 1633+
 1634+ // Output all the video sources:
 1635+ for( var i=0; i < this.mediaElement.sources.length; i++ ){
 1636+ var source = this.mediaElement.sources[i];
 1637+ if( source.src ) {
 1638+ params['src[]'].push(mw.absoluteUrl( source.src ));
 1639+ }
 1640+ }
 1641+ // Output the poster attr
 1642+ if( this.poster ){
 1643+ params.poster = this.poster;
 1644+ }
 1645+
 1646+
 1647+ // Set the skin if set to something other than default
 1648+ if( this.skinName ){
 1649+ params.skin = this.skinName;
 1650+ }
 1651+
 1652+ if( this.duration ) {
 1653+ params['data-durationhint'] = parseFloat( this.duration );
 1654+ }
 1655+ iframeUrl += $j.param( params );
 1656+
 1657+ // Set up embedFrame src path
 1658+ var embedCode = '&lt;iframe src=&quot;' + mw.html.escape( iframeUrl ) + '&quot; ';
 1659+
 1660+ // Set width / height of embed object
 1661+ embedCode += 'width=&quot;' + this.getPlayerWidth() +'&quot; ';
 1662+ embedCode += 'height=&quot;' + this.getPlayerHeight() + '&quot; ';
 1663+ embedCode += 'frameborder=&quot;0&quot; ';
 1664+
 1665+ // Close up the embedCode tag:
 1666+ embedCode+='&gt;&lt/iframe&gt;';
 1667+
 1668+ // Return the embed code
 1669+ return embedCode;
 1670+ },
 1671+
 1672+ /**
 1673+ * Get the share embed Video tag code
 1674+ */
 1675+ getShareEmbedVideoJs: function(){
 1676+
 1677+ // Set the embed tag type:
 1678+ var embedtag = ( this.isAudio() )? 'audio': 'video';
 1679+
 1680+ // Set up the mwEmbed js include:
 1681+ var embedCode = '&lt;script type=&quot;text/javascript&quot; ' +
 1682+ 'src=&quot;' +
 1683+ mw.html.escape(
 1684+ mw.absoluteUrl(
 1685+ mw.getMwEmbedSrc()
 1686+ )
 1687+ ) + '&quot;&gt;&lt;/script&gt' +
 1688+ '&lt;' + embedtag + ' ';
 1689+
 1690+ if( this.poster ) {
 1691+ embedCode += 'poster=&quot;' +
 1692+ mw.html.escape( mw.absoluteUrl( this.poster ) ) +
 1693+ '&quot; ';
 1694+ }
 1695+
 1696+ // Set the skin if set to something other than default
 1697+ if( this.skinName ){
 1698+ embedCode += 'class=&quot;' +
 1699+ mw.html.escape( this.skinName ) +
 1700+ '&quot; ';
 1701+ }
 1702+
 1703+ if( this.duration ) {
 1704+ embedCode +='data-durationhint=&quot;' + parseFloat( this.duration ) + '&quot; ';
 1705+ }
 1706+
 1707+ if( this.width || this.height ){
 1708+ embedCode +='style=&quot;';
 1709+ embedCode += ( this.width )? 'width:' + this.width +'px;': '';
 1710+ embedCode += ( this.height )? 'height:' + this.height +'px;': '';
 1711+ embedCode += '&quot; ';
 1712+ }
 1713+
 1714+ // TODO move to mediaWiki Support module
 1715+ if( this.apiTitleKey ) {
 1716+ embedCode += 'apiTitleKey=&quot;' + mw.html.escape( this.apiTitleKey ) + '&quot; ';
 1717+ if ( this.apiProvider ) {
 1718+ embedCode += 'apiProvider=&quot;' + mw.html.escape( this.apiProvider ) + '&quot; ';
 1719+ }
 1720+ // close the video tag
 1721+ embedCode += '&gt;&lt;/video&gt;';
 1722+
 1723+ } else {
 1724+ // Close the video attr
 1725+ embedCode += '&gt;';
 1726+
 1727+ // Output all the video sources:
 1728+ for( var i=0; i < this.mediaElement.sources.length; i++ ){
 1729+ var source = this.mediaElement.sources[i];
 1730+ if( source.src ) {
 1731+ embedCode +='&lt;source src=&quot;' +
 1732+ mw.absoluteUrl( source.src ) +
 1733+ '&quot; &gt;&lt;/source&gt;';
 1734+ }
 1735+ }
 1736+ // Close the video tag
 1737+ embedCode += '&lt;/video&gt;';
 1738+ }
 1739+
 1740+ return embedCode;
 1741+ },
 1742+
 1743+ /**
 1744+ * Base Embed Controls
 1745+ */
 1746+
 1747+ /**
 1748+ * The Play Action
 1749+ *
 1750+ * Handles play requests, updates relevant states: seeking =false paused =
 1751+ * false Updates pause button Starts the "monitor"
 1752+ */
 1753+ play: function() {
 1754+ var _this = this;
 1755+ mw.log( "EmbedPlayer:: play: " + this._propagateEvents );
 1756+ if( ! this._propagateEvents ){
 1757+ return ;
 1758+ }
 1759+ // Hide any overlay:
 1760+ this.controlBuilder.closeMenuOverlay();
 1761+
 1762+ // Check if thumbnail is being displayed and embed html
 1763+ if ( this.posterDisplayed ) {
 1764+ if ( !this.selectedPlayer ) {
 1765+ this.showPluginMissingHTML();
 1766+ return;
 1767+ } else {
 1768+ this.posterDisplayed = false;
 1769+ // Hide any button if present:
 1770+ this.$interface.find( '.play-btn-large' ).remove();
 1771+ this.doEmbedHTML();
 1772+ }
 1773+ }
 1774+
 1775+ if( this.paused === true ){
 1776+ this.paused = false;
 1777+ // Check if we should Trigger the play event
 1778+ mw.log("EmbedPlayer:: trigger play even::" + !this.paused);
 1779+ if( ! this.doMethodsAutoTrigger() ) {
 1780+ $( this ).trigger( 'play' );
 1781+ }
 1782+ }
 1783+
 1784+ // If we previously finished playing this clip run the "replay hook"
 1785+ if( this.donePlayingCount > 0 && !this.paused) {
 1786+ mw.log("replayEvent");
 1787+ $( this ).trigger( 'replayEvent' );
 1788+ }
 1789+
 1790+ this.$interface.find('.play-btn span')
 1791+ .removeClass( 'ui-icon-play' )
 1792+ .addClass( 'ui-icon-pause' );
 1793+
 1794+ this.$interface.find( '.play-btn' )
 1795+ .unbind()
 1796+ .buttonHover()
 1797+ .click( function( ) {
 1798+ _this.pause();
 1799+ } )
 1800+ .attr( 'title', gM( 'mwe-embedplayer-pause_clip' ) );
 1801+
 1802+ // Start the monitor if not already started
 1803+ this.monitor();
 1804+ },
 1805+ /**
 1806+ * Base embed pause Updates the play/pause button state.
 1807+ *
 1808+ * There is no general way to pause the video must be overwritten by embed
 1809+ * object to support this functionality.
 1810+ */
 1811+ pause: function( event ) {
 1812+ var _this = this;
 1813+ // Trigger the pause event if not already paused and using native
 1814+ // controls:
 1815+ if( this.paused === false ){
 1816+ this.paused = true;
 1817+ mw.log('EmbedPlayer:trigger pause:' + this.paused);
 1818+ if( ! this.doMethodsAutoTrigger() ){
 1819+ $( this ).trigger( 'pause' );
 1820+ }
 1821+ }
 1822+
 1823+ // update the ctrl "paused state"
 1824+ this.$interface.find('.play-btn span' )
 1825+ .removeClass( 'ui-icon-pause' )
 1826+ .addClass( 'ui-icon-play' );
 1827+
 1828+ this.$interface.find('.play-btn' )
 1829+ .unbind('click')
 1830+ .buttonHover()
 1831+ .click( function() {
 1832+ _this.play();
 1833+ } )
 1834+ .attr( 'title', gM( 'mwe-embedplayer-play_clip' ) );
 1835+ },
 1836+ // special per browser check for autoTrigger events
 1837+ // ideally jQuery would not have this inconsistency.
 1838+ doMethodsAutoTrigger: function(){
 1839+ if( $j.browser.mozilla && ! mw.versionIsAtLeast('2.0', $j.browser.version ) ){
 1840+ return true;
 1841+ }
 1842+ return false;
 1843+ },
 1844+
 1845+ /**
 1846+ * Maps the html5 load request. There is no general way to "load" clips so
 1847+ * underling plugin-player libs should override.
 1848+ */
 1849+ load: function() {
 1850+ // should be done by child (no base way to pre-buffer video)
 1851+ mw.log( 'baseEmbed:load call' );
 1852+ },
 1853+
 1854+
 1855+ /**
 1856+ * Base embed stop
 1857+ *
 1858+ * Updates the player to the stop state shows Thumbnail resets Buffer resets
 1859+ * Playhead slider resets Status
 1860+ */
 1861+ stop: function() {
 1862+ var _this = this;
 1863+ mw.log( 'EmbedPlayer::stop:' + this.id );
 1864+
 1865+ // no longer seeking:
 1866+ this.didSeekJump = false;
 1867+
 1868+ // Reset current time and prev time and seek offset
 1869+ this.currentTime = this.previousTime = this.serverSeekTime = 0;
 1870+
 1871+ this.stopMonitor();
 1872+
 1873+ // Issue pause to update interface (only call this parent)
 1874+ if( !this.paused ){
 1875+ this.paused = true;
 1876+ // update the interface
 1877+ if ( this['parent_pause'] ) {
 1878+ this.parent_pause();
 1879+ } else {
 1880+ this.pause();
 1881+ }
 1882+ }
 1883+ // Native player controls:
 1884+ if( this.useNativePlayerControls() ){
 1885+ this.getPlayerElement().currentTime = 0;
 1886+ this.getPlayerElement().pause();
 1887+ // If on android when we "stop" we re add the large play button
 1888+ if( mw.isAndroid2() ){
 1889+ this.addPlayBtnLarge();
 1890+ }
 1891+ } else {
 1892+ // Rewrite the html to thumbnail disp
 1893+ this.showThumbnail();
 1894+ this.bufferedPercent = 0; // reset buffer state
 1895+ this.controlBuilder.setStatus( this.getTimeRange() );
 1896+
 1897+ // Reset the playhead
 1898+ mw.log("EmbedPlayer::Stop:: Reset play head");
 1899+ this.updatePlayHead( 0 );
 1900+
 1901+ }
 1902+ },
 1903+
 1904+ /**
 1905+ * Base Embed mute
 1906+ *
 1907+ * Handles interface updates for toggling mute. Plug-in / player interface
 1908+ * must handle the actual media player update
 1909+ */
 1910+ toggleMute: function() {
 1911+ mw.log( 'f:toggleMute:: (old state:) ' + this.muted );
 1912+ if ( this.muted ) {
 1913+ this.muted = false;
 1914+ var percent = this.preMuteVolume;
 1915+ } else {
 1916+ this.muted = true;
 1917+ this.preMuteVolume = this.volume;
 1918+ var percent = 0;
 1919+ }
 1920+ this.setVolume( percent );
 1921+ // Update the interface
 1922+ this.setInterfaceVolume( percent );
 1923+ },
 1924+
 1925+ /**
 1926+ * Update volume function ( called from interface updates )
 1927+ *
 1928+ * @param {float}
 1929+ * percent Percent of full volume
 1930+ */
 1931+ setVolume: function( percent ) {
 1932+ // ignore NaN percent:
 1933+ if( isNaN( percent ) ){
 1934+ return ;
 1935+ }
 1936+ // Set the local volume attribute
 1937+ this.previousVolume = this.volume = percent;
 1938+
 1939+ // Un-mute if setting positive volume
 1940+ if( percent != 0 ){
 1941+ this.muted = false;
 1942+ }
 1943+
 1944+ // Update the playerElement volume
 1945+ this.setPlayerElementVolume( percent );
 1946+
 1947+ // mw.log(" setVolume:: " + percent + ' this.volume is: ' +
 1948+ // this.volume);
 1949+ $( this ).trigger('volumeChanged', percent );
 1950+ },
 1951+
 1952+ /**
 1953+ * Updates the interface volume
 1954+ *
 1955+ * TODO should move to controlBuilder
 1956+ *
 1957+ * @param {float}
 1958+ * percent Percentage volume to update interface
 1959+ */
 1960+ setInterfaceVolume: function( percent ) {
 1961+ if( this.supports[ 'volumeControl' ] &&
 1962+ this.$interface.find( '.volume-slider' ).length
 1963+ ) {
 1964+ this.$interface.find( '.volume-slider' ).slider( 'value', percent * 100 );
 1965+ }
 1966+ },
 1967+
 1968+ /**
 1969+ * Abstract Update volume Method must be override by plug-in / player
 1970+ * interface
 1971+ */
 1972+ setPlayerElementVolume: function( percent ) {
 1973+ mw.log('Error player does not support volume adjustment' );
 1974+ },
 1975+
 1976+ /**
 1977+ * Abstract get volume Method must be override by plug-in / player interface
 1978+ * (if player does not override we return the abstract player value )
 1979+ */
 1980+ getPlayerElementVolume: function(){
 1981+ // mw.log(' error player does not support getting volume property' );
 1982+ return this.volume;
 1983+ },
 1984+
 1985+ /**
 1986+ * Abstract get volume muted property must be overwritten by plug-in /
 1987+ * player interface (if player does not override we return the abstract
 1988+ * player value )
 1989+ */
 1990+ getPlayerElementMuted: function(){
 1991+ // mw.log(' error player does not support getting mute property' );
 1992+ return this.muted;
 1993+ },
 1994+
 1995+ /**
 1996+ * Passes a fullscreen request to the controlBuilder interface
 1997+ */
 1998+ fullscreen: function() {
 1999+ this.controlBuilder.toggleFullscreen();
 2000+ },
 2001+
 2002+ /**
 2003+ * Abstract method to be run post embedding the player Generally should be
 2004+ * overwritten by the plug-in / player
 2005+ */
 2006+ postEmbedJS:function() {
 2007+ return ;
 2008+ },
 2009+
 2010+ /**
 2011+ * Checks the player state based on thumbnail display & paused state
 2012+ *
 2013+ * @return {Boolean} true if playing false if not playing
 2014+ */
 2015+ isPlaying : function() {
 2016+ if ( this.posterDisplayed ) {
 2017+ // in stopped state
 2018+ return false;
 2019+ } else if ( this.paused ) {
 2020+ // paused state
 2021+ return false;
 2022+ } else {
 2023+ return true;
 2024+ }
 2025+ },
 2026+
 2027+ /**
 2028+ * Get paused state
 2029+ *
 2030+ * @return {Boolean} true if playing false if not playing
 2031+ */
 2032+ isPaused: function() {
 2033+ return this.paused;
 2034+ },
 2035+
 2036+ /**
 2037+ * Get Stopped state
 2038+ *
 2039+ * @return {Boolean} true if stopped false if playing
 2040+ */
 2041+ isStopped: function() {
 2042+ return this.posterDisplayed;
 2043+ },
 2044+
 2045+ // TODO temporary hack we need a better stop monitor system
 2046+ stopMonitor: function(){
 2047+ clearInterval( this.monitorInterval );
 2048+ this.monitorInterval = 0;
 2049+ },
 2050+ // TODO temporary hack we need a better stop monitor system
 2051+ startMonitor: function(){
 2052+ this.monitor();
 2053+ },
 2054+
 2055+ /**
 2056+ * Checks if the currentTime was updated outside of the getPlayerElementTime
 2057+ * function
 2058+ */
 2059+ checkForCurrentTimeSeek: function(){
 2060+ var _this = this;
 2061+ // Check if a javascript currentTime change based seek has occurred
 2062+ if( _this.previousTime != _this.currentTime && !this.userSlide && !this.seeking){
 2063+ // If the time has been updated and is in range issue a seek
 2064+ if( _this.getDuration() && _this.currentTime <= _this.getDuration() ){
 2065+ var seekPercent = _this.currentTime / _this.getDuration();
 2066+ mw.log("checkForCurrentTimeSeek::" + _this.previousTime + ' != ' +
 2067+ _this.currentTime + " javascript based currentTime update to " +
 2068+ seekPercent + ' == ' + _this.currentTime );
 2069+ _this.previousTime = _this.currentTime;
 2070+ this.doSeek( seekPercent );
 2071+ }
 2072+ }
 2073+ },
 2074+
 2075+ /**
 2076+ * Monitor playback and update interface components. underling plugin
 2077+ * objects are responsible for updating currentTime
 2078+ */
 2079+ monitor: function() {
 2080+ var _this = this;
 2081+
 2082+ // Check for current time update outside of embed player
 2083+ this.checkForCurrentTimeSeek();
 2084+
 2085+
 2086+ // Update currentTime via embedPlayer
 2087+ _this.currentTime = _this.getPlayerElementTime();
 2088+
 2089+ // Update any offsets from server seek
 2090+ if( _this.serverSeekTime && _this.supportsURLTimeEncoding() ){
 2091+ _this.currentTime = parseInt( _this.serverSeekTime ) + parseInt( _this.getPlayerElementTime() );
 2092+ }
 2093+
 2094+ // Update the previousTime ( so we can know if the user-javascript
 2095+ // changed currentTime )
 2096+ _this.previousTime = _this.currentTime;
 2097+
 2098+ if( _this.pauseTime && _this.currentTime > _this.pauseTime ){
 2099+ _this.pause();
 2100+ _this.pauseTime = null;
 2101+ }
 2102+
 2103+
 2104+ // Check if volume was set outside of embed player function
 2105+ // mw.log( ' this.volume: ' + _this.volume + ' prev Volume:: ' +
 2106+ // _this.previousVolume );
 2107+ if( Math.round( _this.volume * 100 ) != Math.round( _this.previousVolume * 100 ) ) {
 2108+ _this.setInterfaceVolume( _this.volume );
 2109+ if( _this._propagateEvents ){
 2110+ $( this ).trigger('volumeChanged', _this.volume );
 2111+ }
 2112+ }
 2113+
 2114+ // Update the previous volume
 2115+ _this.previousVolume = _this.volume;
 2116+
 2117+ // Update the volume from the player element
 2118+ _this.volume = this.getPlayerElementVolume();
 2119+
 2120+ // update the mute state from the player element
 2121+ if( _this.muted != _this.getPlayerElementMuted() && ! _this.isStopped() ){
 2122+ mw.log( "EmbedPlayer::monitor: muted does not mach embed player" );
 2123+ _this.toggleMute();
 2124+ // Make sure they match:
 2125+ _this.muted = _this.getPlayerElementMuted();
 2126+ }
 2127+
 2128+ //mw.log( 'Monitor:: ' + this.currentTime + ' duration: ' + ( parseInt(
 2129+ // this.getDuration() ) + 1 ) + ' is seeking: ' + this.seeking );
 2130+
 2131+ if ( this.currentTime >= 0 && this.duration ) {
 2132+ if ( !this.userSlide && !this.seeking ) {
 2133+ if ( parseInt( this.startOffset ) != 0 ) {
 2134+ // If start offset include that calculation
 2135+ this.updatePlayHead( ( this.currentTime - this.startOffset ) / this.duration );
 2136+ var et = ( this.controlBuilder.longTimeDisp ) ? '/' + mw.seconds2npt( parseFloat( this.startOffset ) + parseFloat( this.duration ) ) : '';
 2137+ this.controlBuilder.setStatus( mw.seconds2npt( this.currentTime ) + et );
 2138+ } else {
 2139+ this.updatePlayHead( this.currentTime / this.duration );
 2140+ // Only include the end time if longTimeDisp is enabled:
 2141+ var et = ( this.controlBuilder.longTimeDisp ) ? '/' + mw.seconds2npt( this.duration ) : '';
 2142+ this.controlBuilder.setStatus( mw.seconds2npt( this.currentTime ) + et );
 2143+ }
 2144+ }
 2145+ // Check if we are "done"
 2146+ var endPresentationTime = ( this.startOffset ) ? ( this.startOffset + this.duration ) : this.duration;
 2147+ if ( this.currentTime >= endPresentationTime ) {
 2148+ //mw.log( "mWEmbedPlayer::should run clip done :: " + this.currentTime + ' > ' + endPresentationTime );
 2149+ this.onClipDone();
 2150+ }
 2151+ } else {
 2152+ // Media lacks duration just show end time
 2153+ if ( this.isStopped() ) {
 2154+ this.controlBuilder.setStatus( this.getTimeRange() );
 2155+ } else if ( this.isPaused() ) {
 2156+ this.controlBuilder.setStatus( gM( 'mwe-embedplayer-paused' ) );
 2157+ } else if ( this.isPlaying() ) {
 2158+ if ( this.currentTime && ! this.duration )
 2159+ this.controlBuilder.setStatus( mw.seconds2npt( this.currentTime ) + ' /' );
 2160+ else
 2161+ this.controlBuilder.setStatus( " - - - " );
 2162+ } else {
 2163+ this.controlBuilder.setStatus( this.getTimeRange() );
 2164+ }
 2165+ }
 2166+
 2167+ // Update buffer information
 2168+ this.updateBufferStatus();
 2169+
 2170+ // run the "native" progress event on the virtual html5 object if set
 2171+ if( this.progressEventData ) {
 2172+ // mw.log("trigger:progress event on html5 proxy");
 2173+ if( _this._propagateEvents ){
 2174+ $( this ).trigger( 'progress', this.progressEventData );
 2175+ }
 2176+ }
 2177+
 2178+ // Call monitor at 250ms interval. ( use setInterval to avoid stacking
 2179+ // monitor requests )
 2180+ if( ! this.isStopped() ) {
 2181+ if( !this.monitorInterval ){
 2182+ this.monitorInterval = setInterval( function(){
 2183+ if( _this.monitor )
 2184+ _this.monitor();
 2185+ }, this.monitorRate );
 2186+ }
 2187+ } else {
 2188+ // If stopped "stop" monitor:
 2189+ this.stopMonitor();
 2190+ }
 2191+
 2192+ // mw.log('trigger:monitor:: ' + this.currentTime );
 2193+ if( _this._propagateEvents ){
 2194+ $( this ).trigger( 'monitorEvent' );
 2195+ }
 2196+ },
 2197+
 2198+ /**
 2199+ * Abstract getPlayerElementTime function
 2200+ */
 2201+ getPlayerElementTime: function(){
 2202+ mw.log("Error: getPlayerElementTime should be implemented by embed library");
 2203+ },
 2204+
 2205+ /**
 2206+ * Update the Buffer status based on the local bufferedPercent var
 2207+ */
 2208+ updateBufferStatus: function() {
 2209+
 2210+ // Get the buffer target based for playlist vs clip
 2211+ $buffer = this.$interface.find( '.mw_buffer' );
 2212+
 2213+ //mw.log(' set bufferd %:' + this.bufferedPercent );
 2214+ // Update the buffer progress bar (if available )
 2215+ if ( this.bufferedPercent != 0 ) {
 2216+ // mw.log('Update buffer css: ' + ( this.bufferedPercent * 100 ) +
 2217+ // '% ' + $buffer.length );
 2218+ if ( this.bufferedPercent > 1 ){
 2219+ this.bufferedPercent = 1;
 2220+ }
 2221+ $buffer.css({
 2222+ "width" : ( this.bufferedPercent * 100 ) + '%'
 2223+ });
 2224+ $( this ).trigger( 'updateBufferPercent', this.bufferedPercent );
 2225+ } else {
 2226+ $buffer.css( "width", '0px' );
 2227+ }
 2228+
 2229+ // if we have not already run the buffer start hook
 2230+ if( this.bufferedPercent > 0 && !this.bufferStartFlag ) {
 2231+ this.bufferStartFlag = true;
 2232+ mw.log("bufferStart");
 2233+ $( this ).trigger( 'bufferStartEvent' );
 2234+ }
 2235+
 2236+ // if we have not already run the buffer end hook
 2237+ if( this.bufferedPercent == 1 && !this.bufferEndFlag){
 2238+ this.bufferEndFlag = true;
 2239+ $( this ).trigger( 'bufferEndEvent' );
 2240+ }
 2241+ },
 2242+
 2243+ /**
 2244+ * Update the player playhead
 2245+ *
 2246+ * @param {Float}
 2247+ * perc Value between 0 and 1 for position of playhead
 2248+ */
 2249+ updatePlayHead: function( perc ) {
 2250+ //mw.log( 'EmbedPlayer: updatePlayHead: '+ perc);
 2251+ $playHead = this.$interface.find( '.play_head' );
 2252+ if ( this.controls && $playHead.length != 0 ) {
 2253+ var val = parseInt( perc * 1000 );
 2254+ $playHead.slider( 'value', val );
 2255+ }
 2256+ $( this ).trigger('updatePlayHeadPercent', perc);
 2257+ },
 2258+
 2259+
 2260+ /**
 2261+ * Helper Functions for selected source
 2262+ */
 2263+
 2264+ /**
 2265+ * Get the current selected media source or first source
 2266+ *
 2267+ * @param {Number} Requested time in seconds to be passed to the server if the server supports
 2268+ * supportsURLTimeEncoding
 2269+ * @return src url
 2270+ */
 2271+ getSrc: function( serverSeekTime ) {
 2272+ if( serverSeekTime ){
 2273+ this.serverSeekTime = serverSeekTime;
 2274+ }
 2275+ if( this.currentTime && !this.serverSeekTime){
 2276+ this.serverSeekTime = this.currentTime;
 2277+ }
 2278+
 2279+ // No media element we can't return src
 2280+ if( !this.mediaElement ){
 2281+ return false;
 2282+ }
 2283+
 2284+ // If no source selected auto select the source:
 2285+ if( !this.mediaElement.selectedSource ){
 2286+ this.mediaElement.autoSelectSource();
 2287+ };
 2288+
 2289+ // Return selected source:
 2290+ if( this.mediaElement.selectedSource ){
 2291+ // get the first source:
 2292+ return this.mediaElement.selectedSource.getSrc( this.serverSeekTime );
 2293+ }
 2294+ // No selected source return false:
 2295+ return false;
 2296+ },
 2297+
 2298+ /**
 2299+ * If the selected src supports URL time encoding
 2300+ *
 2301+ * @return {Boolean} true if the src supports url time requests false if the
 2302+ * src does not support url time requests
 2303+ */
 2304+ supportsURLTimeEncoding: function() {
 2305+ var timeUrls = mw.getConfig('EmbedPlayer.EnableURLTimeEncoding') ;
 2306+ if( timeUrls == 'none' ){
 2307+ return false;
 2308+ } else if( timeUrls == 'always' ){
 2309+ return this.mediaElement.selectedSource.URLTimeEncoding;
 2310+ } else if( timeUrls == 'flash' ){
 2311+ if( this.mediaElement.selectedSource.URLTimeEncoding){
 2312+ // see if the current selected player is flash:
 2313+ return ( this.instanceOf == 'Kplayer' );
 2314+ }
 2315+ } else {
 2316+ mw.log("Error:: invalid config value for EmbedPlayer.EnableURLTimeEncoding:: " + mw.getConfig('EmbedPlayer.EnableURLTimeEncoding') );
 2317+ }
 2318+ return false;
 2319+ }
 2320+};
 2321+
 2322+
 2323+} )( mediaWiki, jQuery );
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayer.js
___________________________________________________________________
Added: svn:mime-type
12324 + text/plain
Added: svn:eol-style
22325 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaPlayer.js
@@ -0,0 +1,100 @@
 2+/**
 3+ * mediaPlayer represents a media player plugin.
 4+ *
 5+ * @param {String}
 6+ * id id used for the plugin.
 7+ * @param {Array}
 8+ * supported_types an array of supported MIME types.
 9+ * @param {String}
 10+ * library external script containing the plugin interface code.
 11+ * @constructor
 12+ */
 13+
 14+
 15+function mediaPlayer( id, supported_types, library )
 16+{
 17+ this.id = id;
 18+ this.supported_types = supported_types;
 19+ this.library = library;
 20+ this.loaded = false;
 21+ this.loading_callbacks = new Array();
 22+ return this;
 23+}
 24+mediaPlayer.prototype = {
 25+ // Id of the mediaPlayer
 26+ id:null,
 27+
 28+ // Mime types supported by this player
 29+ supported_types:null,
 30+
 31+ // Player library ie: native, vlc, java etc.
 32+ library:null,
 33+
 34+ // Flag stores the mediaPlayer load state
 35+ loaded:false,
 36+
 37+ /**
 38+ * Checks support for a given MIME type
 39+ *
 40+ * @param {String}
 41+ * type Mime type to check against supported_types
 42+ * @return {Boolean} true if mime type is supported false if mime type is
 43+ * unsupported
 44+ */
 45+ supportsMIMEType: function( type ) {
 46+ for ( var i = 0; i < this.supported_types.length; i++ ) {
 47+ if ( this.supported_types[i] == type )
 48+ return true;
 49+ }
 50+ return false;
 51+ },
 52+
 53+ /**
 54+ * Get the "name" of the player from a predictable msg key
 55+ */
 56+ getName: function() {
 57+ return gM( 'mwe-embedplayer-ogg-player-' + this.id );
 58+ },
 59+
 60+ /**
 61+ * Loads the player library & player skin config ( if needed ) and then
 62+ * calls the callback.
 63+ *
 64+ * @param {Function}
 65+ * callback Function to be called once player library is loaded.
 66+ */
 67+ load: function( callback ) {
 68+ // Load player library ( upper case the first letter of the library )
 69+ mw.load( [
 70+ 'mw.EmbedPlayer' + this.library.substr(0,1).toUpperCase() + this.library.substr(1)
 71+ ], function() {
 72+ callback();
 73+ } );
 74+ }
 75+};
 76+
 77+/**
 78+ * players and supported mime types In an ideal world we would query the plugin
 79+ * to get what mime types it supports in practice not always reliable/available
 80+ *
 81+ * We can't cleanly store these values per library since player library is sometimes
 82+ * loaded post player detection
 83+ */
 84+
 85+// Flash based players:
 86+var kplayer = new mediaPlayer('kplayer', ['video/x-flv', 'video/h264'], 'Kplayer');
 87+
 88+// Java based player
 89+var cortadoPlayer = new mediaPlayer( 'cortado', ['video/ogg', 'audio/ogg', 'application/ogg'], 'Java' );
 90+
 91+// Native html5 players
 92+var oggNativePlayer = new mediaPlayer( 'oggNative', ['video/ogg', 'audio/ogg', 'application/ogg' ], 'Native' );
 93+var h264NativePlayer = new mediaPlayer( 'h264Native', ['video/h264'], 'Native' );
 94+var webmNativePlayer = new mediaPlayer( 'webmNative', ['video/webm'], 'Native' );
 95+
 96+// VLC player
 97+var vlcMineList = ['video/ogg', 'audio/ogg', 'application/ogg', 'video/x-flv', 'video/mp4', 'video/h264', 'video/x-msvideo', 'video/mpeg'];
 98+var vlcPlayer = new mediaPlayer( 'vlc-player', vlcMineList, 'Vlc' );
 99+
 100+// Generic plugin
 101+var oggPluginPlayer = new mediaPlayer( 'oggPlugin', ['video/ogg', 'application/ogg'], 'Generic' );
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaPlayer.js
___________________________________________________________________
Added: svn:mime-type
1102 + text/plain
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.png
___________________________________________________________________
Added: svn:mime-type
2103 + application/octet-stream
Added: svn:executable
3104 + *
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.gif
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/kaltura_open_source_video_platform.gif
___________________________________________________________________
Added: svn:mime-type
4105 + application/octet-stream
Added: svn:executable
5106 + *
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/ksprite.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/images/ksprite.png
___________________________________________________________________
Added: svn:mime-type
6107 + application/octet-stream
Added: svn:executable
7108 + *
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/mw.style.PlayerSkinKskin.css
@@ -0,0 +1,432 @@
 2+/*
 3+* K-skin player
 4+*/
 5+.k-player {
 6+ color: #FFF;
 7+ background-color: #000;
 8+}
 9+.k-player .ui-widget-content {
 10+ color: #999;
 11+}
 12+.k-player .ui-widget-content a{
 13+ color: #999;
 14+}
 15+/* large play button */
 16+.k-player .play-btn-large {
 17+ width: 70px;
 18+ height: 55px;
 19+ background: url(images/ksprite.png) no-repeat 0px -433px;
 20+ position: absolute;
 21+ cursor: pointer;
 22+ border: none;
 23+}
 24+@print {
 25+ .k-player .play-btn-large {
 26+ display: none;
 27+ }
 28+}
 29+/*.ui-state-default */
 30+.k-player .play-btn-large:hover {
 31+ background: url(images/ksprite.png) no-repeat 0px -377px;
 32+}
 33+
 34+/* control icons: */
 35+.k-player .ui-state-default .ui-icon,.k-player .ui-state-hover .ui-icon{
 36+ background: transparent url(images/ksprite.png) no-repeat scroll 0 -48px;
 37+}
 38+
 39+.k-player .ui-state-default .ui-icon-arrow-4-diag {
 40+ background-position: 0 -32px;
 41+}
 42+/* fullscreen */
 43+.k-player .ui-state-hover .ui-icon-arrow-4-diag {
 44+ background-position: -16px -32px;
 45+}
 46+
 47+.k-player .ui-state-hover .ui-icon-volume-on{
 48+ background-position: -16px -48px;
 49+}
 50+
 51+/* cc icon */
 52+.k-player .ui-state-default .ui-icon-comment {
 53+ background-position: 0px -65px;
 54+}
 55+.k-player .ui-state-hover .ui-icon-comment {
 56+ background-position: -17px -65px;
 57+}
 58+
 59+.k-player .ui-state-default .ui-icon-play {
 60+ background: url(images/ksprite.png) no-repeat 0 0;
 61+}
 62+
 63+.k-player .ui-state-hover .ui-icon-play {
 64+ background-position: -16px 0;
 65+}
 66+
 67+.k-player .ui-state-default .ui-icon-pause {
 68+ background: url(images/ksprite.png) no-repeat 0 -17px;
 69+}
 70+
 71+.k-player .ui-state-hover .ui-icon-pause {
 72+ background-position: -16px -17px;
 73+}
 74+
 75+.k-player .control-bar {
 76+ border:1px solid #c8c8c8;
 77+ border-top: 0px;
 78+ border-right: 0px;
 79+ height: 21px;
 80+ padding: 2px 0 0 6px;
 81+ margin-top: 0px;
 82+ background: url(images/ksprite.png) repeat-x 0 -81px;
 83+ font: normal 11px arial, sans-serif;
 84+ color: #555;
 85+}
 86+
 87+.k-player .play_head {
 88+ background: url("images/ksprite.png") repeat-x scroll 0 -350px
 89+ transparent;
 90+ display: inline;
 91+ float: left;
 92+ margin-left: 10px;
 93+ border: 1px solid #EEEEEE;
 94+ height: 8px;
 95+ margin: 5px 2px 0 0px;
 96+ position: relative;
 97+}
 98+
 99+.k-player .play_head .ui-slider-handle {
 100+ background: url("images/ksprite.png") no-repeat scroll -67px -341px
 101+ transparent;
 102+ border: 1px solid #888888;
 103+ display: block;
 104+ height: 8px;
 105+ margin: -1px 0 0 -5px;
 106+ position: absolute;
 107+ top: 0;
 108+ width: 8px;
 109+ cursor: pointer;
 110+ -moz-border-radius:5px 5px 5px 5px;
 111+}
 112+.k-player .ui-corner-all {
 113+ -moz-border-radius:5px 5px 5px 5px;
 114+}
 115+.k-player .time-disp {
 116+ border: medium none;
 117+ display: inline;
 118+ color: #555555;
 119+ font: 11px arial, sans-serif;
 120+ line-height: 20px;
 121+ overflow: hidden;
 122+ width: 48px;
 123+ float: right;
 124+}
 125+
 126+.k-player .source-switch{
 127+ border: medium none;
 128+ display: inline;
 129+ color: #555555;
 130+ font: 11px arial, sans-serif;
 131+ line-height: 20px;
 132+ overflow: hidden;
 133+ width: 50px;
 134+ cursor: pointer;
 135+ float: right;
 136+}
 137+
 138+
 139+.k-player .lButton {
 140+ cursor: pointer;
 141+ float: left;
 142+ list-style: none outside none;
 143+ margin: 2px;
 144+ padding: 0px 0;
 145+ width: 24px;
 146+ height: 16px;
 147+ position: relative;
 148+ background: none repeat scroll 0 0 transparent;
 149+ border: medium none;
 150+}
 151+
 152+.k-player .rButton {
 153+ cursor: pointer;
 154+ float: right;
 155+ list-style: none outside none;
 156+ margin-top: 2px;
 157+ padding: 0px 0;
 158+ width: 22px;
 159+ height: 16px;
 160+ position: relative;
 161+ background: none repeat scroll 0 0 transparent;
 162+ border: medium none;
 163+}
 164+
 165+.k-player .k-options {
 166+ border: 1px solid #AAAAAA !important;
 167+ color: #555555;
 168+ float: right;
 169+ height: 21px;
 170+ margin-top: -2px;
 171+ margin-right: 0px;
 172+ width: 50px;
 173+ float: right;
 174+ background: none repeat scroll 0 0 transparent;
 175+ font-family: Lucida Grande, Lucida Sans, Arial, sans-serif;
 176+ font-size: 11px;
 177+ text-transform: uppercase;
 178+ text-align:center;
 179+}
 180+
 181+.k-player .k-options span {
 182+ position: relative;
 183+ top: 4px;
 184+}
 185+
 186+.k-player .k-menu-screens {
 187+ float: left;
 188+ font-size: 11px;
 189+ text-align: left;
 190+ padding: 13px 10px 15px 15px;
 191+}
 192+
 193+.k-player ul.k-menu-bar {
 194+ background: url("images/ksprite.png") no-repeat scroll -99px -104px
 195+ transparent;
 196+ bottom: 5px;
 197+ height: 128px;
 198+ list-style: none outside none;
 199+ padding: 0 0 5px;
 200+ position: absolute;
 201+ right: 0;
 202+}
 203+
 204+.k-player .k-menu {
 205+ background: none repeat scroll 0 0 #181818;
 206+ border: medium none;
 207+ display: none;
 208+ left: 0;
 209+ position: absolute;
 210+ top: 0;
 211+ z-index: 2;
 212+}
 213+
 214+.k-player .k-menu-bar li a {
 215+ background: url("images/ksprite.png") no-repeat scroll -51px -110px
 216+ transparent;
 217+ display: block;
 218+ height: 32px;
 219+ margin-left: 1px;
 220+ overflow: hidden;
 221+ text-indent: 99999px;
 222+ width: 49px;
 223+}
 224+
 225+.k-menu-bar li a:hover {
 226+ background-position: -1px -110px;
 227+}
 228+
 229+.k-menu-bar li.k-download-btn a {
 230+ background-position: -51px -203px;
 231+}
 232+
 233+.k-menu-bar li.k-download-btn a:hover {
 234+ background-position: -1px -203px;
 235+}
 236+
 237+.k-menu-bar li.k-share-btn a {
 238+ background-position: -51px -172px;
 239+}
 240+
 241+.k-menu-bar li.k-share-btn a:hover {
 242+ background-position: -1px -172px;
 243+}
 244+
 245+.k-menu-bar li.k-credits-btn a {
 246+ background-position: -51px -141px;
 247+}
 248+
 249+.k-menu-bar li.k-credits-btn a:hover {
 250+ background-position: -1px -141px;
 251+}
 252+
 253+
 254+
 255+.k-menu-screens p {
 256+ margin: 6px 0;
 257+}
 258+
 259+.k-menu-screens a img {
 260+ border: none;
 261+}
 262+
 263+.k-menu-screens ul {
 264+ padding: 0;
 265+ margin: 6px 0 0;
 266+ list-style: none outside none;
 267+}
 268+
 269+.k-edit-screen {
 270+ width: 370px;
 271+ height: 223px;
 272+ padding-top: 77px;
 273+ text-align: center;
 274+ background: #181818;
 275+ color: #fff;
 276+}
 277+
 278+.k-edit-screen div {
 279+
 280+}
 281+
 282+.k-edit-screen a {
 283+ color: #7BB8FC;
 284+}
 285+
 286+.k-edit-screen a img {
 287+ border: none;
 288+}
 289+
 290+
 291+.k-menu-screens h2, .k-menu-screens h3 {
 292+ padding: 0 0 5px 1px;
 293+ clear: both;
 294+ font-size: 12px;
 295+ color: #999;
 296+}
 297+
 298+.k-menu-screens p {
 299+ margin: 6px 0;
 300+}
 301+
 302+.k-menu-screens a img {
 303+ border: none;
 304+}
 305+
 306+.k-menu-screens ul {
 307+ padding: 0;
 308+ margin: 6px 0 0;
 309+ list-style: none outside none;
 310+}
 311+
 312+.k-menu-screens li {
 313+ margin-bottom: 6px;
 314+}
 315+
 316+.k-menu-screens li a {
 317+ padding-left: 22px;
 318+ background: url(images/ksprite.png) no-repeat -85px -274px;
 319+ text-decoration: none;
 320+ color: #BBB;
 321+}
 322+
 323+.k-menu-screens li a.active,.k-menu-screens li a:hover .active {
 324+ background-position: -85px -247px;
 325+}
 326+
 327+.k-menu-screens li a:hover {
 328+ background-position: -85px -260px;
 329+}
 330+
 331+.k-menu textarea {
 332+ background: none repeat scroll 0 0 transparent;
 333+ border-color: #000000 -moz-use-text-color -moz-use-text-color #000000;
 334+ border-style: solid none none solid;
 335+ border-width: 2px medium medium 2px;
 336+ color: #CCCCCC;
 337+ font: 11px arial, sans-serif;
 338+ overflow: hidden;
 339+ padding-left: 2px;
 340+ width: 95%;
 341+}
 342+
 343+.menu-screen.menu-share button {
 344+ background: url("images/ksprite.png") no-repeat scroll 0 -81px #D4D4D4;
 345+ border: 1px solid #000000;
 346+ color: #000000;
 347+ float: right;
 348+ height: 34px;
 349+ padding: 0 5px 3px;
 350+ width: 84px;
 351+ font-size: 1em;
 352+}
 353+
 354+.k-player .menu-screen {
 355+ height: 100%;
 356+ overflow-y: auto;
 357+ overflow-x: hidden;
 358+}
 359+
 360+
 361+.k-player .menu-screen.menu-share div.ui-state-highlight {
 362+ background: none repeat scroll 0 0 transparent;
 363+ border-color: #554926;
 364+ color: #FFE96E;
 365+ float: left;
 366+ padding: 2px 5px;
 367+}
 368+
 369+.k-player .menu-screen.menu-share div.ui-state-highlight a {
 370+ color: #FFE96E;
 371+ font-weight: bold;
 372+}
 373+
 374+.k-player .volume_control {
 375+ margin-right: 2px;
 376+ width: 16px;
 377+}
 378+
 379+.k-player .volume_control span {
 380+ margin-right: 0px;
 381+}
 382+
 383+.k-player .volume-slider {
 384+ width: 20px;
 385+}
 386+
 387+.k-player .volume-slider .ui-slider-range {
 388+ -moz-border-radius: 0 0 0 0;
 389+ background: url("images/ksprite.png") repeat-x scroll -66px -306px
 390+ transparent;
 391+ height: 17px;
 392+ position: absolute;
 393+}
 394+
 395+.k-player .volume-slider a.ui-slider-handle {
 396+ background: none repeat scroll 0 0 transparent;
 397+ border: medium none;
 398+ display: block;
 399+ height: 18px;
 400+ margin: -3px 5px 0 -1px;
 401+ position: absolute;
 402+ width: 8px;
 403+}
 404+
 405+.k-player .credits_box {
 406+ background-attachment:scroll;
 407+ background-color:white;
 408+ background-image:none;
 409+ background-position:0 0;
 410+ bottom:30px;
 411+ left:15px;
 412+ position:absolute;
 413+ right:30px;
 414+ top:48px;
 415+}
 416+.k-player .credits_box a{
 417+ color:#666;
 418+}
 419+.k-player .creditline img {
 420+ float: left;
 421+ width: 90px;
 422+ margin: 4px;
 423+}
 424+
 425+.k-player .k-attribution{
 426+ position:absolute;
 427+ bottom: 15px;
 428+ right : 30px;
 429+ background: url("images/kaltura_open_source_video_platform.png");
 430+ width : 51px;
 431+ height : 12px;
 432+ cursor: pointer;
 433+}
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/mw.style.PlayerSkinKskin.css
___________________________________________________________________
Added: svn:mime-type
1434 + text/plain
Added: svn:eol-style
2435 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/mw.PlayerSkinKskin.js
@@ -0,0 +1,382 @@
 2+/**
 3+* Skin js allows you to override contrlBuilder html/class output
 4+*/
 5+( function( mw, $ ) {
 6+
 7+mw.PlayerSkinKskin = {
 8+
 9+ // The parent class for all kskin css:
 10+ playerClass: 'k-player',
 11+
 12+ // Display time string length
 13+ longTimeDisp: false,
 14+
 15+ // Default control bar height
 16+ height: 20,
 17+
 18+ // Volume control layout is horizontal
 19+ volume_layout: 'horizontal',
 20+
 21+ // Skin "kskin" is specific for wikimedia we have an
 22+ // api Title key so the "credits" menu item can be showed.
 23+ supportedMenuItems: {
 24+ 'credits': true
 25+ },
 26+
 27+ // Extends base components with kskin specific options:
 28+ components: {
 29+ 'playButtonLarge' : {
 30+ 'h' : 55
 31+ },
 32+ 'options': {
 33+ 'w':50,
 34+ 'o':function( ctrlObj ) {
 35+ return $( '<div />' )
 36+ .attr( 'title', gM( 'mwe-embedplayer-player_options' ) )
 37+ .addClass( "ui-state-default ui-corner-bl rButton k-options" )
 38+ .append(
 39+ $( '<span />' )
 40+ .text( gM( 'mwe-embedplayer-menu_btn' ) )
 41+ );
 42+ }
 43+ },
 44+ 'volumeControl': {
 45+ 'w':40
 46+ },
 47+ // No attributionButton component for kSkin ( its integrated into the credits screen )
 48+ 'attributionButton' : false,
 49+
 50+ // Time display:
 51+ 'timeDisplay': {
 52+ 'w':45
 53+ },
 54+ 'optionsMenu': {
 55+ 'w' : 0,
 56+ 'o' : function( ctrlObj ) {
 57+ var embedPlayer = ctrlObj.embedPlayer;
 58+
 59+ $menuOverlay = $( '<div />')
 60+ .addClass( 'overlay-win k-menu ui-widget-content' )
 61+ .css( {
 62+ 'width' : '100%',
 63+ 'position': 'absolute',
 64+ 'top' : '0px',
 65+ 'bottom' : ( ctrlObj.getHeight() + 2 ) + 'px'
 66+ } );
 67+
 68+ // Note safari can't display video overlays with text:
 69+ // see bug https://bugs.webkit.org/show_bug.cgi?id=48379
 70+
 71+ var userAgent = navigator.userAgent.toLowerCase();
 72+ if( userAgent.indexOf('safari') != -1 ){
 73+ $menuOverlay.css('opacity', '0.9');
 74+ }
 75+ // Setup menu offset ( if player height < getOverlayHeight )
 76+ // This displays the menu outside of the player on small embeds
 77+ if ( embedPlayer.getPlayerHeight() < ctrlObj.getOverlayHeight() ) {
 78+ var topPos = ( ctrlObj.checkOverlayControls() )
 79+ ? embedPlayer.getPlayerHeight()
 80+ : embedPlayer.getPlayerHeight() + ctrlObj.getHeight();
 81+
 82+ $menuOverlay.css( {
 83+ 'top' : topPos + 'px',
 84+ 'bottom' : null,
 85+ 'width' : ctrlObj.getOverlayWidth(),
 86+ 'height' : ctrlObj.getOverlayHeight() + 'px'
 87+ });
 88+ // Special common overflow hack for thumbnail display of player
 89+ $( embedPlayer ).parents( '.thumbinner' ).css( 'overflow', 'visible' );
 90+ }
 91+
 92+ $menuBar = $( '<ul />' )
 93+ .addClass( 'k-menu-bar' );
 94+
 95+ // dont include about player menu item ( FIXME should be moved to a init function )
 96+ delete ctrlObj.supportedMenuItems['aboutPlayerLibrary'];
 97+
 98+ // Output menu item containers:
 99+ for ( var menuItem in ctrlObj.supportedMenuItems ) {
 100+ $menuBar.append(
 101+ $( '<li />')
 102+ // Add the menu item class:
 103+ .addClass( 'k-' + menuItem + '-btn' )
 104+ .attr( 'rel', menuItem )
 105+ .append(
 106+ $( '<a />' )
 107+ .attr( {
 108+ 'title' : gM( 'mwe-embedplayer-' + menuItem ),
 109+ 'href' : '#'
 110+ })
 111+ )
 112+ );
 113+ }
 114+
 115+ // Add the menuBar to the menuOverlay
 116+ $menuOverlay.append( $menuBar );
 117+
 118+ var $menuScreens = $( '<div />' )
 119+ .addClass( 'k-menu-screens' )
 120+ .css( {
 121+ 'position' : 'absolute',
 122+ 'top' : '0px',
 123+ 'left' : '0px',
 124+ 'bottom' : '0px',
 125+ 'right' : '45px',
 126+ 'overflow' : 'hidden'
 127+ } );
 128+ for ( var menuItem in ctrlObj.supportedMenuItems ) {
 129+ $menuScreens.append(
 130+ $( '<div />' )
 131+ .addClass( 'menu-screen menu-' + menuItem )
 132+ );
 133+ }
 134+
 135+ // Add the menuScreens to the menuOverlay
 136+ $menuOverlay.append( $menuScreens );
 137+
 138+ return $menuOverlay;
 139+
 140+ }
 141+ }
 142+ },
 143+
 144+ /**
 145+ * Get minimal width for interface overlay
 146+ */
 147+ getOverlayWidth: function(){
 148+ return ( this.embedPlayer.getPlayerWidth() < 200 )? 200 : this.embedPlayer.getPlayerWidth();
 149+ },
 150+
 151+ /**
 152+ * Get minimal height for interface overlay
 153+ */
 154+ getOverlayHeight: function(){
 155+ return ( this.embedPlayer.getPlayerHeight() < 160 )? 160 : this.embedPlayer.getPlayerHeight();
 156+ },
 157+
 158+ /**
 159+ * Adds the skin Control Bindings
 160+ */
 161+ addSkinControlBindings: function() {
 162+ var embedPlayer = this.embedPlayer;
 163+ var _this = this;
 164+
 165+ // Set up control bar pointer
 166+ this.$playerTarget = embedPlayer.$interface;
 167+ // Set the menu target:
 168+
 169+
 170+ // Options menu display:
 171+ this.$playerTarget.find( '.k-options' )
 172+ .unbind()
 173+ .click( function() {
 174+ _this.checkMenuOverlay();
 175+ var $kmenu = _this.$playerTarget.find( '.k-menu' );
 176+ if ( $kmenu.is( ':visible' ) ) {
 177+ _this.closeMenuOverlay( );
 178+ } else {
 179+ _this.showMenuOverlay( );
 180+ }
 181+ } );
 182+
 183+ },
 184+
 185+ /**
 186+ * checks for menu overlay and runs menu bindings if unset
 187+ */
 188+ checkMenuOverlay: function(){
 189+ var _this = this;
 190+ var embedPlayer = this.embedPlayer;
 191+ if ( _this.$playerTarget.find( '.k-menu' ).length == 0 ) {
 192+ // Stop the player if it does not support overlays:
 193+ if ( !embedPlayer.supports['overlays'] ) {
 194+ embedPlayer.stop();
 195+ }
 196+
 197+ // Add the menu binding
 198+ _this.addMenuBinding();
 199+ }
 200+ },
 201+
 202+ /**
 203+ * Close the menu overlay
 204+ */
 205+ closeMenuOverlay: function() {
 206+ mw.log("PlayerSkin: close menu overlay" );
 207+
 208+ var $optionsMenu = this.$playerTarget.find( '.k-options' );
 209+ var $kmenu = this.$playerTarget.find( '.k-menu' );
 210+ $kmenu.fadeOut( "fast", function() {
 211+ $optionsMenu.find( 'span' )
 212+ .text ( gM( 'mwe-embedplayer-menu_btn' ) );
 213+ } );
 214+ this.$playerTarget.find( '.play-btn-large' ).fadeIn( 'fast' );
 215+
 216+ // re display the control bar if hidden:
 217+ this.showControlBar();
 218+
 219+ // Set close overlay menu flag:
 220+ this.displayOptionsMenuFlag = false;
 221+ },
 222+
 223+ /**
 224+ * Show the menu overlay
 225+ */
 226+ showMenuOverlay: function( $ktxt ) {
 227+ var $optionsMenu = this.$playerTarget.find( '.k-options' );
 228+ var $kmenu = this.$playerTarget.find( '.k-menu' );
 229+
 230+ $kmenu.fadeIn( "fast", function() {
 231+ $optionsMenu.find( 'span' )
 232+ .text ( gM( 'mwe-embedplayer-close_btn' ) );
 233+ } );
 234+ this.$playerTarget.find( '.play-btn-large' ).fadeOut( 'fast' );
 235+
 236+ $(this.embedPlayer).trigger( 'displayMenuOverlay' );
 237+
 238+ // Set the Options Menu display flag to true:
 239+ this.displayOptionsMenuFlag = true;
 240+ },
 241+
 242+ /**
 243+ * Adds binding for the options menu
 244+ *
 245+ * @param {Object} $tp Target video container for
 246+ */
 247+ addMenuBinding: function() {
 248+ var _this = this;
 249+ var embedPlayer = this.embedPlayer;
 250+ // Set local player target pointer:
 251+ var $playerTarget = embedPlayer.$interface;
 252+
 253+ // Check if k-menu already exists:
 254+ if ( $playerTarget.find( '.k-menu' ).length != 0 )
 255+ return false;
 256+
 257+ // Add options menu to top of player target children:
 258+ $playerTarget.prepend(
 259+ _this.getComponent( 'optionsMenu' )
 260+ );
 261+
 262+ // By default its hidden:
 263+ $playerTarget.find( '.k-menu' ).hide();
 264+
 265+ // Add menu-items bindings:
 266+ for ( var menuItem in _this.supportedMenuItems ) {
 267+ $playerTarget.find( '.k-' + menuItem + '-btn' ).click( function( ) {
 268+
 269+ // Grab the context from the "clicked" menu item
 270+ var mk = $( this ).attr( 'rel' );
 271+
 272+ // hide all menu items
 273+ $targetItem = $playerTarget.find( '.menu-' + mk );
 274+
 275+ // call the function showMenuItem
 276+ _this.showMenuItem( mk );
 277+
 278+ // Hide the others
 279+ $playerTarget.find( '.menu-screen' ).hide();
 280+
 281+ // Show the target menu item:
 282+ $targetItem.fadeIn( "fast" );
 283+
 284+ // Don't follow the # link
 285+ return false;
 286+ } );
 287+ }
 288+ },
 289+
 290+ /**
 291+ * onClipDone action
 292+ * onClipDone for k-skin (with apiTitleKey) show the "credits" screen:
 293+ */
 294+ onClipDone: function(){
 295+ if( this.embedPlayer.apiTitleKey ){
 296+ this.checkMenuOverlay( );
 297+ this.showMenuOverlay();
 298+ this.showMenuItem( 'credits' );
 299+ }
 300+ },
 301+
 302+ /**
 303+ * Shows a selected menu_item
 304+ *
 305+ * NOTE: this should be merged with parent mw.PlayerControlBuilder optionMenuItems
 306+ * binding mode
 307+ *
 308+ * @param {String} menu_itme Menu item key to display
 309+ */
 310+ showMenuItem:function( menuItem ) {
 311+ var embedPlayer = this.embedPlayer;
 312+ //handle special k-skin specific display;
 313+ switch( menuItem ){
 314+ case 'credits':
 315+ this.showCredits();
 316+ break;
 317+ case 'playerSelect':
 318+ embedPlayer.$interface.find( '.menu-playerSelect').html(
 319+ this.getPlayerSelect()
 320+ );
 321+ break;
 322+ case 'download' :
 323+ embedPlayer.$interface.find( '.menu-download').text(
 324+ gM('mwe-loading_txt' )
 325+ );
 326+ // Call show download with the target to be populated
 327+ this.showDownload(
 328+ embedPlayer.$interface.find( '.menu-download')
 329+ );
 330+ break;
 331+ case 'share':
 332+ embedPlayer.$interface.find( '.menu-share' ).html(
 333+ this.getShare()
 334+ );
 335+ break;
 336+ }
 337+ },
 338+
 339+ /**
 340+ * Show the credit screen ( presently specific to kaltura skin )
 341+ */
 342+ showCredits: function() {
 343+ // Set up the shortcuts:
 344+ var embedPlayer = this.embedPlayer;
 345+ var _this = this;
 346+
 347+ var $target = embedPlayer.$interface.find( '.menu-credits' );
 348+
 349+ $target.empty().append(
 350+ $('<h2 />')
 351+ .text( gM( 'mwe-embedplayer-credits' ) ),
 352+ $('<div />')
 353+ .addClass( "credits_box ui-corner-all" )
 354+ .loadingSpinner()
 355+ );
 356+
 357+ if( mw.getConfig( 'EmbedPlayer.KalturaAttribution' ) == true ){
 358+ $target.append(
 359+ $( '<div />' )
 360+ .addClass( 'k-attribution' )
 361+ .attr({
 362+ 'title': gM('mwe-embedplayer-kaltura-platform-title')
 363+ })
 364+ .click( function( ) {
 365+ window.location = 'http://kaltura.com';
 366+ })
 367+ );
 368+ }
 369+ var $creditsTarget = embedPlayer.$interface.find( '.menu-credits .credits_box' );
 370+
 371+ // Allow modules to load and add credits
 372+ $( embedPlayer ).triggerQueueCallback( 'ShowCredits', $creditsTarget, function( status ){
 373+ // Check if the first ShowCredits binding returned false:
 374+ if( !status || status[0] == false ){
 375+ $creditsTarget.text(
 376+ gM('mwe-embedplayer-no-video_credits')
 377+ );
 378+ }
 379+ });
 380+ }
 381+};
 382+
 383+} )( window.mediaWiki, window.jQuery );
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/kskin/mw.PlayerSkinKskin.js
___________________________________________________________________
Added: svn:mime-type
1384 + text/plain
Added: svn:eol-style
2385 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js
@@ -0,0 +1,1920 @@
 2+/**
 3+* Msg text is inherited from embedPlayer
 4+*/
 5+
 6+( function( mw, $ ) {
 7+/**
 8+* mw.PlayerControlBuilder object
 9+* @param the embedPlayer element we are targeting
 10+*/
 11+mw.PlayerControlBuilder = function( embedPlayer, options ) {
 12+ return this.init( embedPlayer, options );
 13+};
 14+
 15+/**
 16+ * ControlsBuilder prototype:
 17+ */
 18+mw.PlayerControlBuilder.prototype = {
 19+ //Default Local values:
 20+
 21+ // Parent css Class name
 22+ playerClass : 'mv-player',
 23+
 24+ // Long string display of time value
 25+ longTimeDisp: true,
 26+
 27+ // Default volume layout is "vertical"
 28+ volume_layout : 'vertical',
 29+
 30+ // Default control bar height
 31+ height: 31,
 32+
 33+ // Default supported components is merged with embedPlayer set of supported types
 34+ supportedComponets: {
 35+ // All playback types support options
 36+ 'options': true
 37+ },
 38+
 39+ // Default supported menu items is merged with skin menu items
 40+ supportedMenuItems: {
 41+ // Player Select
 42+ 'playerSelect' : true,
 43+
 44+ // Download the file menu
 45+ 'download' : true,
 46+
 47+ // Share the video menu
 48+ 'share' : true,
 49+
 50+ // Player library link
 51+ 'aboutPlayerLibrary': true
 52+ },
 53+
 54+ // Flag to store the current fullscreen mode
 55+ fullscreenMode: false,
 56+
 57+ // Flag to store if a warning binding has been added
 58+ addWarningFlag: false,
 59+
 60+ // Flag to store state of overlay on player
 61+ keepControlBarOnScreen: false,
 62+
 63+ /**
 64+ * Initialization Object for the control builder
 65+ *
 66+ * @param {Object} embedPlayer EmbedPlayer interface
 67+ */
 68+ init: function( embedPlayer ) {
 69+ var _this = this;
 70+ this.embedPlayer = embedPlayer;
 71+
 72+ // Check for skin overrides for controlBuilder
 73+ var skinClass = embedPlayer.skinName.substr(0,1).toUpperCase() + embedPlayer.skinName.substr( 1 );
 74+ if ( mw['PlayerSkin' + skinClass ]) {
 75+
 76+ // Clone as to not override prototype with the skin config
 77+ var _this = $j.extend( true, { }, this, mw['PlayerSkin' + skinClass ] );
 78+ return _this;
 79+ }
 80+ // Return the controlBuilder Object:
 81+ return this;
 82+ },
 83+
 84+ /**
 85+ * Get the control bar height
 86+ * @return {Number} control bar height
 87+ */
 88+ getHeight: function(){
 89+ return this.height;
 90+ },
 91+
 92+ /**
 93+ * Add the controls html to player interface
 94+ */
 95+ addControls: function() {
 96+ // Set up local pointer to the embedPlayer
 97+ var embedPlayer = this.embedPlayer;
 98+
 99+ // Set up local controlBuilder
 100+ var _this = this;
 101+
 102+ // Remove any old controls & old overlays:
 103+ embedPlayer.$interface.find( '.control-bar,.overlay-win' ).remove();
 104+
 105+ // Reset flag:
 106+ _this.keepControlBarOnScreen = false;
 107+
 108+
 109+ // Setup the controlBar container ( starts hidden )
 110+ var $controlBar = $('<div />')
 111+ .addClass( 'ui-state-default ui-widget-header ui-helper-clearfix control-bar' )
 112+ .css( 'height', this.height );
 113+
 114+ // Controls are hidden by default if overlaying controls:
 115+ if( _this.checkOverlayControls() ){
 116+ $controlBar.hide();
 117+ }
 118+
 119+ $controlBar.css( {
 120+ 'position': 'absolute',
 121+ 'bottom' : '0px',
 122+ 'left' : '0px',
 123+ 'right' : '0px'
 124+ } );
 125+
 126+ // Check for overlay controls:
 127+ /*if( ! _this.checkOverlayControls() && ! embedPlayer.controls === false ) {
 128+ // Add some space to interface for the control bar ( if not overlaying controls )
 129+ $( embedPlayer ).css( {
 130+ 'height' : parseInt( embedPlayer.height ) - parseInt( this.height )
 131+ } );
 132+ }*/
 133+ // Make room for audio controls in the interface:
 134+ if( embedPlayer.isAudio() && embedPlayer.$interface.height() == 0 ){
 135+ embedPlayer.$interface.css( {
 136+ 'height' : this.height
 137+ } );
 138+ }
 139+
 140+ // Add the controls to the interface
 141+ embedPlayer.$interface.append( $controlBar );
 142+
 143+ // Add the Controls Component
 144+ this.addControlComponents();
 145+
 146+ // Add top level Controls bindings
 147+ this.addControlBindings();
 148+ },
 149+
 150+ /**
 151+ * Add control components as defined per this.components
 152+ */
 153+ addControlComponents: function( ) {
 154+ var _this = this;
 155+
 156+ // Set up local pointer to the embedPlayer
 157+ var embedPlayer = this.embedPlayer;
 158+
 159+ //Set up local var to control container:
 160+ var $controlBar = embedPlayer.$interface.find( '.control-bar' );
 161+
 162+ this.available_width = embedPlayer.getPlayerWidth();
 163+
 164+ mw.log( 'PlayerControlsBuilder:: addControlComponents into:' + this.available_width );
 165+ // Build the supportedComponets list
 166+ this.supportedComponets = $j.extend( this.supportedComponets, embedPlayer.supports );
 167+
 168+ $( embedPlayer ).trigger( 'addControlBarComponent', this);
 169+
 170+ // Check for Attribution button
 171+ if( mw.getConfig( 'EmbedPlayer.AttributionButton' ) && embedPlayer.attributionbutton ){
 172+ this.supportedComponets[ 'attributionButton' ] = true;
 173+ }
 174+
 175+ // Check global fullscreen enabled flag
 176+ if( mw.getConfig( 'EmbedPlayer.EnableFullscreen' ) === false ){
 177+ this.supportedComponets[ 'fullscreen'] = false;
 178+ }
 179+ // Check if the options item is available
 180+ if( mw.getConfig( 'EmbedPlayer.EnableOptionsMenu' ) === false ){
 181+ this.supportedComponets[ 'options'] = false;
 182+ }
 183+
 184+
 185+ var addComponent = function( component_id ){
 186+ if ( _this.supportedComponets[ component_id ] ) {
 187+ if ( _this.available_width > _this.components[ component_id ].w ) {
 188+ // Append the component
 189+ $controlBar.append(
 190+ _this.getComponent( component_id )
 191+ );
 192+ _this.available_width -= _this.components[ component_id ].w;
 193+ } else {
 194+ mw.log( 'Not enough space for control component:' + component_id );
 195+ }
 196+ }
 197+ };
 198+
 199+ // Output components
 200+ for ( var component_id in this.components ) {
 201+ // Check for (component === false ) and skip
 202+ if( this.components[ component_id ] === false ){
 203+ continue;
 204+ }
 205+
 206+ // Special case with playhead and time ( to make sure they are to the left of everything else )
 207+ if ( component_id == 'playHead' || component_id == 'timeDisplay'){
 208+ continue;
 209+ }
 210+
 211+ // Skip "fullscreen" button for assets or where height is 0px ( audio )
 212+ if( component_id == 'fullscreen' && this.embedPlayer.isAudio() ){
 213+ continue;
 214+ }
 215+ addComponent( component_id );
 216+ }
 217+ // Add special case remaining components:
 218+ addComponent( 'timeDisplay' );
 219+ if( this.available_width > 30 ){
 220+ addComponent( 'playHead' );
 221+ }
 222+
 223+ },
 224+
 225+ /**
 226+ * Get a window size for the player while preserving aspect ratio:
 227+ *
 228+ * @param {object} windowSize
 229+ * object that set { 'width': {width}, 'height':{height} } of target window
 230+ * @return {object}
 231+ * css settings for fullscreen player
 232+ */
 233+ getAspectPlayerWindowCss: function( windowSize ) {
 234+ var embedPlayer = this.embedPlayer;
 235+ var _this = this;
 236+ // Setup target height width based on max window size
 237+ if( !windowSize ){
 238+ var windowSize = {
 239+ 'width' : $( window ).width(),
 240+ 'height' : $( window ).height()
 241+ };
 242+ }
 243+ // Set target width
 244+ var targetWidth = windowSize.width;
 245+ var targetHeight = targetWidth * ( embedPlayer.getHeight() / embedPlayer.getWidth() );
 246+
 247+ // Check if it exceeds the height constraint:
 248+ if( targetHeight > windowSize.height ){
 249+ targetHeight = windowSize.height;
 250+ targetWidth = targetHeight * ( embedPlayer.getWidth() / embedPlayer.getHeight() );
 251+ }
 252+ var offsetTop = ( targetHeight < windowSize.height )? ( windowSize.height- targetHeight ) / 2 : 0;
 253+ var offsetLeft = ( targetWidth < windowSize.width )? ( windowSize.width- targetWidth ) / 2 : 0;
 254+
 255+ // See if we need to leave space for control bar
 256+ if( !_this.checkOverlayControls() ){
 257+ targetHeight = targetHeight - this.height;
 258+ offsetTop = offsetTop - this.height;
 259+ if( offsetTop < 0 ) offsetTop = 0;
 260+ }
 261+ //mw.log("left: " + offsetLeft + " targetWidth: " + targetWidth + ' windowSize.width: ' + windowSize.width + ' :: ' + ( windowSize.width- targetWidth ) / 2 );
 262+ return {
 263+ 'position' : 'absolute',
 264+ 'height': targetHeight,
 265+ 'width' : targetWidth,
 266+ 'top' : offsetTop,
 267+ 'left': offsetLeft
 268+ };
 269+ },
 270+
 271+ /**
 272+ * Get the fullscreen play button css
 273+ */
 274+ getFullscreenPlayButtonCss: function( size ) {
 275+ var _this = this;
 276+ var pos = this.getAspectPlayerWindowCss( size );
 277+ if( !_this.checkOverlayControls() ){
 278+ pos.top = pos.top - this.height;
 279+ }
 280+ return {
 281+ 'left' : ( ( pos.width - this.getComponentWidth( 'playButtonLarge' ) ) / 2 ),
 282+ 'top' : ( ( pos.height - this.getComponentHeight( 'playButtonLarge' ) ) / 2 )
 283+ };
 284+ },
 285+
 286+ /**
 287+ * Toggles full screen by calling
 288+ * doFullScreenPlayer to enable fullscreen mode
 289+ * restoreWindowPlayer to restore window mode
 290+ */
 291+ toggleFullscreen: function( forceClose ) {
 292+ var _this = this;
 293+
 294+ // Check if iFrame mode ( fullscreen is handled by the iframe parent dom )
 295+ if( mw.getConfig('EmbedPlayer.IsIframePlayer' ) ){
 296+ if( this.fullscreenMode ){
 297+ $( _this.embedPlayer ).trigger( 'onCloseFullScreen' );
 298+ this.fullscreenMode = false;
 299+ } else {
 300+ $( _this.embedPlayer ).trigger( 'onOpenFullScreen' );
 301+ this.fullscreenMode = true;
 302+ }
 303+ return ;
 304+ }
 305+
 306+ // Do normal in-page fullscreen handling:
 307+ if( this.fullscreenMode ){
 308+ this.restoreWindowPlayer();
 309+ }else{
 310+ this.doFullScreenPlayer();
 311+ }
 312+ },
 313+
 314+ /**
 315+ * Do full-screen mode
 316+ */
 317+ doFullScreenPlayer: function( callback) {
 318+ mw.log(" controlBuilder :: toggle full-screen ");
 319+ // Setup pointer to control builder :
 320+ var _this = this;
 321+
 322+ // Setup local reference to embed player:
 323+ var embedPlayer = this.embedPlayer;
 324+
 325+ // Setup a local reference to the player interface:
 326+ var $interface = embedPlayer.$interface;
 327+
 328+
 329+ // Check fullscreen state ( if already true do nothing )
 330+ if( this.fullscreenMode == true ){
 331+ return ;
 332+ }
 333+ this.fullscreenMode = true;
 334+
 335+ //Remove any old mw-fullscreen-overlay
 336+ $( '.mw-fullscreen-overlay' ).remove();
 337+
 338+ // Special hack for mediawiki monobook skin search box
 339+ if( $( '#p-search,#p-logo' ).length ) {
 340+ $( '#p-search,#p-logo,#ca-nstab-project a' ).css('z-index', 1);
 341+ }
 342+
 343+ // Add the css fixed fullscreen black overlay as a sibling to the video element
 344+ $interface.after(
 345+ $( '<div />' )
 346+ .addClass( 'mw-fullscreen-overlay' )
 347+ // Set some arbitrary high z-index
 348+ .css('z-index', mw.getConfig( 'EmbedPlayer.FullScreenZIndex' ) )
 349+ .hide()
 350+ .fadeIn("slow")
 351+ );
 352+
 353+
 354+ // Change the interface to absolute positioned:
 355+ this.windowPositionStyle = $interface.css( 'position' );
 356+ this.windowZindex = $interface.css( 'z-index' );
 357+
 358+ // Get the base offset:
 359+ this.windowOffset = $interface.offset();
 360+ this.windowOffset.top = this.windowOffset.top - $(document).scrollTop();
 361+ this.windowOffset.left = this.windowOffset.left - $(document).scrollLeft();
 362+
 363+ // Change the z-index of the interface
 364+ $interface.css( {
 365+ 'position' : 'fixed',
 366+ 'z-index' : mw.getConfig( 'EmbedPlayer.FullScreenZIndex' ) + 1,
 367+ 'top' : this.windowOffset.top,
 368+ 'left' : this.windowOffset.left
 369+ } );
 370+
 371+ // If native persistent native player update z-index:
 372+ if( embedPlayer.isPersistentNativePlayer() ){
 373+ $( embedPlayer.getPlayerElement() ).css( {
 374+ 'z-index': mw.getConfig( 'EmbedPlayer.FullScreenZIndex' ) + 1,
 375+ 'position': 'absolute'
 376+ });
 377+ }
 378+
 379+ // Empty out the parent absolute index
 380+ _this.parentsAbsolute = [];
 381+
 382+ // Hide the body scroll bar
 383+ $('body').css( 'overflow', 'hidden' );
 384+
 385+
 386+ var topOffset = '0px';
 387+ var leftOffset = '0px';
 388+
 389+ // Check if we have an offsetParent
 390+ if( $interface.offsetParent().get(0).tagName.toLowerCase() != 'body' ) {
 391+ topOffset = -this.windowOffset.top + 'px';
 392+ leftOffset = -this.windowOffset.left + 'px';
 393+ }
 394+
 395+
 396+ // Set the player height width:
 397+ $( embedPlayer ).css( {
 398+ 'position' : 'relative'
 399+ } );
 400+
 401+ // Overflow hidden in fullscreen:
 402+ $interface.css( 'overlow', 'hidden' );
 403+
 404+ // Resize the player keeping aspect and with the widow scroll offset:
 405+ embedPlayer.resizePlayer({
 406+ 'top' : topOffset,
 407+ 'left' : leftOffset,
 408+ 'width' : $( window ).width(),
 409+ 'height' : $( window ).height()
 410+ }, true, function(){
 411+ // Trigger the enter fullscreen event
 412+ $( _this.embedPlayer ).trigger( 'onOpenFullScreen' );
 413+ });
 414+
 415+ // Remove absolute css of the interface parents
 416+ $interface.parents().each( function() {
 417+ //mw.log(' parent : ' + $( this ).attr('id' ) + ' class: ' + $( this ).attr('class') + ' pos: ' + $( this ).css( 'position' ) );
 418+ if( $( this ).css( 'position' ) == 'absolute' ) {
 419+ _this.parentsAbsolute.push( $( this ) );
 420+ $( this ).css( 'position', null );
 421+ mw.log(' should update position: ' + $( this ).css( 'position' ) );
 422+ }
 423+ });
 424+
 425+
 426+
 427+ // Bind mouse move in interface to hide control bar
 428+ _this.mouseMovedFlag = false;
 429+ $interface.mousemove( function(e){
 430+ _this.mouseMovedFlag = true;
 431+ });
 432+
 433+ // Check every 2 seconds reset flag status if controls are overlay
 434+ if( _this.checkOverlayControls() ){
 435+ function checkMovedMouse(){
 436+ if( _this.fullscreenMode ){
 437+ if( _this.mouseMovedFlag ){
 438+ _this.mouseMovedFlag = false;
 439+ _this.showControlBar();
 440+ // Once we move the mouse keep displayed for 4 seconds
 441+ setTimeout(checkMovedMouse, 4000);
 442+ } else {
 443+ // Check for mouse movement every 250ms
 444+ _this.hideControlBar();
 445+ setTimeout(checkMovedMouse, 250 );
 446+ }
 447+ }
 448+ };
 449+ checkMovedMouse();
 450+ }
 451+
 452+ // Bind Scroll position update
 453+
 454+ // Bind resize resize window to resize window
 455+ $( window ).resize( function() {
 456+ if( _this.fullscreenMode ){
 457+ embedPlayer.resizePlayer({
 458+ 'width' : $( window ).width(),
 459+ 'height' : $( window ).height()
 460+ });
 461+ }
 462+ });
 463+
 464+ // Bind escape to restore in page clip
 465+ $( window ).keyup( function(event) {
 466+ // Escape check
 467+ if( event.keyCode == 27 ){
 468+ _this.restoreWindowPlayer();
 469+ }
 470+ } );
 471+ },
 472+
 473+ /**
 474+ * Resize the player to a target size keeping aspect ratio
 475+ */
 476+ resizePlayer: function( size, animate, callback ){
 477+ var _this = this;
 478+ // Update interface container:
 479+ var interfaceCss = {
 480+ 'top' : ( size.top ) ? size.top : '0px',
 481+ 'left' : ( size.left ) ? size.left : '0px',
 482+ 'width' : size.width,
 483+ 'height' : size.height
 484+ };
 485+ // Set up local pointer to interface:
 486+ var embedPlayer = this.embedPlayer;
 487+ var $interface = embedPlayer.$interface;
 488+ if( animate ){
 489+ $interface.animate( interfaceCss );
 490+ // Update player size
 491+ $( embedPlayer ).animate( _this.getAspectPlayerWindowCss( size ), callback );
 492+ // Update play button pos
 493+ $interface.find('.play-btn-large').animate( _this.getFullscreenPlayButtonCss( size ) );
 494+
 495+ if( embedPlayer.isPersistentNativePlayer() ){
 496+ $( embedPlayer.getPlayerElement() ).animate( _this.getAspectPlayerWindowCss( size ) );
 497+ }
 498+ } else {
 499+ $interface.css( interfaceCss );
 500+ // Update player size
 501+ $( embedPlayer ).css( _this.getAspectPlayerWindowCss( size ) );
 502+ // Update play button pos
 503+ $interface.find('.play-btn-large').css( _this.getFullscreenPlayButtonCss( size ) );
 504+
 505+ if( embedPlayer.isPersistentNativePlayer() ){
 506+ $( embedPlayer.getPlayerElement() ).css( _this.getAspectPlayerWindowCss( size ) );
 507+ }
 508+
 509+ if( callback ){
 510+ callback();
 511+ }
 512+ }
 513+ },
 514+
 515+ /**
 516+ * Restore the window player
 517+ */
 518+ restoreWindowPlayer: function() {
 519+ var _this = this;
 520+ var embedPlayer = this.embedPlayer;
 521+
 522+ // Check fullscreen state
 523+ if( this.fullscreenMode == false ){
 524+ return ;
 525+ }
 526+ // Set fullscreen mode to false
 527+ this.fullscreenMode = false;
 528+
 529+ var $interface = embedPlayer.$interface;
 530+ var interfaceHeight = ( _this.checkOverlayControls() )
 531+ ? embedPlayer.getHeight()
 532+ : embedPlayer.getHeight() + _this.getHeight();
 533+
 534+ mw.log( 'restoreWindowPlayer:: h:' + interfaceHeight + ' w:' + embedPlayer.getWidth());
 535+ $('.mw-fullscreen-overlay').fadeOut( 'slow' );
 536+
 537+ mw.log( 'restore embedPlayer:: ' + embedPlayer.getWidth() + ' h: ' + embedPlayer.getHeight());
 538+ // Restore the player:
 539+ embedPlayer.resizePlayer( {
 540+ 'top' : _this.windowOffset.top + 'px',
 541+ 'left' : _this.windowOffset.left + 'px',
 542+ 'width' : embedPlayer.getWidth(),
 543+ 'height' : embedPlayer.getHeight()
 544+ }, true, function(){
 545+ // Restore non-absolute layout:
 546+ $interface.css({
 547+ 'position' : _this.windowPositionStyle,
 548+ 'z-index' : _this.windowZindex,
 549+ 'overlow' : 'visible',
 550+ 'top' : '0px',
 551+ 'left' : '0px'
 552+ });
 553+
 554+ // Restore absolute layout of parents:
 555+ $j.each( _this.parentsAbsolute, function( na, element ){
 556+ $( element ).css( 'position', 'absolute' );
 557+ } );
 558+ _this.parentsAbsolute = null;
 559+
 560+ // Restore the body scroll bar
 561+ $('body').css( 'overflow', 'auto' );
 562+
 563+ // If native player restore z-index:
 564+ if( embedPlayer.isPersistentNativePlayer() ){
 565+ $( embedPlayer.getPlayerElement() ).css( {
 566+ 'z-index': 'auto'
 567+ });
 568+ }
 569+ });
 570+
 571+ // Trigger the onCloseFullscreen event:
 572+ $( this.embedPlayer ).trigger( 'onCloseFullScreen' );
 573+ },
 574+
 575+ /**
 576+ * Get minimal width for interface overlay
 577+ */
 578+ getOverlayWidth: function( ) {
 579+ return ( this.embedPlayer.getPlayerWidth() < 300 )? 300 : this.embedPlayer.getPlayerWidth();
 580+ },
 581+
 582+ /**
 583+ * Get minimal height for interface overlay
 584+ */
 585+ getOverlayHeight: function( ) {
 586+ return ( this.embedPlayer.getPlayerHeight() < 200 )? 200 : this.embedPlayer.getPlayerHeight();
 587+ },
 588+
 589+ /**
 590+ * addControlBindings
 591+ * Adds control hooks once controls are in the DOM
 592+ */
 593+ addControlBindings: function() {
 594+ // Set up local pointer to the embedPlayer
 595+ var embedPlayer = this.embedPlayer;
 596+ var _this = this;
 597+ var $interface = embedPlayer.$interface;
 598+
 599+ // Remove any old interface bindings
 600+ $interface.unbind();
 601+
 602+ var bindFirstPlay = false;
 603+
 604+ // Bind into play.ctrl namespace ( so we can unbind without affecting other play bindings )
 605+ $(embedPlayer).unbind('play.ctrl').bind('play.ctrl', function() { //Only bind once played
 606+ if(bindFirstPlay) {
 607+ return ;
 608+ }
 609+ bindFirstPlay = true;
 610+
 611+ var dblClickTime = 300;
 612+ var lastClickTime = 0;
 613+ var didDblClick = false;
 614+ // Remove parent dbl click ( so we can handle play clicks )
 615+ $( embedPlayer ).unbind("dblclick").click( function() {
 616+ // Don't bind anything if native controls displayed:
 617+ if( embedPlayer.getPlayerElement().controls ) {
 618+ return ;
 619+ }
 620+ var clickTime = new Date().getTime();
 621+ if( clickTime -lastClickTime < dblClickTime ) {
 622+ embedPlayer.fullscreen();
 623+ didDblClick = true;
 624+ setTimeout( function(){ didDblClick = false; }, dblClickTime + 10 );
 625+ }
 626+ lastClickTime = clickTime;
 627+ setTimeout( function(){
 628+ // check if no click has since the time we called the setTimeout
 629+ if( !didDblClick ){
 630+ if( embedPlayer.paused ) {
 631+ embedPlayer.play();
 632+ } else {
 633+ embedPlayer.pause();
 634+ }
 635+ }
 636+ }, dblClickTime );
 637+
 638+ });
 639+ });
 640+
 641+ var bindSpaceUp = function(){
 642+ $(window).bind('keyup.mwPlayer', function(e) {
 643+ if(e.keyCode == 32) {
 644+ if(embedPlayer.paused) {
 645+ embedPlayer.play();
 646+ } else {
 647+ embedPlayer.pause();
 648+ }
 649+ return false;
 650+ }
 651+ });
 652+ };
 653+
 654+ var bindSpaceDown = function() {
 655+ $(window).unbind('keyup.mwPlayer');
 656+ };
 657+ // Add hide show bindings for control overlay (if overlay is enabled )
 658+ if( ! _this.checkOverlayControls() ) {
 659+ $interface
 660+ .show()
 661+ .hover( bindSpaceUp, bindSpaceDown );
 662+
 663+ } else { // hide show controls:
 664+
 665+ // Show controls on click:
 666+ $(embedPlayer).unbind('click.showCtrlBar').bind('click.showCtrlBar', function(){
 667+ _this.showControlBar();
 668+ });
 669+
 670+ // $interface.css({'background-color': 'red'});
 671+ // Bind a startTouch to show controls
 672+ $interface.bind( 'touchstart', function() {
 673+ _this.showControlBar();
 674+ // ( once the user touched the video "don't hide" )
 675+ } );
 676+
 677+ // Add a special absolute overlay for hover ( to keep menu displayed
 678+
 679+ $interface.hoverIntent({
 680+ 'sensitivity': 100,
 681+ 'timeout' : 1000,
 682+ 'over' : function(){
 683+ // Show controls with a set timeout ( avoid fade in fade out on short mouse over )
 684+ _this.showControlBar();
 685+ bindSpaceUp();
 686+ },
 687+ 'out' : function(){
 688+ _this.hideControlBar();
 689+ bindSpaceDown();
 690+ }
 691+ });
 692+
 693+ }
 694+
 695+ // Add recommend firefox if we have non-native playback:
 696+ if ( _this.checkNativeWarning( ) ) {
 697+ _this.doWarningBindinng( 'EmbedPlayer.ShowNativeWarning',
 698+ gM( 'mwe-embedplayer-for_best_experience', mw.getConfig('EmbedPlayer.FirefoxLink') )
 699+ );
 700+ }
 701+
 702+ // Do png fix for ie6
 703+ if ( $j.browser.msie && $j.browser.version <= 6 ) {
 704+ $( '#' + embedPlayer.id + ' .play-btn-large' ).pngFix();
 705+ }
 706+
 707+ this.doVolumeBinding();
 708+
 709+ // Check if we have any custom skin Bindings to run
 710+ if ( this.addSkinControlBindings && typeof( this.addSkinControlBindings ) == 'function' ){
 711+ this.addSkinControlBindings();
 712+ }
 713+
 714+ mw.log('trigger::addControlBindingsEvent');
 715+ $( embedPlayer ).trigger( 'addControlBindingsEvent');
 716+ },
 717+
 718+ /**
 719+ * Hide the control bar.
 720+ */
 721+ hideControlBar : function( forceClose ){
 722+ var animateDuration = 'fast';
 723+ var _this = this;
 724+
 725+ if( forceClose ){
 726+ _this.keepControlBarOnScreen = false;
 727+ }
 728+
 729+ // Do not hide control bar if overlay menu item is being displayed:
 730+ if( _this.keepControlBarOnScreen ) {
 731+ setTimeout( function(){
 732+ _this.hideControlBar();
 733+ }, 200 );
 734+ return ;
 735+ }
 736+
 737+
 738+ // Hide the control bar
 739+ this.embedPlayer.$interface.find( '.control-bar')
 740+ .fadeOut( animateDuration );
 741+ //mw.log('about to trigger hide control bar')
 742+ // Allow interface items to update:
 743+ $( this.embedPlayer ).trigger('onHideControlBar', {'bottom' : 15} );
 744+
 745+ },
 746+
 747+ /**
 748+ * Show the control bar
 749+ */
 750+ showControlBar: function( keepOnScreen ){
 751+ var animateDuration = 'fast';
 752+ if(! this.embedPlayer )
 753+ return ;
 754+ if( keepOnScreen ){
 755+ this.keepControlBarOnScreen = true;
 756+ }
 757+
 758+ if( this.embedPlayer.getPlayerElement && ! this.embedPlayer.isPersistentNativePlayer() ){
 759+ $( this.embedPlayer.getPlayerElement() ).css( 'z-index', '1' );
 760+ }
 761+ mw.log( 'PlayerControlBuilder:: ShowControlBar' );
 762+
 763+ // Show interface controls
 764+ this.embedPlayer.$interface.find( '.control-bar' )
 765+ .fadeIn( animateDuration );
 766+
 767+ // Trigger the screen overlay with layout info:
 768+ $( this.embedPlayer ).trigger( 'onShowControlBar', {'bottom' : this.getHeight() + 15 } );
 769+ },
 770+
 771+ /**
 772+ * Checks if the browser supports overlays and the controlsOverlay is
 773+ * set to true for the player or via config
 774+ */
 775+ checkOverlayControls: function(){
 776+
 777+ //if the player "supports" overlays:
 778+ if( ! this.embedPlayer.supports['overlays'] ){
 779+ return false;
 780+ }
 781+
 782+ // If disabled via the player
 783+ if( this.embedPlayer.overlaycontrols === false ){
 784+ return false;
 785+ }
 786+
 787+ // If the config is false
 788+ if( mw.getConfig( 'EmbedPlayer.OverlayControls' ) === false){
 789+ return false;
 790+ }
 791+ // iPad supports overlays but the touch events mean we want the controls displayed all the
 792+ // time for now.
 793+ if( mw.isIpad() ){
 794+ return false;
 795+ }
 796+
 797+
 798+ // Don't hide controls when content "height" is 0px ( audio tags )
 799+ if( this.embedPlayer.getPlayerHeight() === 0 &&
 800+ $(this.embedPlayer).css('height').indexOf('%') === -1 ){
 801+ return false;
 802+ }
 803+ if( this.embedPlayer.controls === false ){
 804+ return false;
 805+ }
 806+
 807+ // Past all tests OverlayControls is true:
 808+ return true;
 809+ },
 810+
 811+ /**
 812+ * Check if a warning should be issued to non-native playback systems
 813+ *
 814+ * dependent on mediaElement being setup
 815+ */
 816+ checkNativeWarning: function( ) {
 817+ if( mw.getConfig( 'EmbedPlayer.ShowNativeWarning' ) === false ){
 818+ return false;
 819+ }
 820+
 821+ // If the resolution is too small don't display the warning
 822+ if( this.embedPlayer.getPlayerHeight() < 199 ){
 823+ return false;
 824+ }
 825+ // See if we have we have ogg support
 826+ var supportingPlayers = mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'video/ogg' );
 827+ for ( var i = 0; i < supportingPlayers.length; i++ ) {
 828+
 829+ if ( supportingPlayers[i].id == 'oggNative'
 830+ &&
 831+ // xxx google chrome has broken oggNative playback:
 832+ // http://code.google.com/p/chromium/issues/detail?id=56180
 833+ ! /chrome/.test(navigator.userAgent.toLowerCase() )
 834+ ){
 835+ return false;
 836+ }
 837+ }
 838+
 839+ // Chrome's webM support is oky though:
 840+ if( /chrome/.test(navigator.userAgent.toLowerCase() ) &&
 841+ mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'video/webm' ).length ){
 842+ return false;
 843+ }
 844+
 845+
 846+ // Check for h264 and or flash/flv source and playback support and don't show warning
 847+ if(
 848+ ( mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'video/h264' ).length
 849+ && this.embedPlayer.mediaElement.getSources( 'video/h264' ).length )
 850+ ||
 851+ ( mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( 'video/x-flv' ).length
 852+ && this.embedPlayer.mediaElement.getSources( 'video/x-flv' ).length )
 853+ ){
 854+ // No firefox link if a h.264 or flash/flv stream is present
 855+ return false;
 856+ }
 857+
 858+ // Should issue the native warning
 859+ return true;
 860+ },
 861+
 862+ /**
 863+ * Does a native warning check binding to the player on mouse over.
 864+ * @param {string} preferenceId The preference Id
 865+ * @param {object} warningMsg The jQuery object warning message to be displayed.
 866+ *
 867+ */
 868+ doWarningBindinng: function( preferenceId, warningMsg ) {
 869+ mw.log( 'controlBuilder: doWarningBindinng: ' + preferenceId + ' wm: ' + warningMsg);
 870+ // Set up local pointer to the embedPlayer
 871+ var embedPlayer = this.embedPlayer;
 872+ var _this = this;
 873+
 874+ // make sure the player is large enough
 875+ if( embedPlayer.getWidth() < 200 ){
 876+ return false;
 877+ }
 878+
 879+ $( embedPlayer ).hoverIntent({
 880+ 'timeout': 2000,
 881+ 'over': function() {
 882+ // don't do the overlay if already playing
 883+ if( embedPlayer.isPlaying() ){
 884+ return ;
 885+ }
 886+ if ( $( '#warningOverlay_' + embedPlayer.id ).length == 0 ) {
 887+ $( this ).append(
 888+ $('<div />')
 889+ .attr( {
 890+ 'id': "warningOverlay_" + embedPlayer.id
 891+ } )
 892+ .addClass( 'ui-state-highlight ui-corner-all' )
 893+ .css({
 894+ 'position' : 'absolute',
 895+ 'display' : 'none',
 896+ 'background' : '#FFF',
 897+ 'color' : '#111',
 898+ 'top' : '10px',
 899+ 'left' : '10px',
 900+ 'right' : '10px',
 901+ 'padding' : '4px'
 902+ })
 903+ .html( warningMsg )
 904+ );
 905+
 906+ $targetWarning = $( '#warningOverlay_' + embedPlayer.id );
 907+
 908+ $targetWarning.append(
 909+ $('<br />')
 910+ );
 911+
 912+ $targetWarning.append(
 913+ $( '<input />' )
 914+ .attr({
 915+ 'id' : 'ffwarn_' + embedPlayer.id,
 916+ 'type' : "checkbox",
 917+ 'name' : 'ffwarn_' + embedPlayer.id
 918+ })
 919+ .click( function() {
 920+ mw.log("WarningBindinng:: set " + preferenceId + ' to hidewarning ' );
 921+ // Set up a cookie for 30 days:
 922+ $j.cookie( preferenceId, 'hidewarning', { expires: 30 } );
 923+ // Set the current instance
 924+ mw.setConfig( preferenceId, false );
 925+ $( '#warningOverlay_' + embedPlayer.id ).fadeOut( 'slow' );
 926+ // set the local prefrence to false
 927+ _this.addWarningFlag = false;
 928+ } )
 929+ );
 930+ $targetWarning.append(
 931+ $('<label />')
 932+ .text( gM( 'mwe-embedplayer-do_not_warn_again' ) )
 933+ .attr( 'for', 'ffwarn_' + embedPlayer.id )
 934+ );
 935+ }
 936+ // Check the global config before showing the warning
 937+ if ( mw.getConfig( preferenceId ) === true && $j.cookie( preferenceId ) != 'hidewarning' ){
 938+ mw.log("WarningBindinng:: show warning " + mw.getConfig( preferenceId ) + ' cookie: '+ $j.cookie( preferenceId ) + 'typeof:' + typeof $j.cookie( preferenceId ));
 939+ $( '#warningOverlay_' + embedPlayer.id ).fadeIn( 'slow' );
 940+ };
 941+ },
 942+ 'out': function() {
 943+ $( '#warningOverlay_' + embedPlayer.id ).fadeOut( 'slow' );
 944+ }
 945+ });
 946+ },
 947+
 948+ /**
 949+ * Binds the volume controls
 950+ */
 951+ doVolumeBinding: function( ) {
 952+ var embedPlayer = this.embedPlayer;
 953+ var _this = this;
 954+ $volumeSlider = embedPlayer.$interface.find( '.volume-slider' );
 955+ if( $volumeSlider.length == 0 ){
 956+ return false;
 957+ }
 958+ embedPlayer.$interface.find( '.volume_control' ).unbind().buttonHover().click( function() {
 959+ mw.log( 'Volume control toggle' );
 960+ embedPlayer.toggleMute();
 961+ } );
 962+
 963+ // Add vertical volume display hover
 964+ if ( this.volume_layout == 'vertical' ) {
 965+ // Default volume binding:
 966+ var hoverOverDelay = false;
 967+ var $targetvol = embedPlayer.$interface.find( '.vol_container' ).hide();
 968+ embedPlayer.$interface.find( '.volume_control' ).hover(
 969+ function() {
 970+ $targetvol.addClass( 'vol_container_top' );
 971+ // Set to "below" if playing and embedType != native
 972+ if ( embedPlayer && embedPlayer.isPlaying && embedPlayer.isPlaying() && !embedPlayer.supports['overlays'] ) {
 973+ $targetvol.removeClass( 'vol_container_top' ).addClass( 'vol_container_below' );
 974+ }
 975+ $targetvol.fadeIn( 'fast' );
 976+ hoverOverDelay = true;
 977+ },
 978+ function() {
 979+ hoverOverDelay = false;
 980+ setTimeout( function() {
 981+ if ( !hoverOverDelay ) {
 982+ $targetvol.fadeOut( 'fast' );
 983+ }
 984+ }, 500 );
 985+ }
 986+ );
 987+ }
 988+
 989+ // Setup volume slider:
 990+ var sliderConf = {
 991+ range: "min",
 992+ value: 80,
 993+ min: 0,
 994+ max: 100,
 995+ slide: function( event, ui ) {
 996+ var percent = ui.value / 100;
 997+ mw.log('PlayerControlBuilder::slide:update volume:' + percent);
 998+ embedPlayer.setVolume( percent );
 999+ },
 1000+ change: function( event, ui ) {
 1001+ var percent = ui.value / 100;
 1002+ if ( percent == 0 ) {
 1003+ embedPlayer.$interface.find( '.volume_control span' ).removeClass( 'ui-icon-volume-on' ).addClass( 'ui-icon-volume-off' );
 1004+ } else {
 1005+ embedPlayer.$interface.find( '.volume_control span' ).removeClass( 'ui-icon-volume-off' ).addClass( 'ui-icon-volume-on' );
 1006+ }
 1007+ mw.log('PlayerControlBuilder::change:update volume:' + percent);
 1008+ embedPlayer.setVolume( percent );
 1009+ }
 1010+ };
 1011+
 1012+ if ( this.volume_layout == 'vertical' ) {
 1013+ sliderConf[ 'orientation' ] = "vertical";
 1014+ }
 1015+
 1016+ $volumeSlider.slider( sliderConf );
 1017+ },
 1018+
 1019+ /**
 1020+ * Get the options menu ul with li menu items
 1021+ */
 1022+ getOptionsMenu: function( ) {
 1023+ $optionsMenu = $( '<ul />' );
 1024+ for( var i in this.optionMenuItems ){
 1025+
 1026+ // Make sure its supported in the current controlBuilder config:
 1027+ if( ! this.supportedMenuItems[ i ] ) {
 1028+ continue;
 1029+ }
 1030+
 1031+ $optionsMenu.append(
 1032+ this.optionMenuItems[i]( this )
 1033+ );
 1034+ }
 1035+ return $optionsMenu;
 1036+ },
 1037+
 1038+ /**
 1039+ * Allow the controlBuilder to do interface actions onDone
 1040+ */
 1041+ onClipDone: function(){
 1042+ // Related videos could be shown here
 1043+ },
 1044+
 1045+ /**
 1046+ * The ctrl builder updates the interface on seeking
 1047+ */
 1048+ onSeek: function(){
 1049+ //mw.log( "controlBuilder:: onSeek" );
 1050+ // Update the interface:
 1051+ this.setStatus( gM( 'mwe-embedplayer-seeking' ) );
 1052+ },
 1053+
 1054+ /**
 1055+ * Updates the player status that displays short text msgs and the play clock
 1056+ * @param {String} value Status string value to update
 1057+ */
 1058+ setStatus: function( value ) {
 1059+ // update status:
 1060+ this.embedPlayer.$interface.find( '.time-disp' ).text( value );
 1061+ },
 1062+
 1063+ /**
 1064+ * Option menu items
 1065+ *
 1066+ * @return
 1067+ * 'li' a li line item with click action for that menu item
 1068+ */
 1069+ optionMenuItems: {
 1070+ // Player select menu item
 1071+ 'playerSelect': function( ctrlObj ){
 1072+ return $j.getLineItem(
 1073+ gM( 'mwe-embedplayer-choose_player' ),
 1074+ 'gear',
 1075+ function( ) {
 1076+ ctrlObj.displayMenuOverlay(
 1077+ ctrlObj.getPlayerSelect()
 1078+ );
 1079+ }
 1080+ );
 1081+ },
 1082+
 1083+ // Download the file menu
 1084+ 'download': function( ctrlObj ) {
 1085+ return $j.getLineItem(
 1086+ gM( 'mwe-embedplayer-download' ),
 1087+ 'disk',
 1088+ function( ) {
 1089+ ctrlObj.displayMenuOverlay( gM('mwe-loading_txt' ) );
 1090+ // Call show download with the target to be populated
 1091+ ctrlObj.showDownload(
 1092+ ctrlObj.embedPlayer.$interface.find( '.overlay-content' )
 1093+ );
 1094+ $( ctrlObj.embedPlayer ).trigger( 'showDownloadEvent' );
 1095+ }
 1096+ );
 1097+ },
 1098+
 1099+ // Share the video menu
 1100+ 'share': function( ctrlObj ) {
 1101+ return $j.getLineItem(
 1102+ gM( 'mwe-embedplayer-share' ),
 1103+ 'mail-closed',
 1104+ function( ) {
 1105+ ctrlObj.displayMenuOverlay(
 1106+ ctrlObj.getShare()
 1107+ );
 1108+ $( ctrlObj.embedPlayer ).trigger( 'showShareEvent' );
 1109+ }
 1110+ );
 1111+ },
 1112+
 1113+ 'aboutPlayerLibrary' : function( ctrlObj ){
 1114+ return $j.getLineItem(
 1115+ gM( 'mwe-embedplayer-about-library' ),
 1116+ 'info',
 1117+ function( ) {
 1118+ ctrlObj.displayMenuOverlay(
 1119+ ctrlObj.aboutPlayerLibrary()
 1120+ );
 1121+ $( ctrlObj.embedPlayer ).trigger( 'aboutPlayerLibrary' );
 1122+ }
 1123+ );
 1124+ }
 1125+ },
 1126+
 1127+ /**
 1128+ * Close a menu overlay
 1129+ */
 1130+ closeMenuOverlay: function(){
 1131+ var _this = this;
 1132+ var embedPlayer = this.embedPlayer;
 1133+ var $overlay = embedPlayer.$interface.find( '.overlay-win,.ui-widget-overlay,.ui-widget-shadow' );
 1134+
 1135+ this.keepControlBarOnScreen = false;
 1136+ //mw.log(' closeMenuOverlay: ' + this.keepControlBarOnScreen);
 1137+
 1138+ $overlay.fadeOut( "slow", function() {
 1139+ $overlay.remove();
 1140+ } );
 1141+ // Show the big play button:
 1142+ embedPlayer.$interface.find( '.play-btn-large' ).fadeIn( 'slow' );
 1143+
 1144+
 1145+ $(embedPlayer).trigger( 'closeMenuOverlay' );
 1146+
 1147+ return false; // onclick action return false
 1148+ },
 1149+
 1150+ /**
 1151+ * Generic function to display custom HTML overlay
 1152+ * on video.
 1153+ *
 1154+ * @param {String} overlayContent content to be displayed
 1155+ */
 1156+ displayMenuOverlay: function( overlayContent ) {
 1157+ var _this = this;
 1158+ var embedPlayer = this.embedPlayer;
 1159+ mw.log( 'displayMenuOverlay::' );
 1160+ // set the overlay display flag to true:
 1161+ this.keepControlBarOnScreen = true;
 1162+ mw.log(" set keepControlBarOnScreen:: " + this.keepControlBarOnScreen);
 1163+
 1164+ if ( !this.supportedComponets[ 'overlays' ] ) {
 1165+ embedPlayer.stop();
 1166+ }
 1167+
 1168+
 1169+ // Hide the big play button:
 1170+ embedPlayer.$interface.find( '.play-btn-large' ).hide();
 1171+
 1172+ // Check if overlay window is already present:
 1173+ if ( embedPlayer.$interface.find( '.overlay-win' ).length != 0 ) {
 1174+ //Update the content
 1175+ embedPlayer.$interface.find( '.overlay-content' ).html(
 1176+ overlayContent
 1177+ );
 1178+ return ;
 1179+ }
 1180+
 1181+ // Add an overlay
 1182+ embedPlayer.$interface.append(
 1183+ $('<div />')
 1184+ .addClass( 'ui-widget-overlay' )
 1185+ .css( {
 1186+ 'height' : '100%',
 1187+ 'width' : '100%',
 1188+ 'z-index' : 2
 1189+ } )
 1190+ );
 1191+
 1192+ // Setup the close button
 1193+ $closeButton = $('<span />')
 1194+ .addClass( 'ui-icon ui-icon-closethick' )
 1195+ .css({
 1196+ 'position': 'absolute',
 1197+ 'cursor' : 'pointer',
 1198+ 'top' : '2px',
 1199+ 'right' : '2px'
 1200+ })
 1201+ .click( function() {
 1202+ _this.closeMenuOverlay();
 1203+ } );
 1204+
 1205+ var overlayMenuCss = {
 1206+ 'height' : 200,
 1207+ 'width' : 250,
 1208+ 'position' : 'absolute',
 1209+ 'left' : '10px',
 1210+ 'top': '15px',
 1211+ 'overflow' : 'auto',
 1212+ 'padding' : '4px',
 1213+ 'z-index' : 3
 1214+ };
 1215+ $overlayMenu = $('<div />')
 1216+ .addClass( 'overlay-win ui-state-default ui-widget-header ui-corner-all' )
 1217+ .css( overlayMenuCss )
 1218+ .append(
 1219+ $closeButton,
 1220+ $('<div />')
 1221+ .addClass( 'overlay-content' )
 1222+ .append( overlayContent )
 1223+ );
 1224+
 1225+ // Clone the overlay menu css:
 1226+ var shadowCss = jQuery.extend( true, {}, overlayMenuCss );
 1227+ shadowCss['height' ] = 210;
 1228+ shadowCss['width' ] = 260;
 1229+ shadowCss[ 'z-index' ] = 2;
 1230+ $overlayShadow = $( '<div />' )
 1231+ .addClass('ui-widget-shadow ui-corner-all')
 1232+ .css( shadowCss );
 1233+
 1234+ // Append the overlay menu to the player interface
 1235+ embedPlayer.$interface.prepend(
 1236+ $overlayMenu,
 1237+ $overlayShadow
 1238+ )
 1239+ .find( '.overlay-win' )
 1240+ .fadeIn( "slow" );
 1241+
 1242+ // trigger menu overlay display
 1243+ $(embedPlayer).trigger( 'displayMenuOverlay' );
 1244+
 1245+ return false; // onclick action return false
 1246+ },
 1247+ aboutPlayerLibrary: function(){
 1248+ return $( '<div />' )
 1249+ .append(
 1250+ $( '<h3 />' )
 1251+ .text(
 1252+ gM('mwe-embedplayer-about-library')
 1253+ )
 1254+ ,
 1255+ $( '<span />')
 1256+ .append(
 1257+ gM('mwe-embedplayer-about-library-desc',
 1258+ $('<a />').attr({
 1259+ 'href' : mw.getConfig( 'EmbedPlayer.LibraryPage' ),
 1260+ 'target' : '_new'
 1261+ })
 1262+ )
 1263+ )
 1264+ );
 1265+ },
 1266+ /**
 1267+ * Get the "share" interface
 1268+ *
 1269+ * TODO share should be enabled via <embed> tag usage to be compatible
 1270+ * with sites social networking sites that allow <embed> tags but not js
 1271+ *
 1272+ * @param {Object} $target Target jQuery object to set share html
 1273+ */
 1274+ getShare: function( ) {
 1275+ var embedPlayer = this.embedPlayer;
 1276+ var embed_code = embedPlayer.getEmbeddingHTML();
 1277+ var _this = this;
 1278+
 1279+ var $shareInterface = $('<div />');
 1280+
 1281+ $shareList = $( '<ul />' );
 1282+
 1283+ $shareList
 1284+ .append(
 1285+ $('<li />')
 1286+ .append(
 1287+ $('<a />')
 1288+ .attr('href', '#')
 1289+ .addClass( 'active' )
 1290+ .text(
 1291+ gM( 'mwe-embedplayer-embed_site_or_blog' )
 1292+ )
 1293+ )
 1294+ );
 1295+
 1296+ $shareInterface.append(
 1297+ $( '<h2 />' )
 1298+ .text( gM( 'mwe-embedplayer-share_this_video' ) )
 1299+ .append(
 1300+ $shareList
 1301+ )
 1302+ );
 1303+
 1304+ $shareInterface.append(
 1305+
 1306+ $( '<textarea />' )
 1307+ .attr( 'rows', 4 )
 1308+ .html( embed_code )
 1309+ .click( function() {
 1310+ $( this ).select();
 1311+ }),
 1312+
 1313+ $('<br />'),
 1314+ $('<br />'),
 1315+
 1316+ $('<button />')
 1317+ .addClass( 'ui-state-default ui-corner-all copycode' )
 1318+ .text( gM( 'mwe-embedplayer-copy-code' ) )
 1319+ .click(function() {
 1320+ $shareInterface.find( 'textarea' ).focus().select();
 1321+ // Copy the text if supported:
 1322+ if ( document.selection ) {
 1323+ CopiedTxt = document.selection.createRange();
 1324+ CopiedTxt.execCommand( "Copy" );
 1325+ }
 1326+ } )
 1327+
 1328+ );
 1329+ return $shareInterface;
 1330+ },
 1331+
 1332+ /**
 1333+ * Shows the Player Select interface
 1334+ *
 1335+ * @param {Object} $target jQuery target for output
 1336+ */
 1337+ getPlayerSelect: function( ) {
 1338+ mw.log('ControlBuilder::getPlayerSelect: source:' +
 1339+ this.embedPlayer.mediaElement.selectedSource.getSrc() +
 1340+ ' player: ' + this.embedPlayer.selectedPlayer.id );
 1341+
 1342+ var embedPlayer = this.embedPlayer;
 1343+
 1344+ var _this = this;
 1345+
 1346+ $playerSelect = $('<div />')
 1347+ .append(
 1348+ $( '<h2 />' )
 1349+ .text( gM( 'mwe-embedplayer-choose_player' ) )
 1350+ );
 1351+
 1352+ $j.each( embedPlayer.mediaElement.getPlayableSources(), function( sourceId, source ) {
 1353+
 1354+ var isPlayable = (typeof mw.EmbedTypes.getMediaPlayers().defaultPlayer( source.getMIMEType() ) == 'object' );
 1355+ var is_selected = ( source.getSrc() == embedPlayer.mediaElement.selectedSource.getSrc() );
 1356+
 1357+ $playerSelect.append(
 1358+ $( '<h3 />' )
 1359+ .text( source.getTitle() )
 1360+ );
 1361+
 1362+ if ( isPlayable ) {
 1363+ $playerList = $('<ul />');
 1364+ // output the player select code:
 1365+
 1366+ var supportingPlayers = mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( source.getMIMEType() );
 1367+
 1368+ for ( var i = 0; i < supportingPlayers.length ; i++ ) {
 1369+
 1370+ // Add link to select the player if not already selected )
 1371+ if( embedPlayer.selectedPlayer.id == supportingPlayers[i].id && is_selected ) {
 1372+ // Active player ( no link )
 1373+ $playerLine = $( '<span />' )
 1374+ .append(
 1375+ $('<a />')
 1376+ .attr({
 1377+ 'href' : '#'
 1378+ })
 1379+ .addClass( 'active')
 1380+ .text(
 1381+ supportingPlayers[i].getName()
 1382+ )
 1383+ );
 1384+ //.addClass( 'ui-state-highlight ui-corner-all' ); removed by ran
 1385+ } else {
 1386+ // Non active player add link to select:
 1387+ $playerLine = $( '<a />')
 1388+ .attr({
 1389+ 'href' : '#',
 1390+ 'rel' : 'sel_source',
 1391+ 'id' : 'sc_' + sourceId + '_' + supportingPlayers[i].id
 1392+ })
 1393+ .addClass( 'ui-corner-all')
 1394+ .text( supportingPlayers[i].getName() )
 1395+ .click( function() {
 1396+ var iparts = $( this ).attr( 'id' ).replace(/sc_/ , '' ).split( '_' );
 1397+ var sourceId = iparts[0];
 1398+ var player_id = iparts[1];
 1399+ mw.log( 'source id: ' + sourceId + ' player id: ' + player_id );
 1400+
 1401+ embedPlayer.controlBuilder.closeMenuOverlay();
 1402+
 1403+ // Close fullscreen if we are in fullscreen mode
 1404+ if( _this.fullscreenMode ){
 1405+ _this.restoreWindowPlayer();
 1406+ }
 1407+
 1408+ embedPlayer.mediaElement.selectSource( sourceId );
 1409+ var playableSources = embedPlayer.mediaElement.getPlayableSources();
 1410+
 1411+ mw.EmbedTypes.getMediaPlayers().setPlayerPreference(
 1412+ player_id,
 1413+ playableSources[ sourceId ].getMIMEType()
 1414+ );
 1415+
 1416+ // Issue a stop
 1417+ embedPlayer.stop();
 1418+
 1419+ // Don't follow the # link:
 1420+ return false;
 1421+ } )
 1422+ .hover(
 1423+ function(){
 1424+ $( this ).addClass('active');
 1425+ },
 1426+ function(){
 1427+ $( this ).removeClass('active');
 1428+ }
 1429+ );
 1430+ }
 1431+
 1432+ // Add the player line to the player list:
 1433+ $playerList.append(
 1434+ $( '<li />' ).append(
 1435+ $playerLine
 1436+ )
 1437+ );
 1438+ }
 1439+
 1440+ // Append the player list:
 1441+ $playerSelect.append( $playerList );
 1442+
 1443+ } else {
 1444+ // No player available:
 1445+ $playerSelect.append( gM( 'mwe-embedplayer-no-player', source.getTitle() ) );
 1446+ }
 1447+ } );
 1448+
 1449+ // Return the player select elements
 1450+ return $playerSelect;
 1451+ },
 1452+
 1453+ /**
 1454+ * Loads sources and calls showDownloadWithSources
 1455+ * @param {Object} $target jQuery target to output to
 1456+ */
 1457+ showDownload: function( $target ) {
 1458+ var _this = this;
 1459+ var embedPlayer = this.embedPlayer;
 1460+
 1461+ // Load additional text sources via apiTitleKey:
 1462+ // TODO we should move this to timedText bindings
 1463+ if( embedPlayer.apiTitleKey ) {
 1464+ // Load text interface ( if not already loaded )
 1465+ mw.load( 'TimedText', function() {
 1466+ embedPlayer.timedText.setupTextSources(function(){
 1467+ _this.showDownloadWithSources( $target );
 1468+ });
 1469+ });
 1470+ } else {
 1471+ _this.showDownloadWithSources( $target );
 1472+ }
 1473+ },
 1474+
 1475+ /**
 1476+ * Shows the download interface with sources loaded
 1477+ * @param {Object} $target jQuery target to output to
 1478+ */
 1479+ showDownloadWithSources : function( $target ) {
 1480+ var _this = this;
 1481+ mw.log( 'showDownloadWithSources::' + $target.length );
 1482+ var embedPlayer = this.embedPlayer;
 1483+ // Empty the target:
 1484+ $target.empty();
 1485+
 1486+ var $mediaList = $( '<ul />' );
 1487+ var $textList = $( '<ul />' );
 1488+ $j.each( embedPlayer.mediaElement.getSources(), function( index, source ) {
 1489+ if( source.getSrc() ) {
 1490+ mw.log("showDownloadWithSources:: Add src: " + source.getTitle() );
 1491+ var $dl_line = $( '<li />').append(
 1492+ $('<a />')
 1493+ .attr( 'href', source.getSrc() )
 1494+ .text( source.getTitle() )
 1495+ );
 1496+ // Add link to correct "bucket"
 1497+
 1498+ //Add link to time segment:
 1499+ if ( source.getSrc().indexOf( '?t=' ) !== -1 ) {
 1500+ $target.append( $dl_line );
 1501+ } else if ( this.getMIMEType().indexOf('text') === 0 ) {
 1502+ // Add link to text list
 1503+ $textList.append( $dl_line );
 1504+ } else {
 1505+ // Add link to media list
 1506+ $mediaList.append( $dl_line );
 1507+ }
 1508+
 1509+ }
 1510+ } );
 1511+ if( $mediaList.find('li').length != 0 ) {
 1512+ $target.append(
 1513+ $('<h2 />')
 1514+ .text( gM( 'mwe-embedplayer-download_full' ) ),
 1515+ $mediaList
 1516+ );
 1517+ }
 1518+
 1519+ if( $textList.find('li').length != 0 ) {
 1520+ $target.append(
 1521+ $('<h2 />')
 1522+ .html( gM( 'mwe-embedplayer-download_text' ) ),
 1523+ $textList
 1524+ );
 1525+ }
 1526+ },
 1527+ getSwichSourceMenu: function(){
 1528+ // for each source with "native playback"
 1529+ $sourceMenu = $j('<ul />');
 1530+ $j.each( this.embedPlayer.mediaElement.getPlayableSources(), function( sourceId, source ) {
 1531+ //var isSelected = ( source.getSrc() == this.embedPlayer.mediaElement.selectedSource.getSrc() );
 1532+ // Output the player select code:
 1533+ var supportingPlayers = mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( source.getMIMEType() );
 1534+ for ( var i = 0; i < supportingPlayers.length ; i++ ) {
 1535+ if( supportingPlayers[i].library == 'Native' ){
 1536+ $sourceMenu.append(
 1537+ $.getLineItem( source.shorttitle, 'video', function(){
 1538+ mw.log("Selected source");
 1539+ })
 1540+ )
 1541+ }
 1542+ }
 1543+ });
 1544+ return $sourceMenu;
 1545+ },
 1546+
 1547+ /**
 1548+ * Get component
 1549+ *
 1550+ * @param {String} component_id Component key to grab html output
 1551+ */
 1552+ getComponent: function( component_id ) {
 1553+ if ( this.components[ component_id ] ) {
 1554+ return this.components[ component_id ].o( this );
 1555+ } else {
 1556+ return false;
 1557+ }
 1558+ },
 1559+
 1560+ /**
 1561+ * Get a component height
 1562+ *
 1563+ * @param {String} component_id Component key to grab height
 1564+ * @return height or false if not set
 1565+ */
 1566+ getComponentHeight: function( component_id ) {
 1567+ if ( this.components[ component_id ]
 1568+ && this.components[ component_id ].h )
 1569+ {
 1570+ return this.components[ component_id ].h;
 1571+ }
 1572+ return 0;
 1573+ },
 1574+
 1575+ /**
 1576+ * Get a component width
 1577+ * @param {String} component_id Component key to grab width
 1578+ * @return width or false if not set
 1579+ */
 1580+ getComponentWidth: function( component_id ){
 1581+ if ( this.components[ component_id ]
 1582+ && this.components[ component_id ].w )
 1583+ {
 1584+ return this.components[ component_id ].w;
 1585+ }
 1586+ return 0;
 1587+ },
 1588+
 1589+ /**
 1590+ * Components Object
 1591+ * Take in the embedPlayer and return some html for the given component.
 1592+ *
 1593+ * components can be overwritten by skin javascript
 1594+ *
 1595+ * Component JSON structure is as follows:
 1596+ * 'o' Function to return a binded jQuery object ( accepts the ctrlObject as a parameter )
 1597+ * 'w' The width of the component
 1598+ * 'h' The height of the component ( if height is undefined the height of the control bar is used )
 1599+ */
 1600+ components: {
 1601+ /**
 1602+ * The large play button in center of the player
 1603+ */
 1604+ 'playButtonLarge': {
 1605+ 'w' : 70,
 1606+ 'h' : 53,
 1607+ 'o' : function( ctrlObj ) {
 1608+ return $( '<div/>' )
 1609+ .attr( {
 1610+ 'title' : gM( 'mwe-embedplayer-play_clip' ),
 1611+ 'class' : "play-btn-large"
 1612+ } )
 1613+ // Get dynamic position for big play button
 1614+ .css( {
 1615+ 'left' : ( ( ctrlObj.embedPlayer.getPlayerWidth() - this.w ) / 2 ),
 1616+ 'top' : ( ( ctrlObj.embedPlayer.getPlayerHeight() - this.h ) / 2 )
 1617+ } )
 1618+ // Add play hook:
 1619+ .click( function() {
 1620+ ctrlObj.embedPlayer.play();
 1621+ return false; // Event Stop Propagation
 1622+ } );
 1623+ }
 1624+ },
 1625+
 1626+ /**
 1627+ * The Attribution button ( by default this is kaltura-icon
 1628+ */
 1629+ 'attributionButton' : {
 1630+ 'w' : 24,
 1631+ 'o' : function( ctrlObj ){
 1632+ var buttonConfig = mw.getConfig( 'EmbedPlayer.AttributionButton');
 1633+ // Check for source ( by configuration convention this is a 16x16 image
 1634+ if( buttonConfig.iconurl ){
 1635+ var $icon = $('<img />')
 1636+ .css({'width': '16px', 'height': '16px', 'margin': '-8px 5px 0px 0px'})
 1637+ .attr('src', buttonConfig.iconurl )
 1638+ } else {
 1639+ var $icon = $('<span />')
 1640+ .addClass( 'ui-icon' );
 1641+ if( buttonConfig['class'] ){
 1642+ $icon.addClass( buttonConfig['class'] );
 1643+ }
 1644+ }
 1645+
 1646+ return $('<a />')
 1647+ .attr({
 1648+ 'href': buttonConfig.href,
 1649+ 'title' : buttonConfig.title,
 1650+ 'target' : '_new'
 1651+ })
 1652+ .addClass( 'attributionButton' )
 1653+ .append(
 1654+ $( '<div />' )
 1655+ .addClass( 'rButton' )
 1656+ .css({
 1657+ 'top' : '9px',
 1658+ 'left' : '2px'
 1659+ })
 1660+ .append(
 1661+ $icon
 1662+ )
 1663+ );
 1664+ }
 1665+ },
 1666+
 1667+ /**
 1668+ * The options button, invokes display of the options menu
 1669+ */
 1670+ 'options': {
 1671+ 'w': 50,
 1672+ 'o': function( ctrlObj ) {
 1673+ return $( '<div />' )
 1674+ .attr( 'title', gM( 'mwe-embedplayer-player_options' ) )
 1675+ .addClass( 'ui-state-default ui-corner-all ui-icon_link rButton options-btn' )
 1676+ .append(
 1677+ $('<span />')
 1678+ .addClass( 'ui-icon ui-icon-wrench' )
 1679+ )
 1680+ .buttonHover()
 1681+ // Options binding:
 1682+ .menu( {
 1683+ 'content' : ctrlObj.getOptionsMenu(),
 1684+ 'zindex' : mw.getConfig( 'EmbedPlayer.FullScreenZIndex' ) + 1,
 1685+ 'positionOpts': {
 1686+ 'directionV' : 'up',
 1687+ 'offsetY' : 30,
 1688+ 'directionH' : 'left',
 1689+ 'offsetX' : -28
 1690+ }
 1691+ } );
 1692+ }
 1693+ },
 1694+
 1695+ /**
 1696+ * The fullscreen button for displaying the video fullscreen
 1697+ */
 1698+ 'fullscreen': {
 1699+ 'w': 24,
 1700+ 'o': function( ctrlObj ) {
 1701+
 1702+ // Setup "dobuleclick" fullscreen binding to embedPlayer
 1703+ $( ctrlObj.embedPlayer ).unbind("dblclick").bind("dblclick", function(){
 1704+ ctrlObj.embedPlayer.fullscreen();
 1705+ });
 1706+
 1707+ return $( '<div />' )
 1708+ .attr( 'title', gM( 'mwe-embedplayer-player_fullscreen' ) )
 1709+ .addClass( "ui-state-default ui-corner-all ui-icon_link rButton fullscreen-btn" )
 1710+ .append(
 1711+ $( '<span />' )
 1712+ .addClass( "ui-icon ui-icon-arrow-4-diag" )
 1713+ )
 1714+ // Fullscreen binding:
 1715+ .buttonHover().click( function() {
 1716+ ctrlObj.embedPlayer.fullscreen();
 1717+ } );
 1718+ }
 1719+ },
 1720+
 1721+
 1722+ /**
 1723+ * The pause / play button
 1724+ */
 1725+ 'pause': {
 1726+ 'w': 24,
 1727+ 'o': function( ctrlObj ) {
 1728+ return $( '<div />' )
 1729+ .attr( 'title', gM( 'mwe-embedplayer-play_clip' ) )
 1730+ .addClass ( "ui-state-default ui-corner-all ui-icon_link lButton play-btn" )
 1731+ .append(
 1732+ $( '<span />' )
 1733+ .addClass( "ui-icon ui-icon-play" )
 1734+ )
 1735+ // Play / pause binding
 1736+ .buttonHover()
 1737+ .click( function() {
 1738+ ctrlObj.embedPlayer.play();
 1739+ });
 1740+ }
 1741+ },
 1742+
 1743+
 1744+ /**
 1745+ * The volume control interface html
 1746+ */
 1747+ 'volumeControl': {
 1748+ 'w' : 36,
 1749+ 'o' : function( ctrlObj ) {
 1750+ mw.log( 'PlayerControlBuilder::Set up volume control for: ' + ctrlObj.embedPlayer.id );
 1751+ $volumeOut = $( '<span />' );
 1752+ if ( ctrlObj.volume_layout == 'horizontal' ) {
 1753+ $volumeOut.append(
 1754+ $( '<div />' )
 1755+ .addClass( "ui-slider ui-slider-horizontal rButton volume-slider" )
 1756+ );
 1757+ }
 1758+
 1759+ // Add the volume control icon
 1760+ $volumeOut.append(
 1761+ $('<div />')
 1762+ .attr( 'title', gM( 'mwe-embedplayer-volume_control' ) )
 1763+ .addClass( "ui-state-default ui-corner-all ui-icon_link rButton volume_control" )
 1764+ .append(
 1765+ $( '<span />' )
 1766+ .addClass( "ui-icon ui-icon-volume-on" )
 1767+ )
 1768+ );
 1769+ if ( ctrlObj.volume_layout == 'vertical' ) {
 1770+ $volumeOut.find('.volume_control').append(
 1771+ $( '<div />' )
 1772+ .css( {
 1773+ 'position' : 'absolute',
 1774+ 'left' : '0px'
 1775+ })
 1776+ .hide()
 1777+ .addClass( "vol_container ui-corner-all" )
 1778+ .append(
 1779+ $( '<div />' )
 1780+ .addClass ( "volume-slider" )
 1781+ )
 1782+ );
 1783+ }
 1784+ //Return the inner html
 1785+ return $volumeOut.html();
 1786+ }
 1787+ },
 1788+
 1789+ 'sourceSwitch' : {
 1790+ 'w' : 50,
 1791+ 'o' : function( ctrlObj ){
 1792+ // Stream switching widget ( display the current selected stream text )
 1793+ return $( '<div />' )
 1794+ .addClass('ui-widget source-switch')
 1795+ .append(
 1796+ ctrlObj.embedPlayer.mediaElement.selectedSource.shorttitle
 1797+ ).menu( {
 1798+ 'content' : ctrlObj.getSwichSourceMenu(),
 1799+ 'zindex' : mw.getConfig( 'EmbedPlayer.FullScreenZIndex' ) + 2,
 1800+ 'width' : 75,
 1801+ 'positionOpts' : {
 1802+ 'posY' : 'top',
 1803+ 'directionV' : 'up',
 1804+ 'offsetY' : 23
 1805+ },
 1806+ 'createMenuCallback' : function(){
 1807+ ctrlObj.showControlBar( true );
 1808+ },
 1809+ 'closeMenuCallback' : function(){
 1810+ ctrlObj.hideControlBar( true );
 1811+ }
 1812+ } );
 1813+ }
 1814+ },
 1815+
 1816+ /*
 1817+ * The time display area
 1818+ */
 1819+ 'timeDisplay': {
 1820+ 'w' : 50,
 1821+ 'o' : function( ctrlObj ) {
 1822+ return $( '<div />' )
 1823+ .addClass( "ui-widget time-disp" )
 1824+ .append(
 1825+ ctrlObj.embedPlayer.getTimeRange()
 1826+ );
 1827+ }
 1828+ },
 1829+
 1830+ /**
 1831+ * The playhead component
 1832+ */
 1833+ 'playHead': {
 1834+ 'w':0, // special case (takes up remaining space)
 1835+ 'o':function( ctrlObj ) {
 1836+
 1837+ var sliderConfig = {
 1838+ range: "min",
 1839+ value: 0,
 1840+ min: 0,
 1841+ max: 1000,
 1842+ start: function( event, ui ) {
 1843+ var id = ( embedPlayer.pc != null ) ? embedPlayer.pc.pp.id:embedPlayer.id;
 1844+ embedPlayer.userSlide = true;
 1845+ $( id + ' .play-btn-large' ).fadeOut( 'fast' );
 1846+ // If playlist always start at 0
 1847+ embedPlayer.start_time_sec = ( embedPlayer.instanceOf == 'mvPlayList' ) ? 0:
 1848+ mw.npt2seconds( embedPlayer.getTimeRange().split( '/' )[0] );
 1849+ },
 1850+ slide: function( event, ui ) {
 1851+ var perc = ui.value / 1000;
 1852+ embedPlayer.jump_time = mw.seconds2npt( parseFloat( parseFloat( embedPlayer.getDuration() ) * perc ) + embedPlayer.start_time_sec );
 1853+ // mw.log('perc:' + perc + ' * ' + embedPlayer.getDuration() + ' jt:'+ this.jump_time);
 1854+ if ( _this.longTimeDisp ) {
 1855+ ctrlObj.setStatus( gM( 'mwe-embedplayer-seek_to', embedPlayer.jump_time ) );
 1856+ } else {
 1857+ ctrlObj.setStatus( embedPlayer.jump_time );
 1858+ }
 1859+ // Update the thumbnail / frame
 1860+ if ( embedPlayer.isPlaying == false ) {
 1861+ embedPlayer.updateThumbPerc( perc );
 1862+ }
 1863+ },
 1864+ change:function( event, ui ) {
 1865+ // Only run the onChange event if done by a user slide
 1866+ // (otherwise it runs times it should not)
 1867+ if ( embedPlayer.userSlide ) {
 1868+ embedPlayer.userSlide = false;
 1869+ embedPlayer.seeking = true;
 1870+
 1871+ var perc = ui.value / 1000;
 1872+ // Set seek time (in case we have to do a url seek)
 1873+ embedPlayer.seek_time_sec = mw.npt2seconds( embedPlayer.jump_time, true );
 1874+ mw.log( 'do jump to: ' + embedPlayer.jump_time + ' perc:' + perc + ' sts:' + embedPlayer.seek_time_sec );
 1875+ ctrlObj.setStatus( gM( 'mwe-embedplayer-seeking' ) );
 1876+ embedPlayer.doSeek( perc );
 1877+ }
 1878+ }
 1879+ };
 1880+
 1881+ // Set up the disable playhead function:
 1882+ // TODO this will move into the disableSeekBar binding in the new theme framework
 1883+ ctrlObj.disableSeekBar = function(){
 1884+ ctrlObj.embedPlayer.$interface.find( ".play_head" ).slider( "option", "disabled", true );
 1885+ }
 1886+ ctrlObj.enableSeekBar = function(){
 1887+ ctrlObj.embedPlayer.$interface.find( ".play_head" ).slider( "option", "disabled", false);
 1888+ }
 1889+
 1890+
 1891+ var embedPlayer = ctrlObj.embedPlayer;
 1892+ var _this = this;
 1893+ var $playHead = $( '<div />' )
 1894+ .addClass ( "play_head" )
 1895+ .css({
 1896+ "position" : 'absolute',
 1897+ "left" : '33px',
 1898+ "right" : ( ( embedPlayer.getPlayerWidth() - ctrlObj.available_width ) - 10) + 'px'
 1899+ })
 1900+ // Playhead binding
 1901+ .slider( sliderConfig );
 1902+
 1903+ // Up the z-index of the default status indicator:
 1904+ $playHead.find( '.ui-slider-handle' ).css( 'z-index', 4 );
 1905+ $playHead.find( '.ui-slider-range' ).addClass( 'ui-corner-all' ).css( 'z-index', 2 );
 1906+
 1907+ // Add buffer html:
 1908+ $playHead.append(
 1909+ $('<div />')
 1910+ .addClass( "ui-slider-range ui-slider-range-min ui-widget-header")
 1911+ .addClass( "ui-state-highlight ui-corner-all mw_buffer")
 1912+ );
 1913+
 1914+ return $playHead;
 1915+ }
 1916+ }
 1917+ }
 1918+};
 1919+
 1920+
 1921+} )( window.mediaWiki, window.jQuery );
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js
___________________________________________________________________
Added: svn:mime-type
11922 + text/plain
Added: svn:eol-style
21923 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mw.style.EmbedPlayer.css
@@ -0,0 +1,35 @@
 2+.player_select_list {
 3+ color:white;
 4+ font-size:10pt;
 5+/* display:none;*/
 6+}
 7+.player_select_list a:visited {
 8+ color:white;
 9+}
 10+.mv_playhead {
 11+ position:absolute;
 12+ top:0;
 13+ left:0;
 14+ width:17px;
 15+ height:21px;
 16+ /*http://art.gnome.org/themes/gtk2*/
 17+}
 18+.mv_status {
 19+ font-family:"Times New Roman", Times, serif;
 20+ font-size:14px;
 21+ float:left;
 22+}
 23+.set_ogg_player_pref{
 24+ text-align:left;
 25+}
 26+
 27+.large_play_button {
 28+ display:block;
 29+ width: 130px;
 30+ height: 96px;
 31+ margin: auto;
 32+/* margin: -202px 0 0 154px;*/
 33+ position: absolute;
 34+ z-index: 3;
 35+ cursor: pointer;
 36+}
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mw.style.EmbedPlayer.css
___________________________________________________________________
Added: svn:mime-type
137 + text/plain
Added: svn:eol-style
238 + native
Added: svn:executable
339 + *
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/images/player_big_play_button.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/images/player_big_play_button.png
___________________________________________________________________
Added: svn:mime-type
440 + application/octet-stream
Added: svn:executable
541 + *
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/mw.style.PlayerSkinMvpcf.css
@@ -0,0 +1,169 @@
 2+/**
 3+ * reference player skin
 4+ */
 5+
 6+
 7+/*.ui-state-default */
 8+.mv-player a:link {color: #2060c1; text-decoration: underline;}
 9+.mv-player a:visited {color: #2060c1; text-decoration: underline;}
 10+/*a:visited {color: #75a5e4; text-decoration: underline;}*/ /*Not sure if you want this*/
 11+.mv-player a:hover {color: #75a5e4; text-decoration: underline;}
 12+.mv-player img, .mv-player img a, .mv-player img a:hover {border: 0;}
 13+
 14+
 15+.mv-player .video {
 16+ display: block;
 17+ position: relative;
 18+ font-size: 1px;
 19+ height: 305px;
 20+}
 21+.mv-player .control-bar {
 22+ height: 29px;
 23+ z-index: 2;
 24+}
 25+.mv-player .controlInnerSmall {
 26+/* width: 430px;*/
 27+ height: 29px;
 28+ float: left;
 29+ display: inline;
 30+}
 31+
 32+.mv-player .lButton {
 33+ cursor:pointer;
 34+ float:left;
 35+ list-style:none outside none;
 36+ margin:2px;
 37+ padding:4px 0;
 38+ width: 24px;
 39+ height:16px;
 40+ position:relative;
 41+}
 42+.mv-player .rButton {
 43+ cursor:pointer;
 44+ float:right;
 45+ list-style:none outside none;
 46+ margin:2px;
 47+ padding:4px 0;
 48+ width: 23px;
 49+ height:16px;
 50+ position:relative;
 51+}
 52+
 53+.mv-player .volume_icon {
 54+ float: right;
 55+ display: inline;
 56+ width: 22px;
 57+ height: 29px;
 58+ padding: 0 0 0 0;
 59+
 60+}
 61+
 62+.mv-player .vol_container{
 63+ z-index:99;
 64+ width:23px;
 65+ height:75px;
 66+ width:23px;
 67+ background: #CCC;
 68+}
 69+.mv-player .vol_container_below{
 70+ top:30px;
 71+}
 72+.mv-player .vol_container_top{
 73+ top:-77px;
 74+}
 75+.mv-player .vol_container .volume-slider{
 76+ margin-top:5px;
 77+ height:65px;
 78+ width:10px;
 79+ margin-left: auto ;
 80+ margin-right: auto ;
 81+}
 82+.mv-player .vol_container .ui-slider-handle{
 83+ cursor : pointer;
 84+ width:10px;
 85+ height:10px;
 86+ position:absolute;
 87+ left:-1px;
 88+}
 89+
 90+.mv-player .time-disp {
 91+ line-height: 32px;
 92+ height: 29px;
 93+ overflow: visible;
 94+ font-size: 10.2px;
 95+ width: 85px;
 96+ float: right;
 97+ display: inline;
 98+ border:none;
 99+}
 100+
 101+.mv-player .play_head{
 102+ float: left;
 103+ display: inline;
 104+ height: 10px;
 105+ margin-left:8px;
 106+ margin-top:10px;
 107+ position:relative;
 108+}
 109+
 110+.mv-player .play_head .ui-slider-handle{
 111+ width:10px;
 112+ height:15px;
 113+ margin-left:-5px;
 114+ margin-top: -0px;
 115+ z-index: 2;
 116+}
 117+
 118+.mv-player .inOutSlider .ui-slider-handle{
 119+ width:8px;
 120+ cusror: move;
 121+}
 122+
 123+.mv-player .overlay-win textarea {
 124+ background:none repeat scroll 0 0 transparent;
 125+ border: 2px solid #333;
 126+ color: #fff;
 127+ font: 11px arial,sans-serif;
 128+ height:15px;
 129+ overflow:hidden;
 130+ padding-left:2px;
 131+ width:97%;
 132+}
 133+
 134+.mv-player .overlay-win div.ui-state-highlight {
 135+ background:none repeat scroll 0 0 transparent;
 136+ border-color:#554926;
 137+ color:#FFE96E;
 138+ float:left;
 139+ padding:2px 5px;
 140+}
 141+
 142+.mv-player .videoOptionsComplete div.ui-state-highlight a {
 143+ color:#eee;
 144+ font-weight:bold;
 145+}
 146+
 147+.mv-player .overlay-win h2{
 148+ font-size: 115%;
 149+}
 150+
 151+.mv-player .overlay-win{
 152+ font-family : arial,sans-serif;
 153+ font-size : 85%;
 154+}
 155+.mv-player .overlay-win a{
 156+ text-decoration: none;
 157+}
 158+
 159+.mv-player .overlay-win ul{
 160+ padding-left: 15px;
 161+}
 162+
 163+.mv-player a:hover {}
 164+
 165+.mv-player .overlay-win ul li span { font-weight:bold; color:#fff;}
 166+
 167+.mv-player .overlay-win h2 { font-size:16px;}
 168+.mv-player .overlay-win h3 { font-size:14px;}
 169+
 170+.active { font-size: 12px; }
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/mw.style.PlayerSkinMvpcf.css
___________________________________________________________________
Added: svn:mime-type
1171 + text/plain
Added: svn:eol-style
2172 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/mw.PlayerSkinMvpcf.js
@@ -0,0 +1,6 @@
 2+/*
 3+mvpcf skin config
 4+*/
 5+mw.PlayerSkinMvpcf = {
 6+ playerClass : 'mv-player'
 7+};
\ No newline at end of file
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/skins/mvpcf/mw.PlayerSkinMvpcf.js
___________________________________________________________________
Added: svn:mime-type
18 + text/plain
Added: svn:eol-style
29 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerJava.js
@@ -0,0 +1,248 @@
 2+/**
 3+* List of domains and hosted location of cortado. Lets clients avoid the security warning for cross domain cortado
 4+*/
 5+( function( mw, $ ) {
 6+
 7+window.cortadoDomainLocations = {
 8+ 'upload.wikimedia.org' : 'http://upload.wikimedia.org/jars/cortado.jar'
 9+};
 10+
 11+// Set the default location for CortadoApplet
 12+mw.setDefaultConfig( 'relativeCortadoAppletPath',
 13+ mw.getMwEmbedPath() + 'modules/EmbedPlayer/binPlayers/cortado/cortado-ovtk-stripped-0.6.0.jar'
 14+);
 15+
 16+mw.EmbedPlayerJava = {
 17+
 18+ // Instance name:
 19+ instanceOf: 'Java',
 20+
 21+ // Supported feature set of the cortado applet:
 22+ supports: {
 23+ 'playHead' : true,
 24+ 'pause' : true,
 25+ 'stop' : true,
 26+ 'fullscreen' : false,
 27+ 'timeDisplay' : true,
 28+ 'volumeControl' : false
 29+ },
 30+
 31+ /**
 32+ * Output the the embed html
 33+ */
 34+ doEmbedHTML: function () {
 35+ var _this = this;
 36+ mw.log( "java play url:" + this.getSrc( this.seek_time_sec ) );
 37+
 38+ mw.log('Applet location: ' + this.getAppletLocation() );
 39+ mw.log('Play media: ' + this.getSrc() );
 40+
 41+ // load directly in the page..
 42+ // ( media must be on the same server or applet must be signed )
 43+ var appletCode = '' +
 44+ '<applet id="' + this.pid + '" code="com.fluendo.player.Cortado.class" ' +
 45+ 'archive="' + this.getAppletLocation() + '" width="' + parseInt( this.getWidth() ) + '" ' +
 46+ 'height="' + parseInt( this.getHeight() ) + '"> ' + "\n" +
 47+ '<param name="url" value="' + this.getSrc() + '" /> ' + "\n" +
 48+ '<param name="local" value="false"/>' + "\n" +
 49+ '<param name="keepaspect" value="true" />' + "\n" +
 50+ '<param name="video" value="true" />' + "\n" +
 51+ '<param name="showStatus" value="hide" />' + "\n" +
 52+ '<param name="audio" value="true" />' + "\n" +
 53+ '<param name="seekable" value="true" />' + "\n";
 54+
 55+ // Add the duration attribute if set:
 56+ if( this.getDuration() ){
 57+ appletCode += '<param name="duration" value="' + parseFloat( this.getDuration() ) + '" />' + "\n";
 58+ }
 59+
 60+ appletCode += '<param name="BufferSize" value="4096" />' +
 61+ '<param name="BufferHigh" value="25">' +
 62+ '<param name="BufferLow" value="5">' +
 63+ '</applet>';
 64+
 65+ $( this ).html( appletCode );
 66+
 67+ // Wrap it in an iframe to avoid hanging the event thread in FF 2/3 and similar
 68+ // NOTE: This breaks reference to the applet so disabled for now:
 69+ /*if ( $j.browser.mozilla ) {
 70+ var iframe = document.createElement( 'iframe' );
 71+ iframe.setAttribute( 'width', this.getWidth() );
 72+ iframe.setAttribute( 'height', this.getHeight() );
 73+ iframe.setAttribute( 'scrolling', 'no' );
 74+ iframe.setAttribute( 'frameborder', 0 );
 75+ iframe.setAttribute( 'marginWidth', 0 );
 76+ iframe.setAttribute( 'marginHeight', 0 );
 77+ iframe.setAttribute( 'id', 'cframe_' + this.id )
 78+
 79+ // Append the iframe to the embed object:
 80+ $( this ).html( iframe );
 81+
 82+ // Write out the iframe content:
 83+ var newDoc = iframe.contentDocument;
 84+ newDoc.open();
 85+ newDoc.write( '<html><body>' + appletCode + '</body></html>' );
 86+ // spurious error in some versions of FF, no workaround known
 87+ newDoc.close();
 88+ } else {
 89+ $( this ).html( appletCode );
 90+ //}
 91+ */
 92+
 93+ // Start the monitor:
 94+ _this.monitor();
 95+ },
 96+
 97+ /**
 98+ * Get the applet location
 99+ */
 100+ getAppletLocation: function() {
 101+ var mediaSrc = this.getSrc();
 102+ var appletLoc = false;
 103+ if (
 104+ !mw.isLocalDomain( mediaSrc )
 105+ ||
 106+ !mw.isLocalDomain( mw.getMwEmbedPath()
 107+ ||
 108+ mw.getConfig( 'relativeCortadoAppletPath' ) === false )
 109+ ){
 110+ if ( window.cortadoDomainLocations[ mw.parseUri( mediaSrc ).host ] ) {
 111+ appletLoc = window.cortadoDomainLocations[ mw.parseUri( mediaSrc ).host ];
 112+ } else {
 113+ appletLoc = 'http://theora.org/cortado.jar';
 114+ }
 115+ } else {
 116+ // Get the local relative cortado applet location:
 117+ appletLoc = mw.getConfig( 'relativeCortadoAppletPath' );
 118+ }
 119+ return appletLoc;
 120+ },
 121+
 122+ /**
 123+ * Get the embed player time
 124+ */
 125+ getPlayerElementTime: function() {
 126+ this.getPlayerElement();
 127+ var currentTime = 0;
 128+ if ( this.playerElement ) {
 129+ try {
 130+ // java reads ogg media time.. so no need to add the start or seek offset:
 131+ //mw.log(' ct: ' + this.playerElement.getPlayPosition() + ' ' + this.supportsURLTimeEncoding());
 132+
 133+ currentTime = this.playerElement.currentTime;
 134+ // ( java cortado has -1 time ~sometimes~ )
 135+ /*if ( this.currentTime < 0 ) {
 136+ mw.log( 'pp:' + this.currentTime );
 137+ // Probably reached clip ( should fire ondone event instead )
 138+ this.onClipDone();
 139+ }*/
 140+ } catch ( e ) {
 141+ mw.log( 'could not get time from jPlayer: ' + e );
 142+ }
 143+ }else{
 144+ mw.log(" could not find playerElement " );
 145+ }
 146+ return currentTime;
 147+ },
 148+
 149+ /**
 150+ * Seek in the ogg stream
 151+ * ( Cortado seek does not seem to work very well )
 152+ * @param {Float} percentage Percentage to seek into the stream
 153+ */
 154+ doSeek: function( percentage ) {
 155+ mw.log( 'java:seek:p: ' + percentage + ' : ' + this.supportsURLTimeEncoding() + ' dur: ' + this.getDuration() + ' sts:' + this.seek_time_sec );
 156+ this.getPlayerElement();
 157+
 158+ if ( this.supportsURLTimeEncoding() ) {
 159+ this.parent_doSeek( percentage );
 160+ } else if ( this.playerElement ) {
 161+ // do a (generally broken) local seek:
 162+ mw.log( "Cortado seek is not very accurate :: doSeek::" + ( percentage * parseFloat( this.getDuration() ) ) );
 163+ this.playerElement.currentTime = ( percentage * parseFloat( this.getDuration() ) );
 164+ } else {
 165+ this.doPlayThenSeek( percentage );
 166+ }
 167+
 168+ // Run the onSeeking interface update
 169+ this.controlBuilder.onSeek();
 170+ },
 171+
 172+ /**
 173+ * Issue a play request then seek to a percentage point in the stream
 174+ * @param {Float} percentage Percentage to seek into the stream
 175+ */
 176+ doPlayThenSeek: function( percentage ) {
 177+ mw.log( 'doPlayThenSeek' );
 178+ var _this = this;
 179+ this.play();
 180+ var rfsCount = 0;
 181+ var readyForSeek = function() {
 182+ _this.getPlayerElement();
 183+ // if we have .jre ~in theory~ we can seek (but probably not)
 184+ if ( _this.playerElement ) {
 185+ _this.doSeek( perc );
 186+ } else {
 187+ // try to get player for 10 seconds:
 188+ if ( rfsCount < 200 ) {
 189+ setTimeout( readyForSeek, 50 );
 190+ rfsCount++;
 191+ } else {
 192+ mw.log( 'error:doPlayThenSeek failed' );
 193+ }
 194+ }
 195+ };
 196+ readyForSeek();
 197+ },
 198+
 199+ /**
 200+ * Update the playerElement instance with a pointer to the embed object
 201+ */
 202+ getPlayerElement: function() {
 203+ if( !$( '#' + this.pid ).length ) {
 204+ return false;
 205+ }
 206+ //mw.log( 'getPlayerElement::' + this.pid );
 207+ this.playerElement = $( '#' + this.pid ).get( 0 );
 208+ //this.playerElement = document.applets[ 0 ];
 209+ // NOTE we are currently not using the iframe embed method:
 210+ //if ( $j.browser.mozilla ) {
 211+ // this.playerElement = $('#cframe_' + this.id).contents().find( '#' + this.pid );
 212+ //} else {
 213+ // this.playerElement = $( '#' + this.pid ).get( 0 );
 214+ //}
 215+ return this.playerElement;
 216+ },
 217+
 218+ /**
 219+ * Issue the doPlay request to the playerElement
 220+ * calls parent_play to update interface
 221+ */
 222+ play: function() {
 223+ this.getPlayerElement();
 224+ this.parent_play();
 225+ if ( this.playerElement ) {
 226+ try{
 227+ this.playerElement.play();
 228+ }catch( e ){
 229+ mw.log("EmbedPlayerJava::Could not issue play request");
 230+ }
 231+ }
 232+ },
 233+
 234+ /**
 235+ * Pause playback
 236+ * calls parent_pause to update interface
 237+ */
 238+ pause: function() {
 239+ this.getPlayerElement();
 240+ // Update the interface
 241+ this.parent_pause();
 242+ // Call the pause function if it exists:
 243+ if ( this.playerElement ) {
 244+ this.playerElement.pause();
 245+ }
 246+ }
 247+};
 248+
 249+} )( window.mediaWiki, window.jQuery );
\ No newline at end of file
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerJava.js
___________________________________________________________________
Added: svn:mime-type
1250 + text/plain
Added: svn:eol-style
2251 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaPlayers.js
@@ -0,0 +1,184 @@
 2+/**
 3+ * mediaPlayers is a collection of mediaPlayer objects supported by the client.
 4+ *
 5+ * @constructor
 6+ */
 7+
 8+function mediaPlayers()
 9+{
 10+ this.init();
 11+}
 12+
 13+mediaPlayers.prototype =
 14+{
 15+ // The list of players supported
 16+ players : null,
 17+
 18+ // Store per mime-type prefrences for players
 19+ preference : { },
 20+
 21+ // Stores the default set of players for a given mime type
 22+ defaultPlayers : { },
 23+
 24+ /**
 25+ * Initializartion function sets the default order for players for a given
 26+ * mime type
 27+ */
 28+ init: function() {
 29+ this.players = new Array();
 30+ this.loadPreferences();
 31+
 32+ // set up default players order for each library type
 33+ this.defaultPlayers['video/x-flv'] = ['Kplayer', 'Vlc'];
 34+ this.defaultPlayers['video/h264'] = ['Native', 'Kplayer', 'Vlc'];
 35+
 36+ this.defaultPlayers['video/ogg'] = ['Native', 'Vlc', 'Java', 'Generic'];
 37+ this.defaultPlayers['video/webm'] = ['Native', 'Vlc'];
 38+ this.defaultPlayers['application/ogg'] = ['Native', 'Vlc', 'Java', 'Generic'];
 39+ this.defaultPlayers['audio/ogg'] = ['Native', 'Vlc', 'Java' ];
 40+ this.defaultPlayers['video/mp4'] = ['Vlc'];
 41+ this.defaultPlayers['video/mpeg'] = ['Vlc'];
 42+ this.defaultPlayers['video/x-msvideo'] = ['Vlc'];
 43+
 44+ this.defaultPlayers['text/html'] = ['Html'];
 45+ this.defaultPlayers['image/jpeg'] = ['Html'];
 46+ this.defaultPlayers['image/png'] = ['Html'];
 47+ this.defaultPlayers['image/svg'] = ['Html'];
 48+
 49+ },
 50+
 51+ /**
 52+ * Adds a Player to the player list
 53+ *
 54+ * @param {Object}
 55+ * player Player object to be added
 56+ */
 57+ addPlayer: function( player ) {
 58+ for ( var i = 0; i < this.players.length; i++ ) {
 59+ if ( this.players[i].id == player.id ) {
 60+ // Player already found
 61+ return ;
 62+ }
 63+ }
 64+
 65+
 66+ // Add the player:
 67+ this.players.push( player );
 68+ },
 69+
 70+ /**
 71+ * Checks if a player is supported by id
 72+ */
 73+ isSupportedPlayer: function( playerId ){
 74+ for( var i=0; i < this.players.length; i++ ){
 75+ if( this.players[i].id == playerId ){
 76+ return true;
 77+ }
 78+ }
 79+ return false;
 80+ },
 81+
 82+ /**
 83+ * get players that support a given mimeType
 84+ *
 85+ * @param {String}
 86+ * mimeType Mime type of player set
 87+ * @return {Array} Array of players that support a the requested mime type
 88+ */
 89+ getMIMETypePlayers: function( mimeType ) {
 90+ var mimePlayers = new Array();
 91+ var _this = this;
 92+ if ( this.defaultPlayers[mimeType] ) {
 93+ $j.each( this.defaultPlayers[ mimeType ], function( d, lib ) {
 94+ var library = _this.defaultPlayers[ mimeType ][ d ];
 95+ for ( var i = 0; i < _this.players.length; i++ ) {
 96+ if ( _this.players[i].library == library && _this.players[i].supportsMIMEType( mimeType ) ) {
 97+ mimePlayers.push( _this.players[i] );
 98+ }
 99+ }
 100+ } );
 101+ }
 102+ return mimePlayers;
 103+ },
 104+
 105+ /**
 106+ * Default player for a given mime type
 107+ *
 108+ * @param {String}
 109+ * mimeType Mime type of the requested player
 110+ * @return Player for mime type null if no player found
 111+ */
 112+ defaultPlayer : function( mimeType ) {
 113+ // mw.log( "get defaultPlayer for " + mimeType );
 114+ var mimePlayers = this.getMIMETypePlayers( mimeType );
 115+ if ( mimePlayers.length > 0 )
 116+ {
 117+ // Check for prior preference for this mime type
 118+ for ( var i = 0; i < mimePlayers.length; i++ ) {
 119+ if ( mimePlayers[i].id == this.preference[mimeType] )
 120+ return mimePlayers[i];
 121+ }
 122+ // Otherwise just return the first compatible player
 123+ // (it will be chosen according to the defaultPlayers list
 124+ return mimePlayers[0];
 125+ }
 126+ // mw.log( 'No default player found for ' + mimeType );
 127+ return null;
 128+ },
 129+
 130+ /**
 131+ * Sets the format preference.
 132+ *
 133+ * @param {String}
 134+ * mimeFormat Prefered format
 135+ */
 136+ setFormatPreference : function ( mimeFormat ) {
 137+ this.preference['formatPreference'] = mimeFormat;
 138+ $.cookie( 'EmbedPlayer.Preference', this.preference);
 139+ },
 140+
 141+ /**
 142+ * Sets the player preference
 143+ *
 144+ * @param {String}
 145+ * playerId Prefered player id
 146+ * @param {String}
 147+ * mimeType Mime type for the associated player stream
 148+ */
 149+ setPlayerPreference : function( playerId, mimeType ) {
 150+ var selectedPlayer = null;
 151+ for ( var i = 0; i < this.players.length; i++ ) {
 152+ if ( this.players[i].id == playerId ) {
 153+ selectedPlayer = this.players[i];
 154+ mw.log( 'EmbedPlayer::setPlayerPreference: choosing ' + playerId + ' for ' + mimeType );
 155+ this.preference[ mimeType ] = playerId;
 156+ $.cookie( 'EmbedPlayer.Preference', this.preference );
 157+ break;
 158+ }
 159+ }
 160+ // Update All the player instances on the page
 161+ if ( selectedPlayer ) {
 162+ $('.mwEmbedPlayer').each(function(inx, playerTarget ){
 163+ var embedPlayer = $( playerTarget ).get( 0 );
 164+ if ( embedPlayer.mediaElement.selectedSource
 165+ && ( embedPlayer.mediaElement.selectedSource.mimeType == mimeType ) )
 166+ {
 167+ embedPlayer.selectPlayer( selectedPlayer );
 168+ }
 169+ });
 170+ }
 171+ },
 172+
 173+ /**
 174+ * Loads the user preference settings from a cookie
 175+ */
 176+ loadPreferences : function ( ) {
 177+ this.preference = { };
 178+ // See if we have a cookie set to a clientSupported type:
 179+ if( $.cookie( 'EmbedPlayer.Preference' ) ) {
 180+ this.preference = $.cookie( 'EmbedPlayer.Preference' );
 181+ }
 182+ }
 183+};
 184+
 185+
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaPlayers.js
___________________________________________________________________
Added: svn:mime-type
1186 + text/plain
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaSource.js
@@ -0,0 +1,318 @@
 2+/**
 3+ * mediaSource class represents a source for a media element.
 4+ *
 5+ * @param {Element}
 6+ * element: MIME type of the source.
 7+ * @constructor
 8+ */
 9+
 10+function mediaSource( element ) {
 11+ this.init( element );
 12+}
 13+
 14+mediaSource.prototype = {
 15+ // True if the source has been marked as the default.
 16+ markedDefault: false,
 17+
 18+ // True if the source supports url specification of offset and duration
 19+ URLTimeEncoding:false,
 20+
 21+ // Start offset of the requested segment
 22+ startOffset: 0,
 23+
 24+ // Duration of the requested segment (0 if not known)
 25+ duration:0,
 26+
 27+ /**
 28+ * MediaSource constructor:
 29+ */
 30+ init : function( element ) {
 31+ // mw.log('EmbedPlayer::adding mediaSource: ' + element);
 32+ this.src = $( element ).attr( 'src' );
 33+
 34+ // Set default URLTimeEncoding if we have a time url:
 35+ // not ideal way to discover if content is on an oggz_chop server.
 36+ // should check some other way.
 37+ var pUrl = mw.parseUri ( this.src );
 38+ if ( typeof pUrl[ 'queryKey' ][ 't' ] != 'undefined' ) {
 39+ this.URLTimeEncoding = true;
 40+ }
 41+
 42+ var sourceAttr = mw.getConfig( 'EmbedPlayer.SourceAttributes' );
 43+ for ( var i = 0; i < sourceAttr.length; i++ ) { // array loop:
 44+ var attr = sourceAttr[ i ];
 45+ var attrValue = $( element ).attr( attr );
 46+ if ( attrValue ) {
 47+ // strip data- from the attribute name
 48+ if( attr.indexOf('data-') === 0){
 49+ attr = attr.substr(5);
 50+ }
 51+ this[ attr ] = attrValue;
 52+ }
 53+ }
 54+
 55+ // Set the content type:
 56+ if ( $( element ).attr( 'type' ) ) {
 57+ this.mimeType = $( element ).attr( 'type' );
 58+ }else if ( $( element ).attr( 'content-type' ) ) {
 59+ this.mimeType = $( element ).attr( 'content-type' );
 60+ }else if( $( element ).get(0).tagName.toLowerCase() == 'audio' ){
 61+ // If the element is an "audio" tag set audio format
 62+ this.mimeType = 'audio/ogg';
 63+ } else {
 64+ this.mimeType = this.detectType( this.src );
 65+ }
 66+
 67+ // Conform the mime type to ogg
 68+ if( this.mimeType == 'video/theora') {
 69+ this.mimeType = 'video/ogg';
 70+ }
 71+
 72+ if( this.mimeType == 'audio/vorbis') {
 73+ this.mimeType = 'audio/ogg';
 74+ }
 75+
 76+ // Check for parent elements ( supplies categories in "track" )
 77+ if( $( element ).parent().attr('category') ) {
 78+ this.category = $( element ).parent().attr('category');
 79+ }
 80+
 81+ if( $( element ).attr( 'default' ) ){
 82+ this.markedDefault = true;
 83+ }
 84+
 85+ // Get the url duration ( if applicable )
 86+ this.getURLDuration();
 87+ },
 88+
 89+ /**
 90+ * Update Source title via Element
 91+ *
 92+ * @param {Element}
 93+ * element Source element to update attributes from
 94+ */
 95+ updateSource: function( element ) {
 96+ // for now just update the title:
 97+ if ( $( element ).attr( "title" ) ) {
 98+ this.title = $( element ).attr( "title" );
 99+ }
 100+ },
 101+
 102+ /**
 103+ * Updates the src time and start & end
 104+ *
 105+ * @param {String}
 106+ * start_time: in NPT format
 107+ * @param {String}
 108+ * end_time: in NPT format
 109+ */
 110+ updateSrcTime: function ( start_npt, end_npt ) {
 111+ // mw.log("f:updateSrcTime: "+ start_npt+'/'+ end_npt + ' from org: ' +
 112+ // this.start_npt+ '/'+this.end_npt);
 113+ // mw.log("pre uri:" + this.src);
 114+ // if we have time we can use:
 115+ if ( this.URLTimeEncoding ) {
 116+ // make sure its a valid start time / end time (else set default)
 117+ if ( !mw.npt2seconds( start_npt ) ) {
 118+ start_npt = this.start_npt;
 119+ }
 120+
 121+ if ( !mw.npt2seconds( end_npt ) ) {
 122+ end_npt = this.end_npt;
 123+ }
 124+
 125+ this.src = mw.replaceUrlParams( this.src, {
 126+ 't': start_npt + '/' + end_npt
 127+ });
 128+
 129+ // update the duration
 130+ this.getURLDuration();
 131+ }
 132+ },
 133+
 134+ /**
 135+ * Sets the duration and sets the end time if unset
 136+ *
 137+ * @param {Float}
 138+ * duration: in seconds
 139+ */
 140+ setDuration: function ( duration ) {
 141+ this.duration = duration;
 142+ if ( !this.end_npt ) {
 143+ this.end_npt = mw.seconds2npt( this.startOffset + duration );
 144+ }
 145+ },
 146+
 147+ /**
 148+ * MIME type accessors function.
 149+ *
 150+ * @return {String} the MIME type of the source.
 151+ */
 152+ getMIMEType: function() {
 153+ if( this.mimeType ) {
 154+ return this.mimeType;
 155+ }
 156+ this.mimeType = this.detectType( this.src );
 157+ return this.mimeType;
 158+ },
 159+
 160+ /**
 161+ * URI function.
 162+ *
 163+ * @param {Number}
 164+ * serverSeekTime Int: Used to adjust the URI for url based
 165+ * seeks)
 166+ * @return {String} the URI of the source.
 167+ */
 168+ getSrc: function( serverSeekTime ) {
 169+ if ( !serverSeekTime || !this.URLTimeEncoding ) {
 170+ return this.src;
 171+ }
 172+ var endvar = '';
 173+ if ( this.end_npt ) {
 174+ endvar = '/' + this.end_npt;
 175+ }
 176+ return mw.replaceUrlParams( this.src,
 177+ {
 178+ 't': mw.seconds2npt( serverSeekTime ) + endvar
 179+ }
 180+ );
 181+ },
 182+
 183+ /**
 184+ * Title accessor function.
 185+ *
 186+ * @return {String} Title of the source.
 187+ */
 188+ getTitle : function() {
 189+ if( this.title ){
 190+ return this.title;
 191+ }
 192+ // Text tracks use "label" instead of "title"
 193+ if( this.label ){
 194+ return this.label;
 195+ }
 196+
 197+ // Return a Title based on mime type:
 198+ switch( this.getMIMEType() ) {
 199+ case 'video/h264' :
 200+ return gM( 'mwe-embedplayer-video-h264' );
 201+ break;
 202+ case 'video/x-flv' :
 203+ return gM( 'mwe-embedplayer-video-flv' );
 204+ break;
 205+ case 'video/webm' :
 206+ return gM( 'mwe-embedplayer-video-webm');
 207+ break;
 208+ case 'video/ogg' :
 209+ return gM( 'mwe-embedplayer-video-ogg' );
 210+ break;
 211+ case 'audio/ogg' :
 212+ return gM( 'mwe-embedplayer-video-audio' );
 213+ break;
 214+ case 'video/mpeg' :
 215+ return 'MPEG video'; // FIXME: i18n
 216+ break;
 217+ case 'video/x-msvideo' :
 218+ return 'AVI video'; // FIXME: i18n
 219+ break;
 220+ }
 221+
 222+ // Return tilte based on file name:
 223+ var urlParts = mw.parseUri( this.getSrc() );
 224+ if( urlParts.file ){
 225+ return urlParts.file;
 226+ }
 227+
 228+ // Return the mime type string if not known type.
 229+ return this.mimeType;
 230+ },
 231+
 232+ /**
 233+ *
 234+ * Get Duration of the media in milliseconds from the source url.
 235+ *
 236+ * Supports media_url?t=ntp_start/ntp_end url request format
 237+ */
 238+ getURLDuration : function() {
 239+ // check if we have a URLTimeEncoding:
 240+ if ( this.URLTimeEncoding ) {
 241+ var annoURL = mw.parseUri( this.src );
 242+ if ( annoURL.queryKey.t ) {
 243+ var times = annoURL.queryKey.t.split( '/' );
 244+ this.start_npt = times[0];
 245+ this.end_npt = times[1];
 246+ this.startOffset = mw.npt2seconds( this.start_npt );
 247+ this.duration = mw.npt2seconds( this.end_npt ) - this.startOffset;
 248+ } else {
 249+ // look for this info as attributes
 250+ if ( this.startOffset ) {
 251+ this.start_npt = mw.seconds2npt( this.startOffset );
 252+ }
 253+ if ( this.duration ) {
 254+ this.end_npt = mw.seconds2npt( parseInt( this.duration ) + parseInt( this.startOffset ) );
 255+ }
 256+ }
 257+ }
 258+ },
 259+
 260+ /**
 261+ * Attempts to detect the type of a media file based on the URI.
 262+ *
 263+ * @param {String}
 264+ * uri URI of the media file.
 265+ * @return {String} The guessed MIME type of the file.
 266+ */
 267+ detectType: function( uri ) {
 268+ // NOTE: if media is on the same server as the javascript
 269+ // we can issue a HEAD request and read the mime type of the media...
 270+ // ( this will detect media mime type independently of the url name )
 271+ // http://www.jibbering.com/2002/4/httprequest.html
 272+ var urlParts = mw.parseUri( uri );
 273+ // Get the extension from the url or from the relative name:
 274+ var ext = ( urlParts.file )? /[^.]+$/.exec( urlParts.file ) : /[^.]+$/.exec( uri );
 275+ switch( ext.toString().toLowerCase() ) {
 276+ case 'smil':
 277+ case 'sml':
 278+ return 'application/smil';
 279+ break;
 280+ case 'm4v':
 281+ case 'mp4':
 282+ return 'video/h264';
 283+ break;
 284+ case 'webm':
 285+ return 'video/webm';
 286+ break;
 287+ case 'srt':
 288+ return 'text/x-srt';
 289+ break;
 290+ case 'flv':
 291+ return 'video/x-flv';
 292+ break;
 293+ case 'ogg':
 294+ case 'ogv':
 295+ return 'video/ogg';
 296+ break;
 297+ case 'oga':
 298+ return 'audio/ogg';
 299+ break;
 300+ case 'anx':
 301+ return 'video/ogg';
 302+ break;
 303+ case 'xml':
 304+ return 'text/xml';
 305+ break;
 306+ case 'avi':
 307+ return 'video/x-msvideo';
 308+ break;
 309+ case 'mpg':
 310+ return 'video/mpeg';
 311+ break;
 312+ case 'mpeg':
 313+ return 'video/mpeg';
 314+ break;
 315+ }
 316+ mw.log( "Error: could not detect type of media src: " + uri );
 317+ }
 318+};
 319+
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/MediaSource.js
___________________________________________________________________
Added: svn:mime-type
1320 + text/plain
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerVlc.js
@@ -0,0 +1,369 @@
 2+/*
 3+* VLC embed based on: http://people.videolan.org/~damienf/plugin-0.8.6.html
 4+* javascript api: http://www.videolan.org/doc/play-howto/en/ch04.html
 5+* assume version > 0.8.5.1
 6+*/
 7+( function( mw, $ ) {
 8+
 9+mw.EmbedPlayerVlc = {
 10+
 11+ //Instance Name:
 12+ instanceOf : 'Vlc',
 13+
 14+ //What the vlc player / plug-in supports:
 15+ supports : {
 16+ 'playHead':true,
 17+ 'pause':true,
 18+ 'stop':true,
 19+ 'fullscreen':true,
 20+ 'timeDisplay':true,
 21+ 'volumeControl':true,
 22+
 23+ 'playlist_driver':true, // if the object supports playlist functions
 24+ 'overlay':false
 25+ },
 26+
 27+ // The previous state of the player instance
 28+ prevState : 0,
 29+
 30+ // Counter for waiting for vlc embed to be ready
 31+ waitForVlcCount:0,
 32+
 33+ // Store the current play time for vlc
 34+ vlcCurrentTime: 0,
 35+
 36+ /**
 37+ * Get embed HTML
 38+ */
 39+ doEmbedHTML: function() {
 40+ var _this = this;
 41+ $( this ).html(
 42+ '<object classid="clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921" ' +
 43+ 'codebase="http://downloads.videolan.org/pub/videolan/vlc/latest/win32/axvlc.cab#Version=0,8,6,0" ' +
 44+ 'id="' + this.pid + '" events="True" height="' + this.getPlayerHeight() + '" width="' + this.getPlayerWidth() + '"' +
 45+ '>' +
 46+ '<param name="MRL" value="">' +
 47+ '<param name="ShowDisplay" value="True">' +
 48+ '<param name="AutoLoop" value="False">' +
 49+ '<param name="AutoPlay" value="False">' +
 50+ '<param name="Volume" value="50">' +
 51+ '<param name="StartTime" value="0">' +
 52+ '<embed pluginspage="http://www.videolan.org" type="application/x-vlc-plugin" ' +
 53+ 'progid="VideoLAN.VLCPlugin.2" name="' + this.pid + '" ' +
 54+ 'height="' + this.getHeight() + '" width="' + this.getWidth() + '" ' +
 55+ // set the style too 'just to be sure'
 56+ 'style="width:' + this.getWidth() + 'px;height:' + this.getHeight() + 'px;" ' +
 57+ '>' +
 58+ '</object>'
 59+ )
 60+ /*
 61+ $( this ).html(
 62+ '<embed type="application/x-vlc-plugin" pluginspage="http://www.videolan.org" version="VideoLAN.VLCPlugin.2" '+
 63+ 'width="' + this.width +'" ' +
 64+ 'height="' + this.height + '" ' +
 65+ 'id="' + this.pid + '"> ' +
 66+ '</embed>'
 67+ );*/
 68+
 69+
 70+ // give VLC 150ms to initialize before we start playback
 71+ // @@todo should be able to do this as an ready event
 72+ this.waitForVlcCount = 0;
 73+ setTimeout( function() {
 74+ _this.postEmbedJS();
 75+ }, 150 );
 76+ },
 77+
 78+ /**
 79+ * Javascript to run post vlc embedding
 80+ * Inserts the requested src to the embed instance
 81+ */
 82+ postEmbedJS: function() {
 83+ var _this = this;
 84+ // load a pointer to the vlc into the object (this.playerElement)
 85+ this.getPlayerElement();
 86+ if ( this.playerElement && this.playerElement.playlist) {
 87+ // manipulate the dom object to make sure vlc has the correct size:
 88+ this.playerElement.style.width = this.getWidth();
 89+ this.playerElement.style.height = this.getHeight();
 90+ this.playerElement.playlist.items.clear();
 91+
 92+ // VLC likes absolute urls:
 93+ var src = mw.absoluteUrl( this.getSrc() ) ;
 94+
 95+ // @@todo if client supports seeking no need to send seek_offset to URI
 96+ mw.log( 'vlc play::' + src );
 97+ var itemId = this.playerElement.playlist.add( src );
 98+ if ( itemId != -1 ) {
 99+ // Play
 100+ this.playerElement.playlist.playItem( itemId );
 101+ } else {
 102+ mw.log( "error:cannot play at the moment !" );
 103+ }
 104+ setTimeout( function() {
 105+ _this.monitor();
 106+ }, 100 );
 107+ } else {
 108+ mw.log( 'postEmbedJS: vlc not ready' );
 109+ this.waitForVlcCount++;
 110+ if ( this.waitForVlcCount < 10 ) {
 111+ setTimeout( function() {
 112+ _this.postEmbedJS();
 113+ }, 100 );
 114+ } else {
 115+ mw.log( 'vlc never ready' );
 116+ }
 117+ }
 118+ },
 119+
 120+ /**
 121+ * Handles seek requests based on temporal media source type support
 122+ *
 123+ * @param {Float} percent Seek to this percent of the stream
 124+ */
 125+ doSeek : function( percent ) {
 126+ this.getPlayerElement();
 127+ if ( this.supportsURLTimeEncoding() ) {
 128+ this.parent_doSeek( percent );
 129+ } else if ( this.playerElement ) {
 130+ this.seeking = true;
 131+ mw.log( "do vlc http seek to: " + percent )
 132+ if ( ( this.playerElement.input.state == 3 ) && ( this.playerElement.input.position != percent ) )
 133+ {
 134+ this.playerElement.input.position = percent;
 135+ this.controlBuilder.setStatus( 'seeking...' );
 136+ }
 137+ } else {
 138+ this.doPlayThenSeek( percent );
 139+ }
 140+ this.parent_monitor();
 141+ },
 142+
 143+ /**
 144+ * Issues a play request then seeks to a given time
 145+ *
 146+ * @param {Float} percent Seek to this percent of the stream after playing
 147+ */
 148+ doPlayThenSeek:function( percent ) {
 149+ mw.log( 'doPlayThenSeekHack' );
 150+ var _this = this;
 151+ this.play();
 152+ var rfsCount = 0;
 153+ var readyForSeek = function() {
 154+ _this.getPlayerElement();
 155+ var newState = _this.playerElement.input.state;
 156+ // if playing we are ready to do the
 157+ if ( newState == 3 ) {
 158+ _this.doSeek( percent );
 159+ } else {
 160+ // try to get player for 10 seconds:
 161+ if ( rfsCount < 200 ) {
 162+ setTimeout( readyForSeek, 50 );
 163+ rfsCount++;
 164+ } else {
 165+ mw.log( 'error:doPlayThenSeek failed' );
 166+ }
 167+ }
 168+ }
 169+ readyForSeek();
 170+ },
 171+
 172+ /**
 173+ * Updates the status time and player state
 174+ */
 175+ monitor: function() {
 176+ this.getPlayerElement();
 177+ if ( !this.playerElement )
 178+ return ;
 179+ try{
 180+ //mw.log( 'state:' + this.playerElement.input.state);
 181+ //mw.log('time: ' + this.playerElement.input.time);
 182+ //mw.log('pos: ' + this.playerElement.input.position);
 183+ if ( this.playerElement.log.messages.count > 0 ) {
 184+ // there is one or more messages in the log
 185+ var iter = this.playerElement.log.messages.iterator();
 186+ while ( iter.hasNext ) {
 187+ var msg = iter.next();
 188+ var msgtype = msg.type.toString();
 189+ if ( ( msg.severity == 1 ) && ( msgtype == "input" ) )
 190+ {
 191+ mw.log( msg.message );
 192+ }
 193+ }
 194+ // clear the log once finished to avoid clogging
 195+ this.playerElement.log.messages.clear();
 196+ }
 197+
 198+ var newState = this.playerElement.input.state;
 199+ if ( this.prevState != newState ) {
 200+ if ( newState == 0 )
 201+ {
 202+ // current media has stopped
 203+ this.onStop();
 204+ }
 205+ else if ( newState == 1 )
 206+ {
 207+ // current media is opening/connecting
 208+ this.onOpen();
 209+ }
 210+ else if ( newState == 2 )
 211+ {
 212+ // current media is buffering data
 213+ this.onBuffer();
 214+ }
 215+ else if ( newState == 3 )
 216+ {
 217+ // current media is now playing
 218+ this.onPlay();
 219+ }
 220+ else if ( this.playerElement.input.state == 4 ) {
 221+ // current media is now paused
 222+ this.onPause();
 223+ }
 224+ this.prevState = newState;
 225+ } else if ( newState == 3 ) {
 226+ // current media is playing
 227+ this.onPlaying();
 228+ }
 229+ } catch( e ){
 230+ mw.log("EmbedPlayerVlc::Monitor error");
 231+ }
 232+ // update the status and check timmer via universal parent monitor
 233+ this.parent_monitor();
 234+ },
 235+
 236+ /**
 237+ * Events:
 238+ * NOTE : should be localized:
 239+ */
 240+ onOpen: function() {
 241+ this.controlBuilder.setStatus( "Opening..." );
 242+ },
 243+ onBuffer: function() {
 244+ this.controlBuilder.setStatus( "Buffering..." );
 245+ },
 246+ onPlay: function() {
 247+ this.onPlaying();
 248+ },
 249+ onPlaying: function() {
 250+ this.seeking = false;
 251+ // for now trust the duration from url over vlc input.length
 252+ if ( !this.getDuration() && this.playerElement.input.length > 0 )
 253+ {
 254+ // mw.log('setting duration to ' + this.playerElement.input.length /1000);
 255+ this.duration = this.playerElement.input.length / 1000;
 256+ }
 257+ this.vlcCurrentTime = this.playerElement.input.time / 1000;
 258+ },
 259+
 260+ /**
 261+ * Get the embed player time
 262+ */
 263+ getPlayerElementTime: function(){
 264+ return this.vlcCurrentTime;
 265+ },
 266+
 267+ onPause: function() {
 268+ this.parent_pause(); // update the inteface if paused via native control
 269+ },
 270+ onStop: function() {
 271+ mw.log( 'vlc:onStop:' );
 272+ if ( !this.seeking )
 273+ this.onClipDone();
 274+ },
 275+
 276+ /**
 277+ * Handles play requests
 278+ */
 279+ play : function() {
 280+ mw.log( 'f:vlcPlay' );
 281+ // Update the interface
 282+ this.parent_play();
 283+ if ( this.getPlayerElement() ) {
 284+ // plugin is already being present send play call:
 285+ // clear the message log and enable error logging
 286+ if ( this.playerElement.log ) {
 287+ this.playerElement.log.messages.clear();
 288+ }
 289+ if ( this.playerElement.playlist && typeof this.playerElement.playlist.play == 'function')
 290+ this.playerElement.playlist.play();
 291+
 292+ if( typeof this.playerElement.play == 'function' )
 293+ this.playerElement.play();
 294+
 295+ this.paused = false;
 296+
 297+ // re-start the monitor:
 298+ this.monitor();
 299+ }
 300+ },
 301+
 302+ /**
 303+ * Passes the Pause request to the plugin.
 304+ * calls parent "pause" to update interface
 305+ */
 306+ pause : function() {
 307+ this.parent_pause(); // update the interface if paused via native control
 308+ if ( this.getPlayerElement() ) {
 309+ try{
 310+ this.playerElement.playlist.togglePause();
 311+ } catch( e ){
 312+ mw.log("EmbedPlayerVlc could not pause video " + e);
 313+ }
 314+ }
 315+ },
 316+
 317+ /**
 318+ * Mutes the video
 319+ * calls parent "toggleMute" to update interface
 320+ */
 321+ toggleMute:function() {
 322+ this.parent_toggleMute();
 323+ if ( this.getPlayerElement() )
 324+ this.playerElement.audio.toggleMute();
 325+ },
 326+
 327+ /**
 328+ * Update the player volume
 329+ * @pram {Float} percent Percent of total volume
 330+ */
 331+ setPlayerElementVolume: function ( percent ) {
 332+ if ( this.getPlayerElement() ) {
 333+ this.playerElement.audio.volume = percent * 100;
 334+ }
 335+ },
 336+
 337+ /**
 338+ * Gets the current volume
 339+ * @return {Float} percent percent of total volume
 340+ */
 341+ getVolumen:function() {
 342+ if ( this.getPlayerElement() )
 343+ return this.playerElement.audio.volume / 100;
 344+ },
 345+
 346+ /**
 347+ * Passes fullscreen request to plugin
 348+ */
 349+ fullscreen : function() {
 350+ if ( this.playerElement ) {
 351+ if ( this.playerElement.video ){
 352+ try{
 353+ this.playerElement.video.toggleFullscreen();
 354+ } catch ( e ){
 355+ mw.log("VlcEmbed toggle fullscreen : possible error: " + e);
 356+ }
 357+ }
 358+ }
 359+ },
 360+
 361+ /**
 362+ * Get the embed vlc object
 363+ */
 364+ getPlayerElement : function() {
 365+ this.playerElement = $( '#' + this.pid ).get(0);
 366+ return this.playerElement;
 367+ }
 368+};
 369+
 370+} )( window.mediaWiki, window.jQuery );
\ No newline at end of file
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerVlc.js
___________________________________________________________________
Added: svn:mime-type
1371 + text/plain
Added: svn:eol-style
2372 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/iframeApi/mw.IFramePlayerApiClient.js
@@ -0,0 +1,204 @@
 2+/**
 3+* iFrame api mapping support
 4+*
 5+* Client side ( binds a given iFrames to expose the player api )
 6+*/
 7+
 8+( function( mw ) {
 9+
 10+mw.IFramePlayerApiClient = function( iframe, playerProxy ){
 11+ return this.init( iframe , playerProxy );
 12+}
 13+mw.IFramePlayerApiClient.prototype = {
 14+ // flag to register if the iframe is in fullscreen mode
 15+ inFullScreenMode: null,
 16+
 17+ 'exportedMethods': [
 18+ 'play',
 19+ 'pause'
 20+ ],
 21+ // Local store of the previous sate of player proxy
 22+ '_prevPlayerProxy': {},
 23+
 24+ // Stores the current playerProxy ( can be updated by user js )
 25+ 'init': function( iframe , playerProxy, options ){
 26+ this.iframe = iframe;
 27+ this.playerProxy = playerProxy;
 28+ // Set the iframe server
 29+ var srcParts = mw.parseUri( mw.absoluteUrl( $(this.iframe).attr('src') ) );
 30+ this.iframeServer = srcParts.protocol + '://' + srcParts.authority;
 31+
 32+ this.addPlayerSendApi();
 33+ this.addPlayerReciveApi();
 34+
 35+ this.addIframeFullscreenBinding();
 36+ },
 37+ 'addPlayerSendApi': function(){
 38+ var _this = this;
 39+
 40+ // Allow modules to extend the list of iframeExported bindings
 41+ $( mw ).trigger( 'AddIframePlayerMethods', [ this.exportedMethods ]);
 42+
 43+ $j.each( this.exportedMethods, function(na, method){
 44+ _this.playerProxy[ method ] = function(){
 45+ _this.postMessage( {
 46+ 'method' : method,
 47+ 'args' : $j.makeArray( arguments )
 48+ } );
 49+ };
 50+ });
 51+ },
 52+ 'addPlayerReciveApi': function(){
 53+ var _this = this;
 54+ $j.receiveMessage( function( event ){
 55+ _this.hanldeReciveMsg( event );
 56+ }, this.iframeServer);
 57+ },
 58+ 'addIframeFullscreenBinding': function(){
 59+ var _this = this;
 60+ parentsAbsoluteList = [];
 61+ var fullscreenMode = false;
 62+ var orgSize = {
 63+ 'width' : $( _this.iframe ).width(),
 64+ 'height' : $( _this.iframe ).height(),
 65+ 'position' : null
 66+ };
 67+
 68+ // Bind orientation change to resize player ( if fullscreen )
 69+ $j(window).bind( 'orientationchange', function(e){
 70+ if( _this.inFullScreenMode ){
 71+ doFullscreen();
 72+ }
 73+ });
 74+
 75+ var doFullscreen = function(){
 76+ _this.inFullScreenMode = true;
 77+ // Make the iframe fullscreen
 78+ $( _this.iframe ).css({
 79+ 'z-index': mw.getConfig( 'EmbedPlayer.FullScreenZIndex' ) + 1,
 80+ 'position': 'absolute',
 81+ 'top' : 0,
 82+ 'left' : 0,
 83+ 'width' : $(window).width(),
 84+ 'height' : $(window).height()
 85+ });
 86+
 87+ // Remove absolute css of the interface parents
 88+ $( _this.iframe ).parents().each( function() {
 89+ //mw.log(' parent : ' + $( this ).attr('id' ) + ' class: ' + $( this ).attr('class') + ' pos: ' + $( this ).css( 'position' ) );
 90+ if( $( this ).css( 'position' ) == 'absolute' ) {
 91+ parentsAbsoluteList.push( $( this ) );
 92+ $( this ).css( 'position', null );
 93+ }
 94+ } );
 95+ }
 96+ var restoreWindowMode = function(){
 97+ _this.inFullScreenMode = false;
 98+ $( _this.iframe ).css( orgSize );
 99+ // restore any parent absolute pos:
 100+ $(parentsAbsoluteList).each( function() {
 101+ $( this ).css( 'position', 'absolute' );
 102+ } );
 103+ };
 104+
 105+ $( this.playerProxy ).bind( 'onOpenFullScreen', doFullscreen);
 106+ $( this.playerProxy ).bind( 'onCloseFullScreen', restoreWindowMode);
 107+
 108+ },
 109+ /**
 110+ * Handle received events
 111+ */
 112+ 'hanldeReciveMsg': function( event ){
 113+ var _this = this;
 114+
 115+ // Decode the message
 116+ var msgObject = JSON.parse( event.data );
 117+ var playerAttributes = mw.getConfig( 'EmbedPlayer.Attributes' );
 118+
 119+ // Before we update local attributes check that the object has not been updated by user js
 120+ for( var attrName in playerAttributes ){
 121+ if( attrName != 'id' ){
 122+ if( _this._prevPlayerProxy[ attrName ] != _this.playerProxy[ attrName ] ){
 123+ //mw.log( "IFramePlayerApiClient:: User js update:" + attrName + ' set to: ' + this.playerProxy[ attrName ] + ' != old: ' + _this._prevPlayerProxy[ attrName ] );
 124+ // Send the updated attribute back to the iframe:
 125+ _this.postMessage({
 126+ 'attrName' : attrName,
 127+ 'attrValue' : _this.playerProxy[ attrName ]
 128+ });
 129+ }
 130+ }
 131+ }
 132+ // Update any attributes
 133+ if( msgObject.attributes ){
 134+ for( var i in msgObject.attributes ){
 135+ if( i != 'id' && i != 'class' && i != 'style' ){
 136+ try{
 137+ this.playerProxy[ i ] = msgObject.attributes[i];
 138+ this._prevPlayerProxy[i] = msgObject.attributes[i];
 139+ } catch( e ){
 140+ mw.log("Error could not set:" + i );
 141+ }
 142+ }
 143+ }
 144+ }
 145+ // Trigger any binding events
 146+ if( typeof msgObject.triggerName != 'undefined' && msgObject.triggerArgs != 'undefined') {
 147+ //mw.log('IFramePlayerApiClient:: trigger: ' + msgObject.triggerName );
 148+ $( _this.playerProxy ).trigger( msgObject.triggerName, msgObject.triggerArgs );
 149+ }
 150+ },
 151+ 'postMessage': function( msgObject ){
 152+ /*mw.log( "IFramePlayerApiClient:: postMessage(): " + JSON.stringify( msgObject ) +
 153+ ' iframe: ' + this.iframe + ' cw:' + this.iframe.contentWindow +
 154+ ' src: ' + mw.absoluteUrl( $( this.iframe ).attr('src') ) );*/
 155+ $j.postMessage(
 156+ JSON.stringify( msgObject ),
 157+ mw.absoluteUrl( $( this.iframe ).attr('src') ),
 158+ this.iframe.contentWindow
 159+ );
 160+ }
 161+};
 162+
 163+//Add the jQuery binding
 164+( function( $ ) {
 165+ $.fn.iFramePlayer = function( readyCallback ){
 166+ if( ! this.selector ){
 167+ this.selector = $( this ).get(0);
 168+ }
 169+ // Append '_ifp' ( iframe player ) to id of real iframe so that 'id', and 'src' attributes don't conflict
 170+ var originalIframeId = ( $( this.selector ).attr( 'id' ) )? $( this.selector ).attr( 'id' ) : Math.floor( 9999999 * Math.random() );
 171+
 172+ var iframePlayerId = originalIframeId + '_ifp' ;
 173+
 174+ // Append the div element proxy after the iframe
 175+ $( this.selector )
 176+ .attr('id', iframePlayerId)
 177+ .after(
 178+ $('<div />')
 179+ .attr( 'id', originalIframeId )
 180+ );
 181+
 182+ var playerProxy = $( '#' + originalIframeId ).get(0);
 183+ var iframe = $('#' + iframePlayerId).get(0);
 184+ if(!iframe){
 185+ mw.log("Error invalide iFramePlayer request");
 186+ return false;
 187+ }
 188+ if( !iframe['playerApi'] ){
 189+ iframe['playerApi'] = new mw.IFramePlayerApiClient( iframe, playerProxy );
 190+ }
 191+
 192+ // Allow modules to extend the 'iframe' based player
 193+ $( mw ).trigger( 'newIframePlayerClientSide', [ playerProxy ]);
 194+
 195+ // Bind the iFrame player ready callback
 196+ if( readyCallback ){
 197+ $( playerProxy ).bind( 'playerReady', readyCallback )
 198+ };
 199+
 200+ // Return the player proxy for chaining player events / attributes
 201+ return $( playerProxy );
 202+ };
 203+} )( jQuery );
 204+
 205+} )( window.mw );
\ No newline at end of file
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/iframeApi/mw.IFramePlayerApiClient.js
___________________________________________________________________
Added: svn:mime-type
1206 + text/plain
Added: svn:eol-style
2207 + native
Added: svn:executable
3208 + *
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/iframeApi/mw.IFramePlayerApiServer.js
@@ -0,0 +1,227 @@
 2+/**
 3+* iFrame api mapping support
 4+*
 5+* enables player api to be accesses cross domain as
 6+* if the video element was in page dom
 7+*
 8+* native support in:
 9+* * Internet Explorer 8.0+
 10+* * Firefox 3.0+
 11+* * Safari 4.0+
 12+* * Google Chrome 1.0+
 13+* * Opera 9.5+
 14+*
 15+* fallback iframe cross domain hack will target IE6/7
 16+*/
 17+
 18+( function( mw ) {
 19+
 20+
 21+// Bind apiServer to EmbedPlayerNewPlayer:
 22+$( mw ).bind( 'EmbedPlayerNewPlayer', function( event, embedPlayer ) {
 23+ // Check if the iFrame player api is enabled and we have a parent iframe url:
 24+ if ( mw.getConfig('EmbedPlayer.EnableIframeApi')
 25+ &&
 26+ mw.getConfig( 'EmbedPlayer.IframeParentUrl' )
 27+ ){
 28+ embedPlayer['iFrameServer'] = new mw.IFramePlayerApiServer( embedPlayer );
 29+ }
 30+});
 31+
 32+mw.IFramePlayerApiServer = function( embedPlayer ){
 33+ this.init( embedPlayer );
 34+}
 35+
 36+mw.IFramePlayerApiServer.prototype = {
 37+ // Exported bindings / events. ( all the native html5 events are added in 'init' )
 38+ 'exportedBindings': [
 39+ 'playerReady',
 40+ 'monitorEvent',
 41+ 'onOpenFullScreen',
 42+ 'onCloseFullScreen'
 43+ ],
 44+
 45+ 'init': function( embedPlayer ){
 46+ this.embedPlayer = embedPlayer;
 47+
 48+ // Add the list of native events to the exportedBindings
 49+ for( var i =0 ; i < mw.EmbedPlayerNative.nativeEvents.length; i++ ){
 50+ var bindName = mw.EmbedPlayerNative.nativeEvents[i];
 51+ // The progress event fires too often for the iframe proxy ( instead use mwEmbed monitorEvent )
 52+ if( bindName != 'progress' ) {
 53+ this.exportedBindings.push( bindName );
 54+ }
 55+ }
 56+
 57+ // Allow modules to extend the list of iframeExported bindings
 58+ $( mw ).trigger( 'AddIframePlayerBindings', [ this.exportedBindings ]);
 59+
 60+ this.addIframeListener();
 61+ this.addIframeSender();
 62+ $( mw ).trigger( 'newIframePlayerServerSide', [embedPlayer]);
 63+ },
 64+
 65+ /**
 66+ * Listens to requested methods and triggers their action
 67+ */
 68+ 'addIframeListener': function(){
 69+ var _this = this;
 70+ mw.log('IFramePlayerApiServer::_addIframeListener');
 71+ $j.receiveMessage( function( event ) {
 72+ _this.hanldeMsg( event );
 73+ }, this.getParentUrl() );
 74+ },
 75+ getParentUrl: function(){
 76+ var purl = mw.getConfig( 'EmbedPlayer.IframeParentUrl' );
 77+ if(!purl){
 78+ mw.log("Error: iFramePlayerApiServer:: could not parse parent url. \n" +
 79+ "Player events will be dissabled");
 80+ }
 81+ return purl;
 82+ },
 83+ /**
 84+ * Add iframe sender bindings:
 85+ */
 86+ 'addIframeSender': function(){
 87+ var _this = this;
 88+ // Get the parent page URL as it was passed in, for browsers that don't support
 89+ // window.postMessage (this URL could be hard-coded).
 90+
 91+ // Set the initial attributes once player is "ready"
 92+ $( this.embedPlayer ).bind( 'playerReady', function(){
 93+ _this.sendPlayerAttributes();
 94+ });
 95+ // On monitor event package the attributes for cross domain delivery:
 96+ $( this.embedPlayer ).bind( 'monitorEvent', function(){
 97+ _this.sendPlayerAttributes();
 98+ })
 99+
 100+ $j.each( this.exportedBindings, function( inx, bindName ){
 101+ $( _this.embedPlayer ).bind( bindName, function( event ){
 102+ var argSet = $j.makeArray( arguments );
 103+ // remove the event from the arg set
 104+ argSet.shift();
 105+ // protect against a jQuery event getting past as an arguments:
 106+ if( argSet[0] && argSet[0].originalEvent ){
 107+ argSet.shift();
 108+ }
 109+ //mw.log("IFramePlayerApiServer::postMessage:: " + bindName + ' arg count:' + argSet.length );
 110+ _this.postMessage({
 111+ 'triggerName' : bindName,
 112+ 'triggerArgs' : argSet
 113+ })
 114+ });
 115+ });
 116+ },
 117+
 118+ /**
 119+ * Send all the player attributes to the host
 120+ */
 121+ 'sendPlayerAttributes': function(){
 122+ var _this = this;
 123+
 124+ var playerAttributes = mw.getConfig( 'EmbedPlayer.Attributes' );
 125+ var attrSet = { };
 126+ for( var i in playerAttributes ){
 127+ if( i != 'id' ){
 128+ if( typeof this.embedPlayer[ i ] != 'undefined' ){
 129+ attrSet[i] = this.embedPlayer[ i ];
 130+ }
 131+ }
 132+ }
 133+ //mw.log( "IframePlayerApiServer:: sendPlayerAttributes: " + JSON.stringify( attrSet ) );
 134+ _this.postMessage( {
 135+ 'attributes' : attrSet
 136+ } )
 137+ },
 138+
 139+ 'postMessage': function( msgObject ){
 140+ try {
 141+ var messageString = JSON.stringify( msgObject );
 142+ } catch ( e ){
 143+ mw.log("Error: could not JSON object: " + msgObject + ' ' + e);
 144+ return ;
 145+ }
 146+ // By default postMessage sends the message to the parent frame:
 147+ $j.postMessage(
 148+ messageString,
 149+ this.getParentUrl(),
 150+ window.parent
 151+ );
 152+ },
 153+
 154+ /**
 155+ * Handle a message event and pass it off to the embedPlayer
 156+ *
 157+ * @param {string} event
 158+ */
 159+ 'hanldeMsg': function( event ){
 160+ mw.log( 'IFramePlayerApiServer:: hanldeMsg: ' + event.data );
 161+ // Check if the server should even be enabled
 162+ if( !mw.getConfig( 'EmbedPlayer.EnableIframeApi' )){
 163+ mw.log( 'Error: Loading iFrame playerApi but config EmbedPlayer.EnableIframeApi is false');
 164+ return false;
 165+ }
 166+
 167+ if( !this.eventDomainCheck( event.origin ) ){
 168+ mw.log( 'Error: ' + event.origin + ' domain origin not allowed to send player events');
 169+ return false;
 170+ }
 171+ // Decode the message
 172+ var msgObject = JSON.parse( event.data );
 173+
 174+ // Call a method:
 175+ if( msgObject.method && this.embedPlayer[ msgObject.method ] ){
 176+ this.embedPlayer[ msgObject.method ].apply( this.embedPlayer, $j.makeArray( msgObject.args ) );
 177+ }
 178+ // Update a attribute
 179+ if( typeof msgObject.attrName != 'undefined' && typeof msgObject.attrValue != 'undefined' ){
 180+ try{
 181+ $( this.embedPlayer ).attr( msgObject.attrName, msgObject.attrValue)
 182+ } catch(e){
 183+ // possible error cant set attribute msgObject.attrName
 184+ }
 185+ }
 186+ },
 187+
 188+ /**
 189+ * Check an origin domain against the configuration value: 'EmbedPLayer.IFramePlayer.DomainWhiteList'
 190+ * Returns true if the origin domain is allowed to communicate with the embedPlayer
 191+ * otherwise returns false.
 192+ *
 193+ * @parma {string} origin
 194+ * The origin domain to be checked
 195+ */
 196+ 'eventDomainCheck': function( origin ){
 197+ if( mw.getConfig( 'EmbedPLayer.IFramePlayer.DomainWhiteList' ) ){
 198+ // NOTE this is very similar to the apiProxy function:
 199+ var domainWhiteList = mw.getConfig('EmbedPLayer.IFramePlayer.DomainWhiteList');
 200+ if( domainWhiteList == '*' ){
 201+ // The default very permissive state
 202+ return true;
 203+ }
 204+ // @@FIXME we should also check protocol to avoid
 205+ // http vs https
 206+ var originDomain = mw.parseUri( origin ).host;
 207+
 208+ // Check the domains:
 209+ for ( var i =0; i < domainWhiteList.length; i++ ) {
 210+ whiteDomain = domainWhiteList[i];
 211+ // Check if domain check is a RegEx:
 212+ if( typeof whiteDomain == 'object' ){
 213+ if( originDomain.match( whiteDomain ) ) {
 214+ return true;
 215+ }
 216+ } else {
 217+ if( originDomain == whiteDomain ){
 218+ return true;
 219+ }
 220+ }
 221+ }
 222+ }
 223+ // If no passing domain was found return false
 224+ return false;
 225+ }
 226+};
 227+
 228+} )( window.mw );
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/iframeApi/mw.IFramePlayerApiServer.js
___________________________________________________________________
Added: svn:mime-type
1229 + text/plain
Added: svn:eol-style
2230 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerNative.js
@@ -0,0 +1,697 @@
 2+/**
 3+* Native embed library:
 4+*
 5+* Enables embedPlayer support for native html5 browser playback system
 6+*/
 7+( function( mw, $ ) {
 8+
 9+mw.EmbedPlayerNative = {
 10+
 11+ //Instance Name
 12+ instanceOf: 'Native',
 13+
 14+ // Flag to only load the video ( not play it )
 15+ onlyLoadFlag:false,
 16+
 17+ //Callback fired once video is "loaded"
 18+ onLoadedCallback: null,
 19+
 20+ // The previous "currentTime" to sniff seek actions
 21+ // NOTE the bug where onSeeked does not seem fire consistently may no longer be applicable
 22+ prevCurrentTime: -1,
 23+
 24+ // Store the progress event ( updated during monitor )
 25+ progressEventData: null,
 26+
 27+ // If the media loaded event has been fired
 28+ mediaLoadedFlag: null,
 29+
 30+ // All the native events per:
 31+ // http://www.w3.org/TR/html5/video.html#mediaevents
 32+ nativeEvents : [
 33+ 'loadstart',
 34+ 'progress',
 35+ 'suspend',
 36+ 'abort',
 37+ 'error',
 38+ 'emptied',
 39+ 'stalled',
 40+ 'play',
 41+ 'pause',
 42+ 'loadedmetadata',
 43+ 'loadeddata',
 44+ 'waiting',
 45+ 'playing',
 46+ 'canplay',
 47+ 'canplaythough',
 48+ 'seeking',
 49+ 'seeked',
 50+ 'timeupdate',
 51+ 'ended',
 52+ 'ratechange',
 53+ 'durationchange',
 54+ 'volumechange'
 55+ ],
 56+
 57+ // Native player supported feature set
 58+ supports: {
 59+ 'playHead' : true,
 60+ 'sourceSwitch': true,
 61+ 'pause' : true,
 62+ 'fullscreen' : true,
 63+ 'timeDisplay' : true,
 64+ 'volumeControl' : true,
 65+ 'overlays' : true
 66+ },
 67+
 68+ /**
 69+ * Updates the supported features given the "type of player"
 70+ */
 71+ updateFeatureSupport: function(){
 72+ // The native controls function checks for overly support
 73+ // especially the special case of iPad in-dom or not support
 74+ if( this.useNativePlayerControls() ) {
 75+ this.supports.overlays = false;
 76+ this.supports.volumeControl = false;
 77+ }
 78+ // iOS does not support volume control ( only iPad can have controls )
 79+ if( mw.isIpad() ){
 80+ this.supports.volumeControl = false;
 81+ }
 82+ this.parent_updateFeatureSupport();
 83+ },
 84+
 85+ /**
 86+ * Return the embed code
 87+ */
 88+ doEmbedHTML : function () {
 89+ var _this = this;
 90+
 91+ // Reset some play state flags:
 92+ _this.bufferStartFlag = false;
 93+ _this.bufferEndFlag = false;
 94+
 95+ mw.log( "EmbedPlayerNative:: play url:" + this.getSrc( this.currentTime ) + ' startOffset: ' + this.start_ntp + ' end: ' + this.end_ntp );
 96+
 97+ // Check if using native controls and already the "pid" is already in the DOM
 98+ if( ( this.useNativePlayerControls()
 99+ ||
 100+ this.isPersistentNativePlayer()
 101+ )
 102+ && $( '#' + this.pid ).length
 103+ && typeof $( '#' + this.pid ).get(0).play != 'undefined' ) {
 104+
 105+ // Update the player source:
 106+ $( '#' + this.pid ).attr( 'src', this.getSrc( this.currentTime ) );
 107+ $( '#' + this.pid ).get(0).load();
 108+
 109+ _this.postEmbedJS();
 110+ return ;
 111+ }
 112+
 113+ $( this ).html(
 114+ _this.getNativePlayerHtml()
 115+ );
 116+
 117+ // Directly run postEmbedJS ( if playerElement is not available it will retry )
 118+ _this.postEmbedJS();
 119+ },
 120+
 121+ /**
 122+ * Get the native player embed code.
 123+ *
 124+ * @param {object} playerAttribtues Attributes to be override in function call
 125+ * @return {object} cssSet css to apply to the player
 126+ */
 127+ getNativePlayerHtml: function( playerAttribtues, cssSet ){
 128+ if( !playerAttribtues) {
 129+ playerAttribtues = {};
 130+ }
 131+ // Update required attributes
 132+ if( !playerAttribtues['id'] ) playerAttribtues['id'] = this.pid;
 133+ if( !playerAttribtues['src'] ) playerAttribtues['src'] = this.getSrc( this.currentTime);
 134+
 135+ // If autoplay pass along to attribute ( needed for iPad / iPod no js autoplay support
 136+ if( this.autoplay ) {
 137+ playerAttribtues['autoplay'] = 'true';
 138+ }
 139+
 140+ if( !cssSet ){
 141+ cssSet = {};
 142+ }
 143+ // Set default width height to 100% of parent container
 144+ if( !cssSet['width'] ) cssSet['width'] = '100%';
 145+ if( !cssSet['height'] ) cssSet['height'] = '100%';
 146+
 147+ // Also need to set the loop param directly for iPad / iPod
 148+ if( this.loop ) {
 149+ playerAttribtues['loop'] = 'true';
 150+ }
 151+
 152+ var tagName = ( this.isAudio() ) ? 'audio' : 'video';
 153+
 154+ return $( '<' + tagName + ' />' )
 155+ // Add the special nativeEmbedPlayer to avoid any rewrites of of this video tag.
 156+ .addClass( 'nativeEmbedPlayerPid' )
 157+ .attr( playerAttribtues )
 158+ .css( cssSet )
 159+ },
 160+
 161+ /**
 162+ * Post element javascript, binds event listeners and starts monitor
 163+ */
 164+ postEmbedJS: function() {
 165+ var _this = this;
 166+ mw.log( "f:native:postEmbedJS:" );
 167+
 168+ // Setup local pointer:
 169+ var vid = this.getPlayerElement();
 170+ // Apply media element bindings:
 171+ this.applyMediaElementBindings();
 172+
 173+ // Check for load flag
 174+ if ( this.onlyLoadFlag ) {
 175+ vid.pause();
 176+ vid.load();
 177+ } else {
 178+ // Issue play request
 179+ vid.play();
 180+ }
 181+
 182+ setTimeout( function() {
 183+ _this.monitor();
 184+ }, 100 );
 185+ },
 186+
 187+ /**
 188+ * Apply media element bindings
 189+ */
 190+ applyMediaElementBindings: function(){
 191+ var _this = this;
 192+ var vid = this.getPlayerElement();
 193+ if( ! vid ){
 194+ mw.log( " Error: applyMediaElementBindings without player elemnet");
 195+ return ;
 196+ }
 197+ $j.each( _this.nativeEvents, function( inx, eventName ){
 198+ $( vid ).bind( eventName , function(){
 199+ if( _this._propagateEvents ){
 200+ var argArray = $j.makeArray( arguments );
 201+ // Check if there is local handler:
 202+ if( _this['on' + eventName ] ){
 203+ _this['on' + eventName ].apply( _this, argArray);
 204+ } else {
 205+ // No local handler directly propagate the event to the abstract object:
 206+ $( _this ).trigger( eventName, argArray );
 207+ }
 208+ }
 209+ })
 210+ });
 211+ },
 212+
 213+ // basic monitor function to update buffer
 214+ monitor: function(){
 215+ var _this = this;
 216+ var vid = _this.getPlayerElement();
 217+
 218+ // Update duration
 219+ if( vid && vid.duration ){
 220+ this.duration = vid.duration;
 221+ }
 222+ // Update the bufferedPercent
 223+ if( vid && vid.buffered && vid.buffered.end && vid.duration ) {
 224+ this.bufferedPercent = ( vid.buffered.end(0) / vid.duration );
 225+ }
 226+ _this.parent_monitor();
 227+ },
 228+
 229+
 230+ /**
 231+ * Issue a seeking request.
 232+ *
 233+ * @param {Float} percentage
 234+ */
 235+ doSeek: function( percentage ) {
 236+ mw.log( 'Native::doSeek p: ' + percentage + ' : ' + this.supportsURLTimeEncoding() + ' dur: ' + this.getDuration() + ' sts:' + this.seek_time_sec );
 237+ this.seeking = true;
 238+
 239+ // Run the onSeeking interface update
 240+ this.controlBuilder.onSeek();
 241+
 242+ // @@todo check if the clip is loaded here (if so we can do a local seek)
 243+ if ( this.supportsURLTimeEncoding() ) {
 244+ // Make sure we could not do a local seek instead:
 245+ if ( percentage < this.bufferedPercent && this.playerElement.duration && !this.didSeekJump ) {
 246+ mw.log( "do local seek " + percentage + ' is already buffered < ' + this.bufferedPercent );
 247+ this.doNativeSeek( percentage );
 248+ } else {
 249+ // We support URLTimeEncoding call parent seek:
 250+ this.parent_doSeek( percentage );
 251+ }
 252+ } else if ( this.playerElement && this.playerElement.duration ) {
 253+ // (could also check bufferedPercent > percentage seek (and issue oggz_chop request or not)
 254+ this.doNativeSeek( percentage );
 255+ } else {
 256+ // try to do a play then seek:
 257+ this.doPlayThenSeek( percentage )
 258+ }
 259+ },
 260+
 261+ /**
 262+ * Do a native seek by updating the currentTime
 263+ * @param {float} percentage
 264+ * Percent to seek to of full time
 265+ */
 266+ doNativeSeek: function( percentage ) {
 267+ var _this = this;
 268+ mw.log( 'native::doNativeSeek::' + percentage );
 269+ this.seeking = true;
 270+ this.seek_time_sec = 0;
 271+ this.setCurrentTime( ( percentage * this.duration ) , function(){
 272+ _this.seeking = false;
 273+ _this.monitor();
 274+ })
 275+ },
 276+
 277+ /**
 278+ * Seek in a existing stream
 279+ *
 280+ * @param {Float} percentage
 281+ * Percentage of the stream to seek to between 0 and 1
 282+ */
 283+ doPlayThenSeek: function( percentage ) {
 284+ mw.log( 'native::doPlayThenSeek::' );
 285+ var _this = this;
 286+ this.play();
 287+ var retryCount = 0;
 288+ var readyForSeek = function() {
 289+ _this.getPlayerElement();
 290+ // If we have duration then we are ready to do the seek
 291+ if ( _this.playerElement && _this.playerElement.duration ) {
 292+ _this.doNativeSeek( percentage );
 293+ } else {
 294+ // Try to get player for 40 seconds:
 295+ // (it would be nice if the onmetadata type callbacks where fired consistently)
 296+ if ( retryCount < 800 ) {
 297+ setTimeout( readyForSeek, 50 );
 298+ retryCount++;
 299+ } else {
 300+ mw.log( 'error:doPlayThenSeek failed' );
 301+ }
 302+ }
 303+ }
 304+ readyForSeek();
 305+ },
 306+
 307+ /**
 308+ * Set the current time with a callback
 309+ *
 310+ * @param {Float} position
 311+ * Seconds to set the time to
 312+ * @param {Function} callback
 313+ * Function called once time has been set.
 314+ */
 315+ setCurrentTime: function( time , callback, callbackCount ) {
 316+ var _this = this;
 317+ if( !callbackCount )
 318+ callbackCount = 0;
 319+ this.getPlayerElement();
 320+ if( _this.playerElement.readyState >= 1 ){
 321+ if( _this.playerElement.currentTime == time ){
 322+ callback();
 323+ return;
 324+ }
 325+ var once = function( event ) {
 326+ if( callback ){
 327+ callback();
 328+ }
 329+ _this.playerElement.removeEventListener( 'seeked', once, false );
 330+ };
 331+ // Assume we will get to add the Listener before the seek is done
 332+ _this.playerElement.addEventListener( 'seeked', once, false );
 333+ try {
 334+ _this.playerElement.currentTime = time;
 335+ } catch (e) {
 336+ mw.log("Could not seek to this point. Unbuffered point.");
 337+ callback();
 338+ return;
 339+ }
 340+ } else {
 341+ if( callbackCount >= 300 ){
 342+ mw.log("Error with seek request, media never in ready state");
 343+ return ;
 344+ }
 345+ setTimeout( function(){
 346+ _this.setCurrentTime( time, callback , callbackCount++);
 347+ }, 10 );
 348+ }
 349+ },
 350+
 351+ /**
 352+ * Get the embed player time
 353+ */
 354+ getPlayerElementTime: function() {
 355+ var _this = this;
 356+ // Make sure we have .vid obj
 357+ this.getPlayerElement();
 358+
 359+ if ( !this.playerElement ) {
 360+ mw.log( 'mwEmbedPlayer::getPlayerElementTime: ' + this.id + ' not in dom ( stop monitor)' );
 361+ return false;
 362+ }
 363+ // Return the playerElement currentTime
 364+ return this.playerElement.currentTime;
 365+ },
 366+
 367+ // Update the poster src ( updates the native object if in dom )
 368+ updatePosterSrc: function( src ){
 369+ if( this.getPlayerElement() ){
 370+ this.getPlayerElement().poster = src;
 371+ }
 372+ // Also update the embedPlayer poster
 373+ this.parent_updatePosterSrc( src );
 374+ },
 375+
 376+ /**
 377+ * switchPlaySrc switches the player source working around a few bugs in browsers
 378+ *
 379+ * @param {string}
 380+ * src Video url Source to switch to.
 381+ * @param {function}
 382+ * switchCallback Function to call once the source has been switched
 383+ * @param {function}
 384+ * doneCallback Function to call once the clip has completed playback
 385+ */
 386+ switchPlaySrc: function( src, switchCallback, doneCallback ){
 387+ var _this = this;
 388+ mw.log( 'EmbedPlayerNative:: switchPlaySrc:' + src + ' native time: ' + this.getPlayerElement().currentTime );
 389+ // Update some parent embedPlayer vars:
 390+ this.duration = 0;
 391+ this.currentTime = 0;
 392+ this.previousTime = 0;
 393+ var vid = this.getPlayerElement();
 394+ if ( vid ) {
 395+ try {
 396+ // issue a play request on the source
 397+ vid.play();
 398+ setTimeout(function(){
 399+ // Remove all native player bindings
 400+ $(vid).unbind();
 401+ vid.pause();
 402+ var orginalControlsState = vid.controls;
 403+ // Hide controls ( to not display native play button while switching sources )
 404+ vid.removeAttribute('controls');
 405+
 406+ // Local scope update source and play function to work around google chrome bug
 407+ var updateSrcAndPlay = function() {
 408+ var vid = _this.getPlayerElement();
 409+ if (!vid){
 410+ mw.log( 'Error: switchPlaySrc no vid');
 411+ return ;
 412+ }
 413+ vid.src = src;
 414+ // Give iOS 50ms to figure out the src got updated ( iPad OS 4.0 )
 415+ setTimeout(function() {
 416+ var vid = _this.getPlayerElement();
 417+ if (!vid){
 418+ mw.log( 'Error: switchPlaySrc no vid');
 419+ return ;
 420+ }
 421+ vid.load();
 422+ vid.play();
 423+ // Wait another 100ms then bind the end event and any custom events
 424+ // for the switchCallback
 425+ setTimeout(function() {
 426+ var vid = _this.getPlayerElement();
 427+ // restore controls
 428+ vid.controls = orginalControlsState;
 429+ // add the end binding:
 430+ $(vid).bind('ended', function( event ) {
 431+ if(typeof doneCallback == 'function' ){
 432+ doneCallback();
 433+ }
 434+ return false;
 435+ });
 436+ if (typeof switchCallback == 'function') {
 437+ switchCallback(vid);
 438+ }
 439+ }, 100);
 440+ }, 100);
 441+ };
 442+ if (navigator.userAgent.toLowerCase().indexOf('chrome') != -1) {
 443+ // Null the src and wait 50ms ( helps unload video without crashing
 444+ // google chrome 7.x )
 445+ vid.src = '';
 446+ setTimeout(updateSrcAndPlay, 100);
 447+ } else {
 448+ updateSrcAndPlay();
 449+ }
 450+ }, 100 );
 451+ } catch (e) {
 452+ mw.log("Error: Error in swiching source playback");
 453+ }
 454+ }
 455+ },
 456+
 457+ /**
 458+ * Pause the video playback
 459+ * calls parent_pause to update the interface
 460+ */
 461+ pause: function( ) {
 462+ this.getPlayerElement();
 463+ this.parent_pause(); // update interface
 464+ if ( this.playerElement ) { // update player
 465+ if( !this.playerElement.paused ){
 466+ this.playerElement.pause();
 467+ }
 468+ }
 469+ },
 470+
 471+ /**
 472+ * Play back the video stream
 473+ * calls parent_play to update the interface
 474+ */
 475+ play: function( ) {
 476+ this.getPlayerElement();
 477+ this.parent_play(); // update interface
 478+ if ( this.playerElement && this.playerElement.play ) {
 479+ // issue a play request if the media is paused:
 480+ if( this.playerElement.paused ){
 481+ this.playerElement.play();
 482+ }
 483+ // re-start the monitor:
 484+ this.monitor();
 485+ }
 486+ },
 487+ /**
 488+ * Stop the player ( end all listeners )
 489+ */
 490+ stop:function(){
 491+ if( this.playerElement ){
 492+ $( this.playerElement ).unbind();
 493+ }
 494+ this.parent_stop();
 495+ },
 496+
 497+ /**
 498+ * Toggle the Mute
 499+ * calls parent_toggleMute to update the interface
 500+ */
 501+ toggleMute: function() {
 502+ this.parent_toggleMute();
 503+ this.getPlayerElement();
 504+ if ( this.playerElement )
 505+ this.playerElement.muted = this.muted;
 506+ },
 507+
 508+ /**
 509+ * Update Volume
 510+ *
 511+ * @param {Float} percentage Value between 0 and 1 to set audio volume
 512+ */
 513+ setPlayerElementVolume : function( percentage ) {
 514+ if ( this.getPlayerElement() ) {
 515+ // Disable mute if positive volume
 516+ if( percentage != 0 ) {
 517+ this.playerElement.muted = false;
 518+ }
 519+ this.playerElement.volume = percentage;
 520+ }
 521+ },
 522+
 523+ /**
 524+ * get Volume
 525+ *
 526+ * @return {Float}
 527+ * Audio volume between 0 and 1.
 528+ */
 529+ getPlayerElementVolume: function() {
 530+ if ( this.getPlayerElement() ) {
 531+ return this.playerElement.volume;
 532+ }
 533+ },
 534+ /**
 535+ * get the native muted state
 536+ */
 537+ getPlayerElementMuted: function(){
 538+ if ( this.getPlayerElement() ) {
 539+ return this.playerElement.muted;
 540+ }
 541+ },
 542+
 543+ /**
 544+ * Get the native media duration
 545+ */
 546+ getNativeDuration: function() {
 547+ if ( this.playerElement ) {
 548+ return this.playerElement.duration;
 549+ }
 550+ },
 551+
 552+ /**
 553+ * load the video stream with a callback fired once the video is "loaded"
 554+ *
 555+ * @parma {Function} callbcak Function called once video is loaded
 556+ */
 557+ load: function( callback ) {
 558+ this.getPlayerElement();
 559+ if ( !this.playerElement ) {
 560+ // No vid loaded
 561+ mw.log( 'native::load() ... doEmbed' );
 562+ this.onlyLoadFlag = true;
 563+ this.doEmbedHTML();
 564+ this.onLoadedCallback = callback;
 565+ } else {
 566+ // Should not happen offten
 567+ this.playerElement.load();
 568+ if( callback)
 569+ callback();
 570+ }
 571+ },
 572+
 573+ /**
 574+ * Get /update the playerElement value
 575+ */
 576+ getPlayerElement: function () {
 577+ this.playerElement = $( '#' + this.pid ).get( 0 );
 578+ return this.playerElement;
 579+ },
 580+
 581+ /**
 582+ * Bindings for the Video Element Events
 583+ */
 584+
 585+ /**
 586+ * Local method for seeking event
 587+ * fired when "seeking"
 588+ */
 589+ onseeking: function() {
 590+ mw.log( "native:onSeeking");
 591+ // Trigger the html5 seeking event
 592+ //( if not already set from interface )
 593+ if( !this.seeking ) {
 594+ this.seeking = true;
 595+
 596+ // Run the onSeeking interface update
 597+ this.controlBuilder.onSeek();
 598+
 599+ // Trigger the html5 "seeking" trigger
 600+ mw.log("native:seeking:trigger:: " + this.seeking);
 601+ $( this ).trigger( 'seeking' );
 602+ }
 603+ },
 604+
 605+ /**
 606+ * Local method for seeked event
 607+ * fired when done seeking
 608+ */
 609+ onseeked: function() {
 610+ mw.log("native:onSeeked");
 611+ // Trigger the html5 action on the parent
 612+ if( this.seeking ){
 613+ this.seeking = false;
 614+ $( this ).trigger( 'seeked' );
 615+ }
 616+ this.seeking = false;
 617+ },
 618+
 619+ /**
 620+ * Handle the native paused event
 621+ */
 622+ onpause: function(){
 623+ mw.log( "EmbedPlayer:native: OnPaused:: " + this._propagateEvents );
 624+ if( this._propagateEvents ){
 625+ this.parent_pause();
 626+ }
 627+ },
 628+
 629+ /**
 630+ * Handle the native play event
 631+ */
 632+ onplay: function(){
 633+ mw.log("EmbedPlayer:native:: OnPlay::" + this._propagateEvents );
 634+ // Update the interface ( if paused )
 635+ if( this._propagateEvents ){
 636+ this.parent_play();
 637+ }
 638+ },
 639+
 640+ /**
 641+ * Local method for metadata ready
 642+ * fired when metadata becomes available
 643+ *
 644+ * Used to update the media duration to
 645+ * accurately reflect the src duration
 646+ */
 647+ onloadedmetadata: function() {
 648+ this.getPlayerElement();
 649+ if ( this.playerElement && ! isNaN( this.playerElement.duration ) ) {
 650+ mw.log( 'f:onloadedmetadata metadata ready Update duration:' + this.playerElement.duration + ' old dur: ' + this.getDuration() );
 651+ this.duration = this.playerElement.duration;
 652+ }
 653+
 654+ //Fire "onLoaded" flags if set
 655+ if( typeof this.onLoadedCallback == 'function' ) {
 656+ this.onLoadedCallback();
 657+ }
 658+
 659+ // Trigger "media loaded"
 660+ if( ! this.mediaLoadedFlag ){
 661+ $( this ).trigger( 'mediaLoaded' );
 662+ this.mediaLoadedFlag = true;
 663+ }
 664+ },
 665+
 666+ /**
 667+ * Local method for progress event
 668+ * fired as the video is downloaded / buffered
 669+ *
 670+ * Used to update the bufferedPercent
 671+ *
 672+ * Note: this way of updating buffer was only supported in Firefox 3.x and
 673+ * not supported in Firefox 4.x
 674+ */
 675+ onprogress: function( event ) {
 676+ var e = event.originalEvent;
 677+ if( e && e.loaded && e.total ) {
 678+ this.bufferedPercent = e.loaded / e.total;
 679+ this.progressEventData = e.loaded;
 680+ }
 681+ },
 682+
 683+ /**
 684+ * Local method for progress event
 685+ * fired as the video is downloaded / buffered
 686+ *
 687+ * Used to update the bufferedPercent
 688+ */
 689+ onended: function() {
 690+ var _this = this;
 691+ mw.log( 'EmbedPlayer:native: onended:' + this.playerElement.currentTime + ' real dur:' + this.getDuration() + ' ended ' + this._propagateEvents );
 692+ if( this._propagateEvents ){
 693+ this.onClipDone();
 694+ }
 695+ }
 696+};
 697+
 698+} )( window.mediaWiki, window.jQuery );
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerNative.js
___________________________________________________________________
Added: svn:mime-type
1699 + text/plain
Added: svn:eol-style
2700 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerKplayer.js
@@ -0,0 +1,1252 @@
 2+/*
 3+ * The "kaltura player" embedPlayer interface for fallback h.264 and flv video format support
 4+ */
 5+// Called from the kdp.swf
 6+( function( mw, $ ) {
 7+
 8+function jsInterfaceReadyFunc() {
 9+ return true;
 10+}
 11+
 12+mw.EmbedPlayerKplayer = {
 13+
 14+ // Instance name:
 15+ instanceOf : 'Kplayer',
 16+
 17+ // List of supported features:
 18+ supports : {
 19+ 'playHead' : true,
 20+ 'pause' : true,
 21+ 'stop' : true,
 22+ 'timeDisplay' : true,
 23+ 'volumeControl' : true,
 24+ 'overlays' : true,
 25+ 'fullscreen' : true
 26+ },
 27+
 28+ // Stores the current time as set from flash
 29+ flashCurrentTime : 0,
 30+
 31+ /*
 32+ * Write the Embed html to the target
 33+ */
 34+ doEmbedHTML : function() {
 35+ var _this = this;
 36+
 37+ mw.log("kPlayer:: embed src::" + _this.getSrc());
 38+ var flashvars = {};
 39+ flashvars.autoPlay = "true";
 40+ var playerPath = mw.getMwEmbedPath() + 'modules/EmbedPlayer/binPlayers/kaltura-player';
 41+ flashvars.entryId = mw.absoluteUrl(_this.getSrc());
 42+
 43+ // Use a relative url if the protocal is file://
 44+ if (mw.parseUri(document.URL).protocol == 'file') {
 45+ playerPath = mw.getRelativeMwEmbedPath() + 'modules/EmbedPlayer/binPlayers/kaltura-player';
 46+ flashvars.entryId = _this.getSrc();
 47+ }
 48+
 49+ flashvars.debugMode = "true";
 50+ flashvars.fileSystemMode = "true";
 51+ flashvars.widgetId = "_7463";
 52+ flashvars.partnerId = "7463";
 53+ flashvars.pluginDomain = "kdp3/plugins/";
 54+ flashvars.kml = "local";
 55+ flashvars.kmlPath = playerPath + '/config.xml';
 56+ flashvars.sourceType = "url";
 57+
 58+ // flashvars.host = "www.kaltura.com";
 59+ flashvars.externalInterfaceDisabled = 'false';
 60+ flashvars.skinPath = playerPath + '/skin.swf';
 61+
 62+ flashvars["full.skinPath"] = playerPath + '/LightDoodleskin.swf';
 63+
 64+ var params = {};
 65+ params.quality = "best";
 66+ params.wmode = "opaque";
 67+ params.allowfullscreen = "true";
 68+ params.allowscriptaccess = "always";
 69+
 70+ var attributes = {};
 71+ attributes.id = this.pid;
 72+ attributes.name = this.pid;
 73+
 74+ mw.log(" about to add the pid container");
 75+ $(this).html($('<div />').attr('id', this.pid + '_container'));
 76+ // Call swm dom loaded function:
 77+ swfobject.callDomLoadFunctions();
 78+ // Do the flash embedding with embedSWF
 79+ swfobject.embedSWF(playerPath + "/kdp3.swf", this.pid + '_container',
 80+ '100%', '100%', "10.0.0", playerPath + "/expressInstall.swf",
 81+ flashvars, params, attributes);
 82+
 83+ // Direct object embed
 84+ /*
 85+ * $( this ).html( '<object
 86+ * classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="780"
 87+ * height="420">'+ '<param name="movie" value="myContent.swf" />'+ '<!--[if
 88+ * !IE]>-->'+ '<object type="application/x-shockwave-flash"
 89+ * data="myContent.swf" width="780" height="420">'+ '<!--<![endif]-->'+ '<p>
 90+ * error with flash embed</p>' '<!--[if !IE]>-->'+ '</object>'+ '<!--<![endif]-->'+ '</object>' )
 91+ */
 92+
 93+ setTimeout(function() {
 94+ _this.postEmbedJS();
 95+ }, 100);
 96+
 97+ // Flash player loses its bindings once it changes sizes::
 98+ $(_this).bind('onOpenFullScreen', function() {
 99+ _this.postEmbedJS();
 100+ });
 101+ $(_this).bind('onCloseFullScreen', function() {
 102+ _this.postEmbedJS();
 103+ });
 104+ },
 105+
 106+ /**
 107+ * javascript run post player embedding
 108+ */
 109+ postEmbedJS : function() {
 110+ var _this = this;
 111+ this.getPlayerElement();
 112+
 113+ var bindEventMap = {
 114+ 'doPause' : 'onPause',
 115+ 'doPlay' : 'onPlay',
 116+ 'durationChange' : 'onDurationChange',
 117+ 'playerPlayEnd' : 'onClipDone',
 118+ 'playerUpdatePlayhead' : 'onUpdatePlayhead',
 119+ 'bytesTotalChange' : 'onBytesTotalChange',
 120+ 'bytesDownloadedChange' : 'onBytesDownloadedChange'
 121+ };
 122+
 123+ if (this.playerElement && this.playerElement.addJsListener) {
 124+ $j.each( bindEventMap, function( bindName, localMethod ) {
 125+ _this.bindPlayerFunction(bindName, localMethod);
 126+ } );
 127+ // Start the monitor
 128+ this.monitor();
 129+ } else {
 130+ // Keep trying to get the player element
 131+ // mw.log('insert media: not defined:' + typeof
 132+ // this.playerElement.insertMedia );
 133+ setTimeout(function() {
 134+ _this.postEmbedJS();
 135+ }, 250);
 136+ }
 137+ },
 138+
 139+ /**
 140+ * Bind a Player Function,
 141+ *
 142+ * Does some tricker to bind to "this" player instance:
 143+ *
 144+ * @param {String}
 145+ * flash binding name
 146+ * @param {String}
 147+ * function callback name
 148+ */
 149+ bindPlayerFunction : function(bindName, methodName) {
 150+ // The kaltura kdp can only call a global function by given name
 151+ var gKdpCallbackName = methodName + '_cb_' + this.id;
 152+
 153+ // Create an anonymous function with local player scope
 154+ var createGlobalCB = function(cName, embedPlayer) {
 155+ window[ cName ] = function(data) {
 156+ if ( embedPlayer._propagateEvents ) {
 157+ embedPlayer[methodName](data);
 158+ }
 159+ };
 160+ }(gKdpCallbackName, this);
 161+
 162+ // Add the listener to the KDP flash player:
 163+ this.playerElement.addJsListener(bindName, gKdpCallbackName);
 164+ },
 165+
 166+ /**
 167+ * on Pause callback from the kaltura flash player calls parent_pause to
 168+ * update the interface
 169+ */
 170+ onPause : function() {
 171+ this.parent_pause();
 172+ },
 173+
 174+ /**
 175+ * onPlay function callback from the kaltura flash player directly call the
 176+ * parent_play
 177+ */
 178+ onPlay : function() {
 179+ this.parent_play();
 180+ },
 181+
 182+ onDurationChange : function(data, id) {
 183+ mw.log(" onDurationChange: " + data.newValue);
 184+ // update the duration:
 185+ this.duration = data.newValue;
 186+ },
 187+
 188+ /**
 189+ * play method calls parent_play to update the interface
 190+ */
 191+ play : function() {
 192+ if (this.playerElement && this.playerElement.sendNotification) {
 193+ this.playerElement.sendNotification('doPlay');
 194+ }
 195+ this.parent_play();
 196+ },
 197+
 198+ /**
 199+ * pause method calls parent_pause to update the interface
 200+ */
 201+ pause : function() {
 202+ if (this.playerElement && this.playerElement.sendNotification) {
 203+ this.playerElement.sendNotification('doPause');
 204+ }
 205+ this.parent_pause();
 206+ },
 207+
 208+ /**
 209+ * Issues a seek to the playerElement
 210+ *
 211+ * @param {Float}
 212+ * percentage Percentage of total stream length to seek to
 213+ */
 214+ doSeek : function(percentage) {
 215+ var _this = this;
 216+ var seekTime = percentage * this.getDuration();
 217+ mw.log( 'EmbedPlayerKalturaKplayer:: doSeek: ' + percentage + ' time:' + seekTime );
 218+ if (this.supportsURLTimeEncoding()) {
 219+
 220+ // Make sure we could not do a local seek instead:
 221+ if (!(percentage < this.bufferedPercent
 222+ && this.playerElement.duration && !this.didSeekJump)) {
 223+ // We support URLTimeEncoding call parent seek:
 224+ this.parent_doSeek( percentage );
 225+ return;
 226+ }
 227+ }
 228+
 229+ if (this.playerElement) {
 230+ // Issue the seek to the flash player:
 231+ this.playerElement.sendNotification('doSeek', seekTime);
 232+
 233+ // Kdp is missing seek done callback
 234+ setTimeout(function() {
 235+ _this.seeking = false;
 236+ }, 500);
 237+ } else {
 238+ // try to do a play then seek:
 239+ this.doPlayThenSeek(percentage);
 240+ }
 241+ // Run the onSeeking interface update
 242+ this.controlBuilder.onSeek();
 243+ },
 244+
 245+ /**
 246+ * Seek in a existing stream
 247+ *
 248+ * @param {Float}
 249+ * percentage Percentage of the stream to seek to between 0 and 1
 250+ */
 251+ doPlayThenSeek : function(percentage) {
 252+ mw.log('flash::doPlayThenSeek::');
 253+ var _this = this;
 254+ // issue the play request
 255+ this.play();
 256+
 257+ // let the player know we are seeking
 258+ _this.seeking = true;
 259+
 260+ var getPlayerCount = 0;
 261+ var readyForSeek = function() {
 262+ _this.getPlayerElement();
 263+ // if we have duration then we are ready to do the seek ( flash can't
 264+ // seek untill there is some buffer )
 265+ if (_this.playerElement && _this.playerElement.sendNotification
 266+ && _this.getDuration() && _this.bufferedPercent) {
 267+ var seekTime = percentage * _this.getDuration();
 268+ // Issue the seek to the flash player:
 269+ _this.playerElement.sendNotification('doSeek', seekTime);
 270+ } else {
 271+ // Try to get player for 20 seconds:
 272+ if (getPlayerCount < 400) {
 273+ setTimeout(readyForSeek, 50);
 274+ getPlayerCount++;
 275+ } else {
 276+ mw.log('Error:doPlayThenSeek failed');
 277+ }
 278+ }
 279+ };
 280+ readyForSeek();
 281+ },
 282+
 283+ /**
 284+ * Issues a volume update to the playerElement
 285+ *
 286+ * @param {Float}
 287+ * percentage Percentage to update volume to
 288+ */
 289+ setPlayerElementVolume : function(percentage) {
 290+ if (this.playerElement && this.playerElement.sendNotification) {
 291+ this.playerElement.sendNotification('changeVolume', percentage);
 292+ }
 293+ },
 294+
 295+ /**
 296+ * function called by flash at set interval to update the playhead.
 297+ */
 298+ onUpdatePlayhead : function(playheadValue) {
 299+ this.flashCurrentTime = playheadValue;
 300+ },
 301+
 302+ /**
 303+ * function called by flash when the total media size changes
 304+ */
 305+ onBytesTotalChange : function(data, id) {
 306+ this.bytesTotal = data.newValue;
 307+ },
 308+
 309+ /**
 310+ * function called by flash applet when download bytes changes
 311+ */
 312+ onBytesDownloadedChange : function(data, id) {
 313+ mw.log('onBytesDownloadedChange');
 314+ this.bytesLoaded = data.newValue;
 315+ this.bufferedPercent = this.bytesLoaded / this.bytesTotal;
 316+
 317+ // Fire the parent html5 action
 318+ $(this).trigger('progress', {
 319+ 'loaded' : this.bytesLoaded,
 320+ 'total' : this.bytesTotal
 321+ });
 322+ },
 323+
 324+ /**
 325+ * Get the embed player time
 326+ */
 327+ getPlayerElementTime : function() {
 328+ // update currentTime
 329+ return this.flashCurrentTime;
 330+ },
 331+
 332+ /**
 333+ * Get the embed fla object player Element
 334+ */
 335+ getPlayerElement : function() {
 336+ this.playerElement = document.getElementById(this.pid);
 337+ return this.playerElement;
 338+ }
 339+};
 340+
 341+/**
 342+ * function called once player is ready.
 343+ *
 344+ * NOTE: playerID is not always passed so we can't use this:
 345+ */
 346+function onKdpReady(playerId) {
 347+ mw.log("player is ready::" + playerId);
 348+}
 349+
 350+/*
 351+ * ! SWFObject v2.2 <http://code.google.com/p/swfobject/> is released under the
 352+ * MIT License <http://www.opensource.org/licenses/mit-license.php>
 353+ */
 354+
 355+var swfobject = function() {
 356+
 357+ var UNDEF = "undefined", OBJECT = "object", SHOCKWAVE_FLASH = "Shockwave Flash", SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", FLASH_MIME_TYPE = "application/x-shockwave-flash", EXPRESS_INSTALL_ID = "SWFObjectExprInst", ON_READY_STATE_CHANGE = "onreadystatechange",
 358+
 359+ win = window, doc = document, nav = navigator,
 360+
 361+ plugin = false, domLoadFnArr = [ main ], regObjArr = [], objIdArr = [], listenersArr = [], storedAltContent, storedAltContentId, storedCallbackFn, storedCallbackObj, isDomLoaded = false, isExpressInstallActive = false, dynamicStylesheet, dynamicStylesheetMedia, autoHideShow = true,
 362+
 363+ /*
 364+ * Centralized function for browser feature detection - User agent string
 365+ * detection is only used when no good alternative is possible - Is executed
 366+ * directly for optimal performance
 367+ */
 368+ ua = function() {
 369+ var w3cdom = typeof doc.getElementById != UNDEF
 370+ && typeof doc.getElementsByTagName != UNDEF
 371+ && typeof doc.createElement != UNDEF, u = nav.userAgent
 372+ .toLowerCase(), p = nav.platform.toLowerCase(), windows = p ? /win/
 373+ .test(p)
 374+ : /win/.test(u), mac = p ? /mac/.test(p) : /mac/.test(u), webkit = /webkit/
 375+ .test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,
 376+ "$1")) : false, // returns either the webkit version or false if
 377+ // not webkit
 378+ ie = !+"\v1", // feature detection based on Andrea Giammarchi's
 379+ // solution:
 380+ // http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
 381+ playerVersion = [ 0, 0, 0 ], d = null;
 382+ if (typeof nav.plugins != UNDEF
 383+ && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
 384+ d = nav.plugins[SHOCKWAVE_FLASH].description;
 385+ if (d
 386+ && !(typeof nav.mimeTypes != UNDEF
 387+ && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin
 388+ // indicates
 389+ // whether
 390+ // plug-ins
 391+ // are
 392+ // enabled
 393+ // or
 394+ // disabled
 395+ // in
 396+ // Safari
 397+ // 3+
 398+ plugin = true;
 399+ ie = false; // cascaded feature detection for Internet Explorer
 400+ d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
 401+ playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
 402+ playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"),
 403+ 10);
 404+ playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(
 405+ /^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
 406+ }
 407+ } else if (typeof win.ActiveXObject != UNDEF) {
 408+ try {
 409+ var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
 410+ if (a) { // a will return null when ActiveX is disabled
 411+ d = a.GetVariable("$version");
 412+ if (d) {
 413+ ie = true; // cascaded feature detection for Internet
 414+ // Explorer
 415+ d = d.split(" ")[1].split(",");
 416+ playerVersion = [ parseInt(d[0], 10),
 417+ parseInt(d[1], 10), parseInt(d[2], 10) ];
 418+ }
 419+ }
 420+ } catch (e) {
 421+ }
 422+ }
 423+ return {
 424+ w3 : w3cdom,
 425+ pv : playerVersion,
 426+ wk : webkit,
 427+ ie : ie,
 428+ win : windows,
 429+ mac : mac
 430+ };
 431+ }();
 432+
 433+ function callDomLoadFunctions() {
 434+ if (isDomLoaded) {
 435+ return;
 436+ }
 437+ try { // test if we can really add/remove elements to/from the DOM; we
 438+ // don't want to fire it too early
 439+ var t = doc.getElementsByTagName("body")[0]
 440+ .appendChild(createElement("span"));
 441+ t.parentNode.removeChild(t);
 442+ } catch (e) {
 443+ return;
 444+ }
 445+ isDomLoaded = true;
 446+ var dl = domLoadFnArr.length;
 447+ for ( var i = 0; i < dl; i++) {
 448+ domLoadFnArr[i]();
 449+ }
 450+ }
 451+
 452+ function addDomLoadEvent(fn) {
 453+ if (isDomLoaded) {
 454+ fn();
 455+ } else {
 456+ domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only
 457+ // available in IE5.5+
 458+ }
 459+ }
 460+
 461+ /*
 462+ * Cross-browser onload - Based on James Edwards' solution:
 463+ * http://brothercake.com/site/resources/scripts/onload/ - Will fire an
 464+ * event as soon as a web page including all of its assets are loaded
 465+ */
 466+ function addLoadEvent(fn) {
 467+ if (typeof win.addEventListener != UNDEF) {
 468+ win.addEventListener("load", fn, false);
 469+ } else if (typeof doc.addEventListener != UNDEF) {
 470+ doc.addEventListener("load", fn, false);
 471+ } else if (typeof win.attachEvent != UNDEF) {
 472+ addListener(win, "onload", fn);
 473+ } else if (typeof win.onload == "function") {
 474+ var fnOld = win.onload;
 475+ win.onload = function() {
 476+ fnOld();
 477+ fn();
 478+ };
 479+ } else {
 480+ win.onload = fn;
 481+ }
 482+ }
 483+
 484+ /*
 485+ * Main function - Will preferably execute onDomLoad, otherwise onload (as a
 486+ * fallback)
 487+ */
 488+ function main() {
 489+ if (plugin) {
 490+ testPlayerVersion();
 491+ } else {
 492+ matchVersions();
 493+ }
 494+ }
 495+
 496+ /*
 497+ * Detect the Flash Player version for non-Internet Explorer browsers -
 498+ * Detecting the plug-in version via the object element is more precise than
 499+ * using the plugins collection item's description: a. Both release and
 500+ * build numbers can be detected b. Avoid wrong descriptions by corrupt
 501+ * installers provided by Adobe c. Avoid wrong descriptions by multiple
 502+ * Flash Player entries in the plugin Array, caused by incorrect browser
 503+ * imports - Disadvantage of this method is that it depends on the
 504+ * availability of the DOM, while the plugins collection is immediately
 505+ * available
 506+ */
 507+ function testPlayerVersion() {
 508+ var b = doc.getElementsByTagName("body")[0];
 509+ var o = createElement(OBJECT);
 510+ o.setAttribute("type", FLASH_MIME_TYPE);
 511+ var t = b.appendChild(o);
 512+ if (t) {
 513+ var counter = 0;
 514+ (function() {
 515+ if (typeof t.GetVariable != UNDEF) {
 516+ try {
 517+ var d = t.GetVariable("$version");
 518+ if (d) {
 519+ d = d.split(" ")[1].split(",");
 520+ ua.pv = [ parseInt(d[0], 10), parseInt(d[1], 10),
 521+ parseInt(d[2], 10) ];
 522+ }
 523+ } catch (e) {
 524+ // error in grabbing flash version
 525+ }
 526+ } else if (counter < 10) {
 527+ counter++;
 528+ setTimeout(arguments.callee, 10);
 529+ return;
 530+ }
 531+ b.removeChild(o);
 532+ t = null;
 533+ matchVersions();
 534+ })();
 535+ } else {
 536+ matchVersions();
 537+ }
 538+ }
 539+
 540+ /*
 541+ * Perform Flash Player and SWF version matching; static publishing only
 542+ */
 543+ function matchVersions() {
 544+ var rl = regObjArr.length;
 545+ if (rl > 0) {
 546+ for ( var i = 0; i < rl; i++) { // for each registered object
 547+ // element
 548+ var id = regObjArr[i].id;
 549+ var cb = regObjArr[i].callbackFn;
 550+ var cbObj = {
 551+ success : false,
 552+ id : id
 553+ };
 554+ if (ua.pv[0] > 0) {
 555+ var obj = getElementById(id);
 556+ if (obj) {
 557+ if (hasPlayerVersion(regObjArr[i].swfVersion)
 558+ && !(ua.wk && ua.wk < 312)) { // Flash Player
 559+ // version >=
 560+ // published SWF
 561+ // version:
 562+ // Houston, we
 563+ // have a match!
 564+ setVisibility(id, true);
 565+ if (cb) {
 566+ cbObj.success = true;
 567+ cbObj.ref = getObjectById(id);
 568+ cb(cbObj);
 569+ }
 570+ } else if (regObjArr[i].expressInstall
 571+ && canExpressInstall()) { // show the Adobe
 572+ // Express Install
 573+ // dialog if set by
 574+ // the web page
 575+ // author and if
 576+ // supported
 577+ var att = {};
 578+ att.data = regObjArr[i].expressInstall;
 579+ att.width = obj.getAttribute("width") || "0";
 580+ att.height = obj.getAttribute("height") || "0";
 581+ if (obj.getAttribute("class")) {
 582+ att.styleclass = obj.getAttribute("class");
 583+ }
 584+ if (obj.getAttribute("align")) {
 585+ att.align = obj.getAttribute("align");
 586+ }
 587+ // parse HTML object param element's name-value
 588+ // pairs
 589+ var par = {};
 590+ var p = obj.getElementsByTagName("param");
 591+ var pl = p.length;
 592+ for ( var j = 0; j < pl; j++) {
 593+ if (p[j].getAttribute("name").toLowerCase() != "movie") {
 594+ par[p[j].getAttribute("name")] = p[j]
 595+ .getAttribute("value");
 596+ }
 597+ }
 598+ showExpressInstall(att, par, id, cb);
 599+ } else { // Flash Player and SWF version mismatch or
 600+ // an older Webkit engine that ignores the
 601+ // HTML object element's nested param
 602+ // elements: display alternative content
 603+ // instead of SWF
 604+ displayAltContent(obj);
 605+ if (cb) {
 606+ cb(cbObj);
 607+ }
 608+ }
 609+ }
 610+ } else { // if no Flash Player is installed or the fp version
 611+ // cannot be detected we let the HTML object element
 612+ // do its job (either show a SWF or alternative
 613+ // content)
 614+ setVisibility(id, true);
 615+ if (cb) {
 616+ var o = getObjectById(id); // test whether there is an
 617+ // HTML object element or
 618+ // not
 619+ if (o && typeof o.SetVariable != UNDEF) {
 620+ cbObj.success = true;
 621+ cbObj.ref = o;
 622+ }
 623+ cb(cbObj);
 624+ }
 625+ }
 626+ }
 627+ }
 628+ }
 629+
 630+ function getObjectById(objectIdStr) {
 631+ var r = null;
 632+ var o = getElementById(objectIdStr);
 633+ if (o && o.nodeName == "OBJECT") {
 634+ if (typeof o.SetVariable != UNDEF) {
 635+ r = o;
 636+ } else {
 637+ var n = o.getElementsByTagName(OBJECT)[0];
 638+ if (n) {
 639+ r = n;
 640+ }
 641+ }
 642+ }
 643+ return r;
 644+ }
 645+
 646+ /*
 647+ * Requirements for Adobe Express Install - only one instance can be active
 648+ * at a time - fp 6.0.65 or higher - Win/Mac OS only - no Webkit engines
 649+ * older than version 312
 650+ */
 651+ function canExpressInstall() {
 652+ return !isExpressInstallActive && hasPlayerVersion("6.0.65")
 653+ && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
 654+ }
 655+
 656+ /*
 657+ * Show the Adobe Express Install dialog - Reference:
 658+ * http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
 659+ */
 660+ function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
 661+ isExpressInstallActive = true;
 662+ storedCallbackFn = callbackFn || null;
 663+ storedCallbackObj = {
 664+ success : false,
 665+ id : replaceElemIdStr
 666+ };
 667+ var obj = getElementById(replaceElemIdStr);
 668+ if (obj) {
 669+ if (obj.nodeName == "OBJECT") { // static publishing
 670+ storedAltContent = abstractAltContent(obj);
 671+ storedAltContentId = null;
 672+ } else { // dynamic publishing
 673+ storedAltContent = obj;
 674+ storedAltContentId = replaceElemIdStr;
 675+ }
 676+ att.id = EXPRESS_INSTALL_ID;
 677+ if (typeof att.width == UNDEF
 678+ || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) {
 679+ att.width = "310";
 680+ }
 681+ if (typeof att.height == UNDEF
 682+ || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) {
 683+ att.height = "137";
 684+ }
 685+ doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
 686+ var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn", fv = "MMredirectURL="
 687+ + win.location.toString().replace(/&/g, "%26")
 688+ + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
 689+ if (typeof par.flashvars != UNDEF) {
 690+ par.flashvars += "&" + fv;
 691+ } else {
 692+ par.flashvars = fv;
 693+ }
 694+ // IE only: when a SWF is loading (AND: not available in cache) wait
 695+ // for the readyState of the object element to become 4 before
 696+ // removing it,
 697+ // because you cannot properly cancel a loading SWF file without
 698+ // breaking browser load references, also obj.onreadystatechange
 699+ // doesn't work
 700+ if (ua.ie && ua.win && obj.readyState != 4) {
 701+ var newObj = createElement("div");
 702+ replaceElemIdStr += "SWFObjectNew";
 703+ newObj.setAttribute("id", replaceElemIdStr);
 704+ obj.parentNode.insertBefore(newObj, obj); // insert
 705+ // placeholder div
 706+ // that will be
 707+ // replaced by the
 708+ // object element
 709+ // that loads
 710+ // expressinstall.swf
 711+ obj.style.display = "none";
 712+ (function() {
 713+ if (obj.readyState == 4) {
 714+ obj.parentNode.removeChild(obj);
 715+ } else {
 716+ setTimeout(arguments.callee, 10);
 717+ }
 718+ })();
 719+ }
 720+ createSWF(att, par, replaceElemIdStr);
 721+ }
 722+ }
 723+
 724+ /*
 725+ * Functions to abstract and display alternative content
 726+ */
 727+ function displayAltContent(obj) {
 728+ if (ua.ie && ua.win && obj.readyState != 4) {
 729+ // IE only: when a SWF is loading (AND: not available in cache) wait
 730+ // for the readyState of the object element to become 4 before
 731+ // removing it,
 732+ // because you cannot properly cancel a loading SWF file without
 733+ // breaking browser load references, also obj.onreadystatechange
 734+ // doesn't work
 735+ var el = createElement("div");
 736+ obj.parentNode.insertBefore(el, obj); // insert placeholder div
 737+ // that will be replaced by
 738+ // the alternative content
 739+ el.parentNode.replaceChild(abstractAltContent(obj), el);
 740+ obj.style.display = "none";
 741+ (function() {
 742+ if (obj.readyState == 4) {
 743+ obj.parentNode.removeChild(obj);
 744+ } else {
 745+ setTimeout(arguments.callee, 10);
 746+ }
 747+ })();
 748+ } else {
 749+ obj.parentNode.replaceChild(abstractAltContent(obj), obj);
 750+ }
 751+ }
 752+
 753+ function abstractAltContent(obj) {
 754+ var ac = createElement("div");
 755+ if (ua.win && ua.ie) {
 756+ ac.innerHTML = obj.innerHTML;
 757+ } else {
 758+ var nestedObj = obj.getElementsByTagName(OBJECT)[0];
 759+ if (nestedObj) {
 760+ var c = nestedObj.childNodes;
 761+ if (c) {
 762+ var cl = c.length;
 763+ for ( var i = 0; i < cl; i++) {
 764+ if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM")
 765+ && !(c[i].nodeType == 8)) {
 766+ ac.appendChild(c[i].cloneNode(true));
 767+ }
 768+ }
 769+ }
 770+ }
 771+ }
 772+ return ac;
 773+ }
 774+
 775+ /*
 776+ * Cross-browser dynamic SWF creation
 777+ */
 778+ function createSWF(attObj, parObj, id) {
 779+ var r, el = getElementById(id);
 780+ if (ua.wk && ua.wk < 312) {
 781+ return r;
 782+ }
 783+ if (el) {
 784+ if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the
 785+ // object element, it will
 786+ // inherit the 'id' from the
 787+ // alternative content
 788+ attObj.id = id;
 789+ }
 790+ if (ua.ie && ua.win) { // Internet Explorer + the HTML object
 791+ // element + W3C DOM methods do not combine:
 792+ // fall back to outerHTML
 793+ var att = "";
 794+ for ( var i in attObj) {
 795+ if (attObj[i] != Object.prototype[i]) { // filter out
 796+ // prototype
 797+ // additions from
 798+ // other potential
 799+ // libraries
 800+ if (i.toLowerCase() == "data") {
 801+ parObj.movie = attObj[i];
 802+ } else if (i.toLowerCase() == "styleclass") { // 'class'
 803+ // is an
 804+ // ECMA4
 805+ // reserved
 806+ // keyword
 807+ att += ' class="' + attObj[i] + '"';
 808+ } else if (i.toLowerCase() != "classid") {
 809+ att += ' ' + i + '="' + attObj[i] + '"';
 810+ }
 811+ }
 812+ }
 813+ var par = "";
 814+ for ( var j in parObj) {
 815+ if (parObj[j] != Object.prototype[j]) { // filter out
 816+ // prototype
 817+ // additions from
 818+ // other potential
 819+ // libraries
 820+ par += '<param name="' + j + '" value="' + parObj[j]
 821+ + '" />';
 822+ }
 823+ }
 824+ el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'
 825+ + att + '>' + par + '</object>';
 826+ objIdArr[objIdArr.length] = attObj.id; // stored to fix object
 827+ // 'leaks' on unload
 828+ // (dynamic publishing
 829+ // only)
 830+ r = getElementById(attObj.id);
 831+ } else { // well-behaving browsers
 832+ var o = createElement(OBJECT);
 833+ o.setAttribute("type", FLASH_MIME_TYPE);
 834+ for ( var m in attObj) {
 835+ if (attObj[m] != Object.prototype[m]) { // filter out
 836+ // prototype
 837+ // additions from
 838+ // other potential
 839+ // libraries
 840+ if (m.toLowerCase() == "styleclass") { // 'class' is an
 841+ // ECMA4
 842+ // reserved
 843+ // keyword
 844+ o.setAttribute("class", attObj[m]);
 845+ } else if (m.toLowerCase() != "classid") { // filter
 846+ // out IE
 847+ // specific
 848+ // attribute
 849+ o.setAttribute(m, attObj[m]);
 850+ }
 851+ }
 852+ }
 853+ for ( var n in parObj) {
 854+ if (parObj[n] != Object.prototype[n]
 855+ && n.toLowerCase() != "movie") { // filter out
 856+ // prototype
 857+ // additions
 858+ // from other
 859+ // potential
 860+ // libraries and
 861+ // IE specific
 862+ // param element
 863+ createObjParam(o, n, parObj[n]);
 864+ }
 865+ }
 866+ el.parentNode.replaceChild(o, el);
 867+ r = o;
 868+ }
 869+ }
 870+ return r;
 871+ }
 872+
 873+ function createObjParam(el, pName, pValue) {
 874+ var p = createElement("param");
 875+ p.setAttribute("name", pName);
 876+ p.setAttribute("value", pValue);
 877+ el.appendChild(p);
 878+ }
 879+
 880+ /*
 881+ * Cross-browser SWF removal - Especially needed to safely and completely
 882+ * remove a SWF in Internet Explorer
 883+ */
 884+ function removeSWF(id) {
 885+ var obj = getElementById(id);
 886+ if (obj && obj.nodeName == "OBJECT") {
 887+ if (ua.ie && ua.win) {
 888+ obj.style.display = "none";
 889+ (function() {
 890+ if (obj.readyState == 4) {
 891+ removeObjectInIE(id);
 892+ } else {
 893+ setTimeout(arguments.callee, 10);
 894+ }
 895+ })();
 896+ } else {
 897+ obj.parentNode.removeChild(obj);
 898+ }
 899+ }
 900+ }
 901+
 902+ function removeObjectInIE(id) {
 903+ var obj = getElementById(id);
 904+ if (obj) {
 905+ for ( var i in obj) {
 906+ if (typeof obj[i] == "function") {
 907+ obj[i] = null;
 908+ }
 909+ }
 910+ obj.parentNode.removeChild(obj);
 911+ }
 912+ }
 913+
 914+ /*
 915+ * Functions to optimize JavaScript compression
 916+ */
 917+ function getElementById(id) {
 918+ var el = null;
 919+ try {
 920+ el = doc.getElementById(id);
 921+ } catch (e) {
 922+ }
 923+ return el;
 924+ }
 925+
 926+ function createElement(el) {
 927+ return doc.createElement(el);
 928+ }
 929+
 930+ /*
 931+ * Updated attachEvent function for Internet Explorer - Stores attachEvent
 932+ * information in an Array, so on unload the detachEvent functions can be
 933+ * called to avoid memory leaks
 934+ */
 935+ function addListener(target, eventType, fn) {
 936+ target.attachEvent(eventType, fn);
 937+ listenersArr[listenersArr.length] = [ target, eventType, fn ];
 938+ }
 939+
 940+ /*
 941+ * Flash Player and SWF content version matching
 942+ */
 943+ function hasPlayerVersion(rv) {
 944+ var pv = ua.pv, v = rv.split(".");
 945+ v[0] = parseInt(v[0], 10);
 946+ v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9"
 947+ // instead of "9.0.0"
 948+ v[2] = parseInt(v[2], 10) || 0;
 949+ return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0]
 950+ && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
 951+ }
 952+
 953+ /*
 954+ * Cross-browser dynamic CSS creation - Based on Bobby van der Sluis'
 955+ * solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
 956+ */
 957+ function createCSS(sel, decl, media, newStyle) {
 958+ if (ua.ie && ua.mac) {
 959+ return;
 960+ }
 961+ var h = doc.getElementsByTagName("head")[0];
 962+ if (!h) {
 963+ return;
 964+ } // to also support badly authored HTML pages that lack a head
 965+ // element
 966+ var m = (media && typeof media == "string") ? media : "screen";
 967+ if (newStyle) {
 968+ dynamicStylesheet = null;
 969+ dynamicStylesheetMedia = null;
 970+ }
 971+ if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
 972+ // create dynamic stylesheet + get a global reference to it
 973+ var s = createElement("style");
 974+ s.setAttribute("type", "text/css");
 975+ s.setAttribute("media", m);
 976+ dynamicStylesheet = h.appendChild(s);
 977+ if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF
 978+ && doc.styleSheets.length > 0) {
 979+ dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
 980+ }
 981+ dynamicStylesheetMedia = m;
 982+ }
 983+ // add style rule
 984+ if (ua.ie && ua.win) {
 985+ if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
 986+ dynamicStylesheet.addRule(sel, decl);
 987+ }
 988+ } else {
 989+ if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
 990+ dynamicStylesheet.appendChild(doc.createTextNode(sel + " {"
 991+ + decl + "}"));
 992+ }
 993+ }
 994+ }
 995+
 996+ function setVisibility(id, isVisible) {
 997+ if (!autoHideShow) {
 998+ return;
 999+ }
 1000+ var v = isVisible ? "visible" : "hidden";
 1001+ if (isDomLoaded && getElementById(id)) {
 1002+ getElementById(id).style.visibility = v;
 1003+ } else {
 1004+ createCSS("#" + id, "visibility:" + v);
 1005+ }
 1006+ }
 1007+
 1008+ /*
 1009+ * Filter to avoid XSS attacks
 1010+ */
 1011+ function urlEncodeIfNecessary(s) {
 1012+ var regex = /[\\\"<>\.;]/;
 1013+ var hasBadChars = regex.exec(s) != null;
 1014+ return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s)
 1015+ : s;
 1016+ }
 1017+
 1018+ /*
 1019+ * Release memory to avoid memory leaks caused by closures, fix hanging
 1020+ * audio/video threads and force open sockets/NetConnections to disconnect
 1021+ * (Internet Explorer only)
 1022+ */
 1023+ var cleanup = function() {
 1024+ if (ua.ie && ua.win) {
 1025+ window.attachEvent("onunload", function() {
 1026+ // remove listeners to avoid memory leaks
 1027+ var ll = listenersArr.length;
 1028+ for ( var i = 0; i < ll; i++) {
 1029+ listenersArr[i][0].detachEvent(listenersArr[i][1],
 1030+ listenersArr[i][2]);
 1031+ }
 1032+ // cleanup dynamically embedded objects to fix audio/video
 1033+ // threads and force open sockets and NetConnections to
 1034+ // disconnect
 1035+ var il = objIdArr.length;
 1036+ for ( var j = 0; j < il; j++) {
 1037+ removeSWF(objIdArr[j]);
 1038+ }
 1039+ // cleanup library's main closures to avoid memory leaks
 1040+ for ( var k in ua) {
 1041+ ua[k] = null;
 1042+ }
 1043+ ua = null;
 1044+ for ( var l in swfobject) {
 1045+ swfobject[l] = null;
 1046+ }
 1047+ swfobject = null;
 1048+ });
 1049+ }
 1050+ }();
 1051+
 1052+ return {
 1053+ /*
 1054+ * Public API - Reference:
 1055+ * http://code.google.com/p/swfobject/wiki/documentation
 1056+ */
 1057+ registerObject : function(objectIdStr, swfVersionStr, xiSwfUrlStr,
 1058+ callbackFn) {
 1059+ if (ua.w3 && objectIdStr && swfVersionStr) {
 1060+ var regObj = {};
 1061+ regObj.id = objectIdStr;
 1062+ regObj.swfVersion = swfVersionStr;
 1063+ regObj.expressInstall = xiSwfUrlStr;
 1064+ regObj.callbackFn = callbackFn;
 1065+ regObjArr[regObjArr.length] = regObj;
 1066+ setVisibility(objectIdStr, false);
 1067+ } else if (callbackFn) {
 1068+ callbackFn( {
 1069+ success : false,
 1070+ id : objectIdStr
 1071+ });
 1072+ }
 1073+ },
 1074+
 1075+ getObjectById : function(objectIdStr) {
 1076+ if (ua.w3) {
 1077+ return getObjectById(objectIdStr);
 1078+ }
 1079+ },
 1080+ // XXX added by mdale ( since we know the dom is ready,
 1081+ // this works better.
 1082+ callDomLoadFunctions : function() {
 1083+ callDomLoadFunctions();
 1084+ },
 1085+
 1086+ embedSWF : function(swfUrlStr, replaceElemIdStr, widthStr, heightStr,
 1087+ swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj,
 1088+ callbackFn) {
 1089+ var callbackObj = {
 1090+ success : false,
 1091+ id : replaceElemIdStr
 1092+ };
 1093+ if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr
 1094+ && replaceElemIdStr && widthStr && heightStr
 1095+ && swfVersionStr) {
 1096+ setVisibility(replaceElemIdStr, false);
 1097+ addDomLoadEvent(function() {
 1098+ widthStr += ""; // auto-convert to string
 1099+ heightStr += "";
 1100+ var att = {};
 1101+ if (attObj && typeof attObj === OBJECT) {
 1102+ for ( var i in attObj) { // copy object to avoid the
 1103+ // use of references,
 1104+ // because web authors often
 1105+ // reuse attObj for multiple
 1106+ // SWFs
 1107+ att[i] = attObj[i];
 1108+ }
 1109+ }
 1110+ att.data = swfUrlStr;
 1111+ att.width = widthStr;
 1112+ att.height = heightStr;
 1113+ var par = {};
 1114+ if (parObj && typeof parObj === OBJECT) {
 1115+ for ( var j in parObj) { // copy object to avoid the
 1116+ // use of references,
 1117+ // because web authors often
 1118+ // reuse parObj for multiple
 1119+ // SWFs
 1120+ par[j] = parObj[j];
 1121+ }
 1122+ }
 1123+ if (flashvarsObj && typeof flashvarsObj === OBJECT) {
 1124+ for ( var k in flashvarsObj) { // copy object to avoid
 1125+ // the use of
 1126+ // references, because
 1127+ // web authors often
 1128+ // reuse flashvarsObj
 1129+ // for multiple SWFs
 1130+ if (typeof par.flashvars != UNDEF) {
 1131+ par.flashvars += "&" + k + "="
 1132+ + flashvarsObj[k];
 1133+ } else {
 1134+ par.flashvars = k + "=" + flashvarsObj[k];
 1135+ }
 1136+ }
 1137+ }
 1138+ if (hasPlayerVersion(swfVersionStr)) { // create SWF
 1139+ var obj = createSWF(att, par, replaceElemIdStr);
 1140+ if (att.id == replaceElemIdStr) {
 1141+ setVisibility(replaceElemIdStr, true);
 1142+ }
 1143+ callbackObj.success = true;
 1144+ callbackObj.ref = obj;
 1145+ } else if (xiSwfUrlStr && canExpressInstall()) { // show
 1146+ // Adobe
 1147+ // Express
 1148+ // Install
 1149+ att.data = xiSwfUrlStr;
 1150+ showExpressInstall(att, par, replaceElemIdStr,
 1151+ callbackFn);
 1152+ return;
 1153+ } else { // show alternative content
 1154+ setVisibility(replaceElemIdStr, true);
 1155+ }
 1156+ if (callbackFn) {
 1157+ callbackFn(callbackObj);
 1158+ }
 1159+ });
 1160+ } else if (callbackFn) {
 1161+ callbackFn(callbackObj);
 1162+ }
 1163+ },
 1164+
 1165+ switchOffAutoHideShow : function() {
 1166+ autoHideShow = false;
 1167+ },
 1168+
 1169+ ua : ua,
 1170+
 1171+ getFlashPlayerVersion : function() {
 1172+ return {
 1173+ major : ua.pv[0],
 1174+ minor : ua.pv[1],
 1175+ release : ua.pv[2]
 1176+ };
 1177+ },
 1178+
 1179+ hasFlashPlayerVersion : hasPlayerVersion,
 1180+
 1181+ createSWF : function(attObj, parObj, replaceElemIdStr) {
 1182+ if (ua.w3) {
 1183+ return createSWF(attObj, parObj, replaceElemIdStr);
 1184+ } else {
 1185+ return undefined;
 1186+ }
 1187+ },
 1188+
 1189+ showExpressInstall : function(att, par, replaceElemIdStr, callbackFn) {
 1190+ if (ua.w3 && canExpressInstall()) {
 1191+ showExpressInstall(att, par, replaceElemIdStr, callbackFn);
 1192+ }
 1193+ },
 1194+
 1195+ removeSWF : function(objElemIdStr) {
 1196+ if (ua.w3) {
 1197+ removeSWF(objElemIdStr);
 1198+ }
 1199+ },
 1200+
 1201+ createCSS : function(selStr, declStr, mediaStr, newStyleBoolean) {
 1202+ if (ua.w3) {
 1203+ createCSS(selStr, declStr, mediaStr, newStyleBoolean);
 1204+ }
 1205+ },
 1206+
 1207+ addDomLoadEvent : addDomLoadEvent,
 1208+
 1209+ addLoadEvent : addLoadEvent,
 1210+
 1211+ getQueryParamValue : function(param) {
 1212+ var q = doc.location.search || doc.location.hash;
 1213+ if (q) {
 1214+ if (/\?/.test(q)) {
 1215+ q = q.split("?")[1];
 1216+ } // strip question mark
 1217+ if (param == null) {
 1218+ return urlEncodeIfNecessary(q);
 1219+ }
 1220+ var pairs = q.split("&");
 1221+ for ( var i = 0; i < pairs.length; i++) {
 1222+ if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
 1223+ return urlEncodeIfNecessary(pairs[i].substring((pairs[i]
 1224+ .indexOf("=") + 1)));
 1225+ }
 1226+ }
 1227+ }
 1228+ return "";
 1229+},
 1230+
 1231+// For internal usage only
 1232+ expressInstallCallback : function() {
 1233+ if (isExpressInstallActive) {
 1234+ var obj = getElementById(EXPRESS_INSTALL_ID);
 1235+ if (obj && storedAltContent) {
 1236+ obj.parentNode.replaceChild(storedAltContent, obj);
 1237+ if (storedAltContentId) {
 1238+ setVisibility(storedAltContentId, true);
 1239+ if (ua.ie && ua.win) {
 1240+ storedAltContent.style.display = "block";
 1241+ }
 1242+ }
 1243+ if (storedCallbackFn) {
 1244+ storedCallbackFn(storedCallbackObj);
 1245+ }
 1246+ }
 1247+ isExpressInstallActive = false;
 1248+ }
 1249+ }
 1250+ };
 1251+}();
 1252+
 1253+} )( window.mediaWiki, window.jQuery );
\ No newline at end of file
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerKplayer.js
___________________________________________________________________
Added: svn:mime-type
11254 + text/plain
Added: svn:eol-style
21255 + native
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedTypes.js
@@ -0,0 +1,225 @@
 2+/**
 3+ * mw.EmbedTypes object handles setting and getting of supported embed types:
 4+ * closely mirrors OggHandler so that its easier to share efforts in this area:
 5+ * http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/OggHandler/OggPlayer.js
 6+ */
 7+( function( mw, $ ) {
 8+
 9+mw.EmbedTypes = {
 10+
 11+ // MediaPlayers object ( supports methods for quering set of browser players )
 12+ mediaPlayers: null,
 13+
 14+ // Detect flag for completion
 15+ detect_done:false,
 16+
 17+ /**
 18+ * Runs the detect method and update the detect_done flag
 19+ *
 20+ * @constructor
 21+ */
 22+ init: function() {
 23+ // detect supported types
 24+ this.detect();
 25+ this.detect_done = true;
 26+ },
 27+
 28+ getMediaPlayers: function(){
 29+ if( this.mediaPlayers ){
 30+ return this.mediaPlayers;
 31+ }
 32+ this.mediaPlayers = new mediaPlayers();
 33+ // detect available players
 34+ this.detectPlayers();
 35+ return this.mediaPlayers;
 36+ },
 37+
 38+ /**
 39+ * If the browsers supports a given mimetype
 40+ *
 41+ * @param {String}
 42+ * mimeType Mime type for browser plug-in check
 43+ */
 44+ supportedMimeType: function( mimeType ) {
 45+ for ( var i =0; i < navigator.plugins.length; i++ ) {
 46+ var plugin = navigator.plugins[i];
 47+ if ( typeof plugin[ mimeType ] != "undefined" )
 48+ return true;
 49+ }
 50+ return false;
 51+ },
 52+
 53+ /**
 54+ * Detects what plug-ins the client supports
 55+ */
 56+ detectPlayers: function() {
 57+ mw.log( "embedPlayer: running detect" );
 58+ // In Mozilla, navigator.javaEnabled() only tells us about preferences, we need to
 59+ // search navigator.mimeTypes to see if it's installed
 60+ try{
 61+ var javaEnabled = navigator.javaEnabled();
 62+ } catch ( e ){
 63+
 64+ }
 65+ // Some browsers filter out duplicate mime types, hiding some plugins
 66+ var uniqueMimesOnly = $j.browser.opera || $j.browser.safari;
 67+
 68+ // Opera will switch off javaEnabled in preferences if java can't be
 69+ // found. And it doesn't register an application/x-java-applet mime type like
 70+ // Mozilla does.
 71+ if ( javaEnabled && ( navigator.appName == 'Opera' ) ) {
 72+ this.mediaPlayers.addPlayer( cortadoPlayer );
 73+ }
 74+
 75+ // ActiveX plugins
 76+ if ( $j.browser.msie ) {
 77+ // check for flash
 78+ if ( this.testActiveX( 'ShockwaveFlash.ShockwaveFlash' ) ) {
 79+ this.mediaPlayers.addPlayer( kplayer );
 80+ // this.mediaPlayers.addPlayer( flowPlayer );
 81+ }
 82+ // VLC
 83+ if ( this.testActiveX( 'VideoLAN.VLCPlugin.2' ) ) {
 84+ this.mediaPlayers.addPlayer( vlcPlayer );
 85+ }
 86+
 87+ // Java ActiveX
 88+ if ( this.testActiveX( 'JavaWebStart.isInstalled' ) ) {
 89+ this.mediaPlayers.addPlayer( cortadoPlayer );
 90+ }
 91+
 92+ // quicktime (currently off)
 93+ // if ( this.testActiveX(
 94+ // 'QuickTimeCheckObject.QuickTimeCheck.1' ) )
 95+ // this.mediaPlayers.addPlayer(quicktimeActiveXPlayer);
 96+ }
 97+ // <video> element
 98+ if ( typeof HTMLVideoElement == 'object' // Firefox, Safari
 99+ || typeof HTMLVideoElement == 'function' ) // Opera
 100+ {
 101+ // Test what codecs the native player supports:
 102+ try {
 103+ var dummyvid = document.createElement( "video" );
 104+ if( dummyvid.canPlayType ) {
 105+ // Add the webm player
 106+ if( dummyvid.canPlayType('video/webm; codecs="vp8, vorbis"') ){
 107+ this.mediaPlayers.addPlayer( webmNativePlayer );
 108+ }
 109+
 110+ // Test for h264:
 111+ if ( dummyvid.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"' ) ) {
 112+ this.mediaPlayers.addPlayer( h264NativePlayer );
 113+ }
 114+ // For now if Android assume we support h264Native (FIXME
 115+ // test on real devices )
 116+ if ( mw.isAndroid2() ){
 117+ this.mediaPlayers.addPlayer( h264NativePlayer );
 118+ }
 119+
 120+ // Test for ogg
 121+ if ( dummyvid.canPlayType( 'video/ogg; codecs="theora,vorbis"' ) ) {
 122+ this.mediaPlayers.addPlayer( oggNativePlayer );
 123+ // older versions of safari do not support canPlayType,
 124+ // but xiph qt registers mimetype via quicktime plugin
 125+ } else if ( this.supportedMimeType( 'video/ogg' ) ) {
 126+ this.mediaPlayers.addPlayer( oggNativePlayer );
 127+ }
 128+ }
 129+ } catch ( e ) {
 130+ mw.log( 'could not run canPlayType ' + e );
 131+ }
 132+ }
 133+
 134+ // "navigator" plugins
 135+ if ( navigator.mimeTypes && navigator.mimeTypes.length > 0 ) {
 136+ for ( var i = 0; i < navigator.mimeTypes.length; i++ ) {
 137+ var type = navigator.mimeTypes[i].type;
 138+ var semicolonPos = type.indexOf( ';' );
 139+ if ( semicolonPos > -1 ) {
 140+ type = type.substr( 0, semicolonPos );
 141+ }
 142+ // mw.log( 'on type: ' + type );
 143+ var pluginName = navigator.mimeTypes[i].enabledPlugin ? navigator.mimeTypes[i].enabledPlugin.name : '';
 144+ if ( !pluginName ) {
 145+ // In case it is null or undefined
 146+ pluginName = '';
 147+ }
 148+ if ( pluginName.toLowerCase() == 'vlc multimedia plugin' || pluginName.toLowerCase() == 'vlc multimedia plug-in' ) {
 149+ this.mediaPlayers.addPlayer( vlcPlayer );
 150+ continue;
 151+ }
 152+
 153+ if ( type == 'application/x-java-applet' ) {
 154+ this.mediaPlayers.addPlayer( cortadoPlayer );
 155+ continue;
 156+ }
 157+
 158+ if ( (type == 'video/mpeg' || type == 'video/x-msvideo') &&
 159+ pluginName.toLowerCase() == 'vlc multimedia plugin' ) {
 160+ this.mediaPlayers.addPlayer( vlcMozillaPlayer );
 161+ }
 162+
 163+ if ( type == 'application/ogg' ) {
 164+ if ( pluginName.toLowerCase() == 'vlc multimedia plugin' ) {
 165+ this.mediaPlayers.addPlayer( vlcMozillaPlayer );
 166+ // else if ( pluginName.indexOf( 'QuickTime' ) > -1 )
 167+ // this.mediaPlayers.addPlayer(quicktimeMozillaPlayer);
 168+ } else {
 169+ this.mediaPlayers.addPlayer( oggPluginPlayer );
 170+ }
 171+ continue;
 172+ } else if ( uniqueMimesOnly ) {
 173+ if ( type == 'application/x-vlc-player' ) {
 174+ this.mediaPlayers.addPlayer( vlcMozillaPlayer );
 175+ continue;
 176+ } else if ( type == 'video/quicktime' ) {
 177+ // this.mediaPlayers.addPlayer(quicktimeMozillaPlayer);
 178+ continue;
 179+ }
 180+ }
 181+
 182+ if ( type == 'application/x-shockwave-flash' ) {
 183+
 184+ this.mediaPlayers.addPlayer( kplayer );
 185+ // this.mediaPlayers.addPlayer( flowPlayer );
 186+
 187+ // check version to add omtk:
 188+ if( navigator.plugins["Shockwave Flash"] ){
 189+ var flashDescription = navigator.plugins["Shockwave Flash"].description;
 190+ var descArray = flashDescription.split( " " );
 191+ var tempArrayMajor = descArray[2].split( "." );
 192+ var versionMajor = tempArrayMajor[0];
 193+ // mw.log("version of flash: " + versionMajor);
 194+ }
 195+ continue;
 196+ }
 197+ }
 198+ }
 199+
 200+ // Allow extensions to detect and add their own "players"
 201+ mw.log("trigger::embedPlayerUpdateMediaPlayersEvent");
 202+ $( mw ).trigger( 'embedPlayerUpdateMediaPlayersEvent' , this.mediaPlayers );
 203+
 204+ },
 205+
 206+ /**
 207+ * Test IE for activeX by name
 208+ *
 209+ * @param {String}
 210+ * name Name of ActiveXObject to look for
 211+ */
 212+ testActiveX : function ( name ) {
 213+ mw.log("EmbedPlayer::detect: test testActiveX: " + name);
 214+ var hasObj = true;
 215+ try {
 216+ // No IE, not a class called "name", it's a variable
 217+ var obj = new ActiveXObject( '' + name );
 218+ } catch ( e ) {
 219+ hasObj = false;
 220+ }
 221+ return hasObj;
 222+ }
 223+};
 224+
 225+
 226+} )( mediaWiki, jQuery );
Property changes on: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedTypes.js
___________________________________________________________________
Added: svn:mime-type
1227 + text/plain

Status & tagging log