From: Trevor Parscal Date: Fri, 24 Sep 2010 18:49:19 +0000 (+0000) Subject: * Moved registration generation to startup module. X-Git-Tag: 1.31.0-rc.0~34807 X-Git-Url: http://git.cyclocoop.org/%7B%24www_url%7Dadmin/membres/fiche.php?a=commitdiff_plain;h=09d3b2cf1e229dd33eb9893b56a56b851678fb2b;p=lhc%2Fweb%2Fwiklou.git * Moved registration generation to startup module. * Moved some javascript code generation to reusable functions (more to do). * Reduced the code output by using mutliple calling method for mediaWiki.loader.state. * Moved CSS minification to the end (should be a bit faster than running it for each module). --- diff --git a/includes/ResourceLoader.php b/includes/ResourceLoader.php index 17134d6e16..6da7ffacc3 100644 --- a/includes/ResourceLoader.php +++ b/includes/ResourceLoader.php @@ -53,7 +53,16 @@ class ResourceLoader { } } - protected static function preloadModuleInfo( $modules, ResourceLoaderContext $context ) { + /* + * Loads information stored in the database about modules + * + * This is not inside the module code because it's so much more performant to request all of the information at once + * than it is to have each module requests it's own information. + * + * @param $modules array list of modules to preload information for + * @param $context ResourceLoaderContext context to load the information within + */ + protected static function preloadModuleInfo( array $modules, ResourceLoaderContext $context ) { $dbr = wfGetDb( DB_SLAVE ); $skin = $context->getSkin(); $lang = $context->getLanguage(); @@ -107,8 +116,7 @@ class ResourceLoader { * * @param $filter String: name of filter to run * @param $data String: text to filter, such as JavaScript or CSS text - * @param $file String: path to file being filtered, (optional: only required - * for CSS to resolve paths) + * @param $file String: path to file being filtered, (optional: only required for CSS to resolve paths) * @return String: filtered data */ protected static function filter( $filter, $data ) { @@ -226,54 +234,6 @@ class ResourceLoader { return isset( self::$modules[$name] ) ? self::$modules[$name] : null; } - /** - * Gets registration code for all modules - * - * @param $context ResourceLoaderContext object - * @return String: JavaScript code for registering all modules with the client loader - */ - public static function getModuleRegistrations( ResourceLoaderContext $context ) { - wfProfileIn( __METHOD__ ); - self::initialize(); - - $scripts = ''; - $registrations = array(); - - foreach ( self::$modules as $name => $module ) { - // Support module loader scripts - if ( ( $loader = $module->getLoaderScript() ) !== false ) { - $deps = FormatJson::encode( $module->getDependencies() ); - $group = FormatJson::encode( $module->getGroup() ); - $version = wfTimestamp( TS_ISO_8601, round( $module->getModifiedTime( $context ), -2 ) ); - $scripts .= "( function( name, version, dependencies ) { $loader } )\n" . - "( '$name', '$version', $deps, $group );\n"; - } - // Automatically register module - else { - // Modules without dependencies or a group pass two arguments (name, timestamp) to - // mediaWiki.loader.register() - if ( !count( $module->getDependencies() && $module->getGroup() === null ) ) { - $registrations[] = array( $name, $module->getModifiedTime( $context ) ); - } - // Modules with dependencies but no group pass three arguments (name, timestamp, dependencies) - // to mediaWiki.loader.register() - else if ( $module->getGroup() === null ) { - $registrations[] = array( - $name, $module->getModifiedTime( $context ), $module->getDependencies() ); - } - // Modules with dependencies pass four arguments (name, timestamp, dependencies, group) - // to mediaWiki.loader.register() - else { - $registrations[] = array( - $name, $module->getModifiedTime( $context ), $module->getDependencies(), $module->getGroup() ); - } - } - } - $out = $scripts . "mediaWiki.loader.register( " . FormatJson::encode( $registrations ) . " );\n"; - wfProfileOut( __METHOD__ ); - return $out; - } - /** * Get the highest modification time of all modules, based on a given * combination of language code, skin name and debug mode flag. @@ -356,99 +316,125 @@ class ResourceLoader { return; } - // Use output buffering - ob_start(); - // Pre-fetch blobs $blobs = $context->shouldIncludeMessages() ? MessageBlobStore::get( $modules, $context->getLanguage() ) : array(); // Generate output + $out = ''; foreach ( $modules as $name ) { wfProfileIn( __METHOD__ . '-' . $name ); + // Scripts $scripts = ''; - if ( $context->shouldIncludeScripts() ) { $scripts .= self::$modules[$name]->getScript( $context ) . "\n"; } // Styles $styles = array(); - if ( - $context->shouldIncludeStyles() - && ( count( $styles = self::$modules[$name]->getStyles( $context ) ) ) + $context->shouldIncludeStyles() && + ( count( $styles = self::$modules[$name]->getStyles( $context ) ) ) ) { - foreach ( $styles as $media => $style ) { - if ( self::$modules[$name]->getFlip( $context ) ) { + // Flip CSS on a per-module basis + if ( self::$modules[$name]->getFlip( $context ) ) { + foreach ( $styles as $media => $style ) { $styles[$media] = self::filter( 'flip-css', $style ); } - if ( !$context->getDebug() ) { - $styles[$media] = self::filter( 'minify-css', $style ); - } } } // Messages $messages = isset( $blobs[$name] ) ? $blobs[$name] : '{}'; - // Output - if ( $context->getOnly() === 'styles' ) { - if ( $context->getDebug() ) { - echo "/* $name */\n"; - foreach ( $styles as $media => $style ) { - echo "@media $media {\n" . str_replace( "\n", "\n\t", "\t" . $style ) . "\n}\n"; - } - } else { - foreach ( $styles as $media => $style ) { - if ( strlen( $style ) ) { - echo "@media $media{" . $style . "}"; + // Append output + switch ( $context->getOnly() ) { + case 'scripts': + $out .= $scripts; + break; + case 'styles': + $out .= self::makeCombinedStyles( $styles ); + break; + case 'messages': + $out .= self::makeMessageSetScript( $messages ); + break; + default: + // Minify CSS, unless in debug mode, before embedding in implment script + if ( !$context->getDebug() ) { + foreach ( $styles as $media => $style ) { + $styles[$media] = self::filter( 'minify-css', $style ); } } - } - } else if ( $context->getOnly() === 'scripts' ) { - echo $scripts; - } else if ( $context->getOnly() === 'messages' ) { - echo "mediaWiki.msg.set( $messages );\n"; - } else { - if ( count( $styles ) ) { - $styles = FormatJson::encode( $styles ); - } else { - $styles = 'null'; - } - echo "mediaWiki.loader.implement( '$name', function() {{$scripts}},\n$styles,\n$messages );\n"; + $out .= self::makeLoaderImplementScript( $name, $scripts, $styles, $messages ); + break; } + wfProfileOut( __METHOD__ . '-' . $name ); } - // Update the status of script-only modules - if ( $context->getOnly() === 'scripts' && !in_array( 'startup', $modules ) ) { - $statuses = array(); - - foreach ( $modules as $name ) { - $statuses[$name] = 'ready'; - } - - $statuses = FormatJson::encode( $statuses ); - echo "mediaWiki.loader.state( $statuses );\n"; - } - - // Register missing modules + // Update module states if ( $context->shouldIncludeScripts() ) { - foreach ( $missing as $name ) { - echo "mediaWiki.loader.register( '$name', null, 'missing' );\n"; + // Set the state of modules loaded as only scripts to ready + if ( count( $modules ) && $context->getOnly() === 'scripts' && !in_array( 'startup', $modules ) ) { + $out .= self::makeLoaderStateScript( array_fill_keys( $modules, 'ready' ) ); + } + // Set the state of modules which were requested but unavailable as missing + if ( count( $missing ) ) { + $out .= self::makeLoaderStateScript( array_fill_keys( $missing, 'missing' ) ); } } - // Output the appropriate header - if ( $context->getOnly() !== 'styles' ) { - if ( $context->getDebug() ) { - ob_end_flush(); + // Send output + if ( $context->getDebug() ) { + echo $out; + } else { + if ( $context->getOnly() === 'styles' ) { + echo self::filter( 'minify-css', $out ); } else { - echo self::filter( 'minify-js', ob_get_clean() ); + echo self::filter( 'minify-js', $out ); } } + wfProfileOut( __METHOD__ ); } + + public static function makeLoaderImplementScript( $name, $scripts, $styles, $messages ) { + if ( is_array( $scripts ) ) { + $scripts = implode( $scripts, "\n" ); + } + if ( is_array( $styles ) ) { + $styles = count( $styles ) ? FormatJson::encode( $styles ) : 'null'; + } + if ( is_array( $messages ) ) { + $messages = count( $messages ) ? FormatJson::encode( $messages ) : 'null'; + } + return "mediaWiki.loader.implement( '$name', function() {{$scripts}},\n$styles,\n$messages );\n"; + } + + public static function makeMessageSetScript( $messages ) { + if ( is_array( $messages ) ) { + $messages = count( $messages ) ? FormatJson::encode( $messages ) : 'null'; + } + return "mediaWiki.msg.set( $messages );\n"; + } + + public static function makeCombinedStyles( array $styles ) { + $out = ''; + foreach ( $styles as $media => $style ) { + $out .= "@media $media {\n" . str_replace( "\n", "\n\t", "\t" . $style ) . "\n}\n"; + } + return $out; + } + + public static function makeLoaderStateScript( $name, $state = null ) { + if ( is_array( $name ) ) { + $statuses = FormatJson::encode( $name ); + return "mediaWiki.loader.state( $statuses );\n"; + } else { + $name = Xml::escapeJsString( $name ); + $name = Xml::escapeJsString( $state ); + return "mediaWiki.loader.state( '$name', '$state' );\n"; + } + } } diff --git a/includes/ResourceLoaderContext.php b/includes/ResourceLoaderContext.php index 0de6554ba2..3e4bac87ec 100644 --- a/includes/ResourceLoaderContext.php +++ b/includes/ResourceLoaderContext.php @@ -27,6 +27,7 @@ defined( 'MEDIAWIKI' ) || die( 1 ); * of a specific loader request */ class ResourceLoaderContext { + /* Protected Members */ protected $request; diff --git a/includes/ResourceLoaderModule.php b/includes/ResourceLoaderModule.php index 5a47b97d04..a27502b353 100644 --- a/includes/ResourceLoaderModule.php +++ b/includes/ResourceLoaderModule.php @@ -26,6 +26,7 @@ defined( 'MEDIAWIKI' ) || die( 1 ); * Abstraction for resource loader modules, with name registration and maxage functionality. */ abstract class ResourceLoaderModule { + /* Protected Members */ protected $name = null; @@ -1038,25 +1039,66 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { return $vars; } + /** + * Gets registration code for all modules + * + * @param $context ResourceLoaderContext object + * @return String: JavaScript code for registering all modules with the client loader + */ + public static function getModuleRegistrations( ResourceLoaderContext $context ) { + wfProfileIn( __METHOD__ ); + + $scripts = ''; + $registrations = array(); + foreach ( ResourceLoader::getModules() as $name => $module ) { + // Support module loader scripts + if ( ( $loader = $module->getLoaderScript() ) !== false ) { + $deps = FormatJson::encode( $module->getDependencies() ); + $group = FormatJson::encode( $module->getGroup() ); + $version = wfTimestamp( TS_ISO_8601, round( $module->getModifiedTime( $context ), -2 ) ); + $scripts .= "( function( name, version, dependencies ) { $loader } )\n" . + "( '$name', '$version', $deps, $group );\n"; + } + // Automatically register module + else { + // Modules without dependencies or a group pass two arguments (name, timestamp) to + // mediaWiki.loader.register() + if ( !count( $module->getDependencies() && $module->getGroup() === null ) ) { + $registrations[] = array( $name, $module->getModifiedTime( $context ) ); + } + // Modules with dependencies but no group pass three arguments (name, timestamp, dependencies) + // to mediaWiki.loader.register() + else if ( $module->getGroup() === null ) { + $registrations[] = array( + $name, $module->getModifiedTime( $context ), $module->getDependencies() ); + } + // Modules with dependencies pass four arguments (name, timestamp, dependencies, group) + // to mediaWiki.loader.register() + else { + $registrations[] = array( + $name, $module->getModifiedTime( $context ), $module->getDependencies(), $module->getGroup() ); + } + } + } + $out = $scripts . "mediaWiki.loader.register( " . FormatJson::encode( $registrations ) . " );"; + + wfProfileOut( __METHOD__ ); + return $out; + } + /* Methods */ public function getScript( ResourceLoaderContext $context ) { global $IP, $wgLoadScript; $scripts = file_get_contents( "$IP/resources/startup.js" ); - if ( $context->getOnly() === 'scripts' ) { // Get all module registrations - $registration = ResourceLoader::getModuleRegistrations( $context ); + $registration = self::getModuleRegistrations( $context ); // Build configuration $config = FormatJson::encode( $this->getConfig( $context ) ); // Add a well-known start-up function - $scripts .= << implode( '|', array( 'jquery', 'mediawiki' ) ), @@ -1074,7 +1116,7 @@ JAVASCRIPT; // Build HTML code for loading jquery and mediawiki modules $loadScript = Html::linkedScript( $wgLoadScript . '?' . wfArrayToCGI( $query ) ); // Add code to add jquery and mediawiki loading code; only if the current client is compatible - $scripts .= "if ( isCompatible() ) { document.write( " . FormatJson::encode( $loadScript ) . "); }\n"; + $scripts .= "if ( isCompatible() ) {\n\tdocument.write( " . FormatJson::encode( $loadScript ) . ");\n}\n"; // Delete the compatible function - it's not needed anymore $scripts .= "delete window['isCompatible'];\n"; } @@ -1090,10 +1132,10 @@ JAVASCRIPT; return $this->modifiedTime[$hash]; } $this->modifiedTime[$hash] = filemtime( "$IP/resources/startup.js" ); + // ATTENTION!: Because of the line above, this is not going to cause infinite recursion - think carefully // before making changes to this code! - $this->modifiedTime[$hash] = ResourceLoader::getHighestModifiedTime( $context ); - return $this->modifiedTime[$hash]; + return $this->modifiedTime[$hash] = ResourceLoader::getHighestModifiedTime( $context ); } public function getFlip( $context ) { diff --git a/resources/mediawiki/mediawiki.js b/resources/mediawiki/mediawiki.js index 3c7d4df698..1848917750 100644 --- a/resources/mediawiki/mediawiki.js +++ b/resources/mediawiki/mediawiki.js @@ -735,9 +735,10 @@ window.mediaWiki = new ( function( $ ) { } return; } - if ( module in registry ) { - registry[module].state = state; + if ( !( module in registry ) ) { + that.register( module ); } + registry[module].state = state; }; /**