r81179 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r81178‎ | r81179 | r81180 >
Date:00:18, 29 January 2011
Author:dale
Status:deferred
Tags:
Comment:
More improvments for stand alone support. ( ran into some framework limitations )
Modified paths:
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/DefaultSettings.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedAutoLoader.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedMediaWikiGlobalFunctions.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedMediaWikiStubs.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedResourceLoaderContext.php (added) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedResourceLoaderStartUpModule.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedResourceManager.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedWebStartSetup.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/resourceloader/ResourceLoaderFileModule.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/resourceloader/ResourceLoaderStartUpModule.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/load.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/EmbedPlayer.config.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/EmbedPlayer.loader.js (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/EmbedPlayer.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/iframeApi (added) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/iframeApi/mw.IFramePlayerApiClient.js (added) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/iframeApi/mw.IFramePlayerApiServer.js (added) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players (added) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayer.js (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.IFramePlayerApiClient.js (deleted) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.IFramePlayerApiServer.js (deleted) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/resources (deleted) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/MwEmbedSupport.php (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/jquery/jquery.mwEmbedUtil.js (added) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/jquery/jquery.triggerQueueCallback.js (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/mwEmbedSupport.js (modified) (history)
  • /branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/resources/mediawiki/mediawiki.js (modified) (history)

Diff [purge]

Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/load.php
@@ -30,6 +30,6 @@
3131
3232 // Respond to resource loading request
3333 $resourceLoader = new MwEmbedResourceLoader();
34 -$resourceLoader->respond( new ResourceLoaderContext( $resourceLoader, $wgRequest ) );
 34+$resourceLoader->respond( new MwEmbedResourceLoaderContext( $resourceLoader, $wgRequest ) );
3535
3636
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedWebStartSetup.php
@@ -100,6 +100,7 @@
101101 }
102102 }
103103
104 -# Add the resource loader hook:
 104+# Add the resource loader hooks
105105 $wgHooks['ResourceLoaderRegisterModules'][] = 'MwEmbedResourceManager::registerModules';
 106+$wgHooks['ResourceLoaderGetConfigVars'][] = 'MwEmbedResourceManager::registerConfigVars';
106107
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedMediaWikiGlobalFunctions.php
@@ -193,7 +193,47 @@
194194
195195 return $output;
196196 }
 197+// simple mediaWiki msg retrival:
 198+$wgMessageCache = array();
 199+function wfMsgExt( $key, $options = array() ){
 200+ global $wgLoadedMsgKeysFlag, $wgMessageCache, $mwLanguageCode;
197201
 202+ $langKey = ( $options['language'] )? $options['language'] : 'en';
 203+
 204+ // Check if $wgMessageCache is empty, if so poulate will all its messages:
 205+ if( count( $wgMessageCache) === 0){
 206+ mwEmbedLoadMsgKeys( $langKey );
 207+ }
 208+ if ( isset( $wgMessageCache[ $key ] ) ) {
 209+ return $wgMessageCache[ $key ];
 210+ } else {
 211+ return '[' . $key . ']';
 212+ }
 213+}
 214+
 215+/**
 216+ * Load all the msg keys into $wgMessageCache
 217+ * @param $langKey String Language key to be used
 218+ */
 219+function mwEmbedLoadMsgKeys( $langKey ){
 220+ global $wgExtensionMessagesFiles, $wgMessageCache;
 221+
 222+ foreach( $wgExtensionMessagesFiles as $msgFile ){
 223+ if( !is_file( $msgFile ) ) {
 224+ throw new MWException( "Missing msgFile: " . htmlspecialchars( $msgFile ) . "\n" );
 225+ }
 226+ require( $msgFile );
 227+ // First include the English fallback:
 228+ $wgMessageCache = array_merge( $wgMessageCache, $messages[ 'en' ] );
 229+
 230+ // Then override with the current language:
 231+ if( isset( $messages[ $langKey ] ) ) {
 232+ $wgMessageCache = array_merge( $wgMessageCache, $messages[ $langKey ] );
 233+ }
 234+ }
 235+ $wgLoadedMsgKeysFlag = true;
 236+}
 237+
198238 // Memcached stubs:
199239 /**
200240 * Get a cache key
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedAutoLoader.php
@@ -26,6 +26,7 @@
2727 'JSMin' => 'includes/libs/JSMin.php',
2828
2929 # MwEmbed files ( that get autoloaded ):
 30+ 'MwEmbedResourceLoaderContext' => 'includes/MwEmbedResourceLoaderContext.php',
3031 'MwEmbedResourceLoader' => 'includes/MwEmbedResourceLoader.php',
3132 'MwEmbedResourceLoaderFileModule' => 'includes/MwEmbedResourceLoaderFileModule.php',
3233 'MwEmbedResourceLoaderStartUpModule' => 'includes/MwEmbedResourceLoaderStartUpModule.php',
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedResourceManager.php
@@ -10,6 +10,7 @@
1111 class MwEmbedResourceManager {
1212
1313 protected static $moduleSet = array();
 14+ protected static $moduleConfig = array();
1415
1516 /**
1617 * Register mwEmbeed resource set
@@ -35,7 +36,7 @@
3637 $wgExtensionMessagesFiles[ 'MwEmbed.' . $moduleName ] = $fullResourcePath . '/' . $moduleName . '.i18n.php';
3738
3839 // Get the mwEmbed module config
39 - $resourceList = require_once( $fullResourcePath . '/' . $moduleName . '.php' );
 40+ $resourceList = include( $fullResourcePath . '/' . $moduleName . '.php' );
4041 // Look for special 'messages' => 'moduleFile' key and load all modules file messages:
4142 foreach( $resourceList as $name => $resources ){
4243 if( isset( $resources['messageFile'] ) && is_file( $fullResourcePath . '/' .$resources['messageFile'] ) ){
@@ -53,12 +54,22 @@
5455 );
5556 }
5657
57 - // @@TODO add $moduleInfo['config']
 58+ // Check for module config ( @@TODO support per-module config )
 59+ $configPath = $fullResourcePath . '/' . $moduleName . '.config.php';
 60+ if( is_file( $configPath ) ){
 61+ self::$moduleConfig[ $moduleName ] = include( $configPath );
 62+ }
5863
5964 // Add the resource list into the module set with its provided path
6065 self::$moduleSet[ $mwEmbedResourcePath ] = $resourceList;
6166 }
6267
 68+ public static function registerConfigVars( &$vars ){
 69+ foreach( self::$moduleConfig as $moduleName => $config ){
 70+ $vars = array_merge( $vars, $config );
 71+ }
 72+ return $vars;
 73+ }
6374 /**
6475 * ResourceLoaderRegisterModules hook
6576 *
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedResourceLoaderContext.php
@@ -0,0 +1,17 @@
 2+<?php
 3+
 4+/**
 5+ * Object passed around to modules which contains information about the state
 6+ * of a specific loader request
 7+ */
 8+class MwEmbedResourceLoaderContext extends ResourceLoaderContext{
 9+
 10+ public function getDirection() {
 11+ if ( $this->direction === null ) {
 12+ $this->direction = $this->request->getVal( 'dir' );
 13+ }
 14+ return $this->direction;
 15+ }
 16+}
 17+
 18+?>
\ No newline at end of file
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/DefaultSettings.php
@@ -13,9 +13,9 @@
1414 /**
1515 * Guess at URL to resource loader load.php
1616 */
17 -$wgResourceLoaderUrl = ( isset( $_SERVER['HTTPS'] ) )? 'https' : 'http';
18 -$wgResourceLoaderUrl.= '://' . $_SERVER['SERVER_NAME'] . dirname( $_SERVER['SCRIPT_NAME'] ) . '/load.php';
19 -$wgLoadScript = $wgResourceLoaderUrl;
 17+$protocol = ( isset( $_SERVER['HTTPS'] ) )? 'https' : 'http';
 18+$wgServer = $protocol . '://' . $_SERVER['SERVER_NAME'] . dirname( $_SERVER['SCRIPT_NAME'] ) . '/';
 19+$wgLoadScript = $wgServer . 'load.php';
2020 // The list of enabled modules
2121 $wgMwEmbedEnabledModules = array();
2222 // By default we enable every module in the "modules" folder
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedMediaWikiStubs.php
@@ -95,7 +95,7 @@
9696 $s .= ");\n";
9797 return $s;
9898 }
99 -/**
 99+ /**
100100 * Returns an escaped string suitable for inclusion in a string literal
101101 * for JavaScript source code.
102102 * Illegal control characters are assumed not to be present.
@@ -192,7 +192,7 @@
193193 $blobs = array();
194194 foreach( $modules as $name => $module ){
195195 $messages = array();
196 - foreach ( $module->getMessages() as $key ) {
 196+ foreach ( $module->getMessages() as $key ) {
197197 $messages[$key] = wfMsgExt( $key, array( 'language' => $lang ) );
198198 }
199199 if( count( $messages ) ){
@@ -203,6 +203,19 @@
204204 }
205205 }
206206
 207+/**
 208+ * MediaWiki abstracts the json functions with fallbacks
 209+ * here we just map directly to the native php call:
 210+ */
 211+class FormatJson{
 212+ public static function encode($value, $isHtml=false){
 213+ return json_encode($value);
 214+ }
 215+ public static function decode( $value, $assoc=false ){
 216+ return json_decode( $value, $assoc );
 217+ }
 218+}
 219+
207220 // Stub WebRequest
208221 // Just serving static files, don't have a concept of wiki titles etc.)
209222 class WebRequest {
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/MwEmbedResourceLoaderStartUpModule.php
@@ -34,8 +34,8 @@
3535 * mwEmbedStartup is dependent on mwEmbedSupport
3636 */
3737 public function getStartupModuleList(){
38 - // Startup modules don't respect dependency manually add 'jquery.triggerQueueCallback', 'mwEmbedSupport'
39 - return array( 'jquery', 'mediawiki', 'mwEmbedSupport' );
 38+ // Startup modules don't respect dependency manually add 'mwEmbedSupport' dependencies
 39+ return array( 'jquery', 'mediawiki', 'jquery.triggerQueueCallback', 'jquery.mwEmbedUtil', 'mwEmbedSupport' );
4040 }
4141
4242 public function getScript( ResourceLoaderContext $context ) {
@@ -46,8 +46,15 @@
4747 }
4848
4949 protected function getConfig( $context ) {
50 - // @@todo set all the configuration variables
51 - $vars = array();
 50+ global $wgLoadScript;
 51+
 52+
 53+ $vars = array(
 54+ 'wgLoadScript' => $wgLoadScript,
 55+ 'debug' => $context->getDebug(),
 56+ 'skin' => $context->getSkin(),
 57+ 'wgUserLanguage' => $context->getLanguage(),
 58+ );
5259 wfRunHooks( 'ResourceLoaderGetConfigVars', array( &$vars ) );
5360
5461 return $vars;
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/resourceloader/ResourceLoaderFileModule.php
@@ -262,17 +262,7 @@
263263 }
264264 // Collect referenced files
265265 $this->localFileRefs = array_unique( $this->localFileRefs );
266 - // If the list has been modified since last time we cached it, update the cache
267 - if ( $this->localFileRefs !== $this->getFileDependencies( $context->getSkin() ) ) {
268 - $dbw = wfGetDB( DB_MASTER );
269 - $dbw->replace( 'module_deps',
270 - array( array( 'md_module', 'md_skin' ) ), array(
271 - 'md_module' => $this->getName(),
272 - 'md_skin' => $context->getSkin(),
273 - 'md_deps' => FormatJson::encode( $this->localFileRefs ),
274 - )
275 - );
276 - }
 266+
277267 return $styles;
278268 }
279269
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/includes/resourceloader/ResourceLoaderStartUpModule.php
@@ -169,9 +169,9 @@
170170 // Startup function
171171 $configuration = $this->getConfig( $context );
172172 $registrations = self::getModuleRegistrations( $context );
173 - $out .= "var mwStartUp = function() {\n" .
 173+ $out .= "var mwStartUp = function() {\n" .
 174+ "\t" . Xml::encodeJsCall( 'mediaWiki.config.set', array( $configuration ) ) .
174175 "\t$registrations\n" .
175 - "\t" . Xml::encodeJsCall( 'mediaWiki.config.set', array( $configuration ) ) .
176176 "};\n";
177177
178178 // Conditional script injection
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/EmbedPlayer.php
@@ -1,34 +1,34 @@
2 -<?php
 2+<?php
33
44 // Register all the EmbedPlayer modules
55 return array(
66 "mw.EmbedPlayer" => array(
77 'scripts' => array(
8 - "resources/mw.EmbedPlayer.js",
 8+ "players/mw.EmbedPlayer.js",
99 "skins/mw.PlayerControlBuilder.js",
1010 ),
1111 'dependencies' => array(
 12+
1213 // jQuery dependencies:
1314 'jquery.hoverIntent',
1415 'jquery.cookie',
1516 'jquery.ui.mouse',
16 - '$.fn.menu',
17 - 'mw.style.jquerymenu',
18 - '$.ui.slider'
 17+ 'jquery.menu',
 18+ 'jquery.ui.slider'
1919 ),
2020 'styles' => "skins/mw.style.EmbedPlayer.css",
2121 'messageFile' => 'EmbedPlayer.i18n.php',
2222 ),
2323
24 - "mw.EmbedPlayerKplayer" => array( 'scripts'=> "resources/mw.EmbedPlayerKplayer.js" ),
25 - "mw.EmbedPlayerGeneric" => array( 'scripts'=> "resources/mw.EmbedPlayerGeneric.js" ),
26 - "mw.EmbedPlayerJava" => array( 'scripts'=> "resources/mw.EmbedPlayerJava.js"),
27 - "mw.EmbedPlayerNative" => array( 'scripts'=> "resources/mw.EmbedPlayerNative.js" ),
 24+ "mw.EmbedPlayerKplayer" => array( 'scripts'=> "players/mw.EmbedPlayerKplayer.js" ),
 25+ "mw.EmbedPlayerGeneric" => array( 'scripts'=> "players/mw.EmbedPlayerGeneric.js" ),
 26+ "mw.EmbedPlayerJava" => array( 'scripts'=> "players/mw.EmbedPlayerJava.js"),
 27+ "mw.EmbedPlayerNative" => array( 'scripts'=> "players/mw.EmbedPlayerNative.js" ),
2828
29 - "mw.EmbedPlayerVlc" => array( 'scripts'=> "resources/mw.EmbedPlayerVlc.js" ),
 29+ "mw.EmbedPlayerVlc" => array( 'scripts'=> "players/mw.EmbedPlayerVlc.js" ),
3030
31 - "mw.IFramePlayerApiServer" => array( 'scripts' => "resources/mw.IFramePlayerApiServer.js" ),
32 - "mw.IFramePlayerApiClient" => array( 'scripts' => "resources/mw.IFramePlayerApiClient.js" ),
 31+ "mw.IFramePlayerApiServer" => array( 'scripts' => "iframeApi/mw.IFramePlayerApiServer.js" ),
 32+ "mw.IFramePlayerApiClient" => array( 'scripts' => "iframeApi/mw.IFramePlayerApiClient.js" ),
3333
3434 "mw.PlayerSkinKskin" => array( 'scripts' => "skins/kskin/mw.PlayerSkinKskin.js",
3535 'styles' => "skins/kskin/mw.style.PlayerSkinKskin.css"),
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerGeneric.js
@@ -0,0 +1,31 @@
 2+/*
 3+* Simple embed object for unknown application/ogg plugin
 4+*/
 5+mw.EmbedPlayerGeneric = {
 6+ // List of supported features of the generic plugin
 7+ supports: {
 8+ 'playHead':false,
 9+ 'pause':false,
 10+ 'stop':true,
 11+ 'fullscreen':false,
 12+ 'timeDisplay':false,
 13+ 'volumeControl':false
 14+ },
 15+
 16+ // Instance name:
 17+ instanceOf:'Generic',
 18+
 19+ /*
 20+ * Generic embed html
 21+ *
 22+ * @return {String}
 23+ * embed code for generic ogg plugin
 24+ */
 25+ doEmbedHTML: function() {
 26+ $j( this ).html(
 27+ '<object type="application/ogg" ' +
 28+ 'width="' + this.getWidth() + '" height="' + this.getHeight() + '" ' +
 29+ 'data="' + this.getSrc( this.seek_time_sec ) + '"></object>'
 30+ );
 31+ }
 32+};
Property changes on: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerGeneric.js
___________________________________________________________________
Added: svn:mergeinfo
133 Merged /branches/sqlite/js2/mwEmbed/libEmbedVideo/genericEmbed.js:r58211-58321
234 Merged /branches/REL1_15/phase3/js2/mwEmbed/libEmbedVideo/genericEmbed.js:r51646
Added: svn:eol-style
335 + native
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayer.js
@@ -0,0 +1,3585 @@
 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+ '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+ 'durationHint',
 36+
 37+ // Media start time
 38+ 'start',
 39+
 40+ // Media end time
 41+ 'end',
 42+
 43+ // If the source is the default source
 44+ 'default',
 45+
 46+ // titleKey ( used for api lookups )
 47+ 'titleKey'
 48+] );
 49+
 50+/**
 51+ * Selector based embedPlayer processing
 52+ *
 53+ * @param {Function=}
 54+ * callback Optional Function to be called once video interfaces
 55+ * are ready
 56+ *
 57+ */
 58+mw.processEmbedPlayers = function( playerSelect, callback ) {
 59+ mw.log( 'EmbedPlayer:: processEmbedPlayers' );
 60+
 61+
 62+ /**
 63+ * Adds a player element for the embedPlayer to rewrite
 64+ *
 65+ * uses embedPlayer interface on audio / video elements uses mvPlayList
 66+ * interface on playlist elements
 67+ *
 68+ * Once a player interface is established the following chain of functions
 69+ * are called;
 70+ *
 71+ * _this.checkPlayerSources()
 72+ * _this.setupSourcePlayer()
 73+ * _this.inheritEmbedPlayer()
 74+ * _this.selectedPlayer.load()
 75+ * _this.showPlayer()
 76+ *
 77+ * @param {Element}
 78+ * playerElement DOM element to be swapped
 79+ * @param {Object}
 80+ * [Optional] attributes Extra attributes to apply to the player
 81+ * interface
 82+ */
 83+ var addPlayerElement = function( playerElement ) {
 84+ var _this = this;
 85+ mw.log('processEmbedPlayers: addElement:: ' + playerElement.id );
 86+
 87+ var waitForMeta = true;
 88+
 89+ // Be sure to "stop" the target ( Firefox 3x keeps playing
 90+ // the video even though its been removed from the DOM )
 91+ if( playerElement.pause ){
 92+ playerElement.pause();
 93+ }
 94+
 95+ // Allow modules to override the wait for metadata flag:
 96+ $j( mw ).trigger( 'checkPlayerWaitForMetaData', playerElement );
 97+
 98+ // Update the waitForMeta object if set to boolean false:
 99+ waitForMeta = ( playerElement.waitForMeta === false )? false : true;
 100+
 101+
 102+ // Confirm we want to wait for meta data ( if not already set to false by module )
 103+ if( waitForMeta ){
 104+ waitForMeta = waitForMetaCheck( playerElement );
 105+ }
 106+
 107+ var ranPlayerSwapFlag = false;
 108+
 109+ // Local callback to runPlayer swap once playerElement has metadata
 110+ function runPlayerSwap() {
 111+ // Don't run player swap twice
 112+ if( ranPlayerSwapFlag ){
 113+ return ;
 114+ }
 115+ ranPlayerSwapFlag = true;
 116+ mw.log("EmbedPlayer::runPlayerSwap::" + $j( playerElement ).attr('id') );
 117+
 118+ var playerInterface = new mw.EmbedPlayer( playerElement , attributes);
 119+ var swapPlayer = swapEmbedPlayerElement( playerElement, playerInterface );
 120+
 121+
 122+ // Trigger the newEmbedPlayerEvent for embedPlayer interface
 123+ mw.log("EmbedPlayer::addPlayerElement :trigger " + playerInterface.id );
 124+ $j( mw ).trigger ( 'newEmbedPlayerEvent', $j( '#' + playerInterface.id ).get(0) );
 125+
 126+ // Issue the checkPlayerSources call to the new player
 127+ // interface: make sure to use the element that is in the DOM:
 128+ $j( '#' + playerInterface.id ).get(0).checkPlayerSources();
 129+ }
 130+
 131+ if( waitForMeta && mw.getConfig('EmbedPlayer.WaitForMeta' ) ) {
 132+ mw.log('EmbedPlayer::WaitForMeta ( video missing height (' +
 133+ $j( playerElement ).attr('height') + '), width (' +
 134+ $j( playerElement ).attr('width') + ') or duration: ' +
 135+ $j( playerElement ).attr('duration')
 136+ );
 137+ $j( playerElement ).bind("loadedmetadata", runPlayerSwap );
 138+
 139+ // Time-out of 5 seconds ( maybe still playable but no timely
 140+ // metadata )
 141+ setTimeout( runPlayerSwap, 5000 );
 142+ return ;
 143+ } else {
 144+ runPlayerSwap();
 145+ return ;
 146+ }
 147+ };
 148+
 149+ /**
 150+ * Check if we should wait for metadata.
 151+ *
 152+ * @return true if the size is "likely" to be updated by waiting for metadata
 153+ * false if the size has been set via an attribute or is already loaded
 154+ */
 155+ var waitForMetaCheck = function( playerElement ){
 156+ var waitForMeta = false;
 157+
 158+ // Don't wait for metadata for non html5 media elements
 159+ if( !playerElement ){
 160+ return false;
 161+ }
 162+ if( !playerElement.tagName || ( playerElement.tagName.toLowerCase() != 'audio' && playerElement.tagName.toLowerCase() != 'video' ) ){
 163+ return false;
 164+ }
 165+ // If we don't have a native player don't wait for metadata
 166+ if( !mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'oggNative') &&
 167+ !mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'webmNative') &&
 168+ !mw.EmbedTypes.getMediaPlayers().isSupportedPlayer( 'h264Native' ) )
 169+ {
 170+ return false;
 171+ }
 172+
 173+
 174+ var width = $j( playerElement ).css( 'width' );
 175+ var height = $j( playerElement ).css( 'height' );
 176+
 177+ // Css video defaults ( firefox )
 178+ if( $j( playerElement ).css( 'width' ) == '300px' &&
 179+ $j( playerElement ).css( 'height' ) == '150px'
 180+ ){
 181+ waitForMeta = true;
 182+ } else {
 183+ // Check if we should wait for duration:
 184+ if( $j( playerElement ).attr( 'duration') ||
 185+ $j( playerElement ).attr('durationHint')
 186+ ){
 187+ // height, width and duration set; do not wait for meta data:
 188+ return false;
 189+ } else {
 190+ waitForMeta = true;
 191+ }
 192+ }
 193+
 194+ // Firefox ~ sometimes~ gives -1 for unloaded media
 195+ if ( $j(playerElement).attr( 'width' ) == -1 || $j(playerElement).attr( 'height' ) == -1 ) {
 196+ waitForMeta = true;
 197+ }
 198+
 199+ // Google Chrome / safari gives 0 width height for unloaded media
 200+ if( $j(playerElement).attr( 'width' ) === 0 ||
 201+ $j(playerElement).attr( 'height' ) === 0
 202+ ) {
 203+ waitForMeta = true;
 204+ }
 205+
 206+ // Firefox default width height is ~sometimes~ 150 / 300
 207+ if( this.height == 150 && this.width == 300 ){
 208+ waitForMeta = true;
 209+ }
 210+
 211+ // Make sure we have a src attribute or source child
 212+ // ( i.e not a video tag to be dynamically populated or looked up from
 213+ // xml resource description )
 214+ if( waitForMeta &&
 215+ (
 216+ $j( playerElement ).attr('src') ||
 217+ $j( playerElement ).find("source[src]").length !== 0
 218+ )
 219+ ) {
 220+ // Detect src type ( if no type set )
 221+ return true;
 222+ } else {
 223+ // playerElement is not likely to update its meta data ( no src )
 224+ return false;
 225+ }
 226+ };
 227+
 228+ /**
 229+ * swapEmbedPlayerElement
 230+ *
 231+ * Takes a video element as input and swaps it out with an embed player interface
 232+ *
 233+ * @param {Element}
 234+ * targetElement Element to be swapped
 235+ * @param {Object}
 236+ * playerInterface Interface to swap into the target element
 237+ */
 238+ var swapEmbedPlayerElement = function( targetElement, playerInterface ) {
 239+ mw.log( 'EmbedPlayer::swapEmbedPlayerElement: ' + targetElement.id );
 240+ // Create a new element to swap the player interface into
 241+ var swapPlayerElement = document.createElement('div');
 242+
 243+ // Get properties / methods from playerInterface:
 244+ for ( var method in playerInterface ) {
 245+ if ( method != 'readyState' ) { // readyState crashes IE ( don't include )
 246+ swapPlayerElement[ method ] = playerInterface[ method ];
 247+ }
 248+ }
 249+ // Check if we are using native controls or Persistent player ( should keep the video embed around )
 250+ if( playerInterface.useNativePlayerControls() || playerInterface.isPersistentNativePlayer() ) {
 251+ $j( targetElement )
 252+ .attr( 'id', playerInterface.pid )
 253+ .addClass( 'nativeEmbedPlayerPid' )
 254+ .show()
 255+ .after(
 256+ $j( swapPlayerElement ).css( 'display', 'none' )
 257+ );
 258+
 259+ } else {
 260+ $j( targetElement ).replaceWith( swapPlayerElement );
 261+ }
 262+
 263+
 264+ // Set swapPlayerElement has height / width set and set to loading:
 265+ $j( swapPlayerElement ).css( {
 266+ 'width' : playerInterface.width + 'px',
 267+ 'height' : playerInterface.height + 'px'
 268+ } );
 269+
 270+ // If we don't already have a loadSpiner add one:
 271+ if( $j('#loadingSpinner_' + playerInterface.id ).length == 0 ){
 272+ if( playerInterface.useNativePlayerControls() || playerInterface.isPersistentNativePlayer() ) {
 273+ var $spinner = $j( targetElement )
 274+ .getAbsoluteOverlaySpinner();
 275+ }else{
 276+ var $spinner = $j( swapPlayerElement ).getAbsoluteOverlaySpinner();
 277+ }
 278+ $spinner.attr('id', 'loadingSpinner_' + playerInterface.id );
 279+ }
 280+ return swapPlayerElement;
 281+ };
 282+
 283+
 284+
 285+
 286+
 287+ // Add a loader for <div> embed player rewrites:
 288+ $j( playerSelect ).each( function( index, playerElement) {
 289+
 290+ // Make sure the playerElement has an id:
 291+ if( !$j( playerElement ).attr('id') ){
 292+ $j( playerElement ).attr( "id", 'mwe_v' + ( index ) );
 293+ }
 294+
 295+ // If we are dynamically embedding on a "div" check if we can
 296+ // add a poster image behind the loader:
 297+ if( playerElement.nodeName.toLowerCase() == 'div'
 298+ && ( attributes.poster || $j(playerElement).attr( 'poster' ) ) ){
 299+ var posterSrc = ( attributes.poster ) ? attributes.poster : $j(playerElement).attr( 'poster' );
 300+
 301+ // Set image size:
 302+ var width = $j( playerElement ).width();
 303+ var height = $j( playerElement ).height();
 304+ if( !width ){
 305+ var width = ( attributes.width )? attributes.width : '100%';
 306+ }
 307+ if( !height ){
 308+ var height = ( attributes.height )? attributes.height : '100%';
 309+ }
 310+
 311+ mw.log('EmbedPlayer:: set loading background: ' + posterSrc);
 312+ $j( playerElement ).append(
 313+ $j( '<img />' )
 314+ .attr( 'src', posterSrc)
 315+ .css({
 316+ 'position' : 'absolute',
 317+ 'width' : width,
 318+ 'height' : height
 319+ })
 320+ );
 321+ }
 322+ });
 323+
 324+ // Create the Global Embed Player Manager ( if not already created )
 325+ // legacy EmbedPlayerManagerReady event ( should remove )
 326+ $j( mw ).trigger( 'EmbedPlayerManagerReady' );
 327+
 328+ // Make sure we have user preference setup for setting preferences on video selection
 329+ var addedToPlayerManager = false;
 330+ mw.log("EmbedPlayer:: do: " + $j( playerSelect ).length + ' players ');
 331+
 332+ // Add each selected element to the player manager:
 333+ $j( playerSelect ).each( function( index, playerElement) {
 334+ // Make sure the video tag was not generated by our library:
 335+ if( $j( playerElement ).hasClass( 'nativeEmbedPlayerPid' ) ){
 336+ $j('#loadingSpinner_' + $j( playerElement ).attr('id') ).hide();
 337+ mw.log( 'EmbedPlayer::$j.embedPlayer skip embedPlayer gennerated video: ' + playerElement );
 338+ } else {
 339+ addedToPlayerManager = true;
 340+ // Add the player
 341+ addPlayerElement( playerElement );
 342+ }
 343+ });
 344+ if( addedToPlayerManager ){
 345+ if( callback ){
 346+ $j( mw ).bind( "playersReadyEvent", callback );
 347+ }
 348+ } else {
 349+ // Run the callback directly if no players were added
 350+ if( callback ){
 351+ callback();
 352+ }
 353+ }
 354+};
 355+/**
 356+ * mediaSource class represents a source for a media element.
 357+ *
 358+ * @param {Element}
 359+ * element: MIME type of the source.
 360+ * @constructor
 361+ */
 362+function mediaSource( element ) {
 363+ this.init( element );
 364+}
 365+
 366+mediaSource.prototype = {
 367+ // MIME type of the source.
 368+ mimeType:null,
 369+
 370+ // URI of the source.
 371+ uri:null,
 372+
 373+ // Title of the source.
 374+ title: null,
 375+
 376+ // True if the source has been marked as the default.
 377+ markedDefault: false,
 378+
 379+ // True if the source supports url specification of offset and duration
 380+ URLTimeEncoding:false,
 381+
 382+ // Start offset of the requested segment
 383+ startOffset: 0,
 384+
 385+ // Duration of the requested segment (0 if not known)
 386+ duration:0,
 387+
 388+ // Is the source playable
 389+ is_playable: null,
 390+
 391+ // source id
 392+ id: null,
 393+
 394+ // Start time in npt format
 395+ start_npt: null,
 396+
 397+ // End time in npt format
 398+ end_npt: null,
 399+
 400+ // Language of the file
 401+ srclang: null,
 402+ /**
 403+ * MediaSource constructor:
 404+ */
 405+ init : function( element ) {
 406+ // mw.log('EmbedPlayer::adding mediaSource: ' + element);
 407+ this.src = $j( element ).attr( 'src' );
 408+
 409+ // Set default URLTimeEncoding if we have a time url:
 410+ // not ideal way to discover if content is on an oggz_chop server.
 411+ // should check some other way.
 412+ var pUrl = mw.parseUri ( this.src );
 413+ if ( typeof pUrl[ 'queryKey' ][ 't' ] != 'undefined' ) {
 414+ this.URLTimeEncoding = true;
 415+ }
 416+
 417+ var sourceAttr = mw.getConfig( 'EmbedPlayer.SourceAttributes' );
 418+
 419+ for ( var i = 0; i < sourceAttr.length; i++ ) { // array loop:
 420+ var attr = sourceAttr[ i ];
 421+ var attr_value = element.getAttribute( attr );
 422+ if ( attr_value ) {
 423+ this[ attr ] = attr_value;
 424+ }
 425+ }
 426+
 427+
 428+ // Set the content type:
 429+ if ( $j( element ).attr( 'type' ) ) {
 430+ this.mimeType = $j( element ).attr( 'type' );
 431+ }else if ( $j( element ).attr( 'content-type' ) ) {
 432+ this.mimeType = $j( element ).attr( 'content-type' );
 433+ }else if( $j( element ).get(0).tagName.toLowerCase() == 'audio' ){
 434+ // If the element is an "audio" tag set audio format
 435+ this.mimeType = 'audio/ogg';
 436+ } else {
 437+ this.mimeType = this.detectType( this.src );
 438+ }
 439+
 440+ // Conform the mime type to ogg
 441+ if( this.mimeType == 'video/theora') {
 442+ this.mimeType = 'video/ogg';
 443+ }
 444+
 445+ if( this.mimeType == 'audio/vorbis') {
 446+ this.mimeType = 'audio/ogg';
 447+ }
 448+
 449+ // Check for parent elements ( supplies categories in "track" )
 450+ if( $j( element ).parent().attr('category') ) {
 451+ this.category = $j( element ).parent().attr('category');
 452+ }
 453+
 454+ if( $j( element ).attr( 'default' ) ){
 455+ this.markedDefault = true;
 456+ }
 457+
 458+ // Get the url duration ( if applicable )
 459+ this.getURLDuration();
 460+ },
 461+
 462+ /**
 463+ * Update Source title via Element
 464+ *
 465+ * @param {Element}
 466+ * element Source element to update attributes from
 467+ */
 468+ updateSource: function( element ) {
 469+ // for now just update the title:
 470+ if ( $j( element ).attr( "title" ) ) {
 471+ this.title = $j( element ).attr( "title" );
 472+ }
 473+ },
 474+
 475+ /**
 476+ * Updates the src time and start & end
 477+ *
 478+ * @param {String}
 479+ * start_time: in NPT format
 480+ * @param {String}
 481+ * end_time: in NPT format
 482+ */
 483+ updateSrcTime: function ( start_npt, end_npt ) {
 484+ // mw.log("f:updateSrcTime: "+ start_npt+'/'+ end_npt + ' from org: ' +
 485+ // this.start_npt+ '/'+this.end_npt);
 486+ // mw.log("pre uri:" + this.src);
 487+ // if we have time we can use:
 488+ if ( this.URLTimeEncoding ) {
 489+ // make sure its a valid start time / end time (else set default)
 490+ if ( !mw.npt2seconds( start_npt ) ) {
 491+ start_npt = this.start_npt;
 492+ }
 493+
 494+ if ( !mw.npt2seconds( end_npt ) ) {
 495+ end_npt = this.end_npt;
 496+ }
 497+
 498+ this.src = mw.replaceUrlParams( this.src, {
 499+ 't': start_npt + '/' + end_npt
 500+ });
 501+
 502+ // update the duration
 503+ this.getURLDuration();
 504+ }
 505+ },
 506+
 507+ /**
 508+ * Sets the duration and sets the end time if unset
 509+ *
 510+ * @param {Float}
 511+ * duration: in seconds
 512+ */
 513+ setDuration: function ( duration ) {
 514+ this.duration = duration;
 515+ if ( !this.end_npt ) {
 516+ this.end_npt = mw.seconds2npt( this.startOffset + duration );
 517+ }
 518+ },
 519+
 520+ /**
 521+ * MIME type accessors function.
 522+ *
 523+ * @return {String} the MIME type of the source.
 524+ */
 525+ getMIMEType: function() {
 526+ if( this.mimeType ) {
 527+ return this.mimeType;
 528+ }
 529+ this.mimeType = this.detectType( this.src );
 530+ return this.mimeType;
 531+ },
 532+
 533+ /**
 534+ * URI function.
 535+ *
 536+ * @param {Number}
 537+ * serverSeekTime Int: Used to adjust the URI for url based
 538+ * seeks)
 539+ * @return {String} the URI of the source.
 540+ */
 541+ getSrc: function( serverSeekTime ) {
 542+ if ( !serverSeekTime || !this.URLTimeEncoding ) {
 543+ return this.src;
 544+ }
 545+ var endvar = '';
 546+ if ( this.end_npt ) {
 547+ endvar = '/' + this.end_npt;
 548+ }
 549+ return mw.replaceUrlParams( this.src,
 550+ {
 551+ 't': mw.seconds2npt( serverSeekTime ) + endvar
 552+ }
 553+ );
 554+ },
 555+
 556+ /**
 557+ * Title accessor function.
 558+ *
 559+ * @return {String} Title of the source.
 560+ */
 561+ getTitle : function() {
 562+ if( this.title ){
 563+ return this.title;
 564+ }
 565+
 566+ // Return a Title based on mime type:
 567+ switch( this.getMIMEType() ) {
 568+ case 'video/h264' :
 569+ return gM( 'mwe-embedplayer-video-h264' );
 570+ break;
 571+ case 'video/x-flv' :
 572+ return gM( 'mwe-embedplayer-video-flv' );
 573+ break;
 574+ case 'video/webm' :
 575+ return gM( 'mwe-embedplayer-video-webm');
 576+ break;
 577+ case 'video/ogg' :
 578+ return gM( 'mwe-embedplayer-video-ogg' );
 579+ break;
 580+ case 'audio/ogg' :
 581+ return gM( 'mwe-embedplayer-video-audio' );
 582+ break;
 583+ case 'video/mpeg' :
 584+ return 'MPEG video'; // FIXME: i18n
 585+ break;
 586+ case 'video/x-msvideo' :
 587+ return 'AVI video'; // FIXME: i18n
 588+ break;
 589+ }
 590+
 591+ // Return tilte based on file name:
 592+ var urlParts = mw.parseUri( this.getSrc() );
 593+ if( urlParts.file ){
 594+ return urlParts.file;
 595+ }
 596+
 597+ // Return the mime type string if not known type.
 598+ return this.mimeType;
 599+ },
 600+
 601+ /**
 602+ *
 603+ * Get Duration of the media in milliseconds from the source url.
 604+ *
 605+ * Supports media_url?t=ntp_start/ntp_end url request format
 606+ */
 607+ getURLDuration : function() {
 608+ // check if we have a URLTimeEncoding:
 609+ if ( this.URLTimeEncoding ) {
 610+ var annoURL = mw.parseUri( this.src );
 611+ if ( annoURL.queryKey.t ) {
 612+ var times = annoURL.queryKey.t.split( '/' );
 613+ this.start_npt = times[0];
 614+ this.end_npt = times[1];
 615+ this.startOffset = mw.npt2seconds( this.start_npt );
 616+ this.duration = mw.npt2seconds( this.end_npt ) - this.startOffset;
 617+ } else {
 618+ // look for this info as attributes
 619+ if ( this.startOffset ) {
 620+ this.start_npt = mw.seconds2npt( this.startOffset );
 621+ }
 622+ if ( this.duration ) {
 623+ this.end_npt = mw.seconds2npt( parseInt( this.duration ) + parseInt( this.startOffset ) );
 624+ }
 625+ }
 626+ }
 627+ },
 628+
 629+ /**
 630+ * Attempts to detect the type of a media file based on the URI.
 631+ *
 632+ * @param {String}
 633+ * uri URI of the media file.
 634+ * @return {String} The guessed MIME type of the file.
 635+ */
 636+ detectType: function( uri ) {
 637+ // NOTE: if media is on the same server as the javascript
 638+ // we can issue a HEAD request and read the mime type of the media...
 639+ // ( this will detect media mime type independently of the url name )
 640+ // http://www.jibbering.com/2002/4/httprequest.html
 641+ var urlParts = mw.parseUri( uri );
 642+ // Get the extension from the url or from the relative name:
 643+ var ext = ( urlParts.file )? /[^.]+$/.exec( urlParts.file ) : /[^.]+$/.exec( uri );
 644+ switch( ext.toString().toLowerCase() ) {
 645+ case 'smil':
 646+ case 'sml':
 647+ return 'application/smil';
 648+ break;
 649+ case 'm4v':
 650+ case 'mp4':
 651+ return 'video/h264';
 652+ break;
 653+ case 'webm':
 654+ return 'video/webm';
 655+ break;
 656+ case 'srt':
 657+ return 'text/x-srt';
 658+ break;
 659+ case 'flv':
 660+ return 'video/x-flv';
 661+ break;
 662+ case 'ogg':
 663+ case 'ogv':
 664+ return 'video/ogg';
 665+ break;
 666+ case 'oga':
 667+ return 'audio/ogg';
 668+ break;
 669+ case 'anx':
 670+ return 'video/ogg';
 671+ break;
 672+ case 'xml':
 673+ return 'text/xml';
 674+ break;
 675+ case 'avi':
 676+ return 'video/x-msvideo';
 677+ break;
 678+ case 'mpg':
 679+ return 'video/mpeg';
 680+ break;
 681+ case 'mpeg':
 682+ return 'video/mpeg';
 683+ break;
 684+ }
 685+ mw.log( "Error: could not detect type of media src: " + uri );
 686+ }
 687+};
 688+
 689+/**
 690+ * A media element corresponding to a <video> element.
 691+ *
 692+ * It is implemented as a collection of mediaSource objects. The media sources
 693+ * will be initialized from the <video> element, its child <source> elements,
 694+ * and/or the ROE file referenced by the <video> element.
 695+ *
 696+ * @param {element}
 697+ * videoElement <video> element used for initialization.
 698+ * @constructor
 699+ */
 700+function mediaElement( element ) {
 701+ this.init( element );
 702+}
 703+
 704+mediaElement.prototype = {
 705+
 706+ // The array of mediaSource elements.
 707+ sources: null,
 708+
 709+ // flag for ROE data being added.
 710+ addedROEData: false,
 711+
 712+ // Selected mediaSource element.
 713+ selectedSource: null,
 714+
 715+ // Media element thumbnail
 716+ thumbnail: null,
 717+
 718+ // Media element linkback
 719+ linkback: null,
 720+
 721+ /**
 722+ * Media Element constructor
 723+ *
 724+ * Sets up a mediaElement from a provided top level "video" element adds any
 725+ * child sources that are found
 726+ *
 727+ * @param {Element}
 728+ * videoElement Element that has src attribute or has children
 729+ * source elements
 730+ */
 731+ init: function( videoElement ) {
 732+ var _this = this;
 733+ mw.log( "EmbedPlayer::mediaElement:init:" + videoElement.id );
 734+ this.sources = new Array();
 735+
 736+ // Process the videoElement as a source element:
 737+ if ( $j( videoElement ).attr( "src" ) ) {
 738+ _this.tryAddSource( videoElement );
 739+ }
 740+
 741+ // Process elements source children
 742+ $j( videoElement ).find( 'source,track' ).each( function( ) {
 743+ _this.tryAddSource( this );
 744+ } );
 745+ },
 746+
 747+ /**
 748+ * Updates the time request for all sources that have a standard time
 749+ * request argument (ie &t=start_time/end_time)
 750+ *
 751+ * @param {String}
 752+ * start_npt Start time in npt format
 753+ * @param {String}
 754+ * end_npt End time in npt format
 755+ */
 756+ updateSourceTimes: function( start_npt, end_npt ) {
 757+ var _this = this;
 758+ $j.each( this.sources, function( inx, mediaSource ) {
 759+ mediaSource.updateSrcTime( start_npt, end_npt );
 760+ } );
 761+ },
 762+
 763+ /**
 764+ * Check for Timed Text tracks
 765+ *
 766+ * @return {Boolean} True if text tracks exist, false if no text tracks are
 767+ * found
 768+ */
 769+ textSourceExists: function() {
 770+ for ( var i = 0; i < this.sources.length; i++ ) {
 771+ if ( this.sources[i].mimeType == 'text/cmml' ||
 772+ this.sources[i].mimeType == 'text/x-srt' )
 773+ {
 774+ return true;
 775+ }
 776+ };
 777+ return false;
 778+ },
 779+
 780+ /**
 781+ * Returns the array of mediaSources of this element.
 782+ *
 783+ * @param {String}
 784+ * [mimeFilter] Filter criteria for set of mediaSources to return
 785+ * @return {Array} mediaSource elements.
 786+ */
 787+ getSources: function( mimeFilter ) {
 788+ if ( !mimeFilter ) {
 789+ return this.sources;
 790+ }
 791+ // Apply mime filter:
 792+ var source_set = new Array();
 793+ for ( var i = 0; i < this.sources.length ; i++ ) {
 794+ if ( this.sources[i].mimeType &&
 795+ this.sources[i].mimeType.indexOf( mimeFilter ) != -1 )
 796+ {
 797+ source_set.push( this.sources[i] );
 798+ }
 799+ }
 800+ return source_set;
 801+ },
 802+
 803+ /**
 804+ * Selects a source by id
 805+ *
 806+ * @param {String}
 807+ * source_id Id of the source to select.
 808+ * @return {MediaSource} The selected mediaSource or null if not found
 809+ */
 810+ getSourceById:function( source_id ) {
 811+ for ( var i = 0; i < this.sources.length ; i++ ) {
 812+ if ( this.sources[i].id == source_id ) {
 813+ return this.sources[i];
 814+ }
 815+ }
 816+ return null;
 817+ },
 818+
 819+ /**
 820+ * Selects a particular source for playback updating the "selectedSource"
 821+ *
 822+ * @param {Number}
 823+ * index Index of source element to set as selectedSource
 824+ */
 825+ selectSource:function( index ) {
 826+ mw.log( 'EmbedPlayer::mediaElement:selectSource:' + index );
 827+ var playableSources = this.getPlayableSources();
 828+ for ( var i = 0; i < playableSources.length; i++ ) {
 829+ if ( i == index ) {
 830+ this.selectedSource = playableSources[i];
 831+ // Update the user selected format:
 832+ mw.EmbedTypes.getMediaPlayers().setFormatPreference( playableSources[i].mimeType );
 833+ break;
 834+ }
 835+ }
 836+ },
 837+
 838+ /**
 839+ * Selects the default source via cookie preference, default marked, or by
 840+ * id order
 841+ */
 842+ autoSelectSource: function() {
 843+ mw.log( 'EmbedPlayer::mediaElement::autoSelectSource' );
 844+ var _this = this;
 845+ // Select the default source
 846+ var playableSources = this.getPlayableSources();
 847+ var flash_flag = ogg_flag = false;
 848+
 849+ // Check if there are any playableSources
 850+ if( playableSources.length == 0 ){
 851+ return false;
 852+ }
 853+ var setSelectedSource = function( source ){
 854+ _this.selectedSource = source;
 855+ };
 856+
 857+ // Set via user-preference
 858+ for ( var source = 0; source < playableSources.length; source++ ) {
 859+ var mimeType = playableSources[source].mimeType;
 860+ if ( mw.EmbedTypes.getMediaPlayers().preference[ 'format_preference' ] == mimeType ) {
 861+ mw.log( 'EmbedPlayer::autoSelectSource: Set via preference: ' + playableSources[source].mimeType );
 862+ setSelectedSource( playableSources[source] );
 863+ return true;
 864+ }
 865+ }
 866+
 867+ // Set via module driven preference:
 868+ $j(this).trigger( 'AutoSelectSource', [ playableSources ] );
 869+ if( _this.selectedSource ){
 870+ return true;
 871+ }
 872+
 873+ // Set via marked default:
 874+ for ( var source = 0; source < playableSources.length; source++ ) {
 875+ if ( playableSources[ source ].markedDefault ) {
 876+ mw.log( 'EmbedPlayer::autoSelectSource: Set via marked default: ' + playableSources[source].markedDefault );
 877+ setSelectedSource( playableSources[source] );
 878+ return true;
 879+ }
 880+ }
 881+
 882+ // Prefer native playback ( and prefer WebM over ogg and h.264 )
 883+ var namedSources = [];
 884+ for ( var source = 0; source < playableSources.length; source++ ) {
 885+ var mimeType = playableSources[source].mimeType;
 886+ var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
 887+ if ( player && player.library == 'Native' ) {
 888+ switch( player.id ){
 889+ case 'oggNative':
 890+ namedSources['ogg'] = playableSources[ source ];
 891+ break;
 892+ case 'webmNative':
 893+ namedSources['webm'] = playableSources[ source ];
 894+ break;
 895+ case 'h264Native':
 896+ namedSources['h264'] = playableSources[ source ];
 897+ break;
 898+ }
 899+ }
 900+ }
 901+ var codecPref =mw.getConfig( 'EmbedPlayer.CodecPreference');
 902+ for(var i =0; i < codecPref.length; i++){
 903+ var codec = codecPref[ i ];
 904+ if( namedSources[ codec ]){
 905+ setSelectedSource( namedSources[ codec ] );
 906+ return true;
 907+ }
 908+ };
 909+
 910+
 911+ // Set h264 via native or flash fallback
 912+ for ( var source = 0; source < playableSources.length; source++ ) {
 913+ var mimeType = playableSources[source].mimeType;
 914+ var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType );
 915+ if ( mimeType == 'video/h264'
 916+ && player
 917+ && (
 918+ player.library == 'Native'
 919+ ||
 920+ player.library == 'Kplayer'
 921+ )
 922+ ) {
 923+ mw.log('EmbedPlayer::autoSelectSource: Set h264 via native or flash fallback');
 924+ setSelectedSource( playableSources[ source ] );
 925+ return true;
 926+ }
 927+ };
 928+
 929+ // Else just select first source
 930+ if ( !this.selectedSource ) {
 931+ mw.log( 'EmbedPlayer::autoSelectSource: Set via first source:' + playableSources[0] );
 932+ setSelectedSource( playableSources[0] );
 933+ return true;
 934+ }
 935+ // No Source found so no source selected
 936+ return false;
 937+ },
 938+
 939+ /**
 940+ * check if the mime is ogg
 941+ */
 942+ isOgg: function( mimeType ){
 943+ if ( mimeType == 'video/ogg'
 944+ || mimeType == 'ogg/video'
 945+ || mimeType == 'video/annodex'
 946+ || mimeType == 'application/ogg'
 947+ ) {
 948+ return true;
 949+ }
 950+ return false;
 951+ },
 952+
 953+ /**
 954+ * Returns the thumbnail URL for the media element.
 955+ *
 956+ * @returns {String} thumbnail URL
 957+ */
 958+ getPosterSrc: function( ) {
 959+ return this.poster;
 960+ },
 961+
 962+ /**
 963+ * Checks whether there is a stream of a specified MIME type.
 964+ *
 965+ * @param {String}
 966+ * mimeType MIME type to check.
 967+ * @return {Boolean} true if sources include MIME false if not.
 968+ */
 969+ hasStreamOfMIMEType: function( mimeType )
 970+ {
 971+ for ( var i = 0; i < this.sources.length; i++ )
 972+ {
 973+ if ( this.sources[i].getMIMEType() == mimeType ){
 974+ return true;
 975+ }
 976+ }
 977+ return false;
 978+ },
 979+
 980+ /**
 981+ * Checks if media is a playable type
 982+ */
 983+ isPlayableType: function( mimeType ) {
 984+ if ( mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType ) ) {
 985+ return true;
 986+ } else {
 987+ return false;
 988+ }
 989+ },
 990+
 991+ /**
 992+ * Adds a single mediaSource using the provided element if the element has a
 993+ * 'src' attribute.
 994+ *
 995+ * @param {Element}
 996+ * element <video>, <source> or <mediaSource> <text> element.
 997+ */
 998+ tryAddSource: function( element ) {
 999+ // mw.log( 'f:tryAddSource:' + $j( element ).attr( "src" ) );
 1000+ var newSrc = $j( element ).attr( 'src' );
 1001+ if ( newSrc ) {
 1002+ // make sure an existing element with the same src does not already
 1003+ // exist:
 1004+ for ( var i = 0; i < this.sources.length; i++ ) {
 1005+ if ( this.sources[i].src == newSrc ) {
 1006+ // Source already exists update any new attr:
 1007+ this.sources[i].updateSource( element );
 1008+ return this.sources[i];
 1009+ }
 1010+ }
 1011+ }
 1012+ // Create a new source
 1013+ var source = new mediaSource( element );
 1014+
 1015+ this.sources.push( source );
 1016+ // mw.log( 'tryAddSource: added source ::' + source + 'sl:' +
 1017+ // this.sources.length );
 1018+ return source;
 1019+ },
 1020+
 1021+ /**
 1022+ * Get playable sources
 1023+ *
 1024+ * @returns {Array} of playable sources
 1025+ */
 1026+ getPlayableSources: function() {
 1027+ var playableSources = [];
 1028+ for ( var i = 0; i < this.sources.length; i++ ) {
 1029+ if ( this.isPlayableType( this.sources[i].mimeType ) ) {
 1030+ playableSources.push( this.sources[i] );
 1031+ } else {
 1032+ mw.log( "type " + this.sources[i].mimeType + ' is not playable' );
 1033+ }
 1034+ };
 1035+ return playableSources;
 1036+ },
 1037+
 1038+ /**
 1039+ * Imports media sources from ROE data.
 1040+ *
 1041+ * @param roe_data
 1042+ * ROE data.
 1043+ */
 1044+ addROE: function( roe_data ) {
 1045+ mw.log( 'EmbedPlayer::mediaElement:addROE' );
 1046+ this.addedROEData = true;
 1047+ var _this = this;
 1048+ if ( roe_data ) {
 1049+ var $roeParsed = $j( roe_data.pay_load );
 1050+
 1051+ // Add media sources:
 1052+ $roeParsed.find("mediaSource").each( function( inx, source ) {
 1053+ _this.tryAddSource( source );
 1054+ } );
 1055+
 1056+ // Set the thumbnail:
 1057+ $roeParsed.find( 'img' ).each( function( inx, n ) {
 1058+ if ( $j( n ).attr( "id" ) == "stream_thumb" ) {
 1059+ mw.log( 'roe:set thumb to ' + $j( n ).attr( "src" ) );
 1060+ _this.poster = $j( n ).attr( "src" );
 1061+ }
 1062+ } );
 1063+
 1064+ // Set the linkback:
 1065+ $roeParsed.find( 'link' ).each( function( inx, n ) {
 1066+ if ( $j( n ).attr( 'id' ) == 'html_linkback' ) {
 1067+ mw.log( 'roe:set linkback to ' + $j( n ).attr( "href" ) );
 1068+ _this.linkback = $j( n ).attr( 'href' );
 1069+ }
 1070+ } );
 1071+ } else {
 1072+ mw.log( 'ROE data empty.' );
 1073+ }
 1074+ }
 1075+};
 1076+
 1077+
 1078+/**
 1079+ * Base embedPlayer object
 1080+ *
 1081+ * @param {Element}
 1082+ * element, the element used for initialization.
 1083+ * @param {Object}
 1084+ * customAttributes Attributes for the video interface that are not
 1085+ * already element attributes
 1086+ * @constructor
 1087+ */
 1088+mw.EmbedPlayer = function( element, customAttributes ) {
 1089+ return this.init( element, customAttributes );
 1090+};
 1091+
 1092+mw.EmbedPlayer.prototype = {
 1093+
 1094+ // The mediaElement object containing all mediaSource objects
 1095+ 'mediaElement' : null,
 1096+
 1097+ // Object that describes the supported feature set of the underling plugin /
 1098+ // Support list is described in PlayerControlBuilder components
 1099+ 'supports': { },
 1100+
 1101+ // Preview mode flag,
 1102+ // some plugins don't seek accurately but in preview mode we need
 1103+ // accurate seeks so we do tricks like hide the image until its ready
 1104+ 'previewMode' : false,
 1105+
 1106+ // Ready to play
 1107+ // NOTE: we should switch over to setting the html5 video ready state
 1108+ 'readyToPlay' : false,
 1109+
 1110+ // Stores the loading errors
 1111+ 'loadError' : false,
 1112+
 1113+ // Thumbnail updating flag ( to avoid rewriting an thumbnail thats already
 1114+ // being updated)
 1115+ 'thumbnail_updating' : false,
 1116+
 1117+ // Poster display flag
 1118+ 'posterDisplayed' : true,
 1119+
 1120+ // Local variable to hold CMML meeta data about the current clip
 1121+ // for more on CMML see: http://wiki.xiph.org/CMML
 1122+ 'cmmlData': null,
 1123+
 1124+ // Stores the seek time request, Updated by the doSeek function
 1125+ 'serverSeekTime' : 0,
 1126+
 1127+ // If the embedPlayer is current 'seeking'
 1128+ 'seeking' : false,
 1129+
 1130+ // Percent of the clip buffered:
 1131+ 'bufferedPercent' : 0,
 1132+
 1133+ // Holds the timer interval function
 1134+ 'monitorTimerId' : null,
 1135+
 1136+ // Buffer flags
 1137+ 'bufferStartFlag' : false,
 1138+ 'bufferEndFlag' : false,
 1139+
 1140+ // For supporting media fragments stores the play end time
 1141+ 'pauseTime' : null,
 1142+
 1143+ // On done playing
 1144+ 'donePlayingCount' : 0
 1145+ ,
 1146+ // if player events should be Propagated
 1147+ '_propagateEvents': true,
 1148+
 1149+ // If the onDone interface should be displayed
 1150+ 'onDoneInterfaceFlag': true,
 1151+
 1152+
 1153+ /**
 1154+ * embedPlayer
 1155+ *
 1156+ * @constructor
 1157+ *
 1158+ * @param {Element}
 1159+ * element DOM element that we are building the player interface for.
 1160+ * @param {Object}
 1161+ * customAttributes Attributes supplied via argument (rather than applied to the element)
 1162+ */
 1163+ init: function( element, customAttributes ) {
 1164+ var _this = this;
 1165+ mw.log('EmbedPlayer: initEmbedPlayer: ' + $j(element).width() );
 1166+ // Set customAttributes if unset:
 1167+ if ( !customAttributes ) {
 1168+ customAttributes = { };
 1169+ }
 1170+ var playerAttributes = mw.getConfig( 'EmbedPlayer.Attributes' );
 1171+
 1172+ // Setup the player Interface from supported attributes:
 1173+ for ( var attr in playerAttributes ) {
 1174+ if ( customAttributes[ attr ] || customAttributes[ attr ] === false ) {
 1175+ this[ attr ] = customAttributes[ attr ];
 1176+ } else if ( element.getAttribute( attr ) != null ) {
 1177+ // boolean attributes
 1178+ if( element.getAttribute( attr ) == '' ){
 1179+ this[ attr ] = true;
 1180+ } else {
 1181+ this[ attr ] = element.getAttribute( attr );
 1182+ }
 1183+ } else {
 1184+ this[attr] = playerAttributes[attr];
 1185+ }
 1186+ // string -> boolean
 1187+ if( this[ attr ] == "false" ) this[attr] = false;
 1188+ if( this[ attr ] == "true" ) this[attr] = true;
 1189+ }
 1190+ //
 1191+
 1192+
 1193+ if( this.apiTitleKey ){
 1194+ this.apiTitleKey = decodeURI( this.apiTitleKey );
 1195+ }
 1196+
 1197+ // Hide "controls" if using native player controls:
 1198+ if( this.useNativePlayerControls() ){
 1199+ _this.controls = false;
 1200+ }
 1201+
 1202+ // Set the poster:
 1203+ if ( $j( element ).attr( 'thumbnail' ) ) {
 1204+ _this.poster = $j( element ).attr( 'thumbnail' );
 1205+ }
 1206+ if ( $j( element ).attr( 'poster' ) ) {
 1207+ _this.poster = $j( element ).attr( 'poster' );
 1208+ }
 1209+
 1210+ // Set the skin name from the class
 1211+ var sn = $j(element).attr( 'class' );
 1212+
 1213+ if ( sn && sn != '' ) {
 1214+ for ( var n = 0; n < mw.validSkins.length; n++ ) {
 1215+ if ( sn.indexOf( mw.validSkins[n].toLowerCase() ) !== -1 ) {
 1216+ this.skinName = mw.validSkins[ n ];
 1217+ }
 1218+ }
 1219+ }
 1220+
 1221+ // Set the default skin if unset:
 1222+ if ( !this.skinName ) {
 1223+ this.skinName = mw.getConfig( 'EmbedPlayer.SkinName' );
 1224+ }
 1225+
 1226+ if( !this.monitorRate ){
 1227+ this.monitorRate = mw.getConfig( 'EmbedPlayer.MonitorRate' );
 1228+ }
 1229+
 1230+ // Make sure startOffset is cast as an float:
 1231+ if ( this.startOffset && this.startOffset.split( ':' ).length >= 2 ) {
 1232+ this.startOffset = parseFloat( mw.npt2seconds( this.startOffset ) );
 1233+ }
 1234+
 1235+ // Make sure offset is in float:
 1236+ this.startOffset = parseFloat( this.startOffset );
 1237+
 1238+ // Set the source duration ( if provided in the element metaData or
 1239+ // durationHint )
 1240+ if ( $j( element ).attr( 'duration' ) ) {
 1241+ _this.duration = $j( element ).attr( 'duration' );
 1242+ }
 1243+
 1244+ if ( !_this.duration && $j( element ).attr( 'durationHint' ) ) {
 1245+ _this.durationHint = $j( element ).attr( 'durationHint' );
 1246+ // Convert duration hint if needed:
 1247+ _this.duration = mw.npt2seconds( _this.durationHint );
 1248+ }
 1249+
 1250+ // Make sure duration is a float:
 1251+ this.duration = parseFloat( this.duration );
 1252+ mw.log( 'EmbedPlayer::mediaElement:' + this.id + " duration is: " + this.duration );
 1253+
 1254+ // Set the player size attributes based loaded video element:
 1255+ this.loadPlayerSize( element );
 1256+ // Set the plugin id
 1257+ this.pid = 'pid_' + this.id;
 1258+
 1259+ // Grab any innerHTML and set it to missing_plugin_html
 1260+ // NOTE: we should strip "source" tags instead of checking and skipping
 1261+ if ( element.innerHTML != '' && element.getElementsByTagName( 'source' ).length == 0 ) {
 1262+ // mw.log( 'innerHTML: ' + element.innerHTML );
 1263+ this.user_missing_plugin_html = element.innerHTML;
 1264+ }
 1265+
 1266+ // Add the mediaElement object with the elements sources:
 1267+ this.mediaElement = new mediaElement( element );
 1268+
 1269+ // Process attribute "sources" for dynamic embedding
 1270+ if( customAttributes.sources && customAttributes.sources.length ){
 1271+ for( var i =0; i < customAttributes.sources.length ; i ++ ){
 1272+ var customSource = customAttributes.sources[i];
 1273+ if( customSource.src ){
 1274+ var $source = $j('<source />')
 1275+ .attr( 'src', customSource.src );
 1276+ // xxx todo pull list of valid source attributes from
 1277+ // mediaSource prototype
 1278+ if( customSource.type ){
 1279+ $source.attr('type', customSource.type );
 1280+ }
 1281+ if( customSource.title ){
 1282+ $source.attr('title', customSource.title );
 1283+ }
 1284+ this.mediaElement.tryAddSource( $source.get(0) );
 1285+ }
 1286+ }
 1287+ }
 1288+ },
 1289+
 1290+ stopEventPropagation: function(){
 1291+ this.stopMonitor();
 1292+ this._propagateEvents = false;
 1293+ },
 1294+ restoreEventPropagation: function(){
 1295+ this._propagateEvents = true;
 1296+ this.startMonitor();
 1297+ },
 1298+
 1299+ enableSeekBar: function(){
 1300+ this.controlBuilder.enableSeekBar();
 1301+ $j( this ).trigger( 'onEnableSeekBar');
 1302+ },
 1303+ disableSeekBar: function(){
 1304+ this.controlBuilder.disableSeekBar();
 1305+ $j( this ).trigger( 'ondisableSeekBar');
 1306+ },
 1307+
 1308+ /**
 1309+ * For plugin-players to update supported features
 1310+ */
 1311+ updateFeatureSupport: function(){
 1312+ $j( this ).trigger('updateFeatureSupportEvent', this.supports );
 1313+ return ;
 1314+ },
 1315+
 1316+ /**
 1317+ * Set the width & height from css style attribute, element attribute, or by
 1318+ * default value if no css or attribute is provided set a callback to
 1319+ * resize.
 1320+ *
 1321+ * Updates this.width & this.height
 1322+ *
 1323+ * @param {Element}
 1324+ * element Source element to grab size from
 1325+ */
 1326+ loadPlayerSize: function( element ) {
 1327+ this.height = $j(element).css( 'height' );
 1328+ this.width = $j(element).css( 'width' );
 1329+ // Special check for chrome 100% with re-mapping to 32px
 1330+ // ( hopefully no one embeds video at 32x32 )
 1331+ if( this.height == '32px' || this.height =='32px' ){
 1332+ this.width = '100%';
 1333+ this.height = '100%';
 1334+ }
 1335+ mw.log('EmbedPlayer::loadPlayerSize: css size:' + this.width + ' h: ' + this.height);
 1336+
 1337+ // Set to parent size ( resize events will cause player size updates)
 1338+ if( this.height.indexOf('100%') != -1 || this.width.indexOf('100%') != -1 ){
 1339+ $relativeParent = $j(element).parents().filter(function() {
 1340+ // reduce to only relative position or "body" elements
 1341+ return $j(this).is('body') || $j(this).css('position') == 'relative';
 1342+ }).slice(0,1); // grab only the "first"
 1343+ this.width = $relativeParent.width();
 1344+ this.height = $relativeParent.height();
 1345+ }
 1346+ // Make sure height and width are a number
 1347+ this.height = parseInt( this.height );
 1348+ this.width = parseInt( this.width );
 1349+
 1350+ // Set via attribute if CSS is zero or NaN and we have an attribute value:
 1351+ this.height = ( this.height==0 || isNaN( this.height )
 1352+ && $j(element).attr( 'height' ) ) ?
 1353+ parseInt( $j(element).attr( 'height' ) ): this.height;
 1354+ this.width = ( this.width == 0 || isNaN( this.width )
 1355+ && $j(element).attr( 'width' ) )?
 1356+ parseInt( $j(element).attr( 'width' ) ): this.width;
 1357+
 1358+
 1359+ // Special case for audio
 1360+ // Firefox sets audio height to "0px" while webkit uses 32px .. force
 1361+ // zero:
 1362+ if( element.tagName.toLowerCase() == 'audio' && this.height == '32' ) {
 1363+ this.height = 0;
 1364+ }
 1365+
 1366+ // Use default aspect ration to get height or width ( if rewriting a
 1367+ // non-audio player )
 1368+ if( element.tagName.toLowerCase() != 'audio' && this.videoAspect ) {
 1369+ var aspect = this.videoAspect.split( ':' );
 1370+ if( this.height && !this.width ) {
 1371+ this.width = parseInt( this.height * ( aspect[0] / aspect[1] ) );
 1372+ }
 1373+ if( this.width && !this.height ) {
 1374+ var apectRatio = ( aspect[1] / aspect[0] );
 1375+ this.height = parseInt( this.width * ( aspect[1] / aspect[0] ) );
 1376+ }
 1377+ }
 1378+
 1379+ // On load sometimes attr is temporally -1 as we don't have video metadata yet.
 1380+ // or in IE we get NaN for width height
 1381+ //
 1382+ // NOTE: browsers that do support height width should set "waitForMeta" flag in addElement
 1383+ if( ( isNaN( this.height )|| isNaN( this.width ) ) ||
 1384+ ( this.height == -1 || this.width == -1 ) ||
 1385+ // Check for firefox defaults
 1386+ // Note: ideally firefox would not do random guesses at css
 1387+ // values
 1388+ ( (this.height == 150 || this.height == 64 ) && this.width == 300 )
 1389+ ) {
 1390+ var defaultSize = mw.getConfig( 'EmbedPlayer.DefaultSize' ).split( 'x' );
 1391+ if( isNaN( this.width ) ){
 1392+ this.width = defaultSize[0];
 1393+ }
 1394+
 1395+ // Special height default for audio tag ( if not set )
 1396+ if( element.tagName.toLowerCase() == 'audio' ) {
 1397+ this.height = 0;
 1398+ }else{
 1399+ this.height = defaultSize[1];
 1400+ }
 1401+ }
 1402+ },
 1403+ /**
 1404+ * Resize the player to a new size preserving aspect ratio Wraps the
 1405+ * controlBuilder.resizePlayer function
 1406+ */
 1407+ resizePlayer: function( size , animate, callback){
 1408+ mw.log("EmbedPlayer::resizePlayer:" + size.width + ' x ' + size.height );
 1409+
 1410+ // Check if we are native display then resize the playerElement directly
 1411+ if( this.useNativePlayerControls() ){
 1412+ if( animate ){
 1413+ $j( this.getPlayerElement() ).animate( size , callback);
 1414+ } else {
 1415+ $j( this.getPlayerElement() ).css( size );
 1416+ if( callback ) {
 1417+ callback();
 1418+ }
 1419+ }
 1420+ } else {
 1421+ this.controlBuilder.resizePlayer( size, animate, callback);
 1422+ }
 1423+ $j( this ).trigger( 'onResizePlayer', [size, animate] );
 1424+ },
 1425+
 1426+ /**
 1427+ * Get the player pixel width not including controls
 1428+ *
 1429+ * @return {Number} pixel height of the video
 1430+ */
 1431+ getPlayerWidth: function() {
 1432+ return $j( this ).width();
 1433+ },
 1434+
 1435+ /**
 1436+ * Get the player pixel height not including controls
 1437+ *
 1438+ * @return {Number} pixel height of the video
 1439+ */
 1440+ getPlayerHeight: function() {
 1441+ return $j( this ).height();
 1442+ },
 1443+
 1444+ /**
 1445+ * Check player for sources. If we need to get media sources form an
 1446+ * external file that request is issued here
 1447+ */
 1448+ checkPlayerSources: function() {
 1449+ mw.log( 'EmbedPlayer::checkPlayerSources: ' + this.id );
 1450+ var _this = this;
 1451+
 1452+ // Scope the end of check for player sources so it can be called in a
 1453+ var finishCheckPlayerSources = function(){
 1454+ // Run embedPlayer sources hook
 1455+ $j( _this ).triggerQueueCallback( 'checkPlayerSourcesEvent', function(){
 1456+ _this.setupSourcePlayer();
 1457+ });
 1458+ };
 1459+
 1460+ // NOTE: Should could be moved to mediaWiki Api support module
 1461+ // only load from api if sources are empty:
 1462+ if ( _this.apiTitleKey && this.mediaElement.sources.length == 0) {
 1463+ // Load media from external data
 1464+ mw.log( 'EmbedPlayer::checkPlayerSources: loading apiTitleKey:' + _this.apiTitleKey );
 1465+ _this.loadSourceFromApi( function(){
 1466+ finishCheckPlayerSources();
 1467+ } );
 1468+ return ;
 1469+ } else {
 1470+ finishCheckPlayerSources();
 1471+ }
 1472+ },
 1473+ /**
 1474+ * Empty the player sources
 1475+ */
 1476+ emptySources: function(){
 1477+ if( this.mediaElement ){
 1478+ this.mediaElement.sources = [];
 1479+ this.mediaElement.selectedSource = null;
 1480+ }
 1481+
 1482+ },
 1483+
 1484+ /**
 1485+ * Insert and play a video source ( useful for ads or bumper videos )
 1486+ *
 1487+ * Only works while video is in active play back. Only tested with native
 1488+ * playback atm.
 1489+ */
 1490+ switchPlaySrc: function( source ){
 1491+ mw.log("Error: only native playback supports insertAndPlaySource right now");
 1492+ },
 1493+
 1494+ /**
 1495+ * Load Source video info from mediaWiki Api title key ( this.apiTitleKey )
 1496+ *
 1497+ * @@todo move this to mediaWiki 'api' module
 1498+ * @param {Function}
 1499+ * callback Function called once loading is complete
 1500+ */
 1501+ loadSourceFromApi: function( callback ){
 1502+ var _this = this;
 1503+ if( !_this.apiTitleKey ){
 1504+ mw.log( 'Error no apiTitleKey');
 1505+ return false;
 1506+ }
 1507+
 1508+ // Set local apiProvider via config if not defined
 1509+ if( !_this.apiProvider ) {
 1510+ _this.apiProvider = mw.getConfig( 'EmbedPlayer.ApiProvider' );
 1511+ }
 1512+
 1513+ // Setup the request
 1514+ var request = {
 1515+ 'prop': 'imageinfo',
 1516+ // In case the user added File: or Image: to the apiKey:
 1517+ 'titles': 'File:' + unescape( this.apiTitleKey ).replace( /^(File:|Image:)/ , '' ),
 1518+ 'iiprop': 'url|size|dimensions|metadata',
 1519+ 'iiurlwidth': _this.width,
 1520+ 'redirects' : true // automatically resolve redirects
 1521+ };
 1522+
 1523+ // Run the request:
 1524+ mw.getJSON( mw.getApiProviderURL( _this.apiProvider ), request, function( data ){
 1525+ if ( data.query.pages ) {
 1526+ for ( var i in data.query.pages ) {
 1527+ if( i == '-1' ) {
 1528+ callback( false );
 1529+ return ;
 1530+ }
 1531+ var page = data.query.pages[i];
 1532+ }
 1533+ } else {
 1534+ callback( false );
 1535+ return ;
 1536+ }
 1537+ // Make sure we have imageinfo:
 1538+ if( ! page.imageinfo || !page.imageinfo[0] ){
 1539+ callback( false );
 1540+ return ;
 1541+ }
 1542+ var imageinfo = page.imageinfo[0];
 1543+
 1544+ // Set the poster
 1545+ _this.poster = imageinfo.thumburl;
 1546+
 1547+ // Add the media src
 1548+ _this.mediaElement.tryAddSource(
 1549+ $j('<source />')
 1550+ .attr( 'src', imageinfo.url )
 1551+ .get( 0 )
 1552+ );
 1553+
 1554+ // Set the duration
 1555+ if( imageinfo.metadata[2]['name'] == 'length' ) {
 1556+ _this.duration = imageinfo.metadata[2]['value'];
 1557+ }
 1558+
 1559+ // Set the width height
 1560+ // Make sure we have an accurate aspect ratio
 1561+ if( imageinfo.height != 0 && imageinfo.width != 0 ) {
 1562+ _this.height = parseInt( _this.width * ( imageinfo.height / imageinfo.width ) );
 1563+ }
 1564+
 1565+ // Update the css for the player interface
 1566+ $j( _this ).css( 'height', _this.height);
 1567+
 1568+ callback();
 1569+ });
 1570+ },
 1571+
 1572+ /**
 1573+ * Set up the select source player
 1574+ *
 1575+ * issues autoSelectSource call
 1576+ *
 1577+ * Sets load error if no source is playable
 1578+ */
 1579+ setupSourcePlayer: function() {
 1580+ mw.log("EmbedPlayer::setupSourcePlayer: " + this.id + ' sources: ' + this.mediaElement.sources.length );
 1581+ // Autoseletct the media source
 1582+ this.mediaElement.autoSelectSource();
 1583+
 1584+ // Auto select player based on default order
 1585+ if ( !this.mediaElement.selectedSource ) {
 1586+ mw.log( 'setupSourcePlayer:: no sources, type:' + this.type );
 1587+ } else {
 1588+ this.selectedPlayer = mw.EmbedTypes.getMediaPlayers().defaultPlayer( this.mediaElement.selectedSource.mimeType );
 1589+ }
 1590+
 1591+ if ( this.selectedPlayer ) {
 1592+ // Inherit the playback system of the selected player:
 1593+ this.inheritEmbedPlayer();
 1594+ } else {
 1595+ this.showPluginMissingHTML();
 1596+ }
 1597+ },
 1598+
 1599+ /**
 1600+ * Load and inherit methods from the selected player interface
 1601+ *
 1602+ * @param {Function}
 1603+ * callback Function to be called once playback-system has been
 1604+ * inherited
 1605+ */
 1606+ inheritEmbedPlayer: function( callback ) {
 1607+ mw.log( "EmbedPlayer::inheritEmbedPlayer:duration is: " + this.getDuration() + ' p: ' + this.id );
 1608+
 1609+ // Clear out any non-base embedObj methods:
 1610+ if ( this.instanceOf ) {
 1611+ eval( 'var tmpObj = mw.EmbedPlayer' + this.instanceOf );
 1612+ for ( var i in tmpObj ) { // for in loop oky for object
 1613+ if ( this[ 'parent_' + i ] ) {
 1614+ this[i] = this[ 'parent_' + i];
 1615+ } else {
 1616+ this[i] = null;
 1617+ }
 1618+ }
 1619+ }
 1620+
 1621+ // Set up the new embedObj
 1622+ mw.log( 'EmbedPlayer::inheritEmbedPlayer: embedding with ' + this.selectedPlayer.library );
 1623+ var _this = this;
 1624+
 1625+ // Load the selected player
 1626+ this.selectedPlayer.load( function() {
 1627+ mw.log( 'EmbedPlayer::inheritEmbedPlayer ' + _this.selectedPlayer.library + " player loaded for " + _this.id );
 1628+
 1629+ // Get embed library player Interface
 1630+ var playerInterface = mw[ 'EmbedPlayer' + _this.selectedPlayer.library ];
 1631+
 1632+ for ( var method in playerInterface ) {
 1633+ if ( _this[method] && !_this['parent_' + method] ) {
 1634+ _this['parent_' + method] = _this[method];
 1635+ }
 1636+ _this[ method ] = playerInterface[method];
 1637+ }
 1638+
 1639+ // Update feature support
 1640+ _this.updateFeatureSupport();
 1641+
 1642+ _this.getDuration();
 1643+
 1644+ // Run player display with timeout to avoid function stacking
 1645+ setTimeout(function(){
 1646+ _this.showPlayer();
 1647+ // Call the global player manager to inform this video interface is
 1648+ // ready:
 1649+ mw.playerManager.playerReady( _this );
 1650+
 1651+ // Run the callback if provided
 1652+ if ( typeof callback == 'function' ){
 1653+ callback();
 1654+ }
 1655+ },1);
 1656+
 1657+ } );
 1658+ },
 1659+
 1660+ /**
 1661+ * Select a player playback system
 1662+ *
 1663+ * @param {Object}
 1664+ * player Player playback system to be selected player playback
 1665+ * system include vlc, native, java etc.
 1666+ */
 1667+ selectPlayer: function( player ) {
 1668+ var _this = this;
 1669+ if ( this.selectedPlayer.id != player.id ) {
 1670+ this.selectedPlayer = player;
 1671+ this.inheritEmbedPlayer( function(){
 1672+ // Hide / remove track container
 1673+ _this.$interface.find( '.track' ).remove();
 1674+ // We have to re-bind hoverIntent ( has to happen in this scope
 1675+ // )
 1676+ if( _this.controls && _this.controlBuilder.checkOverlayControls() ){
 1677+ _this.controlBuilder.showControlBar();
 1678+ _this.$interface.hoverIntent({
 1679+ 'sensitivity': 4,
 1680+ 'timeout' : 2000,
 1681+ 'over' : function(){
 1682+ _this.controlBuilder.showControlBar();
 1683+ },
 1684+ 'out' : function(){
 1685+ _this.controlBuilder.hideControlBar();
 1686+ }
 1687+ });
 1688+ }
 1689+ });
 1690+ }
 1691+ },
 1692+
 1693+ /**
 1694+ * Get a time range from the media start and end time
 1695+ *
 1696+ * @return start_npt and end_npt time if present
 1697+ */
 1698+ getTimeRange: function() {
 1699+ var end_time = (this.controlBuilder.longTimeDisp)? '/' + mw.seconds2npt( this.getDuration() ) : '';
 1700+ var default_time_range = '0:00' + end_time;
 1701+ if ( !this.mediaElement )
 1702+ return default_time_range;
 1703+ if ( !this.mediaElement.selectedSource )
 1704+ return default_time_range;
 1705+ if ( !this.mediaElement.selectedSource.end_npt )
 1706+ return default_time_range;
 1707+ return this.mediaElement.selectedSource.start_npt + this.mediaElement.selectedSource.end_npt;
 1708+ },
 1709+
 1710+ /**
 1711+ * Get the duration of the embed player
 1712+ */
 1713+ getDuration: function() {
 1714+ return this.duration;
 1715+ },
 1716+
 1717+ /**
 1718+ * Get the player height
 1719+ */
 1720+ getHeight: function() {
 1721+ return this.height;
 1722+ },
 1723+
 1724+ /**
 1725+ * Get the player width
 1726+ */
 1727+ getWidth: function(){
 1728+ return this.width;
 1729+ },
 1730+
 1731+ /**
 1732+ * Check if the selected source is an audio element:
 1733+ */
 1734+ isAudio: function(){
 1735+ return ( this.mediaElement.selectedSource.mimeType.indexOf('audio/') !== -1 );
 1736+ },
 1737+
 1738+ /**
 1739+ * Get the plugin embed html ( should be implemented by embed player
 1740+ * interface )
 1741+ */
 1742+ doEmbedHTML: function() {
 1743+ return 'Error: function doEmbedHTML should be implemented by embed player interface ';
 1744+ },
 1745+
 1746+ /**
 1747+ * Seek function ( should be implemented by embedPlayer interface
 1748+ * playerNative, playerKplayer etc. ) embedPlayer doSeek only handles URL
 1749+ * time seeks
 1750+ */
 1751+ doSeek: function( percent ) {
 1752+ var _this = this;
 1753+
 1754+ this.seeking = true;
 1755+
 1756+ // See if we should do a server side seek ( player independent )
 1757+ if ( this.supportsURLTimeEncoding() ) {
 1758+ mw.log( 'EmbedPlayer::doSeek:: updated serverSeekTime: ' + mw.seconds2npt ( this.serverSeekTime ) +
 1759+ ' currentTime: ' + _this.currentTime );
 1760+ // make sure we need to seek:
 1761+ if( _this.currentTime == _this.serverSeekTime ){
 1762+ return ;
 1763+ }
 1764+
 1765+ this.stop();
 1766+ this.didSeekJump = true;
 1767+ // Make sure this.serverSeekTime is up-to-date:
 1768+ this.serverSeekTime = mw.npt2seconds( this.start_npt ) + parseFloat( percent * this.getDuration() );
 1769+ // Update the slider
 1770+ this.updatePlayHead( percent );
 1771+ }
 1772+
 1773+ // Do play request in 100ms ( give the dom time to swap out the embed player )
 1774+ setTimeout( function() {
 1775+ _this.seeking = false;
 1776+ _this.play();
 1777+ _this.monitor();
 1778+ }, 100 );
 1779+
 1780+ // Run the onSeeking interface update
 1781+ // NOTE controlBuilder should really bind to html5 events rather
 1782+ // than explicitly calling it or inheriting stuff.
 1783+ this.controlBuilder.onSeek();
 1784+ },
 1785+
 1786+ /**
 1787+ * Seeks to the requested time and issues a callback when ready (should be
 1788+ * overwritten by client that supports frame serving)
 1789+ */
 1790+ setCurrentTime: function( time, callback ) {
 1791+ mw.log( 'Error: base embed setCurrentTime can not frame serve (override via plugin)' );
 1792+ },
 1793+
 1794+ /**
 1795+ * On clip done action. Called once a clip is done playing
 1796+ */
 1797+ onClipDone: function() {
 1798+ var _this = this;
 1799+ // don't run onclipdone if _propagateEvents is off
 1800+ if( !_this._propagateEvents ){
 1801+ return ;
 1802+ }
 1803+ mw.log( 'EmbedPlayer::onClipDone:' + this.id + ' doneCount:' + this.donePlayingCount + ' stop state:' +this.isStopped() );
 1804+ // Only run stopped once:
 1805+ if( !this.isStopped() ){
 1806+ // Stop the monitor and event propagation
 1807+ this.stopEventPropagation();
 1808+
 1809+ // Show the control bar:
 1810+ this.controlBuilder.showControlBar();
 1811+
 1812+ // Update the clip done playing count:
 1813+ this.donePlayingCount ++;
 1814+
 1815+ // Run the ended trigger ( allow the ended object to prevent default actions )
 1816+ mw.log("EmbedPlayer::onClipDone:Trigger ended");
 1817+
 1818+ // TOOD we should improve the end event flow
 1819+ $j( this ).trigger( 'ended' );
 1820+
 1821+ // if the ended event did not trigger more timeline actions run the actual stop:
 1822+ if( this.onDoneInterfaceFlag ){
 1823+ this.stop();
 1824+ // restore event propagation
 1825+ this.restoreEventPropagation();
 1826+ // Check if we have the "loop" property set
 1827+ if( this.loop ) {
 1828+ this.play();
 1829+ return;
 1830+ }
 1831+
 1832+ // Stop the clip (load the thumbnail etc)
 1833+ this.serverSeekTime = 0;
 1834+ this.updatePlayHead( 0 );
 1835+
 1836+ // Make sure we are not in preview mode( no end clip actions in
 1837+ // preview mode)
 1838+ if ( this.previewMode ) {
 1839+ return ;
 1840+ }
 1841+
 1842+ // Do the controlBuilder onClip done interface
 1843+ this.controlBuilder.onClipDone();
 1844+ }
 1845+ }
 1846+ },
 1847+
 1848+
 1849+ /**
 1850+ * Shows the video Thumbnail, updates pause state
 1851+ */
 1852+ showThumbnail: function() {
 1853+ var _this = this;
 1854+ mw.log( 'EmbedPlayer::showThumbnail' + this.posterDisplayed );
 1855+
 1856+ // Close Menu Overlay:
 1857+ this.controlBuilder.closeMenuOverlay();
 1858+
 1859+ // update the thumbnail html:
 1860+ this.updatePosterHTML();
 1861+
 1862+ this.paused = true;
 1863+ this.posterDisplayed = true;
 1864+ // Make sure the controlBuilder bindings are up-to-date
 1865+ this.controlBuilder.addControlBindings();
 1866+
 1867+ // Once the thumbnail is shown run the mediaReady trigger (if not using
 1868+ // native controls)
 1869+ if( !this.useNativePlayerControls() ){
 1870+ mw.log("mediaLoaded");
 1871+ $j( this ).trigger( 'mediaLoaded' );
 1872+ }
 1873+ },
 1874+
 1875+ /**
 1876+ * Show the player
 1877+ */
 1878+ showPlayer: function () {
 1879+ mw.log( 'EmbedPlayer:: Show player: ' + this.id + ' interace: w:' + this.width + ' h:' + this.height );
 1880+ var _this = this;
 1881+ // Set-up the local controlBuilder instance:
 1882+ this.controlBuilder = new mw.PlayerControlBuilder( this );
 1883+ var _this = this;
 1884+
 1885+
 1886+ // Make sure we have mwplayer_interface
 1887+ if( $j( this ).parent( '.mwplayer_interface' ).length == 0 ) {
 1888+ // Select "player"
 1889+ $j( this ).wrap(
 1890+ $j('<div>')
 1891+ .addClass( 'mwplayer_interface ' + this.controlBuilder.playerClass )
 1892+ .css({
 1893+ 'width' : this.width + 'px',
 1894+ 'height' : this.height + 'px',
 1895+ 'position' : 'relative'
 1896+ })
 1897+ )
 1898+ // position the "player" absolute inside the relative interface
 1899+ // parent:
 1900+ .css('position', 'absolute');
 1901+ }
 1902+
 1903+ // Set up local jQuery object reference to "mwplayer_interface"
 1904+ this.$interface = $j( this ).parent( '.mwplayer_interface' );
 1905+ // if a isPersistentNativePlayer ( overlay the controls )
 1906+ if( !this.useNativePlayerControls() && this.isPersistentNativePlayer() ){
 1907+ this.$interface.css({
 1908+ 'position' : 'absolute',
 1909+ 'top' : '0px',
 1910+ 'left' : '0px',
 1911+ 'background': null
 1912+ });
 1913+
 1914+ if( this.isPersistentNativePlayer() && !_this.controlBuilder.checkOverlayControls() ){
 1915+ // if Persistent native player always give it the player height
 1916+ $j('#' + this.pid ).css('height', this.height - _this.controlBuilder.height );
 1917+ }
 1918+ $j( this ).show();
 1919+ this.controls = true;
 1920+ }
 1921+ if( !this.useNativePlayerControls() && !this.isPersistentNativePlayer() && !_this.controlBuilder.checkOverlayControls() ){
 1922+ // Update the video size per available control space.
 1923+ $j(this).css('height', this.height - _this.controlBuilder.height );
 1924+ }
 1925+
 1926+ // Update Thumbnail for the "player"
 1927+ this.updatePosterHTML();
 1928+
 1929+ // Add controls if enabled:
 1930+ if ( this.controls ) {
 1931+ this.controlBuilder.addControls();
 1932+ } else {
 1933+ // Need to think about this some more...
 1934+ // Interface is hidden if controls are "off"
 1935+ this.$interface.hide();
 1936+ }
 1937+
 1938+ // Update temporal url if present
 1939+ this.updateTemporalUrl();
 1940+
 1941+
 1942+ if ( this.autoplay ) {
 1943+ mw.log( 'EmbedPlayer::showPlayer::activating autoplay' );
 1944+ // Issue a non-blocking play request
 1945+ setTimeout(function(){
 1946+ _this.play();
 1947+ },1);
 1948+ }
 1949+
 1950+ },
 1951+ /**
 1952+ * Media framgments handler based on:
 1953+ * http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#fragment-dimensions
 1954+ *
 1955+ * We support seconds and npt ( normal play time )
 1956+ *
 1957+ * Updates the player per fragment url info if present
 1958+ *
 1959+ */
 1960+ updateTemporalUrl: function(){
 1961+ var sourceHash = /[^\#]+$/.exec( this.getSrc() ).toString();
 1962+ if( sourceHash.indexOf('t=') === 0 ){
 1963+ // parse the times
 1964+ var times = sourceHash.substr(2).split(',');
 1965+ if( times[0] ){
 1966+ // update the current time
 1967+ this.currentTime = mw.npt2seconds( times[0].toString() );
 1968+ }
 1969+ if( times[1] ){
 1970+ this.pauseTime = mw.npt2seconds( times[1].toString() );
 1971+ // ignore invalid ranges:
 1972+ if( this.pauseTime < this.currentTime ){
 1973+ this.pauseTime = null;
 1974+ }
 1975+ }
 1976+ // Update the play head
 1977+ this.updatePlayHead( this.currentTime / this.duration );
 1978+ // Update status:
 1979+ this.controlBuilder.setStatus( mw.seconds2npt( this.currentTime ) );
 1980+ }
 1981+ },
 1982+ /**
 1983+ * Get missing plugin html (check for user included code)
 1984+ *
 1985+ * @param {String}
 1986+ * [misssingType] missing type mime
 1987+ */
 1988+ showPluginMissingHTML: function( ) {
 1989+ mw.log("EmbedPlayer::showPluginMissingHTML");
 1990+ // Control builder ( for play button )
 1991+ this.controlBuilder = new mw.PlayerControlBuilder( this );
 1992+
 1993+ // Remove loader
 1994+ $j('#loadingSpinner_' + this.id ).remove();
 1995+
 1996+ // Get mime type for un-supported formats:
 1997+ this.updatePosterHTML();
 1998+
 1999+ // Set the play button to the first available source:
 2000+ $j(this).find('.play-btn-large')
 2001+ .unbind('click')
 2002+ .wrap(
 2003+ $j('<a />').attr( {
 2004+ 'href': this.mediaElement.sources[0].getSrc(),
 2005+ 'title' : gM('mwe-embedplayer-play_clip')
 2006+ } )
 2007+ );
 2008+ },
 2009+
 2010+ /**
 2011+ * Update the video time request via a time request string
 2012+ *
 2013+ * @param {String}
 2014+ * time_req
 2015+ */
 2016+ updateVideoTimeReq: function( time_req ) {
 2017+ mw.log( 'EmbedPlayer::updateVideoTimeReq:' + time_req );
 2018+ var time_parts = time_req.split( '/' );
 2019+ this.updateVideoTime( time_parts[0], time_parts[1] );
 2020+ },
 2021+
 2022+ /**
 2023+ * Update Video time from provided start_npt and end_npt values
 2024+ *
 2025+ * @param {String}
 2026+ * start_npt the new start time in npt format
 2027+ * @pamra {String} end_npt the new end time in npt format
 2028+ */
 2029+ updateVideoTime: function( start_npt, end_npt ) {
 2030+ // update media
 2031+ this.mediaElement.updateSourceTimes( start_npt, end_npt );
 2032+
 2033+ // update mv_time
 2034+ this.controlBuilder.setStatus( start_npt + '/' + end_npt );
 2035+
 2036+ // reset slider
 2037+ this.updatePlayHead( 0 );
 2038+
 2039+ // reset seek_offset:
 2040+ if ( this.mediaElement.selectedSource.URLTimeEncoding ) {
 2041+ this.serverSeekTime = 0;
 2042+ } else {
 2043+ this.serverSeekTime = mw.npt2seconds( start_npt );
 2044+ }
 2045+ },
 2046+
 2047+
 2048+ /**
 2049+ * Update Thumb time with npt formated time
 2050+ *
 2051+ * @param {String}
 2052+ * time NPT formated time to update thumbnail
 2053+ */
 2054+ updateThumbTimeNPT: function( time ) {
 2055+ this.updateThumbTime( mw.npt2seconds( time ) - parseInt( this.startOffset ) );
 2056+ },
 2057+
 2058+ /**
 2059+ * Update the thumb with a new time
 2060+ *
 2061+ * @param {Float}
 2062+ * floatSeconds Time to update the thumb to
 2063+ */
 2064+ updateThumbTime:function( floatSeconds ) {
 2065+ // mw.log('updateThumbTime:'+floatSeconds);
 2066+ var _this = this;
 2067+ if ( typeof this.org_thum_src == 'undefined' ) {
 2068+ this.org_thum_src = this.poster;
 2069+ }
 2070+ if ( this.org_thum_src.indexOf( 't=' ) !== -1 ) {
 2071+ this.last_thumb_url = mw.replaceUrlParams( this.org_thum_src,
 2072+ {
 2073+ 't' : mw.seconds2npt( floatSeconds + parseInt( this.startOffset ) )
 2074+ }
 2075+ );
 2076+ if ( !this.thumbnail_updating ) {
 2077+ this.updatePoster( this.last_thumb_url , false );
 2078+ this.last_thumb_url = null;
 2079+ }
 2080+ }
 2081+ },
 2082+
 2083+ /**
 2084+ * Updates the displayed thumbnail via percent of the stream
 2085+ *
 2086+ * @param {Float}
 2087+ * percent Percent of duration to update thumb
 2088+ */
 2089+ updateThumbPerc:function( percent ) {
 2090+ return this.updateThumbTime( ( this.getDuration() * percent ) );
 2091+ },
 2092+
 2093+ /**
 2094+ * Updates the thumbnail if the thumbnail is being displayed
 2095+ *
 2096+ * @param {String}
 2097+ * src New src of thumbnail
 2098+ * @param {Boolean}
 2099+ * quick_switch true switch happens instantly false / undefined
 2100+ * animated cross fade
 2101+ */
 2102+ updatePosterSrc: function( src, quick_switch ) {
 2103+ // make sure we don't go to the same url if we are not already updating:
 2104+ if ( !this.thumbnail_updating && $j( '#img_thumb_' + this.id ).attr( 'src' ) == src )
 2105+ return false;
 2106+ // if we are already updating don't issue a new update:
 2107+ if ( this.thumbnail_updating && $j( '#new_img_thumb_' + this.id ).attr( 'src' ) == src )
 2108+ return false;
 2109+
 2110+ mw.log( 'update thumb: ' + src );
 2111+
 2112+ if ( quick_switch ) {
 2113+ $j( '#img_thumb_' + this.id ).attr( 'src', src );
 2114+ } else {
 2115+ var _this = this;
 2116+ // if still animating remove new_img_thumb_
 2117+ if ( this.thumbnail_updating == true )
 2118+ $j( '#new_img_thumb_' + this.id ).stop().remove();
 2119+
 2120+ if ( this.posterDisplayed ) {
 2121+ mw.log( 'set to thumb:' + src );
 2122+ this.thumbnail_updating = true;
 2123+ $j( this ).append(
 2124+ $j('<img />')
 2125+ .attr({
 2126+ 'src' : src,
 2127+ 'id' : 'new_img_thumb_' + this.id,
 2128+ 'width' : this.width,
 2129+ 'height': this.height
 2130+ })
 2131+ .css( {
 2132+ 'display' : 'none',
 2133+ 'position' : 'absolute',
 2134+ 'z-index' : 2,
 2135+ 'top' : '0px',
 2136+ 'left' : '0px'
 2137+ })
 2138+ );
 2139+ // mw.log('appended: new_img_thumb_');
 2140+ $j( '#new_img_thumb_' + this.id ).fadeIn( "slow", function() {
 2141+ // once faded in remove org and rename new:
 2142+ $j( '#img_thumb_' + _this.id ).remove();
 2143+ $j( '#new_img_thumb_' + _this.id ).attr( 'id', 'img_thumb_' + _this.id );
 2144+ $j( '#img_thumb_' + _this.id ).css( 'z-index', '1' );
 2145+ _this.thumbnail_updating = false;
 2146+ // mw.log("done fadding in "+
 2147+ // $j('#img_thumb_'+_this.id).attr("src"));
 2148+
 2149+ // if we have a thumb queued update to that
 2150+ if ( _this.last_thumb_url ) {
 2151+ var src_url = _this.last_thumb_url;
 2152+ _this.last_thumb_url = null;
 2153+ _this.updatePosterSrc( src_url );
 2154+ }
 2155+ } );
 2156+ }
 2157+ }
 2158+ },
 2159+ // update the video poster:
 2160+ updatePosterSrc: function( posterSrc ){
 2161+ this.poster = posterSrc;
 2162+ },
 2163+ /**
 2164+ * Returns the HTML code for the video when it is in thumbnail mode.
 2165+ * playing, configuring the player, inline cmml display, HTML linkback,
 2166+ * download, and embed code.
 2167+ */
 2168+ updatePosterHTML: function () {
 2169+ mw.log( 'EmbedPlayer:updatePosterHTML::' + this.id );
 2170+ var thumb_html = '';
 2171+ var class_atr = '';
 2172+ var style_atr = '';
 2173+
 2174+ if( this.useNativePlayerControls() ){
 2175+ this.showNativePlayer();
 2176+ return ;
 2177+ }
 2178+
 2179+ // Set by default thumb value if not found
 2180+ var posterSrc = ( this.poster ) ? this.poster :
 2181+ mw.getConfig( 'imagesPath' ) + 'vid_default_thumb.jpg';
 2182+
 2183+ // Update PersistentNativePlayer poster:
 2184+ if( this.isPersistentNativePlayer() ){
 2185+ $j( '#' + this.pid ).attr('poster', posterSrc);
 2186+ } else {
 2187+ // Poster support is not very consistent in browsers
 2188+ // use a jpg poster image:
 2189+ $j( this ).html(
 2190+ $j( '<img />' )
 2191+ .css({
 2192+ 'position' : 'relative',
 2193+ 'width' : '100%',
 2194+ 'height' : '100%'
 2195+ })
 2196+ .attr({
 2197+ 'id' : 'img_thumb_' + this.id,
 2198+ 'src' : posterSrc
 2199+ })
 2200+ .addClass( 'playerPoster' )
 2201+ );
 2202+ }
 2203+ if ( this.controls && this.controlBuilder
 2204+ && this.height > this.controlBuilder.getComponentHeight( 'playButtonLarge' )
 2205+ ) {
 2206+ $j( this ).append(
 2207+ this.controlBuilder.getComponent( 'playButtonLarge' )
 2208+ );
 2209+ }
 2210+ },
 2211+
 2212+ /**
 2213+ * Checks if native controls should be used
 2214+ *
 2215+ * @param [player]
 2216+ * Object Optional player object to check controls attribute
 2217+ * @returns boolean true if the mwEmbed player interface should be used
 2218+ * false if the mwEmbed player interface should not be used
 2219+ */
 2220+ useNativePlayerControls: function() {
 2221+
 2222+ if( this.usenativecontrols === true ){
 2223+ return true;
 2224+ }
 2225+ if( mw.getConfig('EmbedPlayer.NativeControls') === true ) {
 2226+ return true;
 2227+ }
 2228+
 2229+ // Do some device detection devices that don't support overlays
 2230+ // and go into full screen once play is clicked:
 2231+ if( mw.isAndroid2() || mw.isIpod() || mw.isIphone() ){
 2232+ return true;
 2233+ }
 2234+ // iPad can use html controls if its a persistantPlayer in the dom before loading )
 2235+ // else it needs to use native controls:
 2236+ if( mw.isIpad() ){
 2237+ if( this.isPersistentNativePlayer() && mw.getConfig('EmbedPlayer.EnableIpadHTMLControls') ){
 2238+ return false;
 2239+ } else {
 2240+ // Set warning that your trying to do iPad controls without persistent native player:
 2241+ if( mw.getConfig('EmbedPlayer.EnableIpadHTMLControls') ){
 2242+ mw.log("Error:: Trying to set EnableIpadHTMLControls without Persistent Native Player");
 2243+ }
 2244+ return true;
 2245+ }
 2246+ }
 2247+ return false;
 2248+ },
 2249+
 2250+ isPersistentNativePlayer: function(){
 2251+ return $j('#' + this.pid ).hasClass('persistentNativePlayer');
 2252+ },
 2253+
 2254+
 2255+ /**
 2256+ * Show the native player embed code
 2257+ *
 2258+ * This is for cases where the main library needs to "get out of the way"
 2259+ * since the device only supports a limited subset of the html5 and won't
 2260+ * work with an html javascirpt interface
 2261+ */
 2262+ showNativePlayer: function(){
 2263+ var _this = this;
 2264+ // Empty the player of any child nodes
 2265+ $j(this).empty();
 2266+
 2267+ // Remove the player loader spinner if it exists
 2268+ $j('#loadingSpinner_' + this.id ).remove();
 2269+
 2270+ // Setup the source
 2271+ var source = this.mediaElement.getSources( 'video/h264' )[0];
 2272+ // Support fake user agent
 2273+ if( !source || !source.src ){
 2274+ mw.log( 'Warning: Your probably fakeing the iPhone userAgent ( no h.264 source )' );
 2275+ source = this.mediaElement.getSources( 'video/ogg' )[0];
 2276+ }
 2277+
 2278+ // Setup videoAttribues
 2279+ var videoAttribues = {
 2280+ 'poster': _this.poster,
 2281+ 'src' : source.src,
 2282+ 'controls' : 'true'
 2283+ };
 2284+ if( this.loop ){
 2285+ videoAttribues[ 'loop' ] = 'true';
 2286+ }
 2287+ var cssStyle = {
 2288+ 'width' : _this.width,
 2289+ 'height' : _this.height
 2290+ };
 2291+
 2292+ // If not a persistentNativePlayer swap the video tag
 2293+ // completely instead of just updating properties:
 2294+ $j( '#' + this.pid ).replaceWith(
 2295+ _this.getNativePlayerHtml( videoAttribues, cssStyle )
 2296+ );
 2297+
 2298+ // Bind native events:
 2299+ this.applyMediaElementBindings();
 2300+
 2301+ // Android only can play with a special play button ( no native controls
 2302+ // persistentNativePlayer has no controls:
 2303+ if( mw.isAndroid2() ){
 2304+ this.addPlayBtnLarge();
 2305+ }
 2306+ return ;
 2307+ },
 2308+ addPlayBtnLarge:function(){
 2309+ var _this = this;
 2310+ var $pid = $j( '#' + _this.pid );
 2311+ $pid.siblings('.play-btn-large').remove();
 2312+ $playButton = this.controlBuilder.getComponent('playButtonLarge');
 2313+ $pid.after(
 2314+ $playButton
 2315+ .css({
 2316+ 'left' : parseInt( $pid.position().left ) + parseInt( $playButton.css('left') ),
 2317+ 'top' : parseInt( $pid.position().top ) + parseInt( $playButton.css('top') )
 2318+ })
 2319+ );
 2320+ },
 2321+ /**
 2322+ * Should be set via native embed support
 2323+ */
 2324+ getNativePlayerHtml: function(){
 2325+ return $j('<div />' )
 2326+ .css( 'width', this.getWidth() )
 2327+ .html( 'Error: Trying to get native html5 player without native support for codec' );
 2328+ },
 2329+ /**
 2330+ * Should be set via native embed support
 2331+ */
 2332+ applyMediaElementBindings: function(){
 2333+ return ;
 2334+ },
 2335+
 2336+ /**
 2337+ * Gets code to embed the player remotely for "share" this player links
 2338+ */
 2339+ getEmbeddingHTML: function() {
 2340+ switch( mw.getConfig( 'EmbedPlayer.ShareEmbedMode' ) ){
 2341+ case 'iframe':
 2342+ return this.getShareIframeObject();
 2343+ break;
 2344+ case 'videojs':
 2345+ return this.getShareEmbedVideoJs();
 2346+ break;
 2347+ }
 2348+ },
 2349+
 2350+ /**
 2351+ * Get the share embed object code
 2352+ *
 2353+ * NOTE this could probably share a bit more code with getShareEmbedVideoJs
 2354+ */
 2355+ getShareIframeObject: function(){
 2356+
 2357+ // If using a gadget do the new embed format:
 2358+ // NOTE this should be factored out into mediaWiki gadget helper
 2359+ if( typeof wgServer != 'undefined' && typeof mwCheckForGadget != 'undefined' ){
 2360+ var iframeUrl = wgServer +
 2361+ wgArticlePath.replace( /\$1/, 'File:' +
 2362+ unescape( this.apiTitleKey ).replace( /^(File:|Image:)/ , '' ) ) +
 2363+ '?' + mw.getConfig( 'Mw.AppendWithJS' ) +
 2364+ '&embedplayer=yes';
 2365+ } else if ( typeof(mw.IA) != 'undefined') {
 2366+ var iframeUrl = mw.IA.embedUrl();
 2367+ } else {
 2368+ // old style embed:
 2369+ var iframeUrl = mw.getMwEmbedPath() + 'mwEmbedFrame.php?';
 2370+ var params = {'src[]':[]};
 2371+
 2372+ // TODO move to mediaWiki Support module
 2373+ if( this.apiTitleKey ) {
 2374+ params.apiTitleKey = this.apiTitleKey;
 2375+ if ( this.apiProvider ) {
 2376+ // Commons always uses the commons api provider ( special hack should refactor )
 2377+ if( mw.parseUri( document.URL ).host == 'commons.wikimedia.org'){
 2378+ this.apiProvider = 'commons';
 2379+ }
 2380+ params.apiProvider = this.apiProvider;
 2381+ }
 2382+ } else {
 2383+ // Output all the video sources:
 2384+ for( var i=0; i < this.mediaElement.sources.length; i++ ){
 2385+ var source = this.mediaElement.sources[i];
 2386+ if( source.src ) {
 2387+ params['src[]'].push(mw.absoluteUrl( source.src ));
 2388+ }
 2389+ }
 2390+ // Output the poster attr
 2391+ if( this.poster ){
 2392+ params.poster = this.poster;
 2393+ }
 2394+ }
 2395+
 2396+ // Set the skin if set to something other than default
 2397+ if( this.skinName ){
 2398+ params.skin = this.skinName;
 2399+ }
 2400+
 2401+ if( this.duration ) {
 2402+ params.durationHint = parseFloat( this.duration );
 2403+ }
 2404+ iframeUrl += $j.param( params );
 2405+ }
 2406+
 2407+ // Set up embedFrame src path
 2408+ var embedCode = '&lt;iframe src=&quot;' + mw.escapeQuotesHTML( iframeUrl ) + '&quot; ';
 2409+
 2410+ // Set width / height of embed object
 2411+ embedCode += 'width=&quot;' + this.getPlayerWidth() +'&quot; ';
 2412+ embedCode += 'height=&quot;' + this.getPlayerHeight() + '&quot; ';
 2413+ embedCode += 'frameborder=&quot;0&quot; ';
 2414+
 2415+ // Close up the embedCode tag:
 2416+ embedCode+='&gt;&lt/iframe&gt;';
 2417+
 2418+ // Return the embed code
 2419+ return embedCode;
 2420+ },
 2421+
 2422+ /**
 2423+ * Get the share embed Video tag code
 2424+ */
 2425+ getShareEmbedVideoJs: function(){
 2426+
 2427+ // Set the embed tag type:
 2428+ var embedtag = ( this.isAudio() )? 'audio': 'video';
 2429+
 2430+ // Set up the mwEmbed js include:
 2431+ var embedCode = '&lt;script type=&quot;text/javascript&quot; ' +
 2432+ 'src=&quot;' +
 2433+ mw.escapeQuotesHTML(
 2434+ mw.absoluteUrl(
 2435+ mw.getMwEmbedSrc()
 2436+ )
 2437+ ) + '&quot;&gt;&lt;/script&gt' +
 2438+ '&lt;' + embedtag + ' ';
 2439+
 2440+ if( this.poster ) {
 2441+ embedCode += 'poster=&quot;' +
 2442+ mw.escapeQuotesHTML( mw.absoluteUrl( this.poster ) ) +
 2443+ '&quot; ';
 2444+ }
 2445+
 2446+ // Set the skin if set to something other than default
 2447+ if( this.skinName ){
 2448+ embedCode += 'class=&quot;' +
 2449+ mw.escapeQuotesHTML( this.skinName ) +
 2450+ '&quot; ';
 2451+ }
 2452+
 2453+ if( this.duration ) {
 2454+ embedCode +='durationHint=&quot;' + parseFloat( this.duration ) + '&quot; ';
 2455+ }
 2456+
 2457+ if( this.width || this.height ){
 2458+ embedCode +='style=&quot;';
 2459+ embedCode += ( this.width )? 'width:' + this.width +'px;': '';
 2460+ embedCode += ( this.height )? 'height:' + this.height +'px;': '';
 2461+ embedCode += '&quot; ';
 2462+ }
 2463+
 2464+ // TODO move to mediaWiki Support module
 2465+ if( this.apiTitleKey ) {
 2466+ embedCode += 'apiTitleKey=&quot;' + mw.escapeQuotesHTML( this.apiTitleKey ) + '&quot; ';
 2467+ if ( this.apiProvider ) {
 2468+ embedCode += 'apiProvider=&quot;' + mw.escapeQuotesHTML( this.apiProvider ) + '&quot; ';
 2469+ }
 2470+ // close the video tag
 2471+ embedCode += '&gt;&lt;/video&gt;';
 2472+
 2473+ } else {
 2474+ // Close the video attr
 2475+ embedCode += '&gt;';
 2476+
 2477+ // Output all the video sources:
 2478+ for( var i=0; i < this.mediaElement.sources.length; i++ ){
 2479+ var source = this.mediaElement.sources[i];
 2480+ if( source.src ) {
 2481+ embedCode +='&lt;source src=&quot;' +
 2482+ mw.absoluteUrl( source.src ) +
 2483+ '&quot; &gt;&lt;/source&gt;';
 2484+ }
 2485+ }
 2486+ // Close the video tag
 2487+ embedCode += '&lt;/video&gt;';
 2488+ }
 2489+
 2490+ return embedCode;
 2491+ },
 2492+
 2493+ /**
 2494+ * Base Embed Controls
 2495+ */
 2496+
 2497+ /**
 2498+ * The Play Action
 2499+ *
 2500+ * Handles play requests, updates relevant states: seeking =false paused =
 2501+ * false Updates pause button Starts the "monitor"
 2502+ */
 2503+ play: function() {
 2504+ var _this = this;
 2505+ mw.log( "EmbedPlayer:: play: " + this._propagateEvents );
 2506+ if( ! this._propagateEvents ){
 2507+ return ;
 2508+ }
 2509+ // Hide any overlay:
 2510+ this.controlBuilder.closeMenuOverlay();
 2511+
 2512+ // Check if thumbnail is being displayed and embed html
 2513+ if ( this.posterDisplayed ) {
 2514+ if ( !this.selectedPlayer ) {
 2515+ this.showPluginMissingHTML();
 2516+ return;
 2517+ } else {
 2518+ this.posterDisplayed = false;
 2519+ // Hide any button if present:
 2520+ this.$interface.find( '.play-btn-large' ).remove();
 2521+ this.doEmbedHTML();
 2522+ }
 2523+ }
 2524+
 2525+ if( this.paused === true ){
 2526+ this.paused = false;
 2527+ // Check if we should Trigger the play event
 2528+ mw.log("EmbedPlayer:: trigger play even::" + !this.paused);
 2529+ if( ! this.doMethodsAutoTrigger() ) {
 2530+ $j( this ).trigger( 'play' );
 2531+ }
 2532+ }
 2533+
 2534+ // If we previously finished playing this clip run the "replay hook"
 2535+ if( this.donePlayingCount > 0 && !this.paused) {
 2536+ mw.log("replayEvent");
 2537+ $j( this ).trigger( 'replayEvent' );
 2538+ }
 2539+
 2540+ this.$interface.find('.play-btn span')
 2541+ .removeClass( 'ui-icon-play' )
 2542+ .addClass( 'ui-icon-pause' );
 2543+
 2544+ this.$interface.find( '.play-btn' )
 2545+ .unbind()
 2546+ .buttonHover()
 2547+ .click( function( ) {
 2548+ _this.pause();
 2549+ } )
 2550+ .attr( 'title', gM( 'mwe-embedplayer-pause_clip' ) );
 2551+
 2552+ // Start the monitor if not already started
 2553+ this.monitor();
 2554+ },
 2555+ /**
 2556+ * Base embed pause Updates the play/pause button state.
 2557+ *
 2558+ * There is no general way to pause the video must be overwritten by embed
 2559+ * object to support this functionality.
 2560+ */
 2561+ pause: function( event ) {
 2562+ var _this = this;
 2563+ // Trigger the pause event if not already paused and using native
 2564+ // controls:
 2565+ if( this.paused === false ){
 2566+ this.paused = true;
 2567+ mw.log('EmbedPlayer:trigger pause:' + this.paused);
 2568+ if( ! this.doMethodsAutoTrigger() ){
 2569+ $j( this ).trigger( 'pause' );
 2570+ }
 2571+ }
 2572+
 2573+ // update the ctrl "paused state"
 2574+ this.$interface.find('.play-btn span' )
 2575+ .removeClass( 'ui-icon-pause' )
 2576+ .addClass( 'ui-icon-play' );
 2577+
 2578+ this.$interface.find('.play-btn' )
 2579+ .unbind('click')
 2580+ .buttonHover()
 2581+ .click( function() {
 2582+ _this.play();
 2583+ } )
 2584+ .attr( 'title', gM( 'mwe-embedplayer-play_clip' ) );
 2585+ },
 2586+ // special per browser check for autoTrigger events
 2587+ // ideally jQuery would not have this inconsistency.
 2588+ doMethodsAutoTrigger: function(){
 2589+ if( $j.browser.mozilla && ! mw.versionIsAtLeast('2.0', $j.browser.version ) ){
 2590+ return true;
 2591+ }
 2592+ return false;
 2593+ },
 2594+
 2595+ /**
 2596+ * Maps the html5 load request. There is no general way to "load" clips so
 2597+ * underling plugin-player libs should override.
 2598+ */
 2599+ load: function() {
 2600+ // should be done by child (no base way to pre-buffer video)
 2601+ mw.log( 'baseEmbed:load call' );
 2602+ },
 2603+
 2604+
 2605+ /**
 2606+ * Base embed stop
 2607+ *
 2608+ * Updates the player to the stop state shows Thumbnail resets Buffer resets
 2609+ * Playhead slider resets Status
 2610+ */
 2611+ stop: function() {
 2612+ var _this = this;
 2613+ mw.log( 'EmbedPlayer::stop:' + this.id );
 2614+
 2615+ // no longer seeking:
 2616+ this.didSeekJump = false;
 2617+
 2618+ // Reset current time and prev time and seek offset
 2619+ this.currentTime = this.previousTime = this.serverSeekTime = 0;
 2620+
 2621+ this.stopMonitor();
 2622+
 2623+ // Issue pause to update interface (only call this parent)
 2624+ if( !this.paused ){
 2625+ this.paused = true;
 2626+ // update the interface
 2627+ if ( this['parent_pause'] ) {
 2628+ this.parent_pause();
 2629+ } else {
 2630+ this.pause();
 2631+ }
 2632+ }
 2633+ // Native player controls:
 2634+ if( this.useNativePlayerControls() ){
 2635+ this.getPlayerElement().currentTime = 0;
 2636+ this.getPlayerElement().pause();
 2637+ // If on android when we "stop" we re add the large play button
 2638+ if( mw.isAndroid2() ){
 2639+ this.addPlayBtnLarge();
 2640+ }
 2641+ } else {
 2642+ // Rewrite the html to thumbnail disp
 2643+ this.showThumbnail();
 2644+ this.bufferedPercent = 0; // reset buffer state
 2645+ this.controlBuilder.setStatus( this.getTimeRange() );
 2646+
 2647+ // Reset the playhead
 2648+ mw.log("EmbedPlayer::Stop:: Reset play head");
 2649+ this.updatePlayHead( 0 );
 2650+
 2651+ }
 2652+ },
 2653+
 2654+ /**
 2655+ * Base Embed mute
 2656+ *
 2657+ * Handles interface updates for toggling mute. Plug-in / player interface
 2658+ * must handle the actual media player update
 2659+ */
 2660+ toggleMute: function() {
 2661+ mw.log( 'f:toggleMute:: (old state:) ' + this.muted );
 2662+ if ( this.muted ) {
 2663+ this.muted = false;
 2664+ var percent = this.preMuteVolume;
 2665+ } else {
 2666+ this.muted = true;
 2667+ this.preMuteVolume = this.volume;
 2668+ var percent = 0;
 2669+ }
 2670+ this.setVolume( percent );
 2671+ // Update the interface
 2672+ this.setInterfaceVolume( percent );
 2673+ },
 2674+
 2675+ /**
 2676+ * Update volume function ( called from interface updates )
 2677+ *
 2678+ * @param {float}
 2679+ * percent Percent of full volume
 2680+ */
 2681+ setVolume: function( percent ) {
 2682+ // ignore NaN percent:
 2683+ if( isNaN( percent ) ){
 2684+ return ;
 2685+ }
 2686+ // Set the local volume attribute
 2687+ this.previousVolume = this.volume = percent;
 2688+
 2689+ // Un-mute if setting positive volume
 2690+ if( percent != 0 ){
 2691+ this.muted = false;
 2692+ }
 2693+
 2694+ // Update the playerElement volume
 2695+ this.setPlayerElementVolume( percent );
 2696+
 2697+ // mw.log(" setVolume:: " + percent + ' this.volume is: ' +
 2698+ // this.volume);
 2699+ $j( this ).trigger('volumeChanged', percent );
 2700+ },
 2701+
 2702+ /**
 2703+ * Updates the interface volume
 2704+ *
 2705+ * TODO should move to controlBuilder
 2706+ *
 2707+ * @param {float}
 2708+ * percent Percentage volume to update interface
 2709+ */
 2710+ setInterfaceVolume: function( percent ) {
 2711+ if( this.supports[ 'volumeControl' ] &&
 2712+ this.$interface.find( '.volume-slider' ).length
 2713+ ) {
 2714+ this.$interface.find( '.volume-slider' ).slider( 'value', percent * 100 );
 2715+ }
 2716+ },
 2717+
 2718+ /**
 2719+ * Abstract Update volume Method must be override by plug-in / player
 2720+ * interface
 2721+ */
 2722+ setPlayerElementVolume: function( percent ) {
 2723+ mw.log('Error player does not support volume adjustment' );
 2724+ },
 2725+
 2726+ /**
 2727+ * Abstract get volume Method must be override by plug-in / player interface
 2728+ * (if player does not override we return the abstract player value )
 2729+ */
 2730+ getPlayerElementVolume: function(){
 2731+ // mw.log(' error player does not support getting volume property' );
 2732+ return this.volume;
 2733+ },
 2734+
 2735+ /**
 2736+ * Abstract get volume muted property must be overwritten by plug-in /
 2737+ * player interface (if player does not override we return the abstract
 2738+ * player value )
 2739+ */
 2740+ getPlayerElementMuted: function(){
 2741+ // mw.log(' error player does not support getting mute property' );
 2742+ return this.muted;
 2743+ },
 2744+
 2745+ /**
 2746+ * Passes a fullscreen request to the controlBuilder interface
 2747+ */
 2748+ fullscreen: function() {
 2749+ this.controlBuilder.toggleFullscreen();
 2750+ },
 2751+
 2752+ /**
 2753+ * Abstract method to be run post embedding the player Generally should be
 2754+ * overwritten by the plug-in / player
 2755+ */
 2756+ postEmbedJS:function() {
 2757+ return ;
 2758+ },
 2759+
 2760+ /**
 2761+ * Checks the player state based on thumbnail display & paused state
 2762+ *
 2763+ * @return {Boolean} true if playing false if not playing
 2764+ */
 2765+ isPlaying : function() {
 2766+ if ( this.posterDisplayed ) {
 2767+ // in stopped state
 2768+ return false;
 2769+ } else if ( this.paused ) {
 2770+ // paused state
 2771+ return false;
 2772+ } else {
 2773+ return true;
 2774+ }
 2775+ },
 2776+
 2777+ /**
 2778+ * Get paused state
 2779+ *
 2780+ * @return {Boolean} true if playing false if not playing
 2781+ */
 2782+ isPaused: function() {
 2783+ return this.paused;
 2784+ },
 2785+
 2786+ /**
 2787+ * Get Stopped state
 2788+ *
 2789+ * @return {Boolean} true if stopped false if playing
 2790+ */
 2791+ isStopped: function() {
 2792+ return this.posterDisplayed;
 2793+ },
 2794+
 2795+ // TODO temporary hack we need a better stop monitor system
 2796+ stopMonitor: function(){
 2797+ clearInterval( this.monitorInterval );
 2798+ this.monitorInterval = 0;
 2799+ },
 2800+ // TODO temporary hack we need a better stop monitor system
 2801+ startMonitor: function(){
 2802+ this.monitor();
 2803+ },
 2804+
 2805+ /**
 2806+ * Checks if the currentTime was updated outside of the getPlayerElementTime
 2807+ * function
 2808+ */
 2809+ checkForCurrentTimeSeek: function(){
 2810+ var _this = this;
 2811+ // Check if a javascript currentTime change based seek has occurred
 2812+ if( _this.previousTime != _this.currentTime && !this.userSlide && !this.seeking){
 2813+ // If the time has been updated and is in range issue a seek
 2814+ if( _this.getDuration() && _this.currentTime <= _this.getDuration() ){
 2815+ var seekPercent = _this.currentTime / _this.getDuration();
 2816+ mw.log("checkForCurrentTimeSeek::" + _this.previousTime + ' != ' +
 2817+ _this.currentTime + " javascript based currentTime update to " +
 2818+ seekPercent + ' == ' + _this.currentTime );
 2819+ _this.previousTime = _this.currentTime;
 2820+ this.doSeek( seekPercent );
 2821+ }
 2822+ }
 2823+ },
 2824+
 2825+ /**
 2826+ * Monitor playback and update interface components. underling plugin
 2827+ * objects are responsible for updating currentTime
 2828+ */
 2829+ monitor: function() {
 2830+ var _this = this;
 2831+
 2832+ // Check for current time update outside of embed player
 2833+ this.checkForCurrentTimeSeek();
 2834+
 2835+
 2836+ // Update currentTime via embedPlayer
 2837+ _this.currentTime = _this.getPlayerElementTime();
 2838+
 2839+ // Update any offsets from server seek
 2840+ if( _this.serverSeekTime && _this.supportsURLTimeEncoding() ){
 2841+ _this.currentTime = parseInt( _this.serverSeekTime ) + parseInt( _this.getPlayerElementTime() );
 2842+ }
 2843+
 2844+ // Update the previousTime ( so we can know if the user-javascript
 2845+ // changed currentTime )
 2846+ _this.previousTime = _this.currentTime;
 2847+
 2848+ if( _this.pauseTime && _this.currentTime > _this.pauseTime ){
 2849+ _this.pause();
 2850+ _this.pauseTime = null;
 2851+ }
 2852+
 2853+
 2854+ // Check if volume was set outside of embed player function
 2855+ // mw.log( ' this.volume: ' + _this.volume + ' prev Volume:: ' +
 2856+ // _this.previousVolume );
 2857+ if( Math.round( _this.volume * 100 ) != Math.round( _this.previousVolume * 100 ) ) {
 2858+ _this.setInterfaceVolume( _this.volume );
 2859+ if( _this._propagateEvents ){
 2860+ $j( this ).trigger('volumeChanged', _this.volume );
 2861+ }
 2862+ }
 2863+
 2864+ // Update the previous volume
 2865+ _this.previousVolume = _this.volume;
 2866+
 2867+ // Update the volume from the player element
 2868+ _this.volume = this.getPlayerElementVolume();
 2869+
 2870+ // update the mute state from the player element
 2871+ if( _this.muted != _this.getPlayerElementMuted() && ! _this.isStopped() ){
 2872+ mw.log( "EmbedPlayer::monitor: muted does not mach embed player" );
 2873+ _this.toggleMute();
 2874+ // Make sure they match:
 2875+ _this.muted = _this.getPlayerElementMuted();
 2876+ }
 2877+
 2878+ //mw.log( 'Monitor:: ' + this.currentTime + ' duration: ' + ( parseInt(
 2879+ // this.getDuration() ) + 1 ) + ' is seeking: ' + this.seeking );
 2880+
 2881+ if ( this.currentTime >= 0 && this.duration ) {
 2882+ if ( !this.userSlide && !this.seeking ) {
 2883+ if ( parseInt( this.startOffset ) != 0 ) {
 2884+ // If start offset include that calculation
 2885+ this.updatePlayHead( ( this.currentTime - this.startOffset ) / this.duration );
 2886+ var et = ( this.controlBuilder.longTimeDisp ) ? '/' + mw.seconds2npt( parseFloat( this.startOffset ) + parseFloat( this.duration ) ) : '';
 2887+ this.controlBuilder.setStatus( mw.seconds2npt( this.currentTime ) + et );
 2888+ } else {
 2889+ this.updatePlayHead( this.currentTime / this.duration );
 2890+ // Only include the end time if longTimeDisp is enabled:
 2891+ var et = ( this.controlBuilder.longTimeDisp ) ? '/' + mw.seconds2npt( this.duration ) : '';
 2892+ this.controlBuilder.setStatus( mw.seconds2npt( this.currentTime ) + et );
 2893+ }
 2894+ }
 2895+ // Check if we are "done"
 2896+ var endPresentationTime = ( this.startOffset ) ? ( this.startOffset + this.duration ) : this.duration;
 2897+ if ( this.currentTime >= endPresentationTime ) {
 2898+ //mw.log( "mWEmbedPlayer::should run clip done :: " + this.currentTime + ' > ' + endPresentationTime );
 2899+ this.onClipDone();
 2900+ }
 2901+ } else {
 2902+ // Media lacks duration just show end time
 2903+ if ( this.isStopped() ) {
 2904+ this.controlBuilder.setStatus( this.getTimeRange() );
 2905+ } else if ( this.isPaused() ) {
 2906+ this.controlBuilder.setStatus( gM( 'mwe-embedplayer-paused' ) );
 2907+ } else if ( this.isPlaying() ) {
 2908+ if ( this.currentTime && ! this.duration )
 2909+ this.controlBuilder.setStatus( mw.seconds2npt( this.currentTime ) + ' /' );
 2910+ else
 2911+ this.controlBuilder.setStatus( " - - - " );
 2912+ } else {
 2913+ this.controlBuilder.setStatus( this.getTimeRange() );
 2914+ }
 2915+ }
 2916+
 2917+ // Update buffer information
 2918+ this.updateBufferStatus();
 2919+
 2920+ // run the "native" progress event on the virtual html5 object if set
 2921+ if( this.progressEventData ) {
 2922+ // mw.log("trigger:progress event on html5 proxy");
 2923+ if( _this._propagateEvents ){
 2924+ $j( this ).trigger( 'progress', this.progressEventData );
 2925+ }
 2926+ }
 2927+
 2928+ // Call monitor at 250ms interval. ( use setInterval to avoid stacking
 2929+ // monitor requests )
 2930+ if( ! this.isStopped() ) {
 2931+ if( !this.monitorInterval ){
 2932+ this.monitorInterval = setInterval( function(){
 2933+ if( _this.monitor )
 2934+ _this.monitor();
 2935+ }, this.monitorRate );
 2936+ }
 2937+ } else {
 2938+ // If stopped "stop" monitor:
 2939+ this.stopMonitor();
 2940+ }
 2941+
 2942+ // mw.log('trigger:monitor:: ' + this.currentTime );
 2943+ if( _this._propagateEvents ){
 2944+ $j( this ).trigger( 'monitorEvent' );
 2945+ }
 2946+ },
 2947+
 2948+ /**
 2949+ * Abstract getPlayerElementTime function
 2950+ */
 2951+ getPlayerElementTime: function(){
 2952+ mw.log("Error: getPlayerElementTime should be implemented by embed library");
 2953+ },
 2954+
 2955+ /**
 2956+ * Update the Buffer status based on the local bufferedPercent var
 2957+ */
 2958+ updateBufferStatus: function() {
 2959+
 2960+ // Get the buffer target based for playlist vs clip
 2961+ $buffer = this.$interface.find( '.mw_buffer' );
 2962+
 2963+ //mw.log(' set bufferd %:' + this.bufferedPercent );
 2964+ // Update the buffer progress bar (if available )
 2965+ if ( this.bufferedPercent != 0 ) {
 2966+ // mw.log('Update buffer css: ' + ( this.bufferedPercent * 100 ) +
 2967+ // '% ' + $buffer.length );
 2968+ if ( this.bufferedPercent > 1 ){
 2969+ this.bufferedPercent = 1;
 2970+ }
 2971+ $buffer.css({
 2972+ "width" : ( this.bufferedPercent * 100 ) + '%'
 2973+ });
 2974+ $j( this ).trigger( 'updateBufferPercent', this.bufferedPercent );
 2975+ } else {
 2976+ $buffer.css( "width", '0px' );
 2977+ }
 2978+
 2979+ // if we have not already run the buffer start hook
 2980+ if( this.bufferedPercent > 0 && !this.bufferStartFlag ) {
 2981+ this.bufferStartFlag = true;
 2982+ mw.log("bufferStart");
 2983+ $j( this ).trigger( 'bufferStartEvent' );
 2984+ }
 2985+
 2986+ // if we have not already run the buffer end hook
 2987+ if( this.bufferedPercent == 1 && !this.bufferEndFlag){
 2988+ this.bufferEndFlag = true;
 2989+ $j( this ).trigger( 'bufferEndEvent' );
 2990+ }
 2991+ },
 2992+
 2993+ /**
 2994+ * Update the player playhead
 2995+ *
 2996+ * @param {Float}
 2997+ * perc Value between 0 and 1 for position of playhead
 2998+ */
 2999+ updatePlayHead: function( perc ) {
 3000+ //mw.log( 'EmbedPlayer: updatePlayHead: '+ perc);
 3001+ $playHead = this.$interface.find( '.play_head' );
 3002+ if ( this.controls && $playHead.length != 0 ) {
 3003+ var val = parseInt( perc * 1000 );
 3004+ $playHead.slider( 'value', val );
 3005+ }
 3006+ $j( this ).trigger('updatePlayHeadPercent', perc);
 3007+ },
 3008+
 3009+
 3010+ /**
 3011+ * Helper Functions for selected source
 3012+ */
 3013+
 3014+ /**
 3015+ * Get the current selected media source or first source
 3016+ *
 3017+ * @param {Number} Requested time in seconds to be passed to the server if the server supports
 3018+ * supportsURLTimeEncoding
 3019+ * @return src url
 3020+ */
 3021+ getSrc: function( serverSeekTime ) {
 3022+ if( serverSeekTime ){
 3023+ this.serverSeekTime = serverSeekTime;
 3024+ }
 3025+ if( this.currentTime && !this.serverSeekTime){
 3026+ this.serverSeekTime = this.currentTime;
 3027+ }
 3028+
 3029+ // No media element we can't return src
 3030+ if( !this.mediaElement ){
 3031+ return false;
 3032+ }
 3033+
 3034+ // If no source selected auto select the source:
 3035+ if( !this.mediaElement.selectedSource ){
 3036+ this.mediaElement.autoSelectSource();
 3037+ };
 3038+
 3039+ // Return selected source:
 3040+ if( this.mediaElement.selectedSource ){
 3041+ // get the first source:
 3042+ return this.mediaElement.selectedSource.getSrc( this.serverSeekTime );
 3043+ }
 3044+ // No selected source return false:
 3045+ return false;
 3046+ },
 3047+
 3048+ /**
 3049+ * If the selected src supports URL time encoding
 3050+ *
 3051+ * @return {Boolean} true if the src supports url time requests false if the
 3052+ * src does not support url time requests
 3053+ */
 3054+ supportsURLTimeEncoding: function() {
 3055+ var timeUrls = mw.getConfig('EmbedPlayer.EnableURLTimeEncoding') ;
 3056+ if( timeUrls == 'none' ){
 3057+ return false;
 3058+ } else if( timeUrls == 'always' ){
 3059+ return this.mediaElement.selectedSource.URLTimeEncoding;
 3060+ } else if( timeUrls == 'flash' ){
 3061+ if( this.mediaElement.selectedSource.URLTimeEncoding){
 3062+ // see if the current selected player is flash:
 3063+ return ( this.instanceOf == 'Kplayer' );
 3064+ }
 3065+ } else {
 3066+ mw.log("Error:: invalid config value for EmbedPlayer.EnableURLTimeEncoding:: " + mw.getConfig('EmbedPlayer.EnableURLTimeEncoding') );
 3067+ }
 3068+ return false;
 3069+ }
 3070+};
 3071+
 3072+
 3073+
 3074+/**
 3075+ * mediaPlayer represents a media player plugin.
 3076+ *
 3077+ * @param {String}
 3078+ * id id used for the plugin.
 3079+ * @param {Array}
 3080+ * supported_types an array of supported MIME types.
 3081+ * @param {String}
 3082+ * library external script containing the plugin interface code.
 3083+ * @constructor
 3084+ */
 3085+function mediaPlayer( id, supported_types, library )
 3086+{
 3087+ this.id = id;
 3088+ this.supported_types = supported_types;
 3089+ this.library = library;
 3090+ this.loaded = false;
 3091+ this.loading_callbacks = new Array();
 3092+ return this;
 3093+}
 3094+mediaPlayer.prototype = {
 3095+ // Id of the mediaPlayer
 3096+ id:null,
 3097+
 3098+ // Mime types supported by this player
 3099+ supported_types:null,
 3100+
 3101+ // Player library ie: native, vlc, java etc.
 3102+ library:null,
 3103+
 3104+ // Flag stores the mediaPlayer load state
 3105+ loaded:false,
 3106+
 3107+ /**
 3108+ * Checks support for a given MIME type
 3109+ *
 3110+ * @param {String}
 3111+ * type Mime type to check against supported_types
 3112+ * @return {Boolean} true if mime type is supported false if mime type is
 3113+ * unsupported
 3114+ */
 3115+ supportsMIMEType: function( type ) {
 3116+ for ( var i = 0; i < this.supported_types.length; i++ ) {
 3117+ if ( this.supported_types[i] == type )
 3118+ return true;
 3119+ }
 3120+ return false;
 3121+ },
 3122+
 3123+ /**
 3124+ * Get the "name" of the player from a predictable msg key
 3125+ */
 3126+ getName: function() {
 3127+ return gM( 'mwe-embedplayer-ogg-player-' + this.id );
 3128+ },
 3129+
 3130+ /**
 3131+ * Loads the player library & player skin config ( if needed ) and then
 3132+ * calls the callback.
 3133+ *
 3134+ * @param {Function}
 3135+ * callback Function to be called once player library is loaded.
 3136+ */
 3137+ load: function( callback ) {
 3138+ // Load player library ( upper case the first letter of the library )
 3139+ mw.load( [
 3140+ 'mw.EmbedPlayer' + this.library.substr(0,1).toUpperCase() + this.library.substr(1)
 3141+ ], function() {
 3142+ callback();
 3143+ } );
 3144+ }
 3145+};
 3146+
 3147+/**
 3148+ * players and supported mime types In an ideal world we would query the plugin
 3149+ * to get what mime types it supports in practice not always reliable/available
 3150+ *
 3151+ * We can't cleanly store these values per library since player library is
 3152+ * loaded post player detection
 3153+ *
 3154+ */
 3155+
 3156+// Flash based players:
 3157+
 3158+var kplayer = new mediaPlayer('kplayer', ['video/x-flv', 'video/h264'], 'Kplayer');
 3159+
 3160+// Java based player
 3161+var cortadoPlayer = new mediaPlayer( 'cortado', ['video/ogg', 'audio/ogg', 'application/ogg'], 'Java' );
 3162+
 3163+// Native html5 players
 3164+var oggNativePlayer = new mediaPlayer( 'oggNative', ['video/ogg', 'audio/ogg', 'application/ogg' ], 'Native' );
 3165+var h264NativePlayer = new mediaPlayer( 'h264Native', ['video/h264'], 'Native' );
 3166+var webmNativePlayer = new mediaPlayer( 'webmNative', ['video/webm'], 'Native' );
 3167+
 3168+// VLC player
 3169+var vlcMineList = ['video/ogg', 'audio/ogg', 'application/ogg', 'video/x-flv', 'video/mp4', 'video/h264', 'video/x-msvideo', 'video/mpeg'];
 3170+var vlcPlayer = new mediaPlayer( 'vlc-player', vlcMineList, 'Vlc' );
 3171+
 3172+// Generic plugin
 3173+var oggPluginPlayer = new mediaPlayer( 'oggPlugin', ['video/ogg', 'application/ogg'], 'Generic' );
 3174+
 3175+// HTML player for timed display of html content
 3176+var htmlPlayer = new mediaPlayer( 'html', ['text/html', 'image/jpeg', 'image/png', 'image/svg'], 'Html' );
 3177+
 3178+
 3179+/**
 3180+ * mediaPlayers is a collection of mediaPlayer objects supported by the client.
 3181+ *
 3182+ * @constructor
 3183+ */
 3184+function mediaPlayers()
 3185+{
 3186+ this.init();
 3187+}
 3188+
 3189+mediaPlayers.prototype =
 3190+{
 3191+ // The list of players supported
 3192+ players : null,
 3193+
 3194+ // Store per mime-type prefrences for players
 3195+ preference : { },
 3196+
 3197+ // Stores the default set of players for a given mime type
 3198+ defaultPlayers : { },
 3199+
 3200+ /**
 3201+ * Initializartion function sets the default order for players for a given
 3202+ * mime type
 3203+ */
 3204+ init: function() {
 3205+ this.players = new Array();
 3206+ this.loadPreferences();
 3207+
 3208+ // set up default players order for each library type
 3209+ this.defaultPlayers['video/x-flv'] = ['Kplayer', 'Vlc'];
 3210+ this.defaultPlayers['video/h264'] = ['Native', 'Kplayer', 'Vlc'];
 3211+
 3212+ this.defaultPlayers['video/ogg'] = ['Native', 'Vlc', 'Java', 'Generic'];
 3213+ this.defaultPlayers['video/webm'] = ['Native', 'Vlc'];
 3214+ this.defaultPlayers['application/ogg'] = ['Native', 'Vlc', 'Java', 'Generic'];
 3215+ this.defaultPlayers['audio/ogg'] = ['Native', 'Vlc', 'Java' ];
 3216+ this.defaultPlayers['video/mp4'] = ['Vlc'];
 3217+ this.defaultPlayers['video/mpeg'] = ['Vlc'];
 3218+ this.defaultPlayers['video/x-msvideo'] = ['Vlc'];
 3219+
 3220+ this.defaultPlayers['text/html'] = ['Html'];
 3221+ this.defaultPlayers['image/jpeg'] = ['Html'];
 3222+ this.defaultPlayers['image/png'] = ['Html'];
 3223+ this.defaultPlayers['image/svg'] = ['Html'];
 3224+
 3225+ },
 3226+
 3227+ /**
 3228+ * Adds a Player to the player list
 3229+ *
 3230+ * @param {Object}
 3231+ * player Player object to be added
 3232+ */
 3233+ addPlayer: function( player ) {
 3234+ for ( var i = 0; i < this.players.length; i++ ) {
 3235+ if ( this.players[i].id == player.id ) {
 3236+ // Player already found
 3237+ return ;
 3238+ }
 3239+ }
 3240+
 3241+
 3242+ // Add the player:
 3243+ this.players.push( player );
 3244+ },
 3245+
 3246+ /**
 3247+ * Checks if a player is supported by id
 3248+ */
 3249+ isSupportedPlayer: function( playerId ){
 3250+ for( var i=0; i < this.players.length; i++ ){
 3251+ if( this.players[i].id == playerId ){
 3252+ return true;
 3253+ }
 3254+ }
 3255+ return false;
 3256+ },
 3257+
 3258+ /**
 3259+ * get players that support a given mimeType
 3260+ *
 3261+ * @param {String}
 3262+ * mimeType Mime type of player set
 3263+ * @return {Array} Array of players that support a the requested mime type
 3264+ */
 3265+ getMIMETypePlayers: function( mimeType ) {
 3266+ var mimePlayers = new Array();
 3267+ var _this = this;
 3268+ if ( this.defaultPlayers[mimeType] ) {
 3269+ $j.each( this.defaultPlayers[ mimeType ], function( d, lib ) {
 3270+ var library = _this.defaultPlayers[ mimeType ][ d ];
 3271+ for ( var i = 0; i < _this.players.length; i++ ) {
 3272+ if ( _this.players[i].library == library && _this.players[i].supportsMIMEType( mimeType ) ) {
 3273+ mimePlayers.push( _this.players[i] );
 3274+ }
 3275+ }
 3276+ } );
 3277+ }
 3278+ return mimePlayers;
 3279+ },
 3280+
 3281+ /**
 3282+ * Default player for a given mime type
 3283+ *
 3284+ * @param {String}
 3285+ * mimeType Mime type of the requested player
 3286+ * @return Player for mime type null if no player found
 3287+ */
 3288+ defaultPlayer : function( mimeType ) {
 3289+ // mw.log( "get defaultPlayer for " + mimeType );
 3290+ var mimePlayers = this.getMIMETypePlayers( mimeType );
 3291+ if ( mimePlayers.length > 0 )
 3292+ {
 3293+ // Check for prior preference for this mime type
 3294+ for ( var i = 0; i < mimePlayers.length; i++ ) {
 3295+ if ( mimePlayers[i].id == this.preference[mimeType] )
 3296+ return mimePlayers[i];
 3297+ }
 3298+ // Otherwise just return the first compatible player
 3299+ // (it will be chosen according to the defaultPlayers list
 3300+ return mimePlayers[0];
 3301+ }
 3302+ // mw.log( 'No default player found for ' + mimeType );
 3303+ return null;
 3304+ },
 3305+
 3306+ /**
 3307+ * Sets the format preference.
 3308+ *
 3309+ * @param {String}
 3310+ * mimeFormat Prefered format
 3311+ */
 3312+ setFormatPreference : function ( mimeFormat ) {
 3313+ this.preference['format_preference'] = mimeFormat;
 3314+ mw.setUserConfig( 'playerPref', this.preference);
 3315+ },
 3316+
 3317+ /**
 3318+ * Sets the player preference
 3319+ *
 3320+ * @param {String}
 3321+ * playerId Prefered player id
 3322+ * @param {String}
 3323+ * mimeType Mime type for the associated player stream
 3324+ */
 3325+ setPlayerPreference : function( playerId, mimeType ) {
 3326+ var selectedPlayer = null;
 3327+ for ( var i = 0; i < this.players.length; i++ ) {
 3328+ if ( this.players[i].id == playerId ) {
 3329+ selectedPlayer = this.players[i];
 3330+ mw.log( 'EmbedPlayer::setPlayerPreference: choosing ' + playerId + ' for ' + mimeType );
 3331+ this.preference[ mimeType ] = playerId;
 3332+ mw.setUserConfig( 'playerPref', this.preference );
 3333+ break;
 3334+ }
 3335+ }
 3336+ // Update All the player instances:
 3337+ if ( selectedPlayer ) {
 3338+ var playerList = mw.playerManager.getPlayerList();
 3339+ for ( var i = 0; i < playerList.length; i++ ) {
 3340+ var embed = $j( '#' + playerList[i] ).get( 0 );
 3341+ if ( embed.mediaElement.selectedSource && ( embed.mediaElement.selectedSource.mimeType == mimeType ) )
 3342+ {
 3343+ embed.selectPlayer( selectedPlayer );
 3344+ mw.log( 'EmbedPlayer::setPlayerPreference: using ' + embed.selectedPlayer.getName() + ' for ' + embed.mediaElement.selectedSource.getTitle() );
 3345+ }
 3346+ }
 3347+ }
 3348+ },
 3349+
 3350+ /**
 3351+ * Loads the user preference settings from a cookie
 3352+ */
 3353+ loadPreferences : function ( ) {
 3354+ this.preference = { };
 3355+ // see if we have a cookie set to a clientSupported type:
 3356+ preferenceConfig = $.cookie( 'playerPref' );
 3357+ if( typeof preferenceConfig == 'object' ) {
 3358+ this.preference = preferenceConfig;
 3359+ }
 3360+ }
 3361+};
 3362+
 3363+/**
 3364+ * mw.EmbedTypes object handles setting and getting of supported embed types:
 3365+ * closely mirrors OggHandler so that its easier to share efforts in this area:
 3366+ * http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/OggHandler/OggPlayer.js
 3367+ */
 3368+mw.EmbedTypes = {
 3369+
 3370+ // MediaPlayers object ( supports methods for quering set of browser players )
 3371+ mediaPlayers: null,
 3372+
 3373+ // Detect flag for completion
 3374+ detect_done:false,
 3375+
 3376+ /**
 3377+ * Runs the detect method and update the detect_done flag
 3378+ *
 3379+ * @constructor
 3380+ */
 3381+ init: function() {
 3382+ // detect supported types
 3383+ this.detect();
 3384+ this.detect_done = true;
 3385+ },
 3386+
 3387+ getMediaPlayers: function(){
 3388+ if( this.mediaPlayers ){
 3389+ return this.mediaPlayers;
 3390+ }
 3391+ this.mediaPlayers = new mediaPlayers();
 3392+ // detect available players
 3393+ this.detectPlayers();
 3394+ return this.mediaPlayers;
 3395+ },
 3396+
 3397+ /**
 3398+ * If the browsers supports a given mimetype
 3399+ *
 3400+ * @param {String}
 3401+ * mimeType Mime type for browser plug-in check
 3402+ */
 3403+ supportedMimeType: function( mimeType ) {
 3404+ for ( var i =0; i < navigator.plugins.length; i++ ) {
 3405+ var plugin = navigator.plugins[i];
 3406+ if ( typeof plugin[ mimeType ] != "undefined" )
 3407+ return true;
 3408+ }
 3409+ return false;
 3410+ },
 3411+
 3412+ /**
 3413+ * Detects what plug-ins the client supports
 3414+ */
 3415+ detectPlayers: function() {
 3416+ mw.log( "embedPlayer: running detect" );
 3417+ // every browser supports html rendering:
 3418+ this.mediaPlayers.addPlayer( htmlPlayer );
 3419+ // In Mozilla, navigator.javaEnabled() only tells us about preferences, we need to
 3420+ // search navigator.mimeTypes to see if it's installed
 3421+ try{
 3422+ var javaEnabled = navigator.javaEnabled();
 3423+ } catch ( e ){
 3424+
 3425+ }
 3426+ // Some browsers filter out duplicate mime types, hiding some plugins
 3427+ var uniqueMimesOnly = $j.browser.opera || $j.browser.safari;
 3428+
 3429+ // Opera will switch off javaEnabled in preferences if java can't be
 3430+ // found. And it doesn't register an application/x-java-applet mime type like
 3431+ // Mozilla does.
 3432+ if ( javaEnabled && ( navigator.appName == 'Opera' ) ) {
 3433+ this.mediaPlayers.addPlayer( cortadoPlayer );
 3434+ }
 3435+
 3436+ // ActiveX plugins
 3437+ if ( $j.browser.msie ) {
 3438+ // check for flash
 3439+ if ( this.testActiveX( 'ShockwaveFlash.ShockwaveFlash' ) ) {
 3440+ this.mediaPlayers.addPlayer( kplayer );
 3441+ // this.mediaPlayers.addPlayer( flowPlayer );
 3442+ }
 3443+ // VLC
 3444+ if ( this.testActiveX( 'VideoLAN.VLCPlugin.2' ) ) {
 3445+ this.mediaPlayers.addPlayer( vlcPlayer );
 3446+ }
 3447+
 3448+ // Java ActiveX
 3449+ if ( this.testActiveX( 'JavaWebStart.isInstalled' ) ) {
 3450+ this.mediaPlayers.addPlayer( cortadoPlayer );
 3451+ }
 3452+
 3453+ // quicktime (currently off)
 3454+ // if ( this.testActiveX(
 3455+ // 'QuickTimeCheckObject.QuickTimeCheck.1' ) )
 3456+ // this.mediaPlayers.addPlayer(quicktimeActiveXPlayer);
 3457+ }
 3458+ // <video> element
 3459+ if ( typeof HTMLVideoElement == 'object' // Firefox, Safari
 3460+ || typeof HTMLVideoElement == 'function' ) // Opera
 3461+ {
 3462+ // Test what codecs the native player supports:
 3463+ try {
 3464+ var dummyvid = document.createElement( "video" );
 3465+ if( dummyvid.canPlayType ) {
 3466+ // Add the webm player
 3467+ if( dummyvid.canPlayType('video/webm; codecs="vp8, vorbis"') ){
 3468+ this.mediaPlayers.addPlayer( webmNativePlayer );
 3469+ }
 3470+
 3471+ // Test for h264:
 3472+ if ( dummyvid.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"' ) ) {
 3473+ this.mediaPlayers.addPlayer( h264NativePlayer );
 3474+ }
 3475+ // For now if Android assume we support h264Native (FIXME
 3476+ // test on real devices )
 3477+ if ( mw.isAndroid2() ){
 3478+ this.mediaPlayers.addPlayer( h264NativePlayer );
 3479+ }
 3480+
 3481+ // Test for ogg
 3482+ if ( dummyvid.canPlayType( 'video/ogg; codecs="theora,vorbis"' ) ) {
 3483+ this.mediaPlayers.addPlayer( oggNativePlayer );
 3484+ // older versions of safari do not support canPlayType,
 3485+ // but xiph qt registers mimetype via quicktime plugin
 3486+ } else if ( this.supportedMimeType( 'video/ogg' ) ) {
 3487+ this.mediaPlayers.addPlayer( oggNativePlayer );
 3488+ }
 3489+ }
 3490+ } catch ( e ) {
 3491+ mw.log( 'could not run canPlayType ' + e );
 3492+ }
 3493+ }
 3494+
 3495+ // "navigator" plugins
 3496+ if ( navigator.mimeTypes && navigator.mimeTypes.length > 0 ) {
 3497+ for ( var i = 0; i < navigator.mimeTypes.length; i++ ) {
 3498+ var type = navigator.mimeTypes[i].type;
 3499+ var semicolonPos = type.indexOf( ';' );
 3500+ if ( semicolonPos > -1 ) {
 3501+ type = type.substr( 0, semicolonPos );
 3502+ }
 3503+ // mw.log( 'on type: ' + type );
 3504+ var pluginName = navigator.mimeTypes[i].enabledPlugin ? navigator.mimeTypes[i].enabledPlugin.name : '';
 3505+ if ( !pluginName ) {
 3506+ // In case it is null or undefined
 3507+ pluginName = '';
 3508+ }
 3509+ if ( pluginName.toLowerCase() == 'vlc multimedia plugin' || pluginName.toLowerCase() == 'vlc multimedia plug-in' ) {
 3510+ this.mediaPlayers.addPlayer( vlcPlayer );
 3511+ continue;
 3512+ }
 3513+
 3514+ if ( type == 'application/x-java-applet' ) {
 3515+ this.mediaPlayers.addPlayer( cortadoPlayer );
 3516+ continue;
 3517+ }
 3518+
 3519+ if ( (type == 'video/mpeg' || type == 'video/x-msvideo') &&
 3520+ pluginName.toLowerCase() == 'vlc multimedia plugin' ) {
 3521+ this.mediaPlayers.addPlayer( vlcMozillaPlayer );
 3522+ }
 3523+
 3524+ if ( type == 'application/ogg' ) {
 3525+ if ( pluginName.toLowerCase() == 'vlc multimedia plugin' ) {
 3526+ this.mediaPlayers.addPlayer( vlcMozillaPlayer );
 3527+ // else if ( pluginName.indexOf( 'QuickTime' ) > -1 )
 3528+ // this.mediaPlayers.addPlayer(quicktimeMozillaPlayer);
 3529+ } else {
 3530+ this.mediaPlayers.addPlayer( oggPluginPlayer );
 3531+ }
 3532+ continue;
 3533+ } else if ( uniqueMimesOnly ) {
 3534+ if ( type == 'application/x-vlc-player' ) {
 3535+ this.mediaPlayers.addPlayer( vlcMozillaPlayer );
 3536+ continue;
 3537+ } else if ( type == 'video/quicktime' ) {
 3538+ // this.mediaPlayers.addPlayer(quicktimeMozillaPlayer);
 3539+ continue;
 3540+ }
 3541+ }
 3542+
 3543+ if ( type == 'application/x-shockwave-flash' ) {
 3544+
 3545+ this.mediaPlayers.addPlayer( kplayer );
 3546+ // this.mediaPlayers.addPlayer( flowPlayer );
 3547+
 3548+ // check version to add omtk:
 3549+ if( navigator.plugins["Shockwave Flash"] ){
 3550+ var flashDescription = navigator.plugins["Shockwave Flash"].description;
 3551+ var descArray = flashDescription.split( " " );
 3552+ var tempArrayMajor = descArray[2].split( "." );
 3553+ var versionMajor = tempArrayMajor[0];
 3554+ // mw.log("version of flash: " + versionMajor);
 3555+ }
 3556+ continue;
 3557+ }
 3558+ }
 3559+ }
 3560+
 3561+ // Allow extensions to detect and add their own "players"
 3562+ mw.log("trigger::embedPlayerUpdateMediaPlayersEvent");
 3563+ $j( mw ).trigger( 'embedPlayerUpdateMediaPlayersEvent' , this.mediaPlayers );
 3564+
 3565+ },
 3566+
 3567+ /**
 3568+ * Test IE for activeX by name
 3569+ *
 3570+ * @param {String}
 3571+ * name Name of ActiveXObject to look for
 3572+ */
 3573+ testActiveX : function ( name ) {
 3574+ mw.log("EmbedPlayer::detect: test testActiveX: " + name);
 3575+ var hasObj = true;
 3576+ try {
 3577+ // No IE, not a class called "name", it's a variable
 3578+ var obj = new ActiveXObject( '' + name );
 3579+ } catch ( e ) {
 3580+ hasObj = false;
 3581+ }
 3582+ return hasObj;
 3583+ }
 3584+};
 3585+
 3586+} )( mediaWiki, jQuery );
Property changes on: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayer.js
___________________________________________________________________
Added: svn:mergeinfo
13587 Merged /branches/REL1_15/phase3/js2/mwEmbed/libEmbedVideo/embedVideo.js:r51646
23588 Merged /branches/sqlite/js2/mwEmbed/libEmbedVideo/embedVideo.js:r58211-58321
Added: svn:eol-style
33589 + native
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerJava.js
@@ -0,0 +1,244 @@
 2+/**
 3+* List of domains and hosted location of cortado. Lets clients avoid the security warning for cross domain cortado
 4+*/
 5+window.cortadoDomainLocations = {
 6+ 'upload.wikimedia.org' : 'http://upload.wikimedia.org/jars/cortado.jar'
 7+};
 8+
 9+// Set the default location for CortadoApplet
 10+mw.setDefaultConfig( 'relativeCortadoAppletPath',
 11+ mw.getMwEmbedPath() + 'modules/EmbedPlayer/binPlayers/cortado/cortado-ovtk-stripped-0.6.0.jar'
 12+);
 13+
 14+mw.EmbedPlayerJava = {
 15+
 16+ // Instance name:
 17+ instanceOf: 'Java',
 18+
 19+ // Supported feature set of the cortado applet:
 20+ supports: {
 21+ 'playHead' : true,
 22+ 'pause' : true,
 23+ 'stop' : true,
 24+ 'fullscreen' : false,
 25+ 'timeDisplay' : true,
 26+ 'volumeControl' : false
 27+ },
 28+
 29+ /**
 30+ * Output the the embed html
 31+ */
 32+ doEmbedHTML: function () {
 33+ var _this = this;
 34+ mw.log( "java play url:" + this.getSrc( this.seek_time_sec ) );
 35+
 36+ mw.log('Applet location: ' + this.getAppletLocation() );
 37+ mw.log('Play media: ' + this.getSrc() );
 38+
 39+ // load directly in the page..
 40+ // ( media must be on the same server or applet must be signed )
 41+ var appletCode = '' +
 42+ '<applet id="' + this.pid + '" code="com.fluendo.player.Cortado.class" ' +
 43+ 'archive="' + this.getAppletLocation() + '" width="' + parseInt( this.getWidth() ) + '" ' +
 44+ 'height="' + parseInt( this.getHeight() ) + '"> ' + "\n" +
 45+ '<param name="url" value="' + this.getSrc() + '" /> ' + "\n" +
 46+ '<param name="local" value="false"/>' + "\n" +
 47+ '<param name="keepaspect" value="true" />' + "\n" +
 48+ '<param name="video" value="true" />' + "\n" +
 49+ '<param name="showStatus" value="hide" />' + "\n" +
 50+ '<param name="audio" value="true" />' + "\n" +
 51+ '<param name="seekable" value="true" />' + "\n";
 52+
 53+ // Add the duration attribute if set:
 54+ if( this.getDuration() ){
 55+ appletCode += '<param name="duration" value="' + parseFloat( this.getDuration() ) + '" />' + "\n";
 56+ }
 57+
 58+ appletCode += '<param name="BufferSize" value="4096" />' +
 59+ '<param name="BufferHigh" value="25">' +
 60+ '<param name="BufferLow" value="5">' +
 61+ '</applet>';
 62+
 63+ $j( this ).html( appletCode );
 64+
 65+ // Wrap it in an iframe to avoid hanging the event thread in FF 2/3 and similar
 66+ // NOTE: This breaks reference to the applet so disabled for now:
 67+ /*if ( $j.browser.mozilla ) {
 68+ var iframe = document.createElement( 'iframe' );
 69+ iframe.setAttribute( 'width', this.getWidth() );
 70+ iframe.setAttribute( 'height', this.getHeight() );
 71+ iframe.setAttribute( 'scrolling', 'no' );
 72+ iframe.setAttribute( 'frameborder', 0 );
 73+ iframe.setAttribute( 'marginWidth', 0 );
 74+ iframe.setAttribute( 'marginHeight', 0 );
 75+ iframe.setAttribute( 'id', 'cframe_' + this.id )
 76+
 77+ // Append the iframe to the embed object:
 78+ $j( this ).html( iframe );
 79+
 80+ // Write out the iframe content:
 81+ var newDoc = iframe.contentDocument;
 82+ newDoc.open();
 83+ newDoc.write( '<html><body>' + appletCode + '</body></html>' );
 84+ // spurious error in some versions of FF, no workaround known
 85+ newDoc.close();
 86+ } else {
 87+ $j( this ).html( appletCode );
 88+ //}
 89+ */
 90+
 91+ // Start the monitor:
 92+ _this.monitor();
 93+ },
 94+
 95+ /**
 96+ * Get the applet location
 97+ */
 98+ getAppletLocation: function() {
 99+ var mediaSrc = this.getSrc();
 100+ var appletLoc = false;
 101+ if (
 102+ !mw.isLocalDomain( mediaSrc )
 103+ ||
 104+ !mw.isLocalDomain( mw.getMwEmbedPath()
 105+ ||
 106+ mw.getConfig( 'relativeCortadoAppletPath' ) === false )
 107+ ){
 108+ if ( window.cortadoDomainLocations[ mw.parseUri( mediaSrc ).host ] ) {
 109+ appletLoc = window.cortadoDomainLocations[ mw.parseUri( mediaSrc ).host ];
 110+ } else {
 111+ appletLoc = 'http://theora.org/cortado.jar';
 112+ }
 113+ } else {
 114+ // Get the local relative cortado applet location:
 115+ appletLoc = mw.getConfig( 'relativeCortadoAppletPath' );
 116+ }
 117+ return appletLoc;
 118+ },
 119+
 120+ /**
 121+ * Get the embed player time
 122+ */
 123+ getPlayerElementTime: function() {
 124+ this.getPlayerElement();
 125+ var currentTime = 0;
 126+ if ( this.playerElement ) {
 127+ try {
 128+ // java reads ogg media time.. so no need to add the start or seek offset:
 129+ //mw.log(' ct: ' + this.playerElement.getPlayPosition() + ' ' + this.supportsURLTimeEncoding());
 130+
 131+ currentTime = this.playerElement.currentTime;
 132+ // ( java cortado has -1 time ~sometimes~ )
 133+ /*if ( this.currentTime < 0 ) {
 134+ mw.log( 'pp:' + this.currentTime );
 135+ // Probably reached clip ( should fire ondone event instead )
 136+ this.onClipDone();
 137+ }*/
 138+ } catch ( e ) {
 139+ mw.log( 'could not get time from jPlayer: ' + e );
 140+ }
 141+ }else{
 142+ mw.log(" could not find playerElement " );
 143+ }
 144+ return currentTime;
 145+ },
 146+
 147+ /**
 148+ * Seek in the ogg stream
 149+ * ( Cortado seek does not seem to work very well )
 150+ * @param {Float} percentage Percentage to seek into the stream
 151+ */
 152+ doSeek: function( percentage ) {
 153+ mw.log( 'java:seek:p: ' + percentage + ' : ' + this.supportsURLTimeEncoding() + ' dur: ' + this.getDuration() + ' sts:' + this.seek_time_sec );
 154+ this.getPlayerElement();
 155+
 156+ if ( this.supportsURLTimeEncoding() ) {
 157+ this.parent_doSeek( percentage );
 158+ } else if ( this.playerElement ) {
 159+ // do a (generally broken) local seek:
 160+ mw.log( "Cortado seek is not very accurate :: doSeek::" + ( percentage * parseFloat( this.getDuration() ) ) );
 161+ this.playerElement.currentTime = ( percentage * parseFloat( this.getDuration() ) );
 162+ } else {
 163+ this.doPlayThenSeek( percentage );
 164+ }
 165+
 166+ // Run the onSeeking interface update
 167+ this.controlBuilder.onSeek();
 168+ },
 169+
 170+ /**
 171+ * Issue a play request then seek to a percentage point in the stream
 172+ * @param {Float} percentage Percentage to seek into the stream
 173+ */
 174+ doPlayThenSeek: function( percentage ) {
 175+ mw.log( 'doPlayThenSeek' );
 176+ var _this = this;
 177+ this.play();
 178+ var rfsCount = 0;
 179+ var readyForSeek = function() {
 180+ _this.getPlayerElement();
 181+ // if we have .jre ~in theory~ we can seek (but probably not)
 182+ if ( _this.playerElement ) {
 183+ _this.doSeek( perc );
 184+ } else {
 185+ // try to get player for 10 seconds:
 186+ if ( rfsCount < 200 ) {
 187+ setTimeout( readyForSeek, 50 );
 188+ rfsCount++;
 189+ } else {
 190+ mw.log( 'error:doPlayThenSeek failed' );
 191+ }
 192+ }
 193+ };
 194+ readyForSeek();
 195+ },
 196+
 197+ /**
 198+ * Update the playerElement instance with a pointer to the embed object
 199+ */
 200+ getPlayerElement: function() {
 201+ if( !$j( '#' + this.pid ).length ) {
 202+ return false;
 203+ }
 204+ //mw.log( 'getPlayerElement::' + this.pid );
 205+ this.playerElement = $j( '#' + this.pid ).get( 0 );
 206+ //this.playerElement = document.applets[ 0 ];
 207+ // NOTE we are currently not using the iframe embed method:
 208+ //if ( $j.browser.mozilla ) {
 209+ // this.playerElement = $j('#cframe_' + this.id).contents().find( '#' + this.pid );
 210+ //} else {
 211+ // this.playerElement = $j( '#' + this.pid ).get( 0 );
 212+ //}
 213+ return this.playerElement;
 214+ },
 215+
 216+ /**
 217+ * Issue the doPlay request to the playerElement
 218+ * calls parent_play to update interface
 219+ */
 220+ play: function() {
 221+ this.getPlayerElement();
 222+ this.parent_play();
 223+ if ( this.playerElement ) {
 224+ try{
 225+ this.playerElement.play();
 226+ }catch( e ){
 227+ mw.log("EmbedPlayerJava::Could not issue play request");
 228+ }
 229+ }
 230+ },
 231+
 232+ /**
 233+ * Pause playback
 234+ * calls parent_pause to update interface
 235+ */
 236+ pause: function() {
 237+ this.getPlayerElement();
 238+ // Update the interface
 239+ this.parent_pause();
 240+ // Call the pause function if it exists:
 241+ if ( this.playerElement ) {
 242+ this.playerElement.pause();
 243+ }
 244+ }
 245+};
Property changes on: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerJava.js
___________________________________________________________________
Added: svn:mergeinfo
1246 Merged /branches/REL1_15/phase3/js2/mwEmbed/libEmbedVideo/javaEmbed.js:r51646
2247 Merged /branches/sqlite/js2/mwEmbed/libEmbedVideo/javaEmbed.js:r58211-58321
Added: svn:eol-style
3248 + native
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerVlc.js
@@ -0,0 +1,365 @@
 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+mw.EmbedPlayerVlc = {
 8+
 9+ //Instance Name:
 10+ instanceOf : 'Vlc',
 11+
 12+ //What the vlc player / plug-in supports:
 13+ supports : {
 14+ 'playHead':true,
 15+ 'pause':true,
 16+ 'stop':true,
 17+ 'fullscreen':true,
 18+ 'timeDisplay':true,
 19+ 'volumeControl':true,
 20+
 21+ 'playlist_driver':true, // if the object supports playlist functions
 22+ 'overlay':false
 23+ },
 24+
 25+ // The previous state of the player instance
 26+ prevState : 0,
 27+
 28+ // Counter for waiting for vlc embed to be ready
 29+ waitForVlcCount:0,
 30+
 31+ // Store the current play time for vlc
 32+ vlcCurrentTime: 0,
 33+
 34+ /**
 35+ * Get embed HTML
 36+ */
 37+ doEmbedHTML: function() {
 38+ var _this = this;
 39+ $j( this ).html(
 40+ '<object classid="clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921" ' +
 41+ 'codebase="http://downloads.videolan.org/pub/videolan/vlc/latest/win32/axvlc.cab#Version=0,8,6,0" ' +
 42+ 'id="' + this.pid + '" events="True" height="' + this.getPlayerHeight() + '" width="' + this.getPlayerWidth() + '"' +
 43+ '>' +
 44+ '<param name="MRL" value="">' +
 45+ '<param name="ShowDisplay" value="True">' +
 46+ '<param name="AutoLoop" value="False">' +
 47+ '<param name="AutoPlay" value="False">' +
 48+ '<param name="Volume" value="50">' +
 49+ '<param name="StartTime" value="0">' +
 50+ '<embed pluginspage="http://www.videolan.org" type="application/x-vlc-plugin" ' +
 51+ 'progid="VideoLAN.VLCPlugin.2" name="' + this.pid + '" ' +
 52+ 'height="' + this.getHeight() + '" width="' + this.getWidth() + '" ' +
 53+ // set the style too 'just to be sure'
 54+ 'style="width:' + this.getWidth() + 'px;height:' + this.getHeight() + 'px;" ' +
 55+ '>' +
 56+ '</object>'
 57+ )
 58+ /*
 59+ $j( this ).html(
 60+ '<embed type="application/x-vlc-plugin" pluginspage="http://www.videolan.org" version="VideoLAN.VLCPlugin.2" '+
 61+ 'width="' + this.width +'" ' +
 62+ 'height="' + this.height + '" ' +
 63+ 'id="' + this.pid + '"> ' +
 64+ '</embed>'
 65+ );*/
 66+
 67+
 68+ // give VLC 150ms to initialize before we start playback
 69+ // @@todo should be able to do this as an ready event
 70+ this.waitForVlcCount = 0;
 71+ setTimeout( function() {
 72+ _this.postEmbedJS();
 73+ }, 150 );
 74+ },
 75+
 76+ /**
 77+ * Javascript to run post vlc embedding
 78+ * Inserts the requested src to the embed instance
 79+ */
 80+ postEmbedJS: function() {
 81+ var _this = this;
 82+ // load a pointer to the vlc into the object (this.playerElement)
 83+ this.getPlayerElement();
 84+ if ( this.playerElement && this.playerElement.playlist) {
 85+ // manipulate the dom object to make sure vlc has the correct size:
 86+ this.playerElement.style.width = this.getWidth();
 87+ this.playerElement.style.height = this.getHeight();
 88+ this.playerElement.playlist.items.clear();
 89+
 90+ // VLC likes absolute urls:
 91+ var src = mw.absoluteUrl( this.getSrc() ) ;
 92+
 93+ // @@todo if client supports seeking no need to send seek_offset to URI
 94+ mw.log( 'vlc play::' + src );
 95+ var itemId = this.playerElement.playlist.add( src );
 96+ if ( itemId != -1 ) {
 97+ // Play
 98+ this.playerElement.playlist.playItem( itemId );
 99+ } else {
 100+ mw.log( "error:cannot play at the moment !" );
 101+ }
 102+ setTimeout( function() {
 103+ _this.monitor();
 104+ }, 100 );
 105+ } else {
 106+ mw.log( 'postEmbedJS: vlc not ready' );
 107+ this.waitForVlcCount++;
 108+ if ( this.waitForVlcCount < 10 ) {
 109+ setTimeout( function() {
 110+ _this.postEmbedJS();
 111+ }, 100 );
 112+ } else {
 113+ mw.log( 'vlc never ready' );
 114+ }
 115+ }
 116+ },
 117+
 118+ /**
 119+ * Handles seek requests based on temporal media source type support
 120+ *
 121+ * @param {Float} percent Seek to this percent of the stream
 122+ */
 123+ doSeek : function( percent ) {
 124+ this.getPlayerElement();
 125+ if ( this.supportsURLTimeEncoding() ) {
 126+ this.parent_doSeek( percent );
 127+ } else if ( this.playerElement ) {
 128+ this.seeking = true;
 129+ mw.log( "do vlc http seek to: " + percent )
 130+ if ( ( this.playerElement.input.state == 3 ) && ( this.playerElement.input.position != percent ) )
 131+ {
 132+ this.playerElement.input.position = percent;
 133+ this.controlBuilder.setStatus( 'seeking...' );
 134+ }
 135+ } else {
 136+ this.doPlayThenSeek( percent );
 137+ }
 138+ this.parent_monitor();
 139+ },
 140+
 141+ /**
 142+ * Issues a play request then seeks to a given time
 143+ *
 144+ * @param {Float} percent Seek to this percent of the stream after playing
 145+ */
 146+ doPlayThenSeek:function( percent ) {
 147+ mw.log( 'doPlayThenSeekHack' );
 148+ var _this = this;
 149+ this.play();
 150+ var rfsCount = 0;
 151+ var readyForSeek = function() {
 152+ _this.getPlayerElement();
 153+ var newState = _this.playerElement.input.state;
 154+ // if playing we are ready to do the
 155+ if ( newState == 3 ) {
 156+ _this.doSeek( percent );
 157+ } else {
 158+ // try to get player for 10 seconds:
 159+ if ( rfsCount < 200 ) {
 160+ setTimeout( readyForSeek, 50 );
 161+ rfsCount++;
 162+ } else {
 163+ mw.log( 'error:doPlayThenSeek failed' );
 164+ }
 165+ }
 166+ }
 167+ readyForSeek();
 168+ },
 169+
 170+ /**
 171+ * Updates the status time and player state
 172+ */
 173+ monitor: function() {
 174+ this.getPlayerElement();
 175+ if ( !this.playerElement )
 176+ return ;
 177+ try{
 178+ //mw.log( 'state:' + this.playerElement.input.state);
 179+ //mw.log('time: ' + this.playerElement.input.time);
 180+ //mw.log('pos: ' + this.playerElement.input.position);
 181+ if ( this.playerElement.log.messages.count > 0 ) {
 182+ // there is one or more messages in the log
 183+ var iter = this.playerElement.log.messages.iterator();
 184+ while ( iter.hasNext ) {
 185+ var msg = iter.next();
 186+ var msgtype = msg.type.toString();
 187+ if ( ( msg.severity == 1 ) && ( msgtype == "input" ) )
 188+ {
 189+ mw.log( msg.message );
 190+ }
 191+ }
 192+ // clear the log once finished to avoid clogging
 193+ this.playerElement.log.messages.clear();
 194+ }
 195+
 196+ var newState = this.playerElement.input.state;
 197+ if ( this.prevState != newState ) {
 198+ if ( newState == 0 )
 199+ {
 200+ // current media has stopped
 201+ this.onStop();
 202+ }
 203+ else if ( newState == 1 )
 204+ {
 205+ // current media is opening/connecting
 206+ this.onOpen();
 207+ }
 208+ else if ( newState == 2 )
 209+ {
 210+ // current media is buffering data
 211+ this.onBuffer();
 212+ }
 213+ else if ( newState == 3 )
 214+ {
 215+ // current media is now playing
 216+ this.onPlay();
 217+ }
 218+ else if ( this.playerElement.input.state == 4 ) {
 219+ // current media is now paused
 220+ this.onPause();
 221+ }
 222+ this.prevState = newState;
 223+ } else if ( newState == 3 ) {
 224+ // current media is playing
 225+ this.onPlaying();
 226+ }
 227+ } catch( e ){
 228+ mw.log("EmbedPlayerVlc::Monitor error");
 229+ }
 230+ // update the status and check timmer via universal parent monitor
 231+ this.parent_monitor();
 232+ },
 233+
 234+ /**
 235+ * Events:
 236+ * NOTE : should be localized:
 237+ */
 238+ onOpen: function() {
 239+ this.controlBuilder.setStatus( "Opening..." );
 240+ },
 241+ onBuffer: function() {
 242+ this.controlBuilder.setStatus( "Buffering..." );
 243+ },
 244+ onPlay: function() {
 245+ this.onPlaying();
 246+ },
 247+ onPlaying: function() {
 248+ this.seeking = false;
 249+ // for now trust the duration from url over vlc input.length
 250+ if ( !this.getDuration() && this.playerElement.input.length > 0 )
 251+ {
 252+ // mw.log('setting duration to ' + this.playerElement.input.length /1000);
 253+ this.duration = this.playerElement.input.length / 1000;
 254+ }
 255+ this.vlcCurrentTime = this.playerElement.input.time / 1000;
 256+ },
 257+
 258+ /**
 259+ * Get the embed player time
 260+ */
 261+ getPlayerElementTime: function(){
 262+ return this.vlcCurrentTime;
 263+ },
 264+
 265+ onPause: function() {
 266+ this.parent_pause(); // update the inteface if paused via native control
 267+ },
 268+ onStop: function() {
 269+ mw.log( 'vlc:onStop:' );
 270+ if ( !this.seeking )
 271+ this.onClipDone();
 272+ },
 273+
 274+ /**
 275+ * Handles play requests
 276+ */
 277+ play : function() {
 278+ mw.log( 'f:vlcPlay' );
 279+ // Update the interface
 280+ this.parent_play();
 281+ if ( this.getPlayerElement() ) {
 282+ // plugin is already being present send play call:
 283+ // clear the message log and enable error logging
 284+ if ( this.playerElement.log ) {
 285+ this.playerElement.log.messages.clear();
 286+ }
 287+ if ( this.playerElement.playlist && typeof this.playerElement.playlist.play == 'function')
 288+ this.playerElement.playlist.play();
 289+
 290+ if( typeof this.playerElement.play == 'function' )
 291+ this.playerElement.play();
 292+
 293+ this.paused = false;
 294+
 295+ // re-start the monitor:
 296+ this.monitor();
 297+ }
 298+ },
 299+
 300+ /**
 301+ * Passes the Pause request to the plugin.
 302+ * calls parent "pause" to update interface
 303+ */
 304+ pause : function() {
 305+ this.parent_pause(); // update the interface if paused via native control
 306+ if ( this.getPlayerElement() ) {
 307+ try{
 308+ this.playerElement.playlist.togglePause();
 309+ } catch( e ){
 310+ mw.log("EmbedPlayerVlc could not pause video " + e);
 311+ }
 312+ }
 313+ },
 314+
 315+ /**
 316+ * Mutes the video
 317+ * calls parent "toggleMute" to update interface
 318+ */
 319+ toggleMute:function() {
 320+ this.parent_toggleMute();
 321+ if ( this.getPlayerElement() )
 322+ this.playerElement.audio.toggleMute();
 323+ },
 324+
 325+ /**
 326+ * Update the player volume
 327+ * @pram {Float} percent Percent of total volume
 328+ */
 329+ setPlayerElementVolume: function ( percent ) {
 330+ if ( this.getPlayerElement() ) {
 331+ this.playerElement.audio.volume = percent * 100;
 332+ }
 333+ },
 334+
 335+ /**
 336+ * Gets the current volume
 337+ * @return {Float} percent percent of total volume
 338+ */
 339+ getVolumen:function() {
 340+ if ( this.getPlayerElement() )
 341+ return this.playerElement.audio.volume / 100;
 342+ },
 343+
 344+ /**
 345+ * Passes fullscreen request to plugin
 346+ */
 347+ fullscreen : function() {
 348+ if ( this.playerElement ) {
 349+ if ( this.playerElement.video ){
 350+ try{
 351+ this.playerElement.video.toggleFullscreen();
 352+ } catch ( e ){
 353+ mw.log("VlcEmbed toggle fullscreen : possible error: " + e);
 354+ }
 355+ }
 356+ }
 357+ },
 358+
 359+ /**
 360+ * Get the embed vlc object
 361+ */
 362+ getPlayerElement : function() {
 363+ this.playerElement = $j( '#' + this.pid ).get(0);
 364+ return this.playerElement;
 365+ }
 366+};
Property changes on: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerVlc.js
___________________________________________________________________
Added: svn:mergeinfo
1367 Merged /branches/REL1_15/phase3/js2/mwEmbed/libEmbedVideo/vlcEmbed.js:r51646
2368 Merged /branches/sqlite/js2/mwEmbed/libEmbedVideo/vlcEmbed.js:r58211-58321
Added: svn:eol-style
3369 + native
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerNative.js
@@ -0,0 +1,692 @@
 2+/**
 3+* Native embed library:
 4+*
 5+* Enables embedPlayer support for native html5 browser playback system
 6+*/
 7+mw.EmbedPlayerNative = {
 8+
 9+ //Instance Name
 10+ instanceOf: 'Native',
 11+
 12+ // Flag to only load the video ( not play it )
 13+ onlyLoadFlag:false,
 14+
 15+ //Callback fired once video is "loaded"
 16+ onLoadedCallback: null,
 17+
 18+ // The previous "currentTime" to sniff seek actions
 19+ // NOTE the bug where onSeeked does not seem fire consistently may no longer be applicable
 20+ prevCurrentTime: -1,
 21+
 22+ // Store the progress event ( updated during monitor )
 23+ progressEventData: null,
 24+
 25+ // If the media loaded event has been fired
 26+ mediaLoadedFlag: null,
 27+
 28+ // All the native events per:
 29+ // http://www.w3.org/TR/html5/video.html#mediaevents
 30+ nativeEvents : [
 31+ 'loadstart',
 32+ 'progress',
 33+ 'suspend',
 34+ 'abort',
 35+ 'error',
 36+ 'emptied',
 37+ 'stalled',
 38+ 'play',
 39+ 'pause',
 40+ 'loadedmetadata',
 41+ 'loadeddata',
 42+ 'waiting',
 43+ 'playing',
 44+ 'canplay',
 45+ 'canplaythough',
 46+ 'seeking',
 47+ 'seeked',
 48+ 'timeupdate',
 49+ 'ended',
 50+ 'ratechange',
 51+ 'durationchange',
 52+ 'volumechange'
 53+ ],
 54+
 55+ // Native player supported feature set
 56+ supports: {
 57+ 'playHead' : true,
 58+ 'pause' : true,
 59+ 'fullscreen' : true,
 60+ 'timeDisplay' : true,
 61+ 'volumeControl' : true,
 62+ 'overlays' : true
 63+ },
 64+
 65+ /**
 66+ * Updates the supported features given the "type of player"
 67+ */
 68+ updateFeatureSupport: function(){
 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() ) {
 72+ this.supports.overlays = false;
 73+ this.supports.volumeControl = false;
 74+ }
 75+ // iOS does not support volume control ( only iPad can have controls )
 76+ if( mw.isIpad() ){
 77+ this.supports.volumeControl = false;
 78+ }
 79+ this.parent_updateFeatureSupport();
 80+ },
 81+
 82+ /**
 83+ * Return the embed code
 84+ */
 85+ doEmbedHTML : function () {
 86+ var _this = this;
 87+
 88+ // Reset some play state flags:
 89+ _this.bufferStartFlag = false;
 90+ _this.bufferEndFlag = false;
 91+
 92+ mw.log( "EmbedPlayerNative:: play url:" + this.getSrc( this.currentTime ) + ' startOffset: ' + this.start_ntp + ' end: ' + this.end_ntp );
 93+
 94+ // Check if using native controls and already the "pid" is already in the DOM
 95+ if( ( this.useNativePlayerControls()
 96+ ||
 97+ this.isPersistentNativePlayer()
 98+ )
 99+ && $j( '#' + this.pid ).length
 100+ && typeof $j( '#' + this.pid ).get(0).play != 'undefined' ) {
 101+
 102+ // Update the player source:
 103+ $j( '#' + this.pid ).attr( 'src', this.getSrc( this.currentTime ) );
 104+ $j( '#' + this.pid ).get(0).load();
 105+
 106+ _this.postEmbedJS();
 107+ return ;
 108+ }
 109+
 110+ $j( this ).html(
 111+ _this.getNativePlayerHtml()
 112+ );
 113+
 114+ // Directly run postEmbedJS ( if playerElement is not available it will retry )
 115+ _this.postEmbedJS();
 116+ },
 117+
 118+ /**
 119+ * Get the native player embed code.
 120+ *
 121+ * @param {object} playerAttribtues Attributes to be override in function call
 122+ * @return {object} cssSet css to apply to the player
 123+ */
 124+ getNativePlayerHtml: function( playerAttribtues, cssSet ){
 125+ if( !playerAttribtues) {
 126+ playerAttribtues = {};
 127+ }
 128+ // Update required attributes
 129+ if( !playerAttribtues['id'] ) playerAttribtues['id'] = this.pid;
 130+ if( !playerAttribtues['src'] ) playerAttribtues['src'] = this.getSrc( this.currentTime);
 131+
 132+ // If autoplay pass along to attribute ( needed for iPad / iPod no js autoplay support
 133+ if( this.autoplay ) {
 134+ playerAttribtues['autoplay'] = 'true';
 135+ }
 136+
 137+ if( !cssSet ){
 138+ cssSet = {};
 139+ }
 140+ // Set default width height to 100% of parent container
 141+ if( !cssSet['width'] ) cssSet['width'] = '100%';
 142+ if( !cssSet['height'] ) cssSet['height'] = '100%';
 143+
 144+ // Also need to set the loop param directly for iPad / iPod
 145+ if( this.loop ) {
 146+ playerAttribtues['loop'] = 'true';
 147+ }
 148+
 149+ var tagName = ( this.isAudio() ) ? 'audio' : 'video';
 150+
 151+ return $j( '<' + tagName + ' />' )
 152+ // Add the special nativeEmbedPlayer to avoid any rewrites of of this video tag.
 153+ .addClass( 'nativeEmbedPlayerPid' )
 154+ .attr( playerAttribtues )
 155+ .css( cssSet )
 156+ },
 157+
 158+ /**
 159+ * Post element javascript, binds event listeners and starts monitor
 160+ */
 161+ postEmbedJS: function() {
 162+ var _this = this;
 163+ mw.log( "f:native:postEmbedJS:" );
 164+
 165+ // Setup local pointer:
 166+ var vid = this.getPlayerElement();
 167+ // Apply media element bindings:
 168+ this.applyMediaElementBindings();
 169+
 170+ // Check for load flag
 171+ if ( this.onlyLoadFlag ) {
 172+ vid.pause();
 173+ vid.load();
 174+ } else {
 175+ // Issue play request
 176+ vid.play();
 177+ }
 178+
 179+ setTimeout( function() {
 180+ _this.monitor();
 181+ }, 100 );
 182+ },
 183+
 184+ /**
 185+ * Apply media element bindings
 186+ */
 187+ applyMediaElementBindings: function(){
 188+ var _this = this;
 189+ var vid = this.getPlayerElement();
 190+ if( ! vid ){
 191+ mw.log( " Error: applyMediaElementBindings without player elemnet");
 192+ return ;
 193+ }
 194+ $j.each( _this.nativeEvents, function( inx, eventName ){
 195+ $j( vid ).bind( eventName , function(){
 196+ if( _this._propagateEvents ){
 197+ var argArray = $j.makeArray( arguments );
 198+ // Check if there is local handler:
 199+ if( _this['on' + eventName ] ){
 200+ _this['on' + eventName ].apply( _this, argArray);
 201+ } else {
 202+ // No local handler directly propagate the event to the abstract object:
 203+ $j( _this ).trigger( eventName, argArray );
 204+ }
 205+ }
 206+ })
 207+ });
 208+ },
 209+
 210+ // basic monitor function to update buffer
 211+ monitor: function(){
 212+ var _this = this;
 213+ var vid = _this.getPlayerElement();
 214+
 215+ // Update duration
 216+ if( vid && vid.duration ){
 217+ this.duration = vid.duration;
 218+ }
 219+ // Update the bufferedPercent
 220+ if( vid && vid.buffered && vid.buffered.end && vid.duration ) {
 221+ this.bufferedPercent = ( vid.buffered.end(0) / vid.duration );
 222+ }
 223+ _this.parent_monitor();
 224+ },
 225+
 226+
 227+ /**
 228+ * Issue a seeking request.
 229+ *
 230+ * @param {Float} percentage
 231+ */
 232+ doSeek: function( percentage ) {
 233+ mw.log( 'Native::doSeek p: ' + percentage + ' : ' + this.supportsURLTimeEncoding() + ' dur: ' + this.getDuration() + ' sts:' + this.seek_time_sec );
 234+ this.seeking = true;
 235+
 236+ // Run the onSeeking interface update
 237+ this.controlBuilder.onSeek();
 238+
 239+ // @@todo check if the clip is loaded here (if so we can do a local seek)
 240+ if ( this.supportsURLTimeEncoding() ) {
 241+ // Make sure we could not do a local seek instead:
 242+ if ( percentage < this.bufferedPercent && this.playerElement.duration && !this.didSeekJump ) {
 243+ mw.log( "do local seek " + percentage + ' is already buffered < ' + this.bufferedPercent );
 244+ this.doNativeSeek( percentage );
 245+ } else {
 246+ // We support URLTimeEncoding call parent seek:
 247+ this.parent_doSeek( percentage );
 248+ }
 249+ } else if ( this.playerElement && this.playerElement.duration ) {
 250+ // (could also check bufferedPercent > percentage seek (and issue oggz_chop request or not)
 251+ this.doNativeSeek( percentage );
 252+ } else {
 253+ // try to do a play then seek:
 254+ this.doPlayThenSeek( percentage )
 255+ }
 256+ },
 257+
 258+ /**
 259+ * Do a native seek by updating the currentTime
 260+ * @param {float} percentage
 261+ * Percent to seek to of full time
 262+ */
 263+ doNativeSeek: function( percentage ) {
 264+ var _this = this;
 265+ mw.log( 'native::doNativeSeek::' + percentage );
 266+ this.seeking = true;
 267+ this.seek_time_sec = 0;
 268+ this.setCurrentTime( ( percentage * this.duration ) , function(){
 269+ _this.seeking = false;
 270+ _this.monitor();
 271+ })
 272+ },
 273+
 274+ /**
 275+ * Seek in a existing stream
 276+ *
 277+ * @param {Float} percentage
 278+ * Percentage of the stream to seek to between 0 and 1
 279+ */
 280+ doPlayThenSeek: function( percentage ) {
 281+ mw.log( 'native::doPlayThenSeek::' );
 282+ var _this = this;
 283+ this.play();
 284+ var retryCount = 0;
 285+ var readyForSeek = function() {
 286+ _this.getPlayerElement();
 287+ // If we have duration then we are ready to do the seek
 288+ if ( _this.playerElement && _this.playerElement.duration ) {
 289+ _this.doNativeSeek( percentage );
 290+ } else {
 291+ // Try to get player for 40 seconds:
 292+ // (it would be nice if the onmetadata type callbacks where fired consistently)
 293+ if ( retryCount < 800 ) {
 294+ setTimeout( readyForSeek, 50 );
 295+ retryCount++;
 296+ } else {
 297+ mw.log( 'error:doPlayThenSeek failed' );
 298+ }
 299+ }
 300+ }
 301+ readyForSeek();
 302+ },
 303+
 304+ /**
 305+ * Set the current time with a callback
 306+ *
 307+ * @param {Float} position
 308+ * Seconds to set the time to
 309+ * @param {Function} callback
 310+ * Function called once time has been set.
 311+ */
 312+ setCurrentTime: function( time , callback, callbackCount ) {
 313+ var _this = this;
 314+ if( !callbackCount )
 315+ callbackCount = 0;
 316+ this.getPlayerElement();
 317+ if( _this.playerElement.readyState >= 1 ){
 318+ if( _this.playerElement.currentTime == time ){
 319+ callback();
 320+ return;
 321+ }
 322+ var once = function( event ) {
 323+ if( callback ){
 324+ callback();
 325+ }
 326+ _this.playerElement.removeEventListener( 'seeked', once, false );
 327+ };
 328+ // Assume we will get to add the Listener before the seek is done
 329+ _this.playerElement.addEventListener( 'seeked', once, false );
 330+ try {
 331+ _this.playerElement.currentTime = time;
 332+ } catch (e) {
 333+ mw.log("Could not seek to this point. Unbuffered point.");
 334+ callback();
 335+ return;
 336+ }
 337+ } else {
 338+ if( callbackCount >= 300 ){
 339+ mw.log("Error with seek request, media never in ready state");
 340+ return ;
 341+ }
 342+ setTimeout( function(){
 343+ _this.setCurrentTime( time, callback , callbackCount++);
 344+ }, 10 );
 345+ }
 346+ },
 347+
 348+ /**
 349+ * Get the embed player time
 350+ */
 351+ getPlayerElementTime: function() {
 352+ var _this = this;
 353+ // Make sure we have .vid obj
 354+ this.getPlayerElement();
 355+
 356+ if ( !this.playerElement ) {
 357+ mw.log( 'mwEmbedPlayer::getPlayerElementTime: ' + this.id + ' not in dom ( stop monitor)' );
 358+ return false;
 359+ }
 360+ // Return the playerElement currentTime
 361+ return this.playerElement.currentTime;
 362+ },
 363+
 364+ // Update the poster src ( updates the native object if in dom )
 365+ updatePosterSrc: function( src ){
 366+ if( this.getPlayerElement() ){
 367+ this.getPlayerElement().poster = src;
 368+ }
 369+ // Also update the embedPlayer poster
 370+ this.parent_updatePosterSrc( src );
 371+ },
 372+
 373+ /**
 374+ * switchPlaySrc switches the player source working around a few bugs in browsers
 375+ *
 376+ * @param {string}
 377+ * src Video url Source to switch to.
 378+ * @param {function}
 379+ * switchCallback Function to call once the source has been switched
 380+ * @param {function}
 381+ * doneCallback Function to call once the clip has completed playback
 382+ */
 383+ switchPlaySrc: function( src, switchCallback, doneCallback ){
 384+ var _this = this;
 385+ mw.log( 'EmbedPlayerNative:: switchPlaySrc:' + src + ' native time: ' + this.getPlayerElement().currentTime );
 386+ // Update some parent embedPlayer vars:
 387+ this.duration = 0;
 388+ this.currentTime = 0;
 389+ this.previousTime = 0;
 390+ var vid = this.getPlayerElement();
 391+ if ( vid ) {
 392+ try {
 393+ // issue a play request on the source
 394+ vid.play();
 395+ setTimeout(function(){
 396+ // Remove all native player bindings
 397+ $j(vid).unbind();
 398+ vid.pause();
 399+ var orginalControlsState = vid.controls;
 400+ // Hide controls ( to not display native play button while switching sources )
 401+ vid.removeAttribute('controls');
 402+
 403+ // Local scope update source and play function to work around google chrome bug
 404+ var updateSrcAndPlay = function() {
 405+ var vid = _this.getPlayerElement();
 406+ if (!vid){
 407+ mw.log( 'Error: switchPlaySrc no vid');
 408+ return ;
 409+ }
 410+ vid.src = src;
 411+ // Give iOS 50ms to figure out the src got updated ( iPad OS 4.0 )
 412+ setTimeout(function() {
 413+ var vid = _this.getPlayerElement();
 414+ if (!vid){
 415+ mw.log( 'Error: switchPlaySrc no vid');
 416+ return ;
 417+ }
 418+ vid.load();
 419+ vid.play();
 420+ // Wait another 100ms then bind the end event and any custom events
 421+ // for the switchCallback
 422+ setTimeout(function() {
 423+ var vid = _this.getPlayerElement();
 424+ // restore controls
 425+ vid.controls = orginalControlsState;
 426+ // add the end binding:
 427+ $j(vid).bind('ended', function( event ) {
 428+ if(typeof doneCallback == 'function' ){
 429+ doneCallback();
 430+ }
 431+ return false;
 432+ });
 433+ if (typeof switchCallback == 'function') {
 434+ switchCallback(vid);
 435+ }
 436+ }, 100);
 437+ }, 100);
 438+ };
 439+ if (navigator.userAgent.toLowerCase().indexOf('chrome') != -1) {
 440+ // Null the src and wait 50ms ( helps unload video without crashing
 441+ // google chrome 7.x )
 442+ vid.src = '';
 443+ setTimeout(updateSrcAndPlay, 100);
 444+ } else {
 445+ updateSrcAndPlay();
 446+ }
 447+ }, 100 );
 448+ } catch (e) {
 449+ mw.log("Error: Error in swiching source playback");
 450+ }
 451+ }
 452+ },
 453+
 454+ /**
 455+ * Pause the video playback
 456+ * calls parent_pause to update the interface
 457+ */
 458+ pause: function( ) {
 459+ this.getPlayerElement();
 460+ this.parent_pause(); // update interface
 461+ if ( this.playerElement ) { // update player
 462+ if( !this.playerElement.paused ){
 463+ this.playerElement.pause();
 464+ }
 465+ }
 466+ },
 467+
 468+ /**
 469+ * Play back the video stream
 470+ * calls parent_play to update the interface
 471+ */
 472+ play: function( ) {
 473+ this.getPlayerElement();
 474+ this.parent_play(); // update interface
 475+ if ( this.playerElement && this.playerElement.play ) {
 476+ // issue a play request if the media is paused:
 477+ if( this.playerElement.paused ){
 478+ this.playerElement.play();
 479+ }
 480+ // re-start the monitor:
 481+ this.monitor();
 482+ }
 483+ },
 484+ /**
 485+ * Stop the player ( end all listeners )
 486+ */
 487+ stop:function(){
 488+ if( this.playerElement ){
 489+ $j( this.playerElement ).unbind();
 490+ }
 491+ this.parent_stop();
 492+ },
 493+
 494+ /**
 495+ * Toggle the Mute
 496+ * calls parent_toggleMute to update the interface
 497+ */
 498+ toggleMute: function() {
 499+ this.parent_toggleMute();
 500+ this.getPlayerElement();
 501+ if ( this.playerElement )
 502+ this.playerElement.muted = this.muted;
 503+ },
 504+
 505+ /**
 506+ * Update Volume
 507+ *
 508+ * @param {Float} percentage Value between 0 and 1 to set audio volume
 509+ */
 510+ setPlayerElementVolume : function( percentage ) {
 511+ if ( this.getPlayerElement() ) {
 512+ // Disable mute if positive volume
 513+ if( percentage != 0 ) {
 514+ this.playerElement.muted = false;
 515+ }
 516+ this.playerElement.volume = percentage;
 517+ }
 518+ },
 519+
 520+ /**
 521+ * get Volume
 522+ *
 523+ * @return {Float}
 524+ * Audio volume between 0 and 1.
 525+ */
 526+ getPlayerElementVolume: function() {
 527+ if ( this.getPlayerElement() ) {
 528+ return this.playerElement.volume;
 529+ }
 530+ },
 531+ /**
 532+ * get the native muted state
 533+ */
 534+ getPlayerElementMuted: function(){
 535+ if ( this.getPlayerElement() ) {
 536+ return this.playerElement.muted;
 537+ }
 538+ },
 539+
 540+ /**
 541+ * Get the native media duration
 542+ */
 543+ getNativeDuration: function() {
 544+ if ( this.playerElement ) {
 545+ return this.playerElement.duration;
 546+ }
 547+ },
 548+
 549+ /**
 550+ * load the video stream with a callback fired once the video is "loaded"
 551+ *
 552+ * @parma {Function} callbcak Function called once video is loaded
 553+ */
 554+ load: function( callback ) {
 555+ this.getPlayerElement();
 556+ if ( !this.playerElement ) {
 557+ // No vid loaded
 558+ mw.log( 'native::load() ... doEmbed' );
 559+ this.onlyLoadFlag = true;
 560+ this.doEmbedHTML();
 561+ this.onLoadedCallback = callback;
 562+ } else {
 563+ // Should not happen offten
 564+ this.playerElement.load();
 565+ if( callback)
 566+ callback();
 567+ }
 568+ },
 569+
 570+ /**
 571+ * Get /update the playerElement value
 572+ */
 573+ getPlayerElement: function () {
 574+ this.playerElement = $j( '#' + this.pid ).get( 0 );
 575+ return this.playerElement;
 576+ },
 577+
 578+ /**
 579+ * Bindings for the Video Element Events
 580+ */
 581+
 582+ /**
 583+ * Local method for seeking event
 584+ * fired when "seeking"
 585+ */
 586+ onseeking: function() {
 587+ mw.log( "native:onSeeking");
 588+ // Trigger the html5 seeking event
 589+ //( if not already set from interface )
 590+ if( !this.seeking ) {
 591+ this.seeking = true;
 592+
 593+ // Run the onSeeking interface update
 594+ this.controlBuilder.onSeek();
 595+
 596+ // Trigger the html5 "seeking" trigger
 597+ mw.log("native:seeking:trigger:: " + this.seeking);
 598+ $j( this ).trigger( 'seeking' );
 599+ }
 600+ },
 601+
 602+ /**
 603+ * Local method for seeked event
 604+ * fired when done seeking
 605+ */
 606+ onseeked: function() {
 607+ mw.log("native:onSeeked");
 608+ // Trigger the html5 action on the parent
 609+ if( this.seeking ){
 610+ this.seeking = false;
 611+ $j( this ).trigger( 'seeked' );
 612+ }
 613+ this.seeking = false;
 614+ },
 615+
 616+ /**
 617+ * Handle the native paused event
 618+ */
 619+ onpause: function(){
 620+ mw.log( "EmbedPlayer:native: OnPaused:: " + this._propagateEvents );
 621+ if( this._propagateEvents ){
 622+ this.parent_pause();
 623+ }
 624+ },
 625+
 626+ /**
 627+ * Handle the native play event
 628+ */
 629+ onplay: function(){
 630+ mw.log("EmbedPlayer:native:: OnPlay::" + this._propagateEvents );
 631+ // Update the interface ( if paused )
 632+ if( this._propagateEvents ){
 633+ this.parent_play();
 634+ }
 635+ },
 636+
 637+ /**
 638+ * Local method for metadata ready
 639+ * fired when metadata becomes available
 640+ *
 641+ * Used to update the media duration to
 642+ * accurately reflect the src duration
 643+ */
 644+ onloadedmetadata: function() {
 645+ this.getPlayerElement();
 646+ if ( this.playerElement && ! isNaN( this.playerElement.duration ) ) {
 647+ mw.log( 'f:onloadedmetadata metadata ready Update duration:' + this.playerElement.duration + ' old dur: ' + this.getDuration() );
 648+ this.duration = this.playerElement.duration;
 649+ }
 650+
 651+ //Fire "onLoaded" flags if set
 652+ if( typeof this.onLoadedCallback == 'function' ) {
 653+ this.onLoadedCallback();
 654+ }
 655+
 656+ // Trigger "media loaded"
 657+ if( ! this.mediaLoadedFlag ){
 658+ $j( this ).trigger( 'mediaLoaded' );
 659+ this.mediaLoadedFlag = true;
 660+ }
 661+ },
 662+
 663+ /**
 664+ * Local method for progress event
 665+ * fired as the video is downloaded / buffered
 666+ *
 667+ * Used to update the bufferedPercent
 668+ *
 669+ * Note: this way of updating buffer was only supported in Firefox 3.x and
 670+ * not supported in Firefox 4.x
 671+ */
 672+ onprogress: function( event ) {
 673+ var e = event.originalEvent;
 674+ if( e && e.loaded && e.total ) {
 675+ this.bufferedPercent = e.loaded / e.total;
 676+ this.progressEventData = e.loaded;
 677+ }
 678+ },
 679+
 680+ /**
 681+ * Local method for progress event
 682+ * fired as the video is downloaded / buffered
 683+ *
 684+ * Used to update the bufferedPercent
 685+ */
 686+ onended: function() {
 687+ var _this = this;
 688+ mw.log( 'EmbedPlayer:native: onended:' + this.playerElement.currentTime + ' real dur:' + this.getDuration() + ' ended ' + this._propagateEvents );
 689+ if( this._propagateEvents ){
 690+ this.onClipDone();
 691+ }
 692+ }
 693+};
Property changes on: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerNative.js
___________________________________________________________________
Added: svn:mergeinfo
1694 Merged /branches/REL1_15/phase3/js2/mwEmbed/libEmbedVideo/nativeEmbed.js:r51646
2695 Merged /branches/sqlite/js2/mwEmbed/libEmbedVideo/nativeEmbed.js:r58211-58321
Added: svn:eol-style
3696 + native
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerKplayer.js
@@ -0,0 +1,1249 @@
 2+/*
 3+ * The "kaltura player" embedPlayer interface for fallback h.264 and flv video format support
 4+ */
 5+
 6+// Called from the kdp.swf
 7+function jsInterfaceReadyFunc() {
 8+ return true;
 9+}
 10+
 11+mw.EmbedPlayerKplayer = {
 12+
 13+ // Instance name:
 14+ instanceOf : 'Kplayer',
 15+
 16+ // List of supported features:
 17+ supports : {
 18+ 'playHead' : true,
 19+ 'pause' : true,
 20+ 'stop' : true,
 21+ 'timeDisplay' : true,
 22+ 'volumeControl' : true,
 23+ 'overlays' : true,
 24+ 'fullscreen' : true
 25+ },
 26+
 27+ // Stores the current time as set from flash
 28+ flashCurrentTime : 0,
 29+
 30+ /*
 31+ * Write the Embed html to the target
 32+ */
 33+ doEmbedHTML : function() {
 34+ var _this = this;
 35+
 36+ mw.log("kPlayer:: embed src::" + _this.getSrc());
 37+ var flashvars = {};
 38+ flashvars.autoPlay = "true";
 39+ var playerPath = mw.getMwEmbedPath() + 'modules/EmbedPlayer/binPlayers/kaltura-player';
 40+ flashvars.entryId = mw.absoluteUrl(_this.getSrc());
 41+
 42+ // Use a relative url if the protocal is file://
 43+ if (mw.parseUri(document.URL).protocol == 'file') {
 44+ playerPath = mw.getRelativeMwEmbedPath() + 'modules/EmbedPlayer/binPlayers/kaltura-player';
 45+ flashvars.entryId = _this.getSrc();
 46+ }
 47+
 48+ flashvars.debugMode = "true";
 49+ flashvars.fileSystemMode = "true";
 50+ flashvars.widgetId = "_7463";
 51+ flashvars.partnerId = "7463";
 52+ flashvars.pluginDomain = "kdp3/plugins/";
 53+ flashvars.kml = "local";
 54+ flashvars.kmlPath = playerPath + '/config.xml';
 55+ flashvars.sourceType = "url";
 56+
 57+ // flashvars.host = "www.kaltura.com";
 58+ flashvars.externalInterfaceDisabled = 'false';
 59+ flashvars.skinPath = playerPath + '/skin.swf';
 60+
 61+ flashvars["full.skinPath"] = playerPath + '/LightDoodleskin.swf';
 62+
 63+ var params = {};
 64+ params.quality = "best";
 65+ params.wmode = "opaque";
 66+ params.allowfullscreen = "true";
 67+ params.allowscriptaccess = "always";
 68+
 69+ var attributes = {};
 70+ attributes.id = this.pid;
 71+ attributes.name = this.pid;
 72+
 73+ mw.log(" about to add the pid container");
 74+ $j(this).html($j('<div />').attr('id', this.pid + '_container'));
 75+ // Call swm dom loaded function:
 76+ swfobject.callDomLoadFunctions();
 77+ // Do the flash embedding with embedSWF
 78+ swfobject.embedSWF(playerPath + "/kdp3.swf", this.pid + '_container',
 79+ '100%', '100%', "10.0.0", playerPath + "/expressInstall.swf",
 80+ flashvars, params, attributes);
 81+
 82+ // Direct object embed
 83+ /*
 84+ * $j( this ).html( '<object
 85+ * classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="780"
 86+ * height="420">'+ '<param name="movie" value="myContent.swf" />'+ '<!--[if
 87+ * !IE]>-->'+ '<object type="application/x-shockwave-flash"
 88+ * data="myContent.swf" width="780" height="420">'+ '<!--<![endif]-->'+ '<p>
 89+ * error with flash embed</p>' '<!--[if !IE]>-->'+ '</object>'+ '<!--<![endif]-->'+ '</object>' )
 90+ */
 91+
 92+ setTimeout(function() {
 93+ _this.postEmbedJS();
 94+ }, 100);
 95+
 96+ // Flash player loses its bindings once it changes sizes::
 97+ $j(_this).bind('onOpenFullScreen', function() {
 98+ _this.postEmbedJS();
 99+ });
 100+ $j(_this).bind('onCloseFullScreen', function() {
 101+ _this.postEmbedJS();
 102+ });
 103+ },
 104+
 105+ /**
 106+ * javascript run post player embedding
 107+ */
 108+ postEmbedJS : function() {
 109+ var _this = this;
 110+ this.getPlayerElement();
 111+
 112+ var bindEventMap = {
 113+ 'doPause' : 'onPause',
 114+ 'doPlay' : 'onPlay',
 115+ 'durationChange' : 'onDurationChange',
 116+ 'playerPlayEnd' : 'onClipDone',
 117+ 'playerUpdatePlayhead' : 'onUpdatePlayhead',
 118+ 'bytesTotalChange' : 'onBytesTotalChange',
 119+ 'bytesDownloadedChange' : 'onBytesDownloadedChange'
 120+ }
 121+
 122+ if (this.playerElement && this.playerElement.addJsListener) {
 123+ $j.each( bindEventMap, function( bindName, localMethod ) {
 124+ _this.bindPlayerFunction(bindName, localMethod);
 125+ } );
 126+ // Start the monitor
 127+ this.monitor();
 128+ } else {
 129+ // Keep trying to get the player element
 130+ // mw.log('insert media: not defined:' + typeof
 131+ // this.playerElement.insertMedia );
 132+ setTimeout(function() {
 133+ _this.postEmbedJS();
 134+ }, 250);
 135+ }
 136+ },
 137+
 138+ /**
 139+ * Bind a Player Function,
 140+ *
 141+ * Does some tricker to bind to "this" player instance:
 142+ *
 143+ * @param {String}
 144+ * flash binding name
 145+ * @param {String}
 146+ * function callback name
 147+ */
 148+ bindPlayerFunction : function(bindName, methodName) {
 149+ // The kaltura kdp can only call a global function by given name
 150+ var gKdpCallbackName = methodName + '_cb_' + this.id;
 151+
 152+ // Create an anonymous function with local player scope
 153+ var createGlobalCB = function(cName, embedPlayer) {
 154+ window[ cName ] = function(data) {
 155+ if ( embedPlayer._propagateEvents ) {
 156+ embedPlayer[methodName](data);
 157+ }
 158+ };
 159+ }(gKdpCallbackName, this);
 160+
 161+ // Add the listener to the KDP flash player:
 162+ this.playerElement.addJsListener(bindName, gKdpCallbackName);
 163+ },
 164+
 165+ /**
 166+ * on Pause callback from the kaltura flash player calls parent_pause to
 167+ * update the interface
 168+ */
 169+ onPause : function() {
 170+ this.parent_pause();
 171+ },
 172+
 173+ /**
 174+ * onPlay function callback from the kaltura flash player directly call the
 175+ * parent_play
 176+ */
 177+ onPlay : function() {
 178+ this.parent_play();
 179+ },
 180+
 181+ onDurationChange : function(data, id) {
 182+ mw.log(" onDurationChange: " + data.newValue);
 183+ // update the duration:
 184+ this.duration = data.newValue;
 185+ },
 186+
 187+ /**
 188+ * play method calls parent_play to update the interface
 189+ */
 190+ play : function() {
 191+ if (this.playerElement && this.playerElement.sendNotification) {
 192+ this.playerElement.sendNotification('doPlay');
 193+ }
 194+ this.parent_play();
 195+ },
 196+
 197+ /**
 198+ * pause method calls parent_pause to update the interface
 199+ */
 200+ pause : function() {
 201+ if (this.playerElement && this.playerElement.sendNotification) {
 202+ this.playerElement.sendNotification('doPause');
 203+ }
 204+ this.parent_pause();
 205+ },
 206+
 207+ /**
 208+ * Issues a seek to the playerElement
 209+ *
 210+ * @param {Float}
 211+ * percentage Percentage of total stream length to seek to
 212+ */
 213+ doSeek : function(percentage) {
 214+ var _this = this;
 215+ var seekTime = percentage * this.getDuration();
 216+ mw.log( 'EmbedPlayerKalturaKplayer:: doSeek: ' + percentage + ' time:' + seekTime );
 217+ if (this.supportsURLTimeEncoding()) {
 218+
 219+ // Make sure we could not do a local seek instead:
 220+ if (!(percentage < this.bufferedPercent
 221+ && this.playerElement.duration && !this.didSeekJump)) {
 222+ // We support URLTimeEncoding call parent seek:
 223+ this.parent_doSeek( percentage );
 224+ return;
 225+ }
 226+ }
 227+
 228+ if (this.playerElement) {
 229+ // Issue the seek to the flash player:
 230+ this.playerElement.sendNotification('doSeek', seekTime);
 231+
 232+ // Kdp is missing seek done callback
 233+ setTimeout(function() {
 234+ _this.seeking = false;
 235+ }, 500);
 236+ } else {
 237+ // try to do a play then seek:
 238+ this.doPlayThenSeek(percentage)
 239+ }
 240+ // Run the onSeeking interface update
 241+ this.controlBuilder.onSeek();
 242+ },
 243+
 244+ /**
 245+ * Seek in a existing stream
 246+ *
 247+ * @param {Float}
 248+ * percentage Percentage of the stream to seek to between 0 and 1
 249+ */
 250+ doPlayThenSeek : function(percentage) {
 251+ mw.log('flash::doPlayThenSeek::');
 252+ var _this = this;
 253+ // issue the play request
 254+ this.play();
 255+
 256+ // let the player know we are seeking
 257+ _this.seeking = true;
 258+
 259+ var getPlayerCount = 0;
 260+ var readyForSeek = function() {
 261+ _this.getPlayerElement();
 262+ // if we have duration then we are ready to do the seek ( flash can't
 263+ // seek untill there is some buffer )
 264+ if (_this.playerElement && _this.playerElement.sendNotification
 265+ && _this.getDuration() && _this.bufferedPercent) {
 266+ var seekTime = percentage * _this.getDuration();
 267+ // Issue the seek to the flash player:
 268+ _this.playerElement.sendNotification('doSeek', seekTime);
 269+ } else {
 270+ // Try to get player for 20 seconds:
 271+ if (getPlayerCount < 400) {
 272+ setTimeout(readyForSeek, 50);
 273+ getPlayerCount++;
 274+ } else {
 275+ mw.log('Error:doPlayThenSeek failed');
 276+ }
 277+ }
 278+ }
 279+ readyForSeek();
 280+},
 281+
 282+/**
 283+ * Issues a volume update to the playerElement
 284+ *
 285+ * @param {Float}
 286+ * percentage Percentage to update volume to
 287+ */
 288+setPlayerElementVolume : function(percentage) {
 289+ if (this.playerElement && this.playerElement.sendNotification) {
 290+ this.playerElement.sendNotification('changeVolume', percentage);
 291+ }
 292+},
 293+
 294+/**
 295+ * function called by flash at set interval to update the playhead.
 296+ */
 297+onUpdatePlayhead : function(playheadValue) {
 298+ this.flashCurrentTime = playheadValue;
 299+},
 300+
 301+/**
 302+ * function called by flash when the total media size changes
 303+ */
 304+onBytesTotalChange : function(data, id) {
 305+ this.bytesTotal = data.newValue;
 306+},
 307+
 308+/**
 309+ * function called by flash applet when download bytes changes
 310+ */
 311+onBytesDownloadedChange : function(data, id) {
 312+ mw.log('onBytesDownloadedChange');
 313+ this.bytesLoaded = data.newValue;
 314+ this.bufferedPercent = this.bytesLoaded / this.bytesTotal;
 315+
 316+ // Fire the parent html5 action
 317+ $j(this).trigger('progress', {
 318+ 'loaded' : this.bytesLoaded,
 319+ 'total' : this.bytesTotal
 320+ });
 321+},
 322+
 323+/**
 324+ * Get the embed player time
 325+ */
 326+getPlayerElementTime : function() {
 327+ // update currentTime
 328+ return this.flashCurrentTime;
 329+},
 330+
 331+/**
 332+ * Get the embed fla object player Element
 333+ */
 334+getPlayerElement : function() {
 335+ this.playerElement = document.getElementById(this.pid);
 336+ return this.playerElement;
 337+}
 338+}
 339+
 340+/**
 341+ * function called once player is ready.
 342+ *
 343+ * NOTE: playerID is not always passed so we can't use this:
 344+ */
 345+function onKdpReady(playerId) {
 346+ mw.log("player is ready::" + playerId);
 347+}
 348+
 349+/*
 350+ * ! SWFObject v2.2 <http://code.google.com/p/swfobject/> is released under the
 351+ * MIT License <http://www.opensource.org/licenses/mit-license.php>
 352+ */
 353+
 354+var swfobject = function() {
 355+
 356+ 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",
 357+
 358+ win = window, doc = document, nav = navigator,
 359+
 360+ plugin = false, domLoadFnArr = [ main ], regObjArr = [], objIdArr = [], listenersArr = [], storedAltContent, storedAltContentId, storedCallbackFn, storedCallbackObj, isDomLoaded = false, isExpressInstallActive = false, dynamicStylesheet, dynamicStylesheetMedia, autoHideShow = true,
 361+
 362+ /*
 363+ * Centralized function for browser feature detection - User agent string
 364+ * detection is only used when no good alternative is possible - Is executed
 365+ * directly for optimal performance
 366+ */
 367+ ua = function() {
 368+ var w3cdom = typeof doc.getElementById != UNDEF
 369+ && typeof doc.getElementsByTagName != UNDEF
 370+ && typeof doc.createElement != UNDEF, u = nav.userAgent
 371+ .toLowerCase(), p = nav.platform.toLowerCase(), windows = p ? /win/
 372+ .test(p)
 373+ : /win/.test(u), mac = p ? /mac/.test(p) : /mac/.test(u), webkit = /webkit/
 374+ .test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,
 375+ "$1")) : false, // returns either the webkit version or false if
 376+ // not webkit
 377+ ie = !+"\v1", // feature detection based on Andrea Giammarchi's
 378+ // solution:
 379+ // http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
 380+ playerVersion = [ 0, 0, 0 ], d = null;
 381+ if (typeof nav.plugins != UNDEF
 382+ && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
 383+ d = nav.plugins[SHOCKWAVE_FLASH].description;
 384+ if (d
 385+ && !(typeof nav.mimeTypes != UNDEF
 386+ && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin
 387+ // indicates
 388+ // whether
 389+ // plug-ins
 390+ // are
 391+ // enabled
 392+ // or
 393+ // disabled
 394+ // in
 395+ // Safari
 396+ // 3+
 397+ plugin = true;
 398+ ie = false; // cascaded feature detection for Internet Explorer
 399+ d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
 400+ playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
 401+ playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"),
 402+ 10);
 403+ playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(
 404+ /^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
 405+ }
 406+ } else if (typeof win.ActiveXObject != UNDEF) {
 407+ try {
 408+ var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
 409+ if (a) { // a will return null when ActiveX is disabled
 410+ d = a.GetVariable("$version");
 411+ if (d) {
 412+ ie = true; // cascaded feature detection for Internet
 413+ // Explorer
 414+ d = d.split(" ")[1].split(",");
 415+ playerVersion = [ parseInt(d[0], 10),
 416+ parseInt(d[1], 10), parseInt(d[2], 10) ];
 417+ }
 418+ }
 419+ } catch (e) {
 420+ }
 421+ }
 422+ return {
 423+ w3 : w3cdom,
 424+ pv : playerVersion,
 425+ wk : webkit,
 426+ ie : ie,
 427+ win : windows,
 428+ mac : mac
 429+ };
 430+ }();
 431+
 432+ function callDomLoadFunctions() {
 433+ if (isDomLoaded) {
 434+ return;
 435+ }
 436+ try { // test if we can really add/remove elements to/from the DOM; we
 437+ // don't want to fire it too early
 438+ var t = doc.getElementsByTagName("body")[0]
 439+ .appendChild(createElement("span"));
 440+ t.parentNode.removeChild(t);
 441+ } catch (e) {
 442+ return;
 443+ }
 444+ isDomLoaded = true;
 445+ var dl = domLoadFnArr.length;
 446+ for ( var i = 0; i < dl; i++) {
 447+ domLoadFnArr[i]();
 448+ }
 449+ }
 450+
 451+ function addDomLoadEvent(fn) {
 452+ if (isDomLoaded) {
 453+ fn();
 454+ } else {
 455+ domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only
 456+ // available in IE5.5+
 457+ }
 458+ }
 459+
 460+ /*
 461+ * Cross-browser onload - Based on James Edwards' solution:
 462+ * http://brothercake.com/site/resources/scripts/onload/ - Will fire an
 463+ * event as soon as a web page including all of its assets are loaded
 464+ */
 465+ function addLoadEvent(fn) {
 466+ if (typeof win.addEventListener != UNDEF) {
 467+ win.addEventListener("load", fn, false);
 468+ } else if (typeof doc.addEventListener != UNDEF) {
 469+ doc.addEventListener("load", fn, false);
 470+ } else if (typeof win.attachEvent != UNDEF) {
 471+ addListener(win, "onload", fn);
 472+ } else if (typeof win.onload == "function") {
 473+ var fnOld = win.onload;
 474+ win.onload = function() {
 475+ fnOld();
 476+ fn();
 477+ };
 478+ } else {
 479+ win.onload = fn;
 480+ }
 481+ }
 482+
 483+ /*
 484+ * Main function - Will preferably execute onDomLoad, otherwise onload (as a
 485+ * fallback)
 486+ */
 487+ function main() {
 488+ if (plugin) {
 489+ testPlayerVersion();
 490+ } else {
 491+ matchVersions();
 492+ }
 493+ }
 494+
 495+ /*
 496+ * Detect the Flash Player version for non-Internet Explorer browsers -
 497+ * Detecting the plug-in version via the object element is more precise than
 498+ * using the plugins collection item's description: a. Both release and
 499+ * build numbers can be detected b. Avoid wrong descriptions by corrupt
 500+ * installers provided by Adobe c. Avoid wrong descriptions by multiple
 501+ * Flash Player entries in the plugin Array, caused by incorrect browser
 502+ * imports - Disadvantage of this method is that it depends on the
 503+ * availability of the DOM, while the plugins collection is immediately
 504+ * available
 505+ */
 506+ function testPlayerVersion() {
 507+ var b = doc.getElementsByTagName("body")[0];
 508+ var o = createElement(OBJECT);
 509+ o.setAttribute("type", FLASH_MIME_TYPE);
 510+ var t = b.appendChild(o);
 511+ if (t) {
 512+ var counter = 0;
 513+ (function() {
 514+ if (typeof t.GetVariable != UNDEF) {
 515+ try {
 516+ var d = t.GetVariable("$version");
 517+ if (d) {
 518+ d = d.split(" ")[1].split(",");
 519+ ua.pv = [ parseInt(d[0], 10), parseInt(d[1], 10),
 520+ parseInt(d[2], 10) ];
 521+ }
 522+ } catch (e) {
 523+ // error in grabbing flash version
 524+ }
 525+ } else if (counter < 10) {
 526+ counter++;
 527+ setTimeout(arguments.callee, 10);
 528+ return;
 529+ }
 530+ b.removeChild(o);
 531+ t = null;
 532+ matchVersions();
 533+ })();
 534+ } else {
 535+ matchVersions();
 536+ }
 537+ }
 538+
 539+ /*
 540+ * Perform Flash Player and SWF version matching; static publishing only
 541+ */
 542+ function matchVersions() {
 543+ var rl = regObjArr.length;
 544+ if (rl > 0) {
 545+ for ( var i = 0; i < rl; i++) { // for each registered object
 546+ // element
 547+ var id = regObjArr[i].id;
 548+ var cb = regObjArr[i].callbackFn;
 549+ var cbObj = {
 550+ success : false,
 551+ id : id
 552+ };
 553+ if (ua.pv[0] > 0) {
 554+ var obj = getElementById(id);
 555+ if (obj) {
 556+ if (hasPlayerVersion(regObjArr[i].swfVersion)
 557+ && !(ua.wk && ua.wk < 312)) { // Flash Player
 558+ // version >=
 559+ // published SWF
 560+ // version:
 561+ // Houston, we
 562+ // have a match!
 563+ setVisibility(id, true);
 564+ if (cb) {
 565+ cbObj.success = true;
 566+ cbObj.ref = getObjectById(id);
 567+ cb(cbObj);
 568+ }
 569+ } else if (regObjArr[i].expressInstall
 570+ && canExpressInstall()) { // show the Adobe
 571+ // Express Install
 572+ // dialog if set by
 573+ // the web page
 574+ // author and if
 575+ // supported
 576+ var att = {};
 577+ att.data = regObjArr[i].expressInstall;
 578+ att.width = obj.getAttribute("width") || "0";
 579+ att.height = obj.getAttribute("height") || "0";
 580+ if (obj.getAttribute("class")) {
 581+ att.styleclass = obj.getAttribute("class");
 582+ }
 583+ if (obj.getAttribute("align")) {
 584+ att.align = obj.getAttribute("align");
 585+ }
 586+ // parse HTML object param element's name-value
 587+ // pairs
 588+ var par = {};
 589+ var p = obj.getElementsByTagName("param");
 590+ var pl = p.length;
 591+ for ( var j = 0; j < pl; j++) {
 592+ if (p[j].getAttribute("name").toLowerCase() != "movie") {
 593+ par[p[j].getAttribute("name")] = p[j]
 594+ .getAttribute("value");
 595+ }
 596+ }
 597+ showExpressInstall(att, par, id, cb);
 598+ } else { // Flash Player and SWF version mismatch or
 599+ // an older Webkit engine that ignores the
 600+ // HTML object element's nested param
 601+ // elements: display alternative content
 602+ // instead of SWF
 603+ displayAltContent(obj);
 604+ if (cb) {
 605+ cb(cbObj);
 606+ }
 607+ }
 608+ }
 609+ } else { // if no Flash Player is installed or the fp version
 610+ // cannot be detected we let the HTML object element
 611+ // do its job (either show a SWF or alternative
 612+ // content)
 613+ setVisibility(id, true);
 614+ if (cb) {
 615+ var o = getObjectById(id); // test whether there is an
 616+ // HTML object element or
 617+ // not
 618+ if (o && typeof o.SetVariable != UNDEF) {
 619+ cbObj.success = true;
 620+ cbObj.ref = o;
 621+ }
 622+ cb(cbObj);
 623+ }
 624+ }
 625+ }
 626+ }
 627+ }
 628+
 629+ function getObjectById(objectIdStr) {
 630+ var r = null;
 631+ var o = getElementById(objectIdStr);
 632+ if (o && o.nodeName == "OBJECT") {
 633+ if (typeof o.SetVariable != UNDEF) {
 634+ r = o;
 635+ } else {
 636+ var n = o.getElementsByTagName(OBJECT)[0];
 637+ if (n) {
 638+ r = n;
 639+ }
 640+ }
 641+ }
 642+ return r;
 643+ }
 644+
 645+ /*
 646+ * Requirements for Adobe Express Install - only one instance can be active
 647+ * at a time - fp 6.0.65 or higher - Win/Mac OS only - no Webkit engines
 648+ * older than version 312
 649+ */
 650+ function canExpressInstall() {
 651+ return !isExpressInstallActive && hasPlayerVersion("6.0.65")
 652+ && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
 653+ }
 654+
 655+ /*
 656+ * Show the Adobe Express Install dialog - Reference:
 657+ * http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
 658+ */
 659+ function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
 660+ isExpressInstallActive = true;
 661+ storedCallbackFn = callbackFn || null;
 662+ storedCallbackObj = {
 663+ success : false,
 664+ id : replaceElemIdStr
 665+ };
 666+ var obj = getElementById(replaceElemIdStr);
 667+ if (obj) {
 668+ if (obj.nodeName == "OBJECT") { // static publishing
 669+ storedAltContent = abstractAltContent(obj);
 670+ storedAltContentId = null;
 671+ } else { // dynamic publishing
 672+ storedAltContent = obj;
 673+ storedAltContentId = replaceElemIdStr;
 674+ }
 675+ att.id = EXPRESS_INSTALL_ID;
 676+ if (typeof att.width == UNDEF
 677+ || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) {
 678+ att.width = "310";
 679+ }
 680+ if (typeof att.height == UNDEF
 681+ || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) {
 682+ att.height = "137";
 683+ }
 684+ doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
 685+ var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn", fv = "MMredirectURL="
 686+ + win.location.toString().replace(/&/g, "%26")
 687+ + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
 688+ if (typeof par.flashvars != UNDEF) {
 689+ par.flashvars += "&" + fv;
 690+ } else {
 691+ par.flashvars = fv;
 692+ }
 693+ // IE only: when a SWF is loading (AND: not available in cache) wait
 694+ // for the readyState of the object element to become 4 before
 695+ // removing it,
 696+ // because you cannot properly cancel a loading SWF file without
 697+ // breaking browser load references, also obj.onreadystatechange
 698+ // doesn't work
 699+ if (ua.ie && ua.win && obj.readyState != 4) {
 700+ var newObj = createElement("div");
 701+ replaceElemIdStr += "SWFObjectNew";
 702+ newObj.setAttribute("id", replaceElemIdStr);
 703+ obj.parentNode.insertBefore(newObj, obj); // insert
 704+ // placeholder div
 705+ // that will be
 706+ // replaced by the
 707+ // object element
 708+ // that loads
 709+ // expressinstall.swf
 710+ obj.style.display = "none";
 711+ (function() {
 712+ if (obj.readyState == 4) {
 713+ obj.parentNode.removeChild(obj);
 714+ } else {
 715+ setTimeout(arguments.callee, 10);
 716+ }
 717+ })();
 718+ }
 719+ createSWF(att, par, replaceElemIdStr);
 720+ }
 721+ }
 722+
 723+ /*
 724+ * Functions to abstract and display alternative content
 725+ */
 726+ function displayAltContent(obj) {
 727+ if (ua.ie && ua.win && obj.readyState != 4) {
 728+ // IE only: when a SWF is loading (AND: not available in cache) wait
 729+ // for the readyState of the object element to become 4 before
 730+ // removing it,
 731+ // because you cannot properly cancel a loading SWF file without
 732+ // breaking browser load references, also obj.onreadystatechange
 733+ // doesn't work
 734+ var el = createElement("div");
 735+ obj.parentNode.insertBefore(el, obj); // insert placeholder div
 736+ // that will be replaced by
 737+ // the alternative content
 738+ el.parentNode.replaceChild(abstractAltContent(obj), el);
 739+ obj.style.display = "none";
 740+ (function() {
 741+ if (obj.readyState == 4) {
 742+ obj.parentNode.removeChild(obj);
 743+ } else {
 744+ setTimeout(arguments.callee, 10);
 745+ }
 746+ })();
 747+ } else {
 748+ obj.parentNode.replaceChild(abstractAltContent(obj), obj);
 749+ }
 750+ }
 751+
 752+ function abstractAltContent(obj) {
 753+ var ac = createElement("div");
 754+ if (ua.win && ua.ie) {
 755+ ac.innerHTML = obj.innerHTML;
 756+ } else {
 757+ var nestedObj = obj.getElementsByTagName(OBJECT)[0];
 758+ if (nestedObj) {
 759+ var c = nestedObj.childNodes;
 760+ if (c) {
 761+ var cl = c.length;
 762+ for ( var i = 0; i < cl; i++) {
 763+ if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM")
 764+ && !(c[i].nodeType == 8)) {
 765+ ac.appendChild(c[i].cloneNode(true));
 766+ }
 767+ }
 768+ }
 769+ }
 770+ }
 771+ return ac;
 772+ }
 773+
 774+ /*
 775+ * Cross-browser dynamic SWF creation
 776+ */
 777+ function createSWF(attObj, parObj, id) {
 778+ var r, el = getElementById(id);
 779+ if (ua.wk && ua.wk < 312) {
 780+ return r;
 781+ }
 782+ if (el) {
 783+ if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the
 784+ // object element, it will
 785+ // inherit the 'id' from the
 786+ // alternative content
 787+ attObj.id = id;
 788+ }
 789+ if (ua.ie && ua.win) { // Internet Explorer + the HTML object
 790+ // element + W3C DOM methods do not combine:
 791+ // fall back to outerHTML
 792+ var att = "";
 793+ for ( var i in attObj) {
 794+ if (attObj[i] != Object.prototype[i]) { // filter out
 795+ // prototype
 796+ // additions from
 797+ // other potential
 798+ // libraries
 799+ if (i.toLowerCase() == "data") {
 800+ parObj.movie = attObj[i];
 801+ } else if (i.toLowerCase() == "styleclass") { // 'class'
 802+ // is an
 803+ // ECMA4
 804+ // reserved
 805+ // keyword
 806+ att += ' class="' + attObj[i] + '"';
 807+ } else if (i.toLowerCase() != "classid") {
 808+ att += ' ' + i + '="' + attObj[i] + '"';
 809+ }
 810+ }
 811+ }
 812+ var par = "";
 813+ for ( var j in parObj) {
 814+ if (parObj[j] != Object.prototype[j]) { // filter out
 815+ // prototype
 816+ // additions from
 817+ // other potential
 818+ // libraries
 819+ par += '<param name="' + j + '" value="' + parObj[j]
 820+ + '" />';
 821+ }
 822+ }
 823+ el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'
 824+ + att + '>' + par + '</object>';
 825+ objIdArr[objIdArr.length] = attObj.id; // stored to fix object
 826+ // 'leaks' on unload
 827+ // (dynamic publishing
 828+ // only)
 829+ r = getElementById(attObj.id);
 830+ } else { // well-behaving browsers
 831+ var o = createElement(OBJECT);
 832+ o.setAttribute("type", FLASH_MIME_TYPE);
 833+ for ( var m in attObj) {
 834+ if (attObj[m] != Object.prototype[m]) { // filter out
 835+ // prototype
 836+ // additions from
 837+ // other potential
 838+ // libraries
 839+ if (m.toLowerCase() == "styleclass") { // 'class' is an
 840+ // ECMA4
 841+ // reserved
 842+ // keyword
 843+ o.setAttribute("class", attObj[m]);
 844+ } else if (m.toLowerCase() != "classid") { // filter
 845+ // out IE
 846+ // specific
 847+ // attribute
 848+ o.setAttribute(m, attObj[m]);
 849+ }
 850+ }
 851+ }
 852+ for ( var n in parObj) {
 853+ if (parObj[n] != Object.prototype[n]
 854+ && n.toLowerCase() != "movie") { // filter out
 855+ // prototype
 856+ // additions
 857+ // from other
 858+ // potential
 859+ // libraries and
 860+ // IE specific
 861+ // param element
 862+ createObjParam(o, n, parObj[n]);
 863+ }
 864+ }
 865+ el.parentNode.replaceChild(o, el);
 866+ r = o;
 867+ }
 868+ }
 869+ return r;
 870+ }
 871+
 872+ function createObjParam(el, pName, pValue) {
 873+ var p = createElement("param");
 874+ p.setAttribute("name", pName);
 875+ p.setAttribute("value", pValue);
 876+ el.appendChild(p);
 877+ }
 878+
 879+ /*
 880+ * Cross-browser SWF removal - Especially needed to safely and completely
 881+ * remove a SWF in Internet Explorer
 882+ */
 883+ function removeSWF(id) {
 884+ var obj = getElementById(id);
 885+ if (obj && obj.nodeName == "OBJECT") {
 886+ if (ua.ie && ua.win) {
 887+ obj.style.display = "none";
 888+ (function() {
 889+ if (obj.readyState == 4) {
 890+ removeObjectInIE(id);
 891+ } else {
 892+ setTimeout(arguments.callee, 10);
 893+ }
 894+ })();
 895+ } else {
 896+ obj.parentNode.removeChild(obj);
 897+ }
 898+ }
 899+ }
 900+
 901+ function removeObjectInIE(id) {
 902+ var obj = getElementById(id);
 903+ if (obj) {
 904+ for ( var i in obj) {
 905+ if (typeof obj[i] == "function") {
 906+ obj[i] = null;
 907+ }
 908+ }
 909+ obj.parentNode.removeChild(obj);
 910+ }
 911+ }
 912+
 913+ /*
 914+ * Functions to optimize JavaScript compression
 915+ */
 916+ function getElementById(id) {
 917+ var el = null;
 918+ try {
 919+ el = doc.getElementById(id);
 920+ } catch (e) {
 921+ }
 922+ return el;
 923+ }
 924+
 925+ function createElement(el) {
 926+ return doc.createElement(el);
 927+ }
 928+
 929+ /*
 930+ * Updated attachEvent function for Internet Explorer - Stores attachEvent
 931+ * information in an Array, so on unload the detachEvent functions can be
 932+ * called to avoid memory leaks
 933+ */
 934+ function addListener(target, eventType, fn) {
 935+ target.attachEvent(eventType, fn);
 936+ listenersArr[listenersArr.length] = [ target, eventType, fn ];
 937+ }
 938+
 939+ /*
 940+ * Flash Player and SWF content version matching
 941+ */
 942+ function hasPlayerVersion(rv) {
 943+ var pv = ua.pv, v = rv.split(".");
 944+ v[0] = parseInt(v[0], 10);
 945+ v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9"
 946+ // instead of "9.0.0"
 947+ v[2] = parseInt(v[2], 10) || 0;
 948+ return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0]
 949+ && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
 950+ }
 951+
 952+ /*
 953+ * Cross-browser dynamic CSS creation - Based on Bobby van der Sluis'
 954+ * solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
 955+ */
 956+ function createCSS(sel, decl, media, newStyle) {
 957+ if (ua.ie && ua.mac) {
 958+ return;
 959+ }
 960+ var h = doc.getElementsByTagName("head")[0];
 961+ if (!h) {
 962+ return;
 963+ } // to also support badly authored HTML pages that lack a head
 964+ // element
 965+ var m = (media && typeof media == "string") ? media : "screen";
 966+ if (newStyle) {
 967+ dynamicStylesheet = null;
 968+ dynamicStylesheetMedia = null;
 969+ }
 970+ if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
 971+ // create dynamic stylesheet + get a global reference to it
 972+ var s = createElement("style");
 973+ s.setAttribute("type", "text/css");
 974+ s.setAttribute("media", m);
 975+ dynamicStylesheet = h.appendChild(s);
 976+ if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF
 977+ && doc.styleSheets.length > 0) {
 978+ dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
 979+ }
 980+ dynamicStylesheetMedia = m;
 981+ }
 982+ // add style rule
 983+ if (ua.ie && ua.win) {
 984+ if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
 985+ dynamicStylesheet.addRule(sel, decl);
 986+ }
 987+ } else {
 988+ if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
 989+ dynamicStylesheet.appendChild(doc.createTextNode(sel + " {"
 990+ + decl + "}"));
 991+ }
 992+ }
 993+ }
 994+
 995+ function setVisibility(id, isVisible) {
 996+ if (!autoHideShow) {
 997+ return;
 998+ }
 999+ var v = isVisible ? "visible" : "hidden";
 1000+ if (isDomLoaded && getElementById(id)) {
 1001+ getElementById(id).style.visibility = v;
 1002+ } else {
 1003+ createCSS("#" + id, "visibility:" + v);
 1004+ }
 1005+ }
 1006+
 1007+ /*
 1008+ * Filter to avoid XSS attacks
 1009+ */
 1010+ function urlEncodeIfNecessary(s) {
 1011+ var regex = /[\\\"<>\.;]/;
 1012+ var hasBadChars = regex.exec(s) != null;
 1013+ return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s)
 1014+ : s;
 1015+ }
 1016+
 1017+ /*
 1018+ * Release memory to avoid memory leaks caused by closures, fix hanging
 1019+ * audio/video threads and force open sockets/NetConnections to disconnect
 1020+ * (Internet Explorer only)
 1021+ */
 1022+ var cleanup = function() {
 1023+ if (ua.ie && ua.win) {
 1024+ window.attachEvent("onunload", function() {
 1025+ // remove listeners to avoid memory leaks
 1026+ var ll = listenersArr.length;
 1027+ for ( var i = 0; i < ll; i++) {
 1028+ listenersArr[i][0].detachEvent(listenersArr[i][1],
 1029+ listenersArr[i][2]);
 1030+ }
 1031+ // cleanup dynamically embedded objects to fix audio/video
 1032+ // threads and force open sockets and NetConnections to
 1033+ // disconnect
 1034+ var il = objIdArr.length;
 1035+ for ( var j = 0; j < il; j++) {
 1036+ removeSWF(objIdArr[j]);
 1037+ }
 1038+ // cleanup library's main closures to avoid memory leaks
 1039+ for ( var k in ua) {
 1040+ ua[k] = null;
 1041+ }
 1042+ ua = null;
 1043+ for ( var l in swfobject) {
 1044+ swfobject[l] = null;
 1045+ }
 1046+ swfobject = null;
 1047+ });
 1048+ }
 1049+ }();
 1050+
 1051+ return {
 1052+ /*
 1053+ * Public API - Reference:
 1054+ * http://code.google.com/p/swfobject/wiki/documentation
 1055+ */
 1056+ registerObject : function(objectIdStr, swfVersionStr, xiSwfUrlStr,
 1057+ callbackFn) {
 1058+ if (ua.w3 && objectIdStr && swfVersionStr) {
 1059+ var regObj = {};
 1060+ regObj.id = objectIdStr;
 1061+ regObj.swfVersion = swfVersionStr;
 1062+ regObj.expressInstall = xiSwfUrlStr;
 1063+ regObj.callbackFn = callbackFn;
 1064+ regObjArr[regObjArr.length] = regObj;
 1065+ setVisibility(objectIdStr, false);
 1066+ } else if (callbackFn) {
 1067+ callbackFn( {
 1068+ success : false,
 1069+ id : objectIdStr
 1070+ });
 1071+ }
 1072+ },
 1073+
 1074+ getObjectById : function(objectIdStr) {
 1075+ if (ua.w3) {
 1076+ return getObjectById(objectIdStr);
 1077+ }
 1078+ },
 1079+ // XXX added by mdale ( since we know the dom is ready,
 1080+ // this works better.
 1081+ callDomLoadFunctions : function() {
 1082+ callDomLoadFunctions();
 1083+ },
 1084+
 1085+ embedSWF : function(swfUrlStr, replaceElemIdStr, widthStr, heightStr,
 1086+ swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj,
 1087+ callbackFn) {
 1088+ var callbackObj = {
 1089+ success : false,
 1090+ id : replaceElemIdStr
 1091+ };
 1092+ if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr
 1093+ && replaceElemIdStr && widthStr && heightStr
 1094+ && swfVersionStr) {
 1095+ setVisibility(replaceElemIdStr, false);
 1096+ addDomLoadEvent(function() {
 1097+ widthStr += ""; // auto-convert to string
 1098+ heightStr += "";
 1099+ var att = {};
 1100+ if (attObj && typeof attObj === OBJECT) {
 1101+ for ( var i in attObj) { // copy object to avoid the
 1102+ // use of references,
 1103+ // because web authors often
 1104+ // reuse attObj for multiple
 1105+ // SWFs
 1106+ att[i] = attObj[i];
 1107+ }
 1108+ }
 1109+ att.data = swfUrlStr;
 1110+ att.width = widthStr;
 1111+ att.height = heightStr;
 1112+ var par = {};
 1113+ if (parObj && typeof parObj === OBJECT) {
 1114+ for ( var j in parObj) { // copy object to avoid the
 1115+ // use of references,
 1116+ // because web authors often
 1117+ // reuse parObj for multiple
 1118+ // SWFs
 1119+ par[j] = parObj[j];
 1120+ }
 1121+ }
 1122+ if (flashvarsObj && typeof flashvarsObj === OBJECT) {
 1123+ for ( var k in flashvarsObj) { // copy object to avoid
 1124+ // the use of
 1125+ // references, because
 1126+ // web authors often
 1127+ // reuse flashvarsObj
 1128+ // for multiple SWFs
 1129+ if (typeof par.flashvars != UNDEF) {
 1130+ par.flashvars += "&" + k + "="
 1131+ + flashvarsObj[k];
 1132+ } else {
 1133+ par.flashvars = k + "=" + flashvarsObj[k];
 1134+ }
 1135+ }
 1136+ }
 1137+ if (hasPlayerVersion(swfVersionStr)) { // create SWF
 1138+ var obj = createSWF(att, par, replaceElemIdStr);
 1139+ if (att.id == replaceElemIdStr) {
 1140+ setVisibility(replaceElemIdStr, true);
 1141+ }
 1142+ callbackObj.success = true;
 1143+ callbackObj.ref = obj;
 1144+ } else if (xiSwfUrlStr && canExpressInstall()) { // show
 1145+ // Adobe
 1146+ // Express
 1147+ // Install
 1148+ att.data = xiSwfUrlStr;
 1149+ showExpressInstall(att, par, replaceElemIdStr,
 1150+ callbackFn);
 1151+ return;
 1152+ } else { // show alternative content
 1153+ setVisibility(replaceElemIdStr, true);
 1154+ }
 1155+ if (callbackFn) {
 1156+ callbackFn(callbackObj);
 1157+ }
 1158+ });
 1159+ } else if (callbackFn) {
 1160+ callbackFn(callbackObj);
 1161+ }
 1162+ },
 1163+
 1164+ switchOffAutoHideShow : function() {
 1165+ autoHideShow = false;
 1166+ },
 1167+
 1168+ ua : ua,
 1169+
 1170+ getFlashPlayerVersion : function() {
 1171+ return {
 1172+ major : ua.pv[0],
 1173+ minor : ua.pv[1],
 1174+ release : ua.pv[2]
 1175+ };
 1176+ },
 1177+
 1178+ hasFlashPlayerVersion : hasPlayerVersion,
 1179+
 1180+ createSWF : function(attObj, parObj, replaceElemIdStr) {
 1181+ if (ua.w3) {
 1182+ return createSWF(attObj, parObj, replaceElemIdStr);
 1183+ } else {
 1184+ return undefined;
 1185+ }
 1186+ },
 1187+
 1188+ showExpressInstall : function(att, par, replaceElemIdStr, callbackFn) {
 1189+ if (ua.w3 && canExpressInstall()) {
 1190+ showExpressInstall(att, par, replaceElemIdStr, callbackFn);
 1191+ }
 1192+ },
 1193+
 1194+ removeSWF : function(objElemIdStr) {
 1195+ if (ua.w3) {
 1196+ removeSWF(objElemIdStr);
 1197+ }
 1198+ },
 1199+
 1200+ createCSS : function(selStr, declStr, mediaStr, newStyleBoolean) {
 1201+ if (ua.w3) {
 1202+ createCSS(selStr, declStr, mediaStr, newStyleBoolean);
 1203+ }
 1204+ },
 1205+
 1206+ addDomLoadEvent : addDomLoadEvent,
 1207+
 1208+ addLoadEvent : addLoadEvent,
 1209+
 1210+ getQueryParamValue : function(param) {
 1211+ var q = doc.location.search || doc.location.hash;
 1212+ if (q) {
 1213+ if (/\?/.test(q)) {
 1214+ q = q.split("?")[1];
 1215+ } // strip question mark
 1216+ if (param == null) {
 1217+ return urlEncodeIfNecessary(q);
 1218+ }
 1219+ var pairs = q.split("&");
 1220+ for ( var i = 0; i < pairs.length; i++) {
 1221+ if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
 1222+ return urlEncodeIfNecessary(pairs[i].substring((pairs[i]
 1223+ .indexOf("=") + 1)));
 1224+ }
 1225+ }
 1226+ }
 1227+ return "";
 1228+},
 1229+
 1230+// For internal usage only
 1231+ expressInstallCallback : function() {
 1232+ if (isExpressInstallActive) {
 1233+ var obj = getElementById(EXPRESS_INSTALL_ID);
 1234+ if (obj && storedAltContent) {
 1235+ obj.parentNode.replaceChild(storedAltContent, obj);
 1236+ if (storedAltContentId) {
 1237+ setVisibility(storedAltContentId, true);
 1238+ if (ua.ie && ua.win) {
 1239+ storedAltContent.style.display = "block";
 1240+ }
 1241+ }
 1242+ if (storedCallbackFn) {
 1243+ storedCallbackFn(storedCallbackObj);
 1244+ }
 1245+ }
 1246+ isExpressInstallActive = false;
 1247+ }
 1248+ }
 1249+ };
 1250+}();
