For bug 27488: move the startup script, jquery+mediawiki and the mw.config.set()...
authorRoan Kattouw <catrope@users.mediawiki.org>
Thu, 7 Apr 2011 12:07:25 +0000 (12:07 +0000)
committerRoan Kattouw <catrope@users.mediawiki.org>
Thu, 7 Apr 2011 12:07:25 +0000 (12:07 +0000)
Also rearranges the loading order a little bit such that only=messages comes before only=scripts, and config comes before everything except startup and jquery+mediawiki

includes/OutputPage.php
includes/Skin.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderModule.php
resources/Resources.php

index a484444..71d4b9c 100644 (file)
@@ -390,15 +390,17 @@ class OutputPage {
         * Filter an array of modules to remove insufficiently trustworthy members, and modules
         * which are no longer registered (eg a page is cached before an extension is disabled)
         * @param $modules Array
+        * @param $position String if not null, only return modules with this position
         * @return Array
         */
-       protected function filterModules( $modules, $type = ResourceLoaderModule::TYPE_COMBINED ){
+       protected function filterModules( $modules, $position = null, $type = ResourceLoaderModule::TYPE_COMBINED ){
                $resourceLoader = $this->getResourceLoader();
                $filteredModules = array();
                foreach( $modules as $val ){
                        $module = $resourceLoader->getModule( $val );
                        if( $module instanceof ResourceLoaderModule
-                               && $module->getOrigin() <= $this->getAllowedModules( $type ) )
+                               && $module->getOrigin() <= $this->getAllowedModules( $type )
+                               && ( is_null( $position ) || $module->getPosition() == $position ) )
                        {
                                $filteredModules[] = $val;
                        }
@@ -410,12 +412,13 @@ class OutputPage {
         * Get the list of modules to include on this page
         *
         * @param $filter Bool whether to filter out insufficiently trustworthy modules
+        * @param $position String if not null, only return modules with this position
         * @return Array of module names
         */
-       public function getModules( $filter = false, $param = 'mModules' ) {
+       public function getModules( $filter = false, $position = null, $param = 'mModules' ) {
                $modules = array_values( array_unique( $this->$param ) );
                return $filter
-                       ? $this->filterModules( $modules )
+                       ? $this->filterModules( $modules, $position )
                        : $modules;
        }
 
@@ -434,8 +437,8 @@ class OutputPage {
         * Get the list of module JS to include on this page
         * @return array of module names
         */
-       public function getModuleScripts( $filter = false ) {
-               return $this->getModules( $filter, 'mModuleScripts' );
+       public function getModuleScripts( $filter = false, $position = null ) {
+               return $this->getModules( $filter, $position, 'mModuleScripts' );
        }
 
        /**
@@ -454,8 +457,8 @@ class OutputPage {
         *
         * @return Array of module names
         */
-       public function getModuleStyles( $filter = false ) {
-               return $this->getModules( $filter, 'mModuleStyles' );
+       public function getModuleStyles( $filter = false, $position = null ) {
+               return $this->getModules( $filter,  $position, 'mModuleStyles' );
        }
 
        /**
@@ -474,8 +477,8 @@ class OutputPage {
         *
         * @return Array of module names
         */
-       public function getModuleMessages( $filter = false ) {
-               return $this->getModules( $filter, 'mModuleMessages' );
+       public function getModuleMessages( $filter = false, $position = null ) {
+               return $this->getModules( $filter, $position, 'mModuleMessages' );
        }
 
        /**
@@ -2344,6 +2347,7 @@ class OutputPage {
                $ret .= implode( "\n", array(
                        $this->getHeadLinks( $sk, true ),
                        $this->buildCssLinks( $sk ),
+                       $this->getHeadScripts( $sk ),
                        $this->getHeadItems()
                ) );
 
@@ -2589,36 +2593,68 @@ class OutputPage {
        }
 
        /**
-        * Gets the global variables and mScripts; also adds userjs to the end if
-        * enabled. Despite the name, these scripts are no longer put in the
-        * <head> but at the bottom of the <body>
+        * JS stuff to put in the <head>. This is the startup module, config
+        * vars and modules marked with position 'top'
         *
         * @param $sk Skin object to use
         * @return String: HTML fragment
         */
        function getHeadScripts( Skin $sk ) {
-               global $wgUseSiteJs, $wgAllowUserJs;
-
                // Startup - this will immediately load jquery and mediawiki modules
                $scripts = $this->makeResourceLoaderLink( $sk, 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true );
-
-               // Script and Messages "only" requests
-               $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleScripts( true ), ResourceLoaderModule::TYPE_SCRIPTS );
-               $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleMessages( true ), ResourceLoaderModule::TYPE_MESSAGES );
-
-               // Modules requests - let the client calculate dependencies and batch requests as it likes
-               $loader = '';
-               if ( $this->getModules( true ) ) {
-                       $loader = Xml::encodeJsCall( 'mw.loader.load', array( $this->getModules( true ) ) ) .
-                               Xml::encodeJsCall( 'mw.loader.go', array() );
-               }
                
+               // Load config before anything else
                $scripts .= Html::inlineScript(
                        ResourceLoader::makeLoaderConditionalScript(
-                               ResourceLoader::makeConfigSetScript( $this->getJSVars() ) . $loader
+                               ResourceLoader::makeConfigSetScript( $this->getJSVars() )
                        )
                );
+               
+               // Script and Messages "only" requests marked for top inclusion
+               // Messages should go first
+               $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleMessages( true, 'top' ), ResourceLoaderModule::TYPE_MESSAGES );
+               $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleScripts( true, 'top' ), ResourceLoaderModule::TYPE_SCRIPTS );
+
+               // Modules requests - let the client calculate dependencies and batch requests as it likes
+               // Only load modules that have marked themselves for loading at the top
+               $modules = $this->getModules( true, 'top' );
+               if ( $modules ) {
+                       $scripts .= Html::inlineScript(
+                               ResourceLoader::makeLoaderConditionalScript(
+                                       Xml::encodeJsCall( 'mw.loader.load', array( $modules ) ) .
+                                       Xml::encodeJsCall( 'mw.loader.go', array() )
+                               )
+                       );
+               }
+
+               return $scripts;
+       }
+       
+       /**
+        * JS stuff to put at the bottom of the <body>: modules marked with position 'bottom',
+        * legacy scripts ($this->mScripts), user preferences, site JS and user JS
+        */
+       function getBottomScripts( Skin $sk ) {
+               global $wgUseSiteJs, $wgAllowUserJs;
+               
+               // Script and Messages "only" requests marked for bottom inclusion
+               // Messages should go first
+               $scripts = $this->makeResourceLoaderLink( $sk, $this->getModuleMessages( true, 'bottom' ), ResourceLoaderModule::TYPE_MESSAGES );
+               $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleScripts( true, 'bottom' ), ResourceLoaderModule::TYPE_SCRIPTS );
 
+               // Modules requests - let the client calculate dependencies and batch requests as it likes
+               // Only load modules that have marked themselves for loading at the top
+               $modules = $this->getModules( true, 'bottom' );
+               if ( $modules ) {
+                       $scripts .= Html::inlineScript(
+                               ResourceLoader::makeLoaderConditionalScript(
+                                       Xml::encodeJsCall( 'mw.loader.load', array( $modules ) ) .
+                                       // the go() call is unnecessary if we inserted top modules, but we don't know for sure that we did
+                                       Xml::encodeJsCall( 'mw.loader.go', array() )
+                               )
+                       );
+               }
+               
                // Legacy Scripts
                $scripts .= "\n" . $this->mScripts;
 
@@ -2645,7 +2681,7 @@ class OutputPage {
                        }
                }
                $scripts .= $this->makeResourceLoaderLink( $sk, $userScripts, ResourceLoaderModule::TYPE_SCRIPTS );
-
+               
                return $scripts;
        }
 
index f1f0cde..5b1eedb 100644 (file)
@@ -738,7 +738,10 @@ abstract class Skin {
         * @return String HTML-wrapped JS code to be put before </body>
         */
        function bottomScripts( $out ) {
-               $bottomScriptText = "\n" . $out->getHeadScripts( $this );
+               // TODO and the suckage continues. This function is really just a wrapper around
+               // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned
+               // up at some point
+               $bottomScriptText = $out->getBottomScripts( $this );
                wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) );
 
                return $bottomScriptText;
index 5bb38fe..03e7bc9 100644 (file)
@@ -78,6 +78,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
        protected $messages = array();
        /** String: Name of group to load this module in */
        protected $group;
+       /** String: Position on the page to load this module at */
+       protected $position = 'bottom';
        /** Boolean: Link to raw files in debug mode */
        protected $debugRaw = true;
        /**
@@ -138,6 +140,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *              'messages' => [array of message key strings],
         *              // Group which this module should be loaded together with
         *              'group' => [group name string],
+        *              // Position on the page to load this module at
+        *              'position' => ['bottom' (default) or 'top']
         *      )
         * @endcode
         */
@@ -189,6 +193,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                                        break;
                                // Single strings
                                case 'group':
+                               case 'position':
                                case 'localBasePath':
                                case 'remoteBasePath':
                                        $this->{$member} = (string) $option;
@@ -295,6 +300,10 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
        public function getGroup() {
                return $this->group;
        }
+       
+       public function getPosition() {
+               return $this->position;
+       }
 
        /**
         * Gets list of names of modules this module depends on.
index 73fcddf..7d611fb 100644 (file)
@@ -159,6 +159,15 @@ abstract class ResourceLoaderModule {
                // Stub, override expected
                return null;
        }
+       
+       /**
+        * Where on the HTML page should this module's JS be loaded?
+        * 'top': in the <head>
+        * 'bottom': at the bottom of the <body>
+        */
+       public function getPosition() {
+               return 'bottom';
+       }
 
        /**
         * Get the loader JS for this module, if set.
index ba7f7bf..4cc7ac1 100644 (file)
@@ -537,6 +537,7 @@ return array(
                'remoteBasePath' => $GLOBALS['wgStylePath'],
                'localBasePath' => "{$GLOBALS['IP']}/skins",
                'dependencies' => 'mediawiki.legacy.wikibits',
+               'position' => 'top',
        ),
        'mediawiki.legacy.edit' => array(
                'scripts' => 'common/edit.js',