r77223 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r77222‎ | r77223 | r77224 >
Date:09:30, 24 November 2010
Author:dale
Status:deferred
Tags:
Comment:
sync with kaltura svn update
* mobile support fixes
* started moving some the player attributes to modules
* timedText bindings for onShowControlBar onHideControlBar
* bumped version number for deploy
Modified paths:
  • /branches/MwEmbedStandAlone/loader.js (modified) (history)
  • /branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.EmbedPlayer.js (modified) (history)
  • /branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.EmbedPlayerNative.js (modified) (history)
  • /branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.IFramePlayerApiClient.js (modified) (history)
  • /branches/MwEmbedStandAlone/modules/EmbedPlayer/skins/mw.PlayerControlBuilder.js (modified) (history)
  • /branches/MwEmbedStandAlone/modules/TimedText/loader.js (modified) (history)
  • /branches/MwEmbedStandAlone/modules/TimedText/mw.TimedText.js (modified) (history)
  • /branches/MwEmbedStandAlone/mwEmbed.js (modified) (history)
  • /branches/MwEmbedStandAlone/remotes/mediaWiki.js (modified) (history)

Diff [purge]

Index: branches/MwEmbedStandAlone/loader.js
@@ -123,6 +123,9 @@
124124 "mwEmbed" : "mwEmbed.js",
125125 "window.jQuery" : "libraries/jquery/jquery-1.4.2.js",
126126
 127+ "$j.mobile" : "libraries/jquerymobile/jquery.mobile-1.0a2.js",
 128+ "mw.style.mobile" : "libraries/jquerymobile/jquery.mobile-1.0a2.css",
 129+
127130 "mw.Language" : "components/mw.Language.js",
128131 "mw.Parser" : "components/mw.Parser.js",
129132 "mw.Api" : "components/mw.Api.js",
Index: branches/MwEmbedStandAlone/mwEmbed.js
@@ -482,6 +482,7 @@
483483 }
484484 return cleanRequest;
485485 },
 486+
486487 /**
487488 * Load a set of scripts. Will issue many load requests or package the
488489 * request for the resource loader
@@ -793,7 +794,7 @@
794795 }
795796 return moduleList;
796797 },
797 -
 798+
798799 /**
799800 * Loads javascript or css associated with a resourceName
800801 *
@@ -1160,7 +1161,7 @@
11611162 return $j( '#mwTempLoaderDialog' );
11621163 };
11631164 mw.isIphone = function(){
1164 - return ( navigator.userAgent.indexOf('iPhone') != -1 );
 1165+ return ( navigator.userAgent.indexOf('iPhone') != -1 && ! mw.isIpad() );
11651166 };
11661167 mw.isIpod = function(){
11671168 return ( navigator.userAgent.indexOf('iPod') != -1 );
@@ -1181,17 +1182,44 @@
11821183 *
11831184 */
11841185 mw.isHTML5FallForwardNative = function(){
1185 - // Check for a mobile html5 user agent:
1186 - if ( ( mw.isIphone() ) ||
1187 - ( mw.isIpod() ) ||
1188 - ( mw.isIpad() ) ||
1189 - ( mw.isAndroid2() ) ||
1190 - // to debug in chrome / desktop safari
 1186+ if( mw.isMobileHTML5() ){
 1187+ return true;
 1188+ }
 1189+ // Fall forward native:
 1190+ // if the browser supports flash ( don't use html5 )
 1191+ if( mw.supportsFlash() ){
 1192+ return false;
 1193+ }
 1194+ // No flash return true if the browser supports html5 video tag with basic support for canPlayType:
 1195+ if( mw.supportsHTML5() ){
 1196+ return true;
 1197+ }
 1198+
 1199+ return false;
 1200+ }
 1201+
 1202+ mw.isMobileHTML5 = function(){
 1203+ // Check for a mobile html5 user agent:
 1204+ if ( (navigator.userAgent.indexOf('iPhone') != -1) ||
 1205+ (navigator.userAgent.indexOf('iPod') != -1) ||
 1206+ (navigator.userAgent.indexOf('iPad') != -1) ||
 1207+ (navigator.userAgent.indexOf('Android 2.') != -1) ||
 1208+ // Force html5 for chrome / desktop safari
11911209 (document.URL.indexOf('forceMobileHTML5') != -1 )
1192 - ) {
 1210+ ){
11931211 return true;
11941212 }
1195 -
 1213+ return false;
 1214+ }
 1215+ mw.supportsHTML5 = function(){
 1216+ var dummyvid = document.createElement( "video" );
 1217+ if( dummyvid.canPlayType ) {
 1218+ return true;
 1219+ }
 1220+ return false;
 1221+ }
 1222+
 1223+ mw.supportsFlash = function(){
11961224 // Check if the client does not have flash and has the video tag
11971225 if ( navigator.mimeTypes && navigator.mimeTypes.length > 0 ) {
11981226 for ( var i = 0; i < navigator.mimeTypes.length; i++ ) {
@@ -1201,31 +1229,26 @@
12021230 type = type.substr( 0, semicolonPos );
12031231 }
12041232 if (type == 'application/x-shockwave-flash' ) {
1205 - // flash is installed don't use html5
1206 - return false;
 1233+ // flash is installed
 1234+ return true;
12071235 }
12081236 }
1209 - }
1210 - // For IE:
 1237+ }
 1238+
 1239+ // for IE:
12111240 var hasObj = true;
1212 - try {
1213 - var obj = new ActiveXObject( 'ShockwaveFlash.ShockwaveFlash' );
1214 - } catch ( e ) {
1215 - hasObj = false;
 1241+ if( typeof ActiveXObject != 'undefined' ){
 1242+ try {
 1243+ var obj = new ActiveXObject( 'ShockwaveFlash.ShockwaveFlash' );
 1244+ } catch ( e ) {
 1245+ hasObj = false;
 1246+ }
 1247+ if( hasObj ){
 1248+ return true;
 1249+ }
12161250 }
1217 - if( hasObj ){
1218 - return false;
1219 - }
1220 - // No flash return true if the browser supports html5 video tag with basic support for canPlayType:
1221 - var dummyvid = document.createElement( "video" );
1222 - // temporary hack firefox does not work well with native player:
1223 - if( dummyvid.canPlayType && !$j.browser.mozilla) {
1224 - return true;
1225 - }
1226 - // No video tag or flash, return false ( normal "install flash" user flow )
12271251 return false;
12281252 };
1229 -
12301253 /**
12311254 * Similar to php isset function checks if the variable exists. Does a safe
12321255 * check of a descendant method or variable
Index: branches/MwEmbedStandAlone/modules/TimedText/loader.js
@@ -13,7 +13,11 @@
1414
1515 "RemoteMwTimedText" : "remotes/RemoteMwTimedText.js"
1616 } );
17 -
 17+
 18+ mw.setDefaultConfig( 'EmbedPlayer.SourceAttributes', [
 19+ 'srclang'
 20+ ]);
 21+
1822 mw.setDefaultConfig( {
1923 // If the Timed Text interface should be displayed:
2024 // 'always' Displays link and call to contribute always
Index: branches/MwEmbedStandAlone/modules/TimedText/mw.TimedText.js
@@ -132,7 +132,21 @@
133133 $j( embedPlayer ).bind( 'play', function() {
134134 // Will load and setup timedText sources (if not loaded already loaded )
135135 _this.setupTextSources();
136 - } );
 136+ } );
 137+
 138+ // Setup display binding
 139+ $j( embedPlayer ).bind( 'onShowControlBar', function(event, layout ){
 140+ // Move the text track if present
 141+ embedPlayer.$interface.find( '.track' )
 142+ .animate( layout, 'slow' );
 143+ });
 144+
 145+ $j( embedPlayer ).bind( 'onHideControlBar', function(event, layout ){
 146+ // Move the text track down if present
 147+ embedPlayer.$interface.find( '.track' )
 148+ .animate( layout, 'slow' );
 149+ });
 150+
137151 },
138152
139153 /**
@@ -323,7 +337,6 @@
324338 */
325339 autoSelectSource: function() {
326340 this.enabledSources = [];
327 -
328341 // Check if any source matches our "local"
329342 for( var i=0; i < this.textSources.length; i++ ) {
330343 var source = this.textSources[ i ];
@@ -1395,7 +1408,6 @@
13961409 var langKey = subPage.title.split( '.' );
13971410 var extension = langKey.pop();
13981411 langKey = langKey.pop();
1399 -
14001412 //NOTE: we hard code the mw-srt type
14011413 // ( This is because mediaWiki srt files can have wiki-text and parsed as such )
14021414 if( extension == 'srt' ) {
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.EmbedPlayer.js
@@ -25,9 +25,6 @@
2626 // media url
2727 'src',
2828
29 - // media codecs attribute ( if provided )
30 - 'codecs',
31 -
3229 // Title string for the source asset
3330 'title',
3431
@@ -51,17 +48,8 @@
5249 // If the source is the default source
5350 'default',
5451
55 - // Language key used for subtitle tracks
56 - 'srclang',
57 -
5852 // titleKey ( used for api lookups )
59 - 'titleKey',
60 -
61 - // The provider type ( for what type of api query to make )
62 - 'provider_type',
63 -
64 - // The api url for the provider
65 - 'provider_url'
 53+ 'titleKey'
6654 ] );
6755
6856 /**
@@ -460,7 +448,7 @@
461449
462450 // Check if we are using native controls ( should keep the video embed
463451 // around )
464 - if( playerInterface.useNativePlayerControls() ) {
 452+ if( playerInterface.useNativePlayerControls() || playerInterface.isPersistentNativePlayer() ) {
465453 $j( targetElement )
466454 .attr( 'id', playerInterface.pid )
467455 .addClass( 'nativeEmbedPlayerPid' )
@@ -583,13 +571,9 @@
584572
585573 // End time in npt format
586574 end_npt: null,
587 -
588 - // A provider "id" to identify api request type
589 - provider_type : null,
590 -
591 - // The api url for the provider
592 - provider_url : null,
593 -
 575+
 576+ // Language of the file
 577+ srclang: null,
594578 /**
595579 * MediaSource constructor:
596580 */
@@ -729,7 +713,7 @@
730714 * seeks)
731715 * @return {String} the URI of the source.
732716 */
733 - getSrc : function( serverSeekTime ) {
 717+ getSrc: function( serverSeekTime ) {
734718 if ( !serverSeekTime || !this.URLTimeEncoding ) {
735719 return this.src;
736720 }
@@ -1441,9 +1425,11 @@
14421426 },
14431427
14441428 stopEventPropagation: function(){
 1429+ this.stopMonitor();
14451430 this._propagateEvents = false;
14461431 },
14471432 restoreEventPropagation: function(){
 1433+ this.startMonitor();
14481434 this._propagateEvents = true;
14491435 },
14501436 /**
@@ -1738,15 +1724,7 @@
17391725
17401726 // Auto select player based on default order
17411727 if ( !this.mediaElement.selectedSource ) {
1742 - // check for parent clip:
1743 - if ( typeof this.pc != 'undefined' ) {
1744 - mw.log( 'no sources, type:' + this.type + ' check for html' );
1745 - // do load player if just displaying innerHTML:
1746 - if ( this.pc.type == 'text/html' ) {
1747 - this.selectedPlayer = mw.EmbedTypes.players.defaultPlayer( 'text/html' );
1748 - mw.log( 'set selected player:' + this.selectedPlayer.mimeType );
1749 - }
1750 - }
 1728+ mw.log( 'setupSourcePlayer:: no sources, type:' + this.type );
17511729 } else {
17521730 this.selectedPlayer = mw.EmbedTypes.players.defaultPlayer( this.mediaElement.selectedSource.mimeType );
17531731 }
@@ -1912,12 +1890,16 @@
19131891 var _this = this;
19141892
19151893 this.seeking = true;
1916 - // Run the seeking hook
1917 - $j( this.embedPlayer ).trigger( 'onSeek' );
19181894
19191895 // See if we should do a server side seek ( player independent )
19201896 if ( this.supportsURLTimeEncoding() ) {
1921 - mw.log( 'EmbedPlayer::doSeek:: updated serverSeekTime: ' + mw.seconds2npt ( this.serverSeekTime ) );
 1897+ mw.log( 'EmbedPlayer::doSeek:: updated serverSeekTime: ' + mw.seconds2npt ( this.serverSeekTime ) +
 1898+ ' currentTime: ' + _this.currentTime );
 1899+ // make sure we need to seek:
 1900+ if( _this.currentTime == _this.serverSeekTime ){
 1901+ return ;
 1902+ }
 1903+
19221904 this.stop();
19231905 this.didSeekJump = true;
19241906 // Make sure this.serverSeekTime is up-to-date:
@@ -1951,10 +1933,13 @@
19521934 /**
19531935 * On clip done action. Called once a clip is done playing
19541936 */
1955 - onClipDone: function() {
 1937+ onClipDone: function() {
 1938+ var _this = this;
 1939+ // don't run onclipdone if _propagateEvents is off
 1940+ if( !_this._propagateEvents ){
 1941+ return ;
 1942+ }
19561943 mw.log( 'EmbedPlayer::onClipDone:' + this.id + ' doneCount:' + this.donePlayingCount + ' stop state:' +this.isStopped() );
1957 - var _this = this;
1958 -
19591944 // Only run stopped once:
19601945 if( !this.isStopped() ){
19611946 // Stop the monitor:
@@ -2034,6 +2019,7 @@
20352020 * Show the player
20362021 */
20372022 showPlayer : function () {
 2023+
20382024 mw.log( 'EmbedPlayer:: Show player: ' + this.id + ' interace: w:' + this.width + ' h:' + this.height );
20392025 var _this = this;
20402026 // Set-up the local controlBuilder instance:
@@ -2056,14 +2042,26 @@
20572043 // position the "player" absolute inside the relative interface
20582044 // parent:
20592045 .css('position', 'absolute');
2060 - }
2061 -
 2046+ }
 2047+
20622048 // Set up local jQuery object reference to "mwplayer_interface"
20632049 this.$interface = $j( this ).parent( '.mwplayer_interface' );
20642050
 2051+ // if a isPersistentNativePlayer ( overlay the controls )
 2052+ if( this.isPersistentNativePlayer() ){
 2053+ this.$interface.css({
 2054+ 'position' : 'absolute',
 2055+ 'top' : '0px',
 2056+ 'left' : '0px',
 2057+ 'background': null
 2058+ })
 2059+ $j(this).show();
 2060+ this.controls = true;
 2061+ }
 2062+
20652063 // Update Thumbnail for the "player"
20662064 this.updatePosterHTML();
2067 -
 2065+
20682066 // Add controls if enabled:
20692067 if ( this.controls ) {
20702068 this.controlBuilder.addControls();
@@ -2090,6 +2088,7 @@
20912089 * [misssingType] missing type mime
20922090 */
20932091 showPluginMissingHTML: function( ) {
 2092+ mw.log("showPluginMissingHTML");
20942093 // Get mime type for unsuported formats:
20952094 var missingType = '';
20962095 var or = '';
@@ -2098,7 +2097,7 @@
20992098 or = ' or ';
21002099 }
21012100 // Remove the loading spinner if present:
2102 - $j('.playerLoadingSpinner').remove();
 2101+ $j('.playerLoadingSpinner,.loadingSpinner').remove();
21032102
21042103 // If the native video is already displayed hide it:
21052104 if( $j( '#' + this.pid ).length != 0 ){
@@ -2117,9 +2116,6 @@
21182117 }
21192118 var source = this.mediaElement.sources[0];
21202119 // Check if we have user defined missing html msg:
2121 - if ( this.user_missing_plugin_html ) {
2122 - $j( this ).html( this.user_missing_plugin_html );
2123 - } else {
21242120 $j( this ).html(
21252121 $j('<div />').append(
21262122 gM( 'mwe-embedplayer-generic_missing_plugin', missingType ),
@@ -2132,7 +2128,6 @@
21332129 .text( gM( 'mwe-embedplayer-download_clip' ) )
21342130 )
21352131 );
2136 - }
21372132 // hide
21382133 },
21392134
@@ -2296,33 +2291,36 @@
22972292 var thumb_html = '';
22982293 var class_atr = '';
22992294 var style_atr = '';
2300 -
2301 -
 2295+
23022296 if( this.useNativePlayerControls() ){
23032297 this.showNativePlayer();
23042298 return ;
23052299 }
2306 -
 2300+
23072301 // Set by default thumb value if not found
23082302 var posterSrc = ( this.poster ) ? this.poster :
23092303 mw.getConfig( 'imagesPath' ) + 'vid_default_thumb.jpg';
23102304
2311 - // Poster support is not very consistent in browsers
2312 - // use a jpg poster image:
2313 - $j( this ).html(
2314 - $j( '<img />' )
2315 - .css({
2316 - 'position' : 'relative',
2317 - 'width' : '100%',
2318 - 'height' : '100%'
2319 - })
2320 - .attr({
2321 - 'id' : 'img_thumb_' + this.id,
2322 - 'src' : posterSrc
2323 - })
2324 - .addClass( 'playerPoster' )
2325 - );
2326 -
 2305+ // Update PersistentNativePlayer poster:
 2306+ if( this.isPersistentNativePlayer() ){
 2307+ $j( '#' + this.pid ).attr('poster', posterSrc);
 2308+ } else {
 2309+ // Poster support is not very consistent in browsers
 2310+ // use a jpg poster image:
 2311+ $j( this ).html(
 2312+ $j( '<img />' )
 2313+ .css({
 2314+ 'position' : 'relative',
 2315+ 'width' : '100%',
 2316+ 'height' : '100%'
 2317+ })
 2318+ .attr({
 2319+ 'id' : 'img_thumb_' + this.id,
 2320+ 'src' : posterSrc
 2321+ })
 2322+ .addClass( 'playerPoster' )
 2323+ );
 2324+ }
23272325 if ( this.controls
23282326 && this.height > this.controlBuilder.getComponentHeight( 'playButtonLarge' )
23292327 ) {
@@ -2343,18 +2341,31 @@
23442342 useNativePlayerControls: function() {
23452343 if( this.usenativecontrols === true ){
23462344 return true;
2347 - }
2348 -
 2345+ }
23492346 if( mw.getConfig('EmbedPlayer.NativeControls') === true ) {
23502347 return true;
23512348 }
2352 - if( mw.getConfig('EmbedPlayer.NativeControlsMobileSafari' ) &&
2353 - mw.isHTML5FallForwardNative()
2354 - ){
 2349+
 2350+ // Do some device detection devices that don't support overlays
 2351+ // and go into full screen once play is clicked:
 2352+ if( mw.isAndroid2() || mw.isIpod() || mw.isIphone() ){
23552353 return true;
 2354+ }
 2355+ // iPad can use html controls if its a persistantPlayer in the dom before loading )
 2356+ // else it needs to use native controls:
 2357+ if( mw.isIpad() ){
 2358+ if( this.isPersistentNativePlayer() ){
 2359+ return false;
 2360+ } else {
 2361+ return true;
 2362+ }
23562363 }
23572364 return false;
23582365 },
 2366+
 2367+ isPersistentNativePlayer: function(){
 2368+ return $j('#' + this.pid ).hasClass('persistentNativePlayer');
 2369+ },
23592370
23602371
23612372 /**
@@ -2366,62 +2377,46 @@
23672378 */
23682379 showNativePlayer: function(){
23692380 var _this = this;
2370 - // Empty the player
2371 - $j(this).empty();
 2381+
 2382+ // Empty the player of any child nodes
 2383+ $j(this).empty();
23722384
23732385 // Remove the player loader spinner if it exists
23742386 $j('#loadingSpinner_' + this.id ).remove();
23752387
2376 -
2377 - // Check if we need to refresh mobile safari
2378 - var mobileSafariNeedsRefresh = false;
2379 -
2380 - // Unhide the original video element if not part of a playerThemer embed
2381 - if( !$j( '#' + this.pid ).hasClass('PlayerThemer') ){
2382 - $j( '#' + this.pid )
2383 - .css( {
2384 - 'position' : 'absolute'
2385 - } )
2386 - .show()
2387 - .attr('controls', 'true');
2388 -
2389 - mobileSafariNeedsRefresh = true;
 2388+ // Setup the source
 2389+ var source = this.mediaElement.getSources( 'video/h264' )[0];
 2390+ // Support fake user agent
 2391+ if( !source || !source.src ){
 2392+ mw.log( 'Warning: Your probably fakeing the iPhone userAgent ( no h.264 source )' );
 2393+ source = this.mediaElement.getSources( 'video/ogg' )[0];
23902394 }
2391 -
2392 - // iPad does not handle video tag update for attributes like "controls"
2393 - // so we have to do a full replace ( if controls are not included
2394 - // initially )
2395 - if( mw.isHTML5FallForwardNative() && mobileSafariNeedsRefresh ) {
2396 - var source = this.mediaElement.getSources( 'video/h264' )[0];
2397 - // XXX note this should be updated once mobile supports h.264
2398 - if( !source || !source.src ){
2399 - mw.log( 'Warning: Your probably fakeing the iPhone userAgent ( no h.264 source )' );
2400 - source = this.mediaElement.getSources( 'video/ogg' )[0];
2401 - }
2402 -
2403 - var videoAttribues = {
2404 - 'id' : _this.pid,
2405 - 'poster': _this.poster,
2406 - 'src' : source.src,
2407 - 'controls' : 'true'
2408 - }
2409 -
2410 - if( this.loop ){
2411 - videoAttribues[ 'loop' ] = 'true';
2412 - }
2413 - var cssStyle = {
2414 - 'width' : _this.width,
2415 - 'height' : _this.height
2416 - };
2417 - $j( '#' + this.pid ).replaceWith(
2418 - _this.getNativePlayerHtml( videoAttribues, cssStyle )
2419 - )
2420 - // Bind native events:
2421 - this.applyMediaElementBindings();
 2395+
 2396+ // Setup videoAttribues
 2397+ var videoAttribues = {
 2398+ 'poster': _this.poster,
 2399+ 'src' : source.src,
 2400+ 'controls' : 'true'
24222401 }
 2402+ if( this.loop ){
 2403+ videoAttribues[ 'loop' ] = 'true';
 2404+ }
 2405+ var cssStyle = {
 2406+ 'width' : _this.width,
 2407+ 'height' : _this.height
 2408+ };
 2409+
 2410+ // If not a persistentNativePlayer swap the video tag
 2411+ // completely instead of just updating properties:
 2412+ $j( '#' + this.pid ).replaceWith(
 2413+ _this.getNativePlayerHtml( videoAttribues, cssStyle )
 2414+ )
 2415+
 2416+ // Bind native events:
 2417+ this.applyMediaElementBindings();
 2418+
24232419 // Android only can play with a special play button ( no native controls
2424 - // in the dom , and no auto-play )
2425 - // and only with 'native display'
 2420+ // persistentNativePlayer has no controls:
24262421 if( mw.isAndroid2() ){
24272422 this.addPlayBtnLarge();
24282423 }
@@ -2494,7 +2489,7 @@
24952490 } else {
24962491 // old style embed:
24972492 var iframeUrl = mw.getMwEmbedPath() + 'mwEmbedFrame.php?';
2498 - var params = {};
 2493+ var params = {'src[]':[]};
24992494
25002495 if ( this.roe ) {
25012496 params.roe = this.roe;
@@ -2512,7 +2507,7 @@
25132508 for( var i=0; i < this.mediaElement.sources.length; i++ ){
25142509 var source = this.mediaElement.sources[i];
25152510 if( source.src ) {
2516 - params['src[]'] = mw.absoluteUrl( source.src );
 2511+ params['src[]'].push(mw.absoluteUrl( source.src ));
25172512 }
25182513 }
25192514 // Output the poster attr
@@ -2654,15 +2649,7 @@
26552650 */
26562651 play: function() {
26572652 var _this = this;
2658 -
2659 - if( this.paused && this.bubbleEventCheck() ){
2660 - this.paused = false;
2661 - mw.log("trigger play event::" + !this.paused);
2662 - $j( this ).trigger( 'play' );
2663 - }
2664 - this.paused = false;
2665 -
2666 - mw.log( "EmbedPlayer:: play" );
 2653+ mw.log( "EmbedPlayer:: play: " + this._propagateEvents );
26672654 // Hide any overlay:
26682655 this.controlBuilder.closeMenuOverlay();
26692656
@@ -2673,14 +2660,22 @@
26742661 return;
26752662 } else {
26762663 this.thumbnail_disp = false;
 2664+ // hide any button if present:
 2665+ this.$interface.find( '.play-btn-large' ).remove();
26772666 this.doEmbedHTML();
26782667 }
26792668 } else {
26802669 // the plugin is already being displayed
26812670 this.seeking = false;
26822671 }
 2672+ // Trigger the play event
 2673+ if( this.paused && this.bubbleEventCheck() ){
 2674+ this.paused = false;
 2675+ mw.log("trigger play event::" + !this.paused);
 2676+ $j( this ).trigger( 'play' );
 2677+ }
 2678+ this.paused = false;
26832679
2684 -
26852680 this.$interface.find('.play-btn span')
26862681 .removeClass( 'ui-icon-play' )
26872682 .addClass( 'ui-icon-pause' );
@@ -2691,8 +2686,8 @@
26922687 .click( function( ) {
26932688 _this.pause();
26942689 } )
2695 - .attr( 'title', gM( 'mwe-embedplayer-pause_clip' ) );
2696 -
 2690+ .attr( 'title', gM( 'mwe-embedplayer-pause_clip' ) );
 2691+
26972692 // Start the monitor if not already started
26982693 this.monitor();
26992694
@@ -2949,6 +2944,10 @@
29502945 stopMonitor: function(){
29512946 this.thumbnail_disp = true;
29522947 },
 2948+ // xxx temporary hack we need a better stop monitor system
 2949+ startMonitor: function(){
 2950+ this.thumbnail_disp = false;
 2951+ },
29532952
29542953 /**
29552954 * Checks if the currentTime was updated outside of the getPlayerElementTime
@@ -2961,8 +2960,9 @@
29622961 // If the time has been updated and is in range issue a seek
29632962 if( _this.getDuration() && _this.currentTime <= _this.getDuration() ){
29642963 var seekPercent = _this.currentTime / _this.getDuration();
2965 - mw.log("monitor::" + _this.previousTime + ' != ' +
 2964+ mw.log("checkForCurrentTimeSeek::" + _this.previousTime + ' != ' +
29662965 _this.currentTime + " javascript based currentTime update to " + seekPercent);
 2966+ _this.previousTime = _this.currentTime;
29672967 this.doSeek( seekPercent );
29682968 }
29692969 }
@@ -2974,17 +2974,18 @@
29752975 */
29762976 monitor: function() {
29772977 var _this = this;
2978 -
 2978+
29792979 // Check for current time update outside of embed player
29802980 this.checkForCurrentTimeSeek();
2981 -
 2981+
 2982+
29822983 // Update currentTime via embedPlayer
2983 - _this.currentTime = _this.getPlayerElementTime();
 2984+ _this.currentTime = _this.getPlayerElementTime();
29842985
29852986 // Update any offsets from server seek
2986 - if( _this.serverSeekTime && _this.supportsURLTimeEncoding ){
2987 - _this.currentTime = _this.serverSeekTime + _this.getPlayerElementTime()
2988 - }
 2987+ if( _this.serverSeekTime && _this.supportsURLTimeEncoding() ){
 2988+ _this.currentTime = parseInt( _this.serverSeekTime ) + parseInt( _this.getPlayerElementTime() );
 2989+ }
29892990
29902991 // Update the previousTime ( so we can know if the user-javascript
29912992 // changed currentTime )
@@ -2996,7 +2997,9 @@
29972998 // _this.previousVolume );
29982999 if( Math.round( _this.volume * 100 ) != Math.round( _this.previousVolume * 100 ) ) {
29993000 _this.setInterfaceVolume( _this.volume );
3000 - $j( this ).trigger('volumeChanged', _this.volume );
 3001+ if( _this._propagateEvents ){
 3002+ $j( this ).trigger('volumeChanged', _this.volume );
 3003+ }
30013004 }
30023005
30033006 // Update the previous volume
@@ -3013,8 +3016,9 @@
30143017 _this.muted = _this.getPlayerElementMuted();
30153018 }
30163019
3017 - // mw.log( 'Monitor:: ' + this.currentTime + ' duration: ' + ( parseInt(
 3020+ //mw.log( 'Monitor:: ' + this.currentTime + ' duration: ' + ( parseInt(
30183021 // this.getDuration() ) + 1 ) + ' is seeking: ' + this.seeking );
 3022+
30193023 if ( this.currentTime >= 0 && this.duration ) {
30203024 if ( !this.userSlide && !this.seeking ) {
30213025 if ( parseInt( this.startOffset ) != 0 ) {
@@ -3032,8 +3036,8 @@
30333037 // Check if we are "done"
30343038 var endPresentationTime = ( this.startOffset ) ? ( this.startOffset + this.duration ) : this.duration;
30353039 if ( this.currentTime >= endPresentationTime ) {
3036 - mw.log( "should run clip done :: " + this.currentTime + ' > ' + endPresentationTime );
3037 - this.onClipDone();
 3040+ mw.log( "should run clip done :: " + this.currentTime + ' > ' + endPresentationTime );
 3041+ this.onClipDone();
30383042 }
30393043 } else {
30403044 // Media lacks duration just show end time
@@ -3057,7 +3061,9 @@
30583062 // run the "native" progress event on the virtual html5 object if set
30593063 if( this.progressEventData ) {
30603064 // mw.log("trigger:progress event on html5 proxy");
3061 - $j( this ).trigger( 'progress', this.progressEventData );
 3065+ if( _this._propagateEvents ){
 3066+ $j( this ).trigger( 'progress', this.progressEventData );
 3067+ }
30623068 }
30633069
30643070 // Call monitor at 250ms interval. ( use setInterval to avoid stacking
@@ -3076,7 +3082,9 @@
30773083 }
30783084
30793085 // mw.log('trigger:monitor:: ' + this.currentTime );
3080 - $j( this ).trigger( 'monitorEvent' );
 3086+ if( _this._propagateEvents ){
 3087+ $j( this ).trigger( 'monitorEvent' );
 3088+ }
30813089 },
30823090
30833091 /**
@@ -3206,13 +3214,19 @@
32073215 */
32083216
32093217 /**
3210 - * Get the current selected media source
 3218+ * Get the current selected media source or first source
32113219 *
32123220 * @return src url
32133221 */
32143222 getSrc: function() {
 3223+ if( this.currentTime && !this.serverSeekTime){
 3224+ this.serverSeekTime = this.currentTime;
 3225+ }
32153226 if( this.mediaElement.selectedSource ){
32163227 return this.mediaElement.selectedSource.getSrc( this.serverSeekTime );
 3228+ } else if( this.mediaElement ){
 3229+ // get the first source:
 3230+ return this.mediaElement.getSources()[0].getSrc( this.serverSeekTime );
32173231 }
32183232 return false;
32193233 },
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.IFramePlayerApiClient.js
@@ -1,7 +1,7 @@
22 /**
3 -* iFrame api mapping support
4 -*
5 -* Client side ( binds a given iFrames to expose the player api )
 3+* iFrame api mapping support
 4+*
 5+* Client side ( binds a given iFrames to expose the player api )
66 */
77 mw.IFramePlayerApiClient = function( iframe, playerProxy, options ){
88 return this.init( iframe , playerProxy, options);
@@ -48,11 +48,11 @@
4949 //mw.log("IframePlayerApiClient:: hanldeReciveMsg ");
5050 // Confirm the event is coming for the target host:
5151 if( event.origin != this.iframeServer){
52 - mw.log("Skip msg from host does not match iFrame player: " + event.origin +
 52+ mw.log("Skip msg from host does not match iFrame player: " + event.origin +
5353 ' != iframe Server: ' + this.iframeServer )
5454 return ;
5555 };
56 - // Decode the message
 56+ // Decode the message
5757 var msgObject = JSON.parse( event.data );
5858 var playerAttributes = mw.getConfig( 'EmbedPlayer.Attributes' );
5959 // Before we update local attributes check that the object has not been updated by user js
@@ -60,7 +60,7 @@
6161 if( attrName != 'id' ){
6262 if( _this._prevPlayerProxy[ attrName ] != _this.playerProxy[ attrName ] ){
6363 mw.log( "IFramePlayerApiClient:: User js update:" + attrName + ' set to: ' + this.playerProxy[ attrName ] + ' != old: ' + _this._prevPlayerProxy[ attrName ] );
64 - // Send the updated attribute back to the iframe:
 64+ // Send the updated attribute back to the iframe:
6565 _this.postMessage({
6666 'attrName' : attrName,
6767 'attrValue' : _this.playerProxy[ attrName ]
@@ -81,7 +81,7 @@
8282 }
8383 }
8484 }
85 - // Trigger any binding events
 85+ // Trigger any binding events
8686 if( typeof msgObject.triggerName != 'undefined' && msgObject.triggerArgs != 'undefined') {
8787 mw.log('IFramePlayerApiClient:: trigger: ' + msgObject.triggerName );
8888 $j( _this.playerProxy ).trigger( msgObject.triggerName, msgObject.triggerArgs );
@@ -91,9 +91,9 @@
9292 'postMessage': function( msgObj ){
9393 mw.log( "IFramePlayerApiClient:: postMessage(): " + JSON.stringify( msgObj ) );
9494 $j.postMessage(
95 - JSON.stringify( msgObj ),
96 - mw.absoluteUrl( $j( this.iframe ).attr('src') ),
97 - this.iframe.contentWindow
 95+ JSON.stringify( msgObj ),
 96+ mw.absoluteUrl( $j( this.iframe ).attr('src') ),
 97+ this.iframe.contentWindow
9898 );
9999 }
100100 };
@@ -107,7 +107,7 @@
108108 // Append '_ifp' ( iframe player ) to id of real iframe so that 'id', and 'src' attributes don't conflict
109109 var originalIframeId = ( $( this.selector ).attr( 'id' ) )? $( this.selector ).attr( 'id' ) : Math.floor( 9999999 * Math.random() );
110110 var iframePlayerId = originalIframeId + '_ifp' ; // here we use random to generate a unique id
111 - // Append the div element proxy after the iframe
 111+ // Append the div element proxy after the iframe
112112 $j( this.selector )
113113 .attr('id', iframePlayerId)
114114 .after(
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/skins/mw.PlayerControlBuilder.js
@@ -89,7 +89,7 @@
9090 },
9191
9292 /**
93 - * Add the controls html to palyer interface
 93+ * Add the controls html to player interface
9494 */
9595 addControls: function() {
9696 // Set up local pointer to the embedPlayer
@@ -105,11 +105,16 @@
106106 _this.displayOptionsMenuFlag = false;
107107
108108
109 - // Setup the controlBar container
 109+ // Setup the controlBar container ( starts hidden )
110110 var $controlBar = $j('<div />')
111111 .addClass( 'ui-state-default ui-widget-header ui-helper-clearfix control-bar' )
112 - .css( 'height', this.height );
 112+ .css( 'height', this.height )
113113
 114+ // Controls are hidden by default if overlaying controls:
 115+ if( _this.checkOverlayControls() ){
 116+ $controlBar.hide();
 117+ }
 118+
114119 $controlBar.css( {
115120 'position': 'absolute',
116121 'bottom' : '0px',
@@ -118,10 +123,10 @@
119124 } );
120125
121126 // Check for overlay controls:
122 - if( ! _this.checkOverlayControls() ) {
 127+ if( ! _this.checkOverlayControls() && ! embedPlayer.controls === false) {
123128 // Add some space to interface for the control bar ( if not overlaying controls )
124 - embedPlayer.$interface.css( {
125 - 'height' : parseInt( embedPlayer.height ) + parseInt( this.height ) +2
 129+ $j( embedPlayer ).css( {
 130+ 'height' : parseInt( embedPlayer.height ) - parseInt( this.height )
126131 } );
127132 }
128133
@@ -219,7 +224,7 @@
220225 // Set target width
221226 var targetWidth = windowSize.width;
222227 var targetHeight = targetWidth * ( embedPlayer.getHeight() / embedPlayer.getWidth() );
223 -
 228+
224229 // Check if it exceeds the height constraint:
225230 if( targetHeight > windowSize.height ){
226231 targetHeight = windowSize.height;
@@ -622,14 +627,10 @@
623628 // Hide the control bar
624629 this.embedPlayer.$interface.find( '.control-bar')
625630 .fadeOut( animateDuration );
 631+ mw.log('about to trigger hide control bar')
 632+ // Allow interface items to update:
 633+ $j( this.embedPlayer ).trigger('onHideControlBar', {'bottom' : 10} );
626634
627 - // Move the timed text XXX this should go into timedText module
628 - this.embedPlayer.$interface.find( '.track' )
629 - .stop()
630 - .animate( {
631 - 'bottom' : 10
632 - }, 'slow' );
633 -
634635 },
635636
636637 /**
@@ -643,18 +644,13 @@
644645 $j( this.embedPlayer.getPlayerElement() ).css( 'z-index', '1' );
645646 }
646647 mw.log( 'PlayerControlBuilder:: ShowControlBar' );
647 - // Move up text track if present
648 - this.embedPlayer.$interface.find( '.track' )
649 - .animate(
650 - {
651 - 'bottom' : this.getHeight() + 10
652 - },
653 - animateDuration
654 - );
655 -
 648+
656649 // Show interface controls
657650 this.embedPlayer.$interface.find( '.control-bar' )
658651 .fadeIn( animateDuration );
 652+
 653+ // Trigger the screen overlay with layout info:
 654+ $j( this.embedPlayer ).trigger( 'onShowControlBar', {'bottom' : this.getHeight() + 10 } );
659655 },
660656
661657 /**
@@ -674,18 +670,25 @@
675671 }
676672
677673 // If the config is false
678 - if( mw.getConfig( 'EmbedPlayer.OverlayControls' ) == false){
 674+ if( mw.getConfig( 'EmbedPlayer.OverlayControls' ) === false){
679675 return false;
680676 }
 677+ // iPad supports overlays but the touch events mean we want the controls displayed all the
 678+ // time for now.
 679+ if( mw.isIpad() ){
 680+ return false;
 681+ }
 682+
681683
682684 // Don't hide controls when content "height" is 0px ( audio tags )
683685 if( this.embedPlayer.getPlayerHeight() === 0 &&
684 - $j(this.embedPlayer).css('height').indexOf('%') == -1 ){
 686+ $j(this.embedPlayer).css('height').indexOf('%') === -1 ){
685687 return false;
686688 }
687689 if( this.embedPlayer.controls === false ){
688690 return false;
689691 }
 692+
690693 // Past all tests OverlayControls is true:
691694 return true;
692695 },
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/mw.EmbedPlayerNative.js
@@ -8,9 +8,6 @@
99 //Instance Name
1010 instanceOf: 'Native',
1111
12 - // Counts the number of times we tried to access the video element
13 - grab_try_count:0,
14 -
1512 // Flag to only load the video ( not play it )
1613 onlyLoadFlag:false,
1714
@@ -27,7 +24,6 @@
2825 // If the media loaded event has been fired
2926 mediaLoadedFlag: null,
3027
31 -
3228 // All the native events per:
3329 // http://www.w3.org/TR/html5/video.html#mediaevents
3430 nativeEvents : [
@@ -69,11 +65,16 @@
7066 * Updates the supported features given the "type of player"
7167 */
7268 updateFeatureSupport: function(){
73 - // iWhatever devices appear to have a broken
74 - // dom overlay implementation of video atm. (hopefully iphone OS 4 fixes this )
75 - if( mw.isHTML5FallForwardNative() ) {
 69+ // The native controls function checks for overly support
 70+ // especially the special case of iPad in-dom or not support
 71+ if( this.useNativePlayerControls() ) {
7672 this.supports.overlays = false;
 73+ this.supports.volumeControl = false;
7774 }
 75+ // iOS does not support volume control ( only iPad can have controls )
 76+ if( mw.isIpad() ){
 77+ this.supports.volumeControl = false;
 78+ }
7879 },
7980
8081 /**
@@ -85,12 +86,21 @@
8687 // Reset some play state flags:
8788 _this.bufferStartFlag = false;
8889 _this.bufferEndFlag = false;
 90+
 91+ mw.log( "native play url:" + this.getSrc( this.currentTime ) + ' startOffset: ' + this.start_ntp + ' end: ' + this.end_ntp );
8992
90 - mw.log( "native play url:" + this.getSrc() + ' startOffset: ' + this.start_ntp + ' end: ' + this.end_ntp );
91 -
9293 // Check if using native controls and already the "pid" is already in the DOM
93 - if( this.useNativePlayerControls() && $j( '#' + this.pid ).length &&
94 - typeof $j( '#' + this.pid ).get(0).play != 'undefined' ) {
 94+ if( ( this.useNativePlayerControls()
 95+ ||
 96+ this.isPersistentNativePlayer()
 97+ )
 98+ && $j( '#' + this.pid ).length
 99+ && typeof $j( '#' + this.pid ).get(0).play != 'undefined' ) {
 100+
 101+ // Update the player source:
 102+ $j( '#' + this.pid ).attr('src', this.getSrc( this.currentTime ) );
 103+ $j( '#' + this.pid ).get(0).load();
 104+
95105 _this.postEmbedJS();
96106 return ;
97107 }
@@ -115,14 +125,13 @@
116126 }
117127 // Update required attributes
118128 if( !playerAttribtues[ 'id'] ) playerAttribtues['id'] = this.pid;
119 - if( !playerAttribtues['src'] ) playerAttribtues['src'] = this.getSrc();
 129+ if( !playerAttribtues['src'] ) playerAttribtues['src'] = this.getSrc( this.currentTime);
120130
121131 // If autoplay pass along to attribute ( needed for iPad / iPod no js autoplay support
122132 if( this.autoplay ) {
123133 playerAttribtues['autoplay'] = 'true';
124134 }
125135
126 -
127136 if( !cssSet ){
128137 cssSet = {};
129138 }
@@ -200,7 +209,11 @@
201210 monitor: function(){
202211 var _this = this;
203212 var vid = _this.getPlayerElement();
204 -
 213+
 214+ // Update duration
 215+ if( vid && vid.duration ){
 216+ this.duration = vid.duration;
 217+ }
205218 // Update the bufferedPercent
206219 if( vid && vid.buffered && vid.buffered.end && vid.duration ) {
207220 this.bufferedPercent = ( vid.buffered.end(0) / vid.duration );
@@ -217,8 +230,6 @@
218231 doSeek: function( percentage ) {
219232 mw.log( 'Native::doSeek p: ' + percentage + ' : ' + this.supportsURLTimeEncoding() + ' dur: ' + this.getDuration() + ' sts:' + this.seek_time_sec );
220233 this.seeking = true;
221 - // Run the seeking hook
222 - $j( this.embedPlayer ).trigger( 'onSeek' );
223234
224235 // Run the onSeeking interface update
225236 this.controlBuilder.onSeek();
@@ -487,8 +498,6 @@
488499 //( if not already set from interface )
489500 if( !this.seeking ) {
490501 this.seeking = true;
491 - // Run the seeking hook (somewhat redundant )
492 - $j( this ).trigger( 'onSeek' );
493502
494503 // Run the onSeeking interface update
495504 this.controlBuilder.onSeek();
@@ -506,7 +515,6 @@
507516 onseeked: function() {
508517 mw.log("native:onSeeked");
509518
510 - mw.log("native:onSeeked:trigger");
511519 // Trigger the html5 action on the parent
512520 if( this.seeking && this.useNativePlayerControls() ){
513521 this.seeking = false;
@@ -527,9 +535,11 @@
528536 * Handle the native play event
529537 */
530538 onplay: function(){
531 - mw.log("EmbedPlayer:native:: OnPlay");
 539+ mw.log("EmbedPlayer:native:: OnPlay::" + this._propagateEvents );
532540 // Update the interface ( if paused )
533 - this.parent_play();
 541+ if( this._propagateEvents ){
 542+ this.parent_play();
 543+ }
534544 },
535545
536546 /**
@@ -551,7 +561,7 @@
552562 this.onLoadedCallback();
553563 }
554564
555 - // Tigger "media loaded"
 565+ // Trigger "media loaded"
556566 if( ! this.mediaLoadedFlag ){
557567 $j( this ).trigger( 'mediaLoaded' );
558568 this.mediaLoadedFlag = true;
@@ -569,7 +579,6 @@
570580 */
571581 onprogress: function( event ) {
572582 var e = event.originalEvent;
573 - //mw.log("onprogress: e:" + e + ' ' + e.loaded + ' && ' + e.total );
574583 if( e && e.loaded && e.total ) {
575584 this.bufferedPercent = e.loaded / e.total;
576585 this.progressEventData = e.loaded;
@@ -584,8 +593,9 @@
585594 */
586595 onended: function() {
587596 var _this = this;
588 - mw.log( 'EmbedPlayer:native: onended:' + this.playerElement.currentTime + ' real dur:' + this.getDuration() );
589 -
590 - this.onClipDone();
 597+ mw.log( 'EmbedPlayer:native: onended:' + this.playerElement.currentTime + ' real dur:' + this.getDuration() + ' ended ' + this._propagateEvents );
 598+ if( this._propagateEvents ){
 599+ this.onClipDone();
 600+ }
591601 }
592602 };
Index: branches/MwEmbedStandAlone/remotes/mediaWiki.js
@@ -4,7 +4,7 @@
55 */
66 var urlparts = getRemoteEmbedPath();
77 var mwEmbedHostPath = urlparts[0];
8 -var mwRemoteVersion = 'r177';
 8+var mwRemoteVersion = 'r179';
99 var mwUseScriptLoader = true;
1010
1111 // Log the mwRemote version makes it easy to debug cache issues
@@ -92,7 +92,7 @@
9393 // NOTE this is REQUIRED for apiProxy to work across projects where the user has not universally enabled the gadget
9494 mw.setConfig( 'Mw.AppendWithJS', 'withJS=MediaWiki:MwEmbed.js');
9595
96 -// Allow all wikimedia regEx domains matches to support api-proxy requests
 96+// Allow all wikimedia RegEx domains matches to support api-proxy requests
9797 // NOTE remember to put $ at the end of the domain or it would match en.wikipedia.org.evil.com
9898 mw.setConfig( 'ApiProxy.DomainWhiteList',
9999 [ /wikimedia\.org$/ , /wikipedia\.org$/ , /wiktionary.org$/ , /wikinews.org$/ , /wikibooks.org$/ , /wikisource.org$/ , /wikiversity.org$/ , /wikiquote.org$/ ]
@@ -280,9 +280,11 @@
281281
282282 // Check for special "embedplayer" yes and set relevent config:
283283 if( mwReqParam['embedplayer'] == 'yes' ){
284 - mwAddCommonStyleSheet();
 284+ mwAddCommonStyleSheet();
 285+
285286 // Only rewrite the main embed player
286 - var playerDiv = document.getElementById( 'file' ).childNodes[0].cloneNode( true );
 287+ var playerDiv = document.getElementById( 'file' ).childNodes[0].cloneNode( true );
 288+ document.body.style.overflow = 'hidden';
287289 document.body.innerHTML = '<div id="loadingPlayer" style="height:100%;width:100%"><div class="loadingSpinner" style="position:absolute;left:50%;top:50%"></div></div>';
288290 document.body.appendChild( playerDiv );
289291 }

Status & tagging log