\ No newline at end of file
Property changes on: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/players/mw.EmbedPlayerKplayer.js
___________________________________________________________________
Added: svn:mergeinfo
11251 Merged /branches/REL1_15/phase3/js2/mwEmbed/libEmbedVideo/kplayerEmbed.js:r51646
21252 Merged /branches/sqlite/js2/mwEmbed/libEmbedVideo/kplayerEmbed.js:r58211-58321
Added: svn:eol-style
31253 + native
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/EmbedPlayer.config.php
@@ -4,7 +4,7 @@
55 * $wgMwEmbedModuleConfig[ {ModuleName} ][ {configuration name} ] = value; format
66 */
77
8 - return array(
 8+ return array (
99 // If the player controls should be overlaid on top of the video ( if supported by playback method)
1010 // can be set to false per embed player via overlayControls attribute
1111 'EmbedPlayer.OverlayControls' => true,
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/EmbedPlayer.loader.js
@@ -10,10 +10,10 @@
1111 */
1212 $( mw ).bind( 'SetupInterface', function( event, callback ){
1313
14 - // Allow modules to do tag rewrites as well:
 14+ // Allow modules to extend EmbedPlayerRewritePlayerTags rewrites as well:
1515 var doModuleTagRewrites = function(){
16 - $(mw).triggerQueueCallback( 'LoadeRewritePlayerTags', callback );
17 - }
 16+ $( mw ).triggerQueueCallback( 'EmbedPlayerRewritePlayerTags', callback );
 17+ };
1818 // Check if we have tags to rewrite:
1919 if( $( mw.getConfig( 'EmbedPlayer.RewriteTags' ) ).length ) {
2020 var rewriteElementCount = 0;
@@ -70,14 +70,14 @@
7171 mw.getConfig( 'EmbedPlayer.IframeParentUrl' )
7272 ){
7373 dependencySet.push('mw.EmbedPlayerNative');
74 - dependencySet.push('$.postMessage');
 74+ dependencySet.push('jquery.postMessage');
7575 dependencySet.push('mw.IFramePlayerApiServer');
7676 }
7777
7878 // Allow modules to update the set of dependencies:
7979 var rewriteElementCount = 0;
80 - $.each( playerSelect, function(inx, playerElement){
81 -
 80+ $( playerSelect).each( function(inx, playerElement){
 81+
8282 // Assign an the element an ID ( if its missing one )
8383 if ( $( playerElement ).attr( "id" ) == '' ) {
8484 $( playerElement ).attr( "id", 'v' + ( rewriteElementCount++ ) );
@@ -86,7 +86,7 @@
8787 // Add an overlay loader
8888 $( playerElement )
8989 .getAbsoluteOverlaySpinner()
90 - .attr('id', 'loadingSpinner_' + $( element ).attr('id') )
 90+ .attr('id', 'loadingSpinner_' + $( playerElement ).attr('id') )
9191 .addClass( 'playerLoadingSpinner' );
9292
9393 // Add core "skin/interface" loader
@@ -107,7 +107,12 @@
108108
109109 // Do the request and process the playerElements with updated dependency set
110110 mediaWiki.loader.using( dependencySet, function(){
 111+ setTimeout( function(){
 112+ var cat = mw.processEmbedPlayers;
 113+ debugger;
 114+ }, 300);
111115 // EmbedPlayer should be ready:
 116+ //mw.processEmbedPlayers( playerSelect, readyCallback );
112117 });
113118 };
114119
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/iframeApi/mw.IFramePlayerApiClient.js
@@ -0,0 +1,192 @@
 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+ 'exportedMethods': [
 15+ 'play',
 16+ 'pause'
 17+ ],
 18+ // Local store of the previous sate of player proxy
 19+ '_prevPlayerProxy': {},
 20+
 21+ // Stores the current playerProxy ( can be updated by user js )
 22+ 'init': function( iframe , playerProxy, options ){
 23+ this.iframe = iframe;
 24+ this.playerProxy = playerProxy;
 25+ // Set the iframe server
 26+ var srcParts = mw.parseUri( mw.absoluteUrl( $j(this.iframe).attr('src') ) );
 27+ this.iframeServer = srcParts.protocol + '://' + srcParts.authority;
 28+
 29+ this.addPlayerSendApi();
 30+ this.addPlayerReciveApi();
 31+
 32+ this.addIframeFullscreenBinding();
 33+ },
 34+ 'addPlayerSendApi': function(){
 35+ var _this = this;
 36+
 37+ // Allow modules to extend the list of iframeExported bindings
 38+ $j( mw ).trigger( 'AddIframePlayerMethods', [ this.exportedMethods ]);
 39+
 40+ $j.each( this.exportedMethods, function(na, method){
 41+ _this.playerProxy[ method ] = function(){
 42+ _this.postMessage( {
 43+ 'method' : method,
 44+ 'args' : $j.makeArray( arguments )
 45+ } );
 46+ };
 47+ });
 48+ },
 49+ 'addPlayerReciveApi': function(){
 50+ var _this = this;
 51+ $j.receiveMessage( function( event ){
 52+ _this.hanldeReciveMsg( event )
 53+ }, this.iframeServer);
 54+ },
 55+ 'addIframeFullscreenBinding': function(){
 56+ var _this = this;
 57+ parentsAbsoluteList = [];
 58+ var fullscreenMode = false;
 59+ var orgSize = {
 60+ 'width' : $j( _this.iframe ).width(),
 61+ 'height' : $j( _this.iframe ).height(),
 62+ 'position' : null
 63+ }
 64+
 65+ var doFullscreen = function(){
 66+ // Make the iframe fullscreen
 67+ $j( _this.iframe ).css({
 68+ 'z-index': mw.getConfig( 'EmbedPlayer.fullScreenZIndex' ) + 1,
 69+ 'position': 'absolute',
 70+ 'top' : 0,
 71+ 'left' : 0,
 72+ 'width' : $j(window).width(),
 73+ 'height' : $j(window).height()
 74+ })
 75+
 76+ // Remove absolute css of the interface parents
 77+ $j( _this.iframe ).parents().each( function() {
 78+ //mw.log(' parent : ' + $j( this ).attr('id' ) + ' class: ' + $j( this ).attr('class') + ' pos: ' + $j( this ).css( 'position' ) );
 79+ if( $j( this ).css( 'position' ) == 'absolute' ) {
 80+ parentsAbsoluteList.push( $j( this ) );
 81+ $j( this ).css( 'position', null );
 82+ }
 83+ } );
 84+ }
 85+ var restoreWindowMode = function(){
 86+ $j( _this.iframe ).css( orgSize );
 87+ // restore any parent absolute pos:
 88+ $j(parentsAbsoluteList).each( function() {
 89+ $j( this ).css( 'position', 'absolute' );
 90+ } );
 91+ };
 92+
 93+ $j( this.playerProxy ).bind( 'onOpenFullScreen', doFullscreen);
 94+ $j( this.playerProxy ).bind( 'onCloseFullScreen', restoreWindowMode);
 95+
 96+ },
 97+ /**
 98+ * Handle received events
 99+ */
 100+ 'hanldeReciveMsg': function( event ){
 101+ var _this = this;
 102+
 103+ // Decode the message
 104+ var msgObject = JSON.parse( event.data );
 105+ var playerAttributes = mw.getConfig( 'EmbedPlayer.Attributes' );
 106+
 107+ // Before we update local attributes check that the object has not been updated by user js
 108+ for( var attrName in playerAttributes ){
 109+ if( attrName != 'id' ){
 110+ if( _this._prevPlayerProxy[ attrName ] != _this.playerProxy[ attrName ] ){
 111+ //mw.log( "IFramePlayerApiClient:: User js update:" + attrName + ' set to: ' + this.playerProxy[ attrName ] + ' != old: ' + _this._prevPlayerProxy[ attrName ] );
 112+ // Send the updated attribute back to the iframe:
 113+ _this.postMessage({
 114+ 'attrName' : attrName,
 115+ 'attrValue' : _this.playerProxy[ attrName ]
 116+ });
 117+ }
 118+ }
 119+ }
 120+ // Update any attributes
 121+ if( msgObject.attributes ){
 122+ for( var i in msgObject.attributes ){
 123+ if( i != 'id' && i != 'class' && i != 'style' ){
 124+ try{
 125+ this.playerProxy[ i ] = msgObject.attributes[i];
 126+ this._prevPlayerProxy[i] = msgObject.attributes[i];
 127+ } catch( e ){
 128+ mw.log("Error could not set:" + i );
 129+ }
 130+ }
 131+ }
 132+ }
 133+ // Trigger any binding events
 134+ if( typeof msgObject.triggerName != 'undefined' && msgObject.triggerArgs != 'undefined') {
 135+ //mw.log('IFramePlayerApiClient:: trigger: ' + msgObject.triggerName );
 136+ $j( _this.playerProxy ).trigger( msgObject.triggerName, msgObject.triggerArgs );
 137+ }
 138+ },
 139+ 'postMessage': function( msgObject ){
 140+ /*mw.log( "IFramePlayerApiClient:: postMessage(): " + JSON.stringify( msgObject ) +
 141+ ' iframe: ' + this.iframe + ' cw:' + this.iframe.contentWindow +
 142+ ' src: ' + mw.absoluteUrl( $j( this.iframe ).attr('src') ) );*/
 143+ $j.postMessage(
 144+ JSON.stringify( msgObject ),
 145+ mw.absoluteUrl( $j( this.iframe ).attr('src') ),
 146+ this.iframe.contentWindow
 147+ );
 148+ }
 149+};
 150+
 151+//Add the jQuery binding
 152+( function( $ ) {
 153+ $.fn.iFramePlayer = function( readyCallback ){
 154+ if( ! this.selector ){
 155+ this.selector = $j( this ).get(0);
 156+ }
 157+ // Append '_ifp' ( iframe player ) to id of real iframe so that 'id', and 'src' attributes don't conflict
 158+ var originalIframeId = ( $( this.selector ).attr( 'id' ) )? $( this.selector ).attr( 'id' ) : Math.floor( 9999999 * Math.random() );
 159+
 160+ var iframePlayerId = originalIframeId + '_ifp' ;
 161+
 162+ // Append the div element proxy after the iframe
 163+ $j( this.selector )
 164+ .attr('id', iframePlayerId)
 165+ .after(
 166+ $('<div />')
 167+ .attr( 'id', originalIframeId )
 168+ );
 169+
 170+ var playerProxy = $j( '#' + originalIframeId ).get(0);
 171+ var iframe = $j('#' + iframePlayerId).get(0);
 172+ if(!iframe){
 173+ mw.log("Error invalide iFramePlayer request");
 174+ return false;
 175+ }
 176+ if( !iframe['playerApi'] ){
 177+ iframe['playerApi'] = new mw.IFramePlayerApiClient( iframe, playerProxy );
 178+ }
 179+
 180+ // Allow modules to extend the 'iframe' based player
 181+ $j( mw ).trigger( 'newIframePlayerClientSide', [ playerProxy ]);
 182+
 183+ // Bind the iFrame player ready callback
 184+ if( readyCallback ){
 185+ $j( playerProxy ).bind( 'playerReady', readyCallback )
 186+ };
 187+
 188+ // Return the player proxy for chaining player events / attributes
 189+ return $j( playerProxy );
 190+ };
 191+} )( jQuery );
 192+
 193+} )( window.mw );
\ No newline at end of file
Property changes on: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/iframeApi/mw.IFramePlayerApiClient.js
___________________________________________________________________
Added: svn:mime-type
1194 + text/plain
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/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 newEmbedPlayers:
 22+$j( mw ).bind( 'newEmbedPlayerEvent', 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+ $j( mw ).trigger( 'AddIframePlayerBindings', [ this.exportedBindings ]);
 59+
 60+ this.addIframeListener();
 61+ this.addIframeSender();
 62+ $j( 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+ $j( this.embedPlayer ).bind( 'playerReady', function(){
 93+ _this.sendPlayerAttributes();
 94+ });
 95+ // On monitor event package the attributes for cross domain delivery:
 96+ $j( this.embedPlayer ).bind( 'monitorEvent', function(){
 97+ _this.sendPlayerAttributes();
 98+ })
 99+
 100+ $j.each( this.exportedBindings, function( inx, bindName ){
 101+ $j( _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+ $j( 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: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/EmbedPlayer/iframeApi/mw.IFramePlayerApiServer.js
___________________________________________________________________
Added: svn:mime-type
1229 + text/plain
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/jquery/jquery.mwEmbedUtil.js
@@ -0,0 +1,145 @@
 2+/**
 3+ * mwEmbed jQuery utility functions that are too small for their own file
 4+ */
 5+
 6+( function( $ ) {
 7+
 8+ /**
 9+ * Set a given selector html to the loading spinner:
 10+ */
 11+ $.fn.loadingSpinner = function( ) {
 12+ if ( this ) {
 13+ $j( this ).html(
 14+ $j( '<div />' )
 15+ .addClass( "loadingSpinner" )
 16+ );
 17+ }
 18+ return this;
 19+ };
 20+ /**
 21+ * Add an absolute overlay spinner useful for cases where the
 22+ * element does not display child elements, ( images, video )
 23+ */
 24+ $.fn.getAbsoluteOverlaySpinner = function(){
 25+ var pos = $j( this ).offset();
 26+ var posLeft = ( $j( this ).width() ) ?
 27+ parseInt( pos.left + ( .5 * $j( this ).width() ) -16 ) :
 28+ pos.left + 30;
 29+
 30+ var posTop = ( $j( this ).height() ) ?
 31+ parseInt( pos.top + ( .5 * $j( this ).height() ) -16 ) :
 32+ pos.top + 30;
 33+
 34+ var $spinner = $j('<div />')
 35+ .loadingSpinner()
 36+ .css({
 37+ 'width' : 32,
 38+ 'height' : 32,
 39+ 'position': 'absolute',
 40+ 'top' : posTop + 'px',
 41+ 'left' : posLeft + 'px'
 42+ });
 43+ $j('body').append( $spinner );
 44+ return $spinner;
 45+ };
 46+
 47+
 48+ /**
 49+ * Shortcut to a themed button Should be depreciated for $.button
 50+ * bellow
 51+ */
 52+ $.btnHtml = function( msg, styleClass, iconId, opt ) {
 53+ if ( !opt )
 54+ opt = { };
 55+ var href = ( opt.href ) ? opt.href : '#';
 56+ var target_attr = ( opt.target ) ? ' target="' + opt.target + '" ' : '';
 57+ var style_attr = ( opt.style ) ? ' style="' + opt.style + '" ' : '';
 58+ return '<a href="' + href + '" ' + target_attr + style_attr +
 59+ ' class="ui-state-default ui-corner-all ui-icon_link ' +
 60+ styleClass + '"><span class="ui-icon ui-icon-' + iconId + '" ></span>' +
 61+ '<span class="btnText">' + msg + '</span></a>';
 62+ };
 63+
 64+ // Shortcut to generate a jQuery button
 65+ var mw_default_button_options = {
 66+ // The class name for the button link
 67+ 'class' : '',
 68+
 69+ // The style properties for the button link
 70+ 'style' : { },
 71+
 72+ // The text of the button link
 73+ 'text' : '',
 74+
 75+ // The icon id that precedes the button link:
 76+ 'icon' : 'carat-1-n'
 77+ };
 78+
 79+ $.button = function( options ) {
 80+ var options = $j.extend( {}, mw_default_button_options, options);
 81+
 82+ // Button:
 83+ var $button = $j('<a />')
 84+ .attr('href', '#')
 85+ .addClass( 'ui-state-default ui-corner-all ui-icon_link' );
 86+ // Add css if set:
 87+ if( options.css ) {
 88+ $button.css( options.css );
 89+ }
 90+
 91+ if( options['class'] ) {
 92+ $button.addClass( options['class'] );
 93+ }
 94+
 95+
 96+ // return the button:
 97+ $button.append(
 98+ $j('<span />').addClass( 'ui-icon ui-icon-' + options.icon ),
 99+ $j('<span />').addClass( 'btnText' )
 100+ .text( options.text )
 101+ )
 102+ .buttonHover(); // add buttonHover binding;
 103+ if( !options.text ){
 104+ $button.css('padding', '1em');
 105+ }
 106+ return $button;
 107+ };
 108+
 109+ // Shortcut to bind hover state
 110+ $.fn.buttonHover = function() {
 111+ $j( this ).hover(
 112+ function() {
 113+ $j( this ).addClass( 'ui-state-hover' );
 114+ },
 115+ function() {
 116+ $j( this ).removeClass( 'ui-state-hover' );
 117+ }
 118+ );
 119+ return this;
 120+ };
 121+
 122+ /**
 123+ * Resize a dialog to fit the window
 124+ *
 125+ * @param {Object}
 126+ * options horizontal and vertical space ( default 50 )
 127+ */
 128+ $.fn.dialogFitWindow = function( options ) {
 129+ var opt_default = { 'hspace':50, 'vspace':50 };
 130+ if ( !options )
 131+ var options = { };
 132+ options = $j.extend( opt_default, options );
 133+ $j( this.selector ).dialog( 'option', 'width', $j( window ).width() - options.hspace );
 134+ $j( this.selector ).dialog( 'option', 'height', $j( window ).height() - options.vspace );
 135+ $j( this.selector ).dialog( 'option', 'position', 'center' );
 136+ // update the child position: (some of this should be pushed
 137+ // up-stream via dialog config options
 138+ $j( this.selector + '~ .ui-dialog-buttonpane' ).css( {
 139+ 'position':'absolute',
 140+ 'left':'0px',
 141+ 'right':'0px',
 142+ 'bottom':'0px'
 143+ } );
 144+ };
 145+
 146+} )( jQuery );
\ No newline at end of file
Property changes on: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/jquery/jquery.mwEmbedUtil.js
___________________________________________________________________
Added: svn:mime-type
1147 + text/plain
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/jquery/jquery.triggerQueueCallback.js
@@ -69,4 +69,4 @@
7070 $( this ).trigger( triggerName, [ doCallbackCheck ] );
7171 }
7272 };
73 -} )( window.mw );
\ No newline at end of file
 73+} )( jQuery );
\ No newline at end of file
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/MwEmbedSupport.php
@@ -3,11 +3,12 @@
44 return array(
55 "mwEmbedSupport" => array(
66 'scripts' => array(
7 - "mwEmbedSupport.js",
 7+ "mwEmbedSupport.js",
88 ),
99 'dependencies' => array(
10 - // jQuery dependencies:
11 - 'jquery.triggerQueueCallback',
 10+ // jQuery dependencies:
 11+ 'jquery.triggerQueueCallback',
 12+ 'jquery.mwEmbedUtil',
1213 ),
1314 'messageFile' => 'MwEmbedSupport.i18n.php',
1415 ),
@@ -16,6 +17,7 @@
1718 'styles' => 'jquery.menu/jquery.menu.css'
1819 ),
1920 "jquery.triggerQueueCallback" => array( 'scripts'=> "jquery/jquery.triggerQueueCallback.js" ),
 21+ "jquery.mwEmbedUtil" => array( 'scripts' => "jquery/jquery.mwEmbedUtil.js" ),
2022 )
2123
2224 ?>
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/modules/MwEmbedSupport/mwEmbedSupport.js
@@ -17,9 +17,8 @@
1818 *
1919 * Libraries used include code license in headers
2020 *
21 - * @dependency
 21+ * @dependencies
2222 */
23 -alert('wtf');
2423
2524 ( function( mw, $ ) {
2625
@@ -38,17 +37,19 @@
3938 }
4039
4140 /**
42 - * Enables javascript to target a "interfaces ready" state.
 41+ * Enables javascript modules and pages to target a "interfaces ready" state.
4342 *
4443 * mw.ready is equivalent to calling:
4544 * $j(mw).bind( 'InterfacesReady', callback );
4645 *
4746 * This is different from jQuery(document).ready() ( jQuery ready is not
4847 * friendly with dynamic includes and not friendly with core interface
49 - * asynchronous build out. ) This allows an interface to do async calls and be globally ready
 48+ * asynchronous build out. ) This allows core interface components to do async conditional
 49+ * load calls, and trigger a ready event once the javascript interface build out is complete
5050 *
5151 * For example making <video> tags on the page have a video api even if the browser
52 - * does not support html5.
 52+ * does not support html5 requires dynamic loading that can only happen once the page dom is
 53+ * ready
5354 *
5455 * @param {Function}
5556 * callback Function to run once DOM and jQuery are ready
@@ -87,7 +88,7 @@
8889 mediaWiki.config.set( name, value );
8990 };
9091 mw.getConfig = function( name, value ){
91 - mediaWiki.config.get( name, value );
 92+ return mediaWiki.config.get( name, value );
9293 };
9394 mw.setDefaultConfig = function( name, value ){
9495 if( ! mediaWiki.config.get( name ) ){
@@ -98,6 +99,13 @@
99100 mediaWiki.using( resources, callback, function(){
100101 // failed to load
101102 });
 103+ };
 104+
 105+ /**
 106+ * legacy support to get the mwEmbed resource path:
 107+ */
 108+ mw.getMwEmbedPath = function(){
 109+ return mediaWiki.config.get( 'wgLoadScript' ).replace('load.php', '');
102110 };
103111
104112 /**
@@ -110,23 +118,25 @@
111119 });
112120 return ;
113121 }
114 - // Check if we should "merge" the config
115 - if( typeof value == 'object' && typeof mw.getConfig( name ) == 'object' ) {
116 - if ( value.constructor.toString().indexOf("Array") != -1 &&
117 - mw.getConfig( name ).constructor.toString().indexOf("Array") != -1
118 - ){
119 - // merge in the array
120 - mw.setConfig( name, $j.merge( mw.getConfig( name ), value ) );
121 - } else {
122 - mw.setConfig( name, value );
123 - }
 122+ if( !mediaWiki.config.get( name )){
 123+ mw.setConfig( name, value );
124124 return ;
125125 }
126 - // else do a normal setConfig
127 - mw.setConfig( name, value );
 126+ if( typeof mediaWiki.config.get( name ) == 'object' ){
 127+ mw.setConfig( name, $.extend( mediaWiki.config.get( name ), value ) );
 128+ }
128129 };
129130
 131+
130132 /**
 133+ * gM ( get Message ) in js2 conflated jQuery return type with string return type
 134+ * Do a legacy check for input parameters and 'do the right thing'
 135+ */
 136+ window.gM = function( key, parameters ){
 137+
 138+ };
 139+
 140+ /**
131141 * Utility Functions
132142 *
133143 * TOOD some of these utility functions are used in the upload Wizard, break them out into
Index: branches/MwEmbedStandAloneRL1_17/MwEmbedStandAlone/resources/mediawiki/mediawiki.js
@@ -576,8 +576,8 @@
577577 var jobs = [];
578578 // Flag indicating that requests should be suspended
579579 var suspended = true;
580 - // Flag inidicating that document ready has occured
581 - var ready = false;
 580+ // Flag inidicating that document is ready
 581+ var documentReady = false;
582582 // Marker element for adding dynamic styles
583583 var $marker = $( 'head meta[name=ResourceLoaderDynamicStyles]' );
584584
@@ -614,7 +614,7 @@
615615 pad( d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds() ), 'Z'
616616 ].join( '' );
617617 }
618 -
 618+
619619 /**
620620 * Recursively resolves dependencies and detects circular references
621621 */
@@ -853,6 +853,47 @@
854854 }
855855 return sorted;
856856 }
 857+
 858+ /**
 859+ * Appends a set of scripts to the dom
 860+ * @param url {Mixed} Array or single url string
 861+ */
 862+ function appendScripts( url, callback, error ){
 863+ if( ! url )
 864+ return ;
 865+ if( $.isArray( url ) ){
 866+ var requestCount = 0;
 867+ $.each( url, function(inx, singleUrl ){
 868+ requestCount++;
 869+ appendScripts( singleUrl, function(){
 870+ requestCount--;
 871+ if( requestCount == 0 && typeof callback == 'function')
 872+ callback();
 873+ });
 874+ });
 875+ return ;
 876+ }
 877+
 878+ // Load asynchronously after document ready
 879+ if ( documentReady ) {
 880+ // Load without jQuery to avoid $.globalEval issue
 881+ var script = document.createElement( "script" );
 882+ script.setAttribute( 'src', url );
 883+ script.setAttribute( 'type', 'text/javascript' );
 884+ if( callback ){
 885+ script.onload = callback;
 886+ }
 887+ document.getElementsByTagName("body")[ 0 ].appendChild( script );
 888+ } else {
 889+ var script = mediaWiki.html.element( 'script',
 890+ { type: 'text/javascript', src: url }, '' );
 891+ document.write( script );
 892+ // document.write blocks script execution so we can directly run the callback:
 893+ if( callback ){
 894+ callback();
 895+ }
 896+ }
 897+ }
857898
858899 /* Public Methods */
859900
@@ -917,23 +958,16 @@
918959 // include modules which are already loaded
919960 batch = [];
920961 // Asynchronously append a script tag to the end of the body
921 - function request() {
922 - var html = '';
 962+ function getRequestUrs() {
 963+ var urls = [];
923964 for ( var r = 0; r < requests.length; r++ ) {
924965 requests[r] = sortQuery( requests[r] );
925966 // Build out the HTML
926 - var src = mediaWiki.config.get( 'wgLoadScript' ) + '?' + $.param( requests[r] );
927 - html += mediaWiki.html.element( 'script',
928 - { type: 'text/javascript', src: src }, '' );
 967+ urls.push( mediaWiki.config.get( 'wgLoadScript' ) + '?' + $.param( requests[r] ) );
929968 }
930 - return html;
 969+ return urls;
931970 }
932 - // Load asynchronously after doumument ready
933 - if ( ready ) {
934 - setTimeout( function() { $( 'body' ).append( request() ); }, 0 );
935 - } else {
936 - document.write( request() );
937 - }
 971+ appendScripts( getRequestUrs() );
938972 }
939973 };
940974
@@ -1097,13 +1131,7 @@
10981132 .attr( 'href', modules ) );
10991133 return true;
11001134 } else if ( type === 'text/javascript' || typeof type === 'undefined' ) {
1101 - var script = mediaWiki.html.element( 'script',
1102 - { type: 'text/javascript', src: modules }, '' );
1103 - if ( ready ) {
1104 - $( 'body' ).append( script );
1105 - } else {
1106 - document.write( script );
1107 - }
 1135+ appendScripts( modules );
11081136 return true;
11091137 }
11101138 // Unknown type
@@ -1170,7 +1198,7 @@
11711199
11721200 /* Cache document ready status */
11731201
1174 - $(document).ready( function() { ready = true; } );
 1202+ $(document).ready( function() { documentReady = true; } );
11751203 } )();
11761204
11771205 /** HTML construction helper functions */

Status & tagging log