Index: trunk/phase3/includes/resourceloader/ResourceLoader.php |
— | — | @@ -30,12 +30,17 @@ |
31 | 31 | |
32 | 32 | /* Protected Static Members */ |
33 | 33 | protected static $filterCacheVersion = 4; |
| 34 | + protected static $requiredSourceProperties = array( 'loadScript' ); |
34 | 35 | |
35 | 36 | /** Array: List of module name/ResourceLoaderModule object pairs */ |
36 | 37 | protected $modules = array(); |
| 38 | + |
37 | 39 | /** Associative array mapping module name to info associative array */ |
38 | 40 | protected $moduleInfos = array(); |
39 | 41 | |
| 42 | + /** array( 'source-id' => array( 'loadScript' => 'http://.../load.php' ) ) **/ |
| 43 | + protected $sources = array(); |
| 44 | + |
40 | 45 | /* Protected Methods */ |
41 | 46 | |
42 | 47 | /** |
— | — | @@ -178,10 +183,16 @@ |
179 | 184 | * Registers core modules and runs registration hooks. |
180 | 185 | */ |
181 | 186 | public function __construct() { |
182 | | - global $IP, $wgResourceModules; |
| 187 | + global $IP, $wgResourceModules, $wgResourceLoaderSources, $wgLoadScript; |
183 | 188 | |
184 | 189 | wfProfileIn( __METHOD__ ); |
185 | 190 | |
| 191 | + // Add 'local' source first |
| 192 | + $this->addSource( 'local', array( 'loadScript' => $wgLoadScript ) ); |
| 193 | + |
| 194 | + // Add other sources |
| 195 | + $this->addSource( $wgResourceLoaderSources ); |
| 196 | + |
186 | 197 | // Register core modules |
187 | 198 | $this->register( include( "$IP/resources/Resources.php" ) ); |
188 | 199 | // Register extension modules |
— | — | @@ -250,7 +261,43 @@ |
251 | 262 | wfProfileOut( __METHOD__ ); |
252 | 263 | } |
253 | 264 | |
254 | | - /** |
| 265 | + /** |
| 266 | + * Add a foreign source of modules. |
| 267 | + * |
| 268 | + * Source properties: |
| 269 | + * 'loadScript': URL (either fully-qualified or protocol-relative) of load.php for this source |
| 270 | + * |
| 271 | + * @param $id Mixed: source ID (string), or array( id1 => props1, id2 => props2, ... ) |
| 272 | + * @param $properties Array: source properties |
| 273 | + */ |
| 274 | + public function addSource( $id, $properties = null) { |
| 275 | + // Allow multiple sources to be registered in one call |
| 276 | + if ( is_array( $id ) ) { |
| 277 | + foreach ( $id as $key => $value ) { |
| 278 | + $this->addSource( $key, $value ); |
| 279 | + } |
| 280 | + return; |
| 281 | + } |
| 282 | + |
| 283 | + // Disallow duplicates |
| 284 | + if ( isset( $this->sources[$id] ) ) { |
| 285 | + throw new MWException( |
| 286 | + 'ResourceLoader duplicate source addition error. ' . |
| 287 | + 'Another source has already been registered as ' . $id |
| 288 | + ); |
| 289 | + } |
| 290 | + |
| 291 | + // Validate properties |
| 292 | + foreach ( self::$requiredSourceProperties as $prop ) { |
| 293 | + if ( !isset( $properties[$prop] ) ) { |
| 294 | + throw new MWException( "Required property $prop missing from source ID $id" ); |
| 295 | + } |
| 296 | + } |
| 297 | + |
| 298 | + $this->sources[$id] = $properties; |
| 299 | + } |
| 300 | + |
| 301 | + /** |
255 | 302 | * Get a list of module names |
256 | 303 | * |
257 | 304 | * @return Array: List of module names |
— | — | @@ -292,6 +339,15 @@ |
293 | 340 | } |
294 | 341 | |
295 | 342 | /** |
| 343 | + * Get the list of sources |
| 344 | + * |
| 345 | + * @return Array: array( id => array of properties, .. ) |
| 346 | + */ |
| 347 | + public function getSources() { |
| 348 | + return $this->sources; |
| 349 | + } |
| 350 | + |
| 351 | + /** |
296 | 352 | * Outputs a response to a resource load-request, including a content-type header. |
297 | 353 | * |
298 | 354 | * @param $context ResourceLoaderContext: Context in which a response should be formed |
— | — | @@ -660,30 +716,31 @@ |
661 | 717 | * @param $version Integer: Module version number as a timestamp |
662 | 718 | * @param $dependencies Array: List of module names on which this module depends |
663 | 719 | * @param $group String: Group which the module is in. |
| 720 | + * @param $source String: Source of the module, or 'local' if not foreign. |
664 | 721 | * @param $script String: JavaScript code |
665 | 722 | * |
666 | 723 | * @return string |
667 | 724 | */ |
668 | | - public static function makeCustomLoaderScript( $name, $version, $dependencies, $group, $script ) { |
| 725 | + public static function makeCustomLoaderScript( $name, $version, $dependencies, $group, $source, $script ) { |
669 | 726 | $script = str_replace( "\n", "\n\t", trim( $script ) ); |
670 | 727 | return Xml::encodeJsCall( |
671 | | - "( function( name, version, dependencies, group ) {\n\t$script\n} )", |
672 | | - array( $name, $version, $dependencies, $group ) ); |
| 728 | + "( function( name, version, dependencies, group, source ) {\n\t$script\n} )", |
| 729 | + array( $name, $version, $dependencies, $group, $source ) ); |
673 | 730 | } |
674 | 731 | |
675 | 732 | /** |
676 | 733 | * Returns JS code which calls mw.loader.register with the given |
677 | 734 | * parameters. Has three calling conventions: |
678 | 735 | * |
679 | | - * - ResourceLoader::makeLoaderRegisterScript( $name, $version, $dependencies, $group ): |
| 736 | + * - ResourceLoader::makeLoaderRegisterScript( $name, $version, $dependencies, $group, $source ): |
680 | 737 | * Register a single module. |
681 | 738 | * |
682 | 739 | * - ResourceLoader::makeLoaderRegisterScript( array( $name1, $name2 ) ): |
683 | 740 | * Register modules with the given names. |
684 | 741 | * |
685 | 742 | * - ResourceLoader::makeLoaderRegisterScript( array( |
686 | | - * array( $name1, $version1, $dependencies1, $group1 ), |
687 | | - * array( $name2, $version2, $dependencies1, $group2 ), |
| 743 | + * array( $name1, $version1, $dependencies1, $group1, $source1 ), |
| 744 | + * array( $name2, $version2, $dependencies1, $group2, $source2 ), |
688 | 745 | * ... |
689 | 746 | * ) ): |
690 | 747 | * Registers modules with the given names and parameters. |
— | — | @@ -692,22 +749,44 @@ |
693 | 750 | * @param $version Integer: Module version number as a timestamp |
694 | 751 | * @param $dependencies Array: List of module names on which this module depends |
695 | 752 | * @param $group String: group which the module is in. |
| 753 | + * @param $source String: source of the module, or 'local' if not foreign |
696 | 754 | * |
697 | 755 | * @return string |
698 | 756 | */ |
699 | 757 | public static function makeLoaderRegisterScript( $name, $version = null, |
700 | | - $dependencies = null, $group = null ) |
| 758 | + $dependencies = null, $group = null, $source = null ) |
701 | 759 | { |
702 | 760 | if ( is_array( $name ) ) { |
703 | 761 | return Xml::encodeJsCall( 'mw.loader.register', array( $name ) ); |
704 | 762 | } else { |
705 | 763 | $version = (int) $version > 1 ? (int) $version : 1; |
706 | 764 | return Xml::encodeJsCall( 'mw.loader.register', |
707 | | - array( $name, $version, $dependencies, $group ) ); |
| 765 | + array( $name, $version, $dependencies, $group, $source ) ); |
708 | 766 | } |
709 | 767 | } |
710 | 768 | |
711 | 769 | /** |
| 770 | + * Returns JS code which calls mw.loader.addSource() with the given |
| 771 | + * parameters. Has two calling conventions: |
| 772 | + * |
| 773 | + * - ResourceLoader::makeLoaderSourcesScript( $id, $properties ): |
| 774 | + * Register a single source |
| 775 | + * |
| 776 | + * - ResourceLoader::makeLoaderSourcesScript( array( $id1 => $props1, $id2 => $props2, ... ) ); |
| 777 | + * Register sources with the given IDs and properties. |
| 778 | + * |
| 779 | + * @param $id String: source ID |
| 780 | + * @param $properties Array: source properties (see addSource()) |
| 781 | + */ |
| 782 | + public static function makeLoaderSourcesScript( $id, $properties = null ) { |
| 783 | + if ( is_array( $id ) ) { |
| 784 | + return Xml::encodeJsCall( 'mw.loader.addSource', array( $id ) ); |
| 785 | + } else { |
| 786 | + return Xml::encodeJsCall( 'mw.loader.addSource', array( $id, $properties ) ); |
| 787 | + } |
| 788 | + } |
| 789 | + |
| 790 | + /** |
712 | 791 | * Returns JS code which runs given JS code if the client-side framework is |
713 | 792 | * present. |
714 | 793 | * |
Index: trunk/phase3/includes/resourceloader/ResourceLoaderModule.php |
— | — | @@ -159,6 +159,16 @@ |
160 | 160 | // Stub, override expected |
161 | 161 | return null; |
162 | 162 | } |
| 163 | + |
| 164 | + /** |
| 165 | + * Get the origin of this module. Should only be overridden for foreign modules. |
| 166 | + * |
| 167 | + * @return String: Origin name, 'local' for local modules |
| 168 | + */ |
| 169 | + public function getSource() { |
| 170 | + // Stub, override expected |
| 171 | + return 'local'; |
| 172 | + } |
163 | 173 | |
164 | 174 | /** |
165 | 175 | * Where on the HTML page should this module's JS be loaded? |
Index: trunk/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php |
— | — | @@ -137,6 +137,11 @@ |
138 | 138 | $out = ''; |
139 | 139 | $registrations = array(); |
140 | 140 | $resourceLoader = $context->getResourceLoader(); |
| 141 | + |
| 142 | + // Register sources |
| 143 | + $out .= ResourceLoader::makeLoaderSourcesScript( $resourceLoader->getSources() ); |
| 144 | + |
| 145 | + // Register modules |
141 | 146 | foreach ( $resourceLoader->getModuleNames() as $name ) { |
142 | 147 | $module = $resourceLoader->getModule( $name ); |
143 | 148 | // Support module loader scripts |
— | — | @@ -144,9 +149,10 @@ |
145 | 150 | if ( $loader !== false ) { |
146 | 151 | $deps = $module->getDependencies(); |
147 | 152 | $group = $module->getGroup(); |
| 153 | + $source = $module->getSource(); |
148 | 154 | $version = wfTimestamp( TS_ISO_8601_BASIC, |
149 | 155 | $module->getModifiedTime( $context ) ); |
150 | | - $out .= ResourceLoader::makeCustomLoaderScript( $name, $version, $deps, $group, $loader ); |
| 156 | + $out .= ResourceLoader::makeCustomLoaderScript( $name, $version, $deps, $group, $source, $loader ); |
151 | 157 | } |
152 | 158 | // Automatically register module |
153 | 159 | else { |
— | — | @@ -154,23 +160,29 @@ |
155 | 161 | // seem to do that, and custom implementations might forget. Coerce it to TS_UNIX |
156 | 162 | $moduleMtime = wfTimestamp( TS_UNIX, $module->getModifiedTime( $context ) ); |
157 | 163 | $mtime = max( $moduleMtime, wfTimestamp( TS_UNIX, $wgCacheEpoch ) ); |
158 | | - // Modules without dependencies or a group pass two arguments (name, timestamp) to |
| 164 | + // Modules without dependencies, a group or a foreign source pass two arguments (name, timestamp) to |
159 | 165 | // mw.loader.register() |
160 | | - if ( !count( $module->getDependencies() && $module->getGroup() === null ) ) { |
| 166 | + if ( !count( $module->getDependencies() && $module->getGroup() === null && $module->getSource() === 'local' ) ) { |
161 | 167 | $registrations[] = array( $name, $mtime ); |
162 | 168 | } |
163 | | - // Modules with dependencies but no group pass three arguments |
| 169 | + // Modules with dependencies but no group or foreign source pass three arguments |
164 | 170 | // (name, timestamp, dependencies) to mw.loader.register() |
165 | | - elseif ( $module->getGroup() === null ) { |
| 171 | + elseif ( $module->getGroup() === null && $module->getSource() === 'local' ) { |
166 | 172 | $registrations[] = array( |
167 | 173 | $name, $mtime, $module->getDependencies() ); |
168 | 174 | } |
169 | | - // Modules with dependencies pass four arguments (name, timestamp, dependencies, group) |
| 175 | + // Modules with a group but no foreign source pass four arguments (name, timestamp, dependencies, group) |
170 | 176 | // to mw.loader.register() |
171 | | - else { |
| 177 | + else if ( $module->getSource() === 'local' ) { |
172 | 178 | $registrations[] = array( |
173 | 179 | $name, $mtime, $module->getDependencies(), $module->getGroup() ); |
174 | 180 | } |
| 181 | + // Modules with a foreign source pass five arguments (name, timestamp, dependencies, group, source) |
| 182 | + // to mw.loader.register() |
| 183 | + else { |
| 184 | + $registrations[] = array( |
| 185 | + $name, $mtime, $module->getDependencies(), $module->getGroup(), $module->getSource() ); |
| 186 | + } |
175 | 187 | } |
176 | 188 | } |
177 | 189 | $out .= ResourceLoader::makeLoaderRegisterScript( $registrations ); |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -2439,6 +2439,16 @@ |
2440 | 2440 | $wgResourceModules = array(); |
2441 | 2441 | |
2442 | 2442 | /** |
| 2443 | + * Extensions should register foreign module sources here. 'local' is a |
| 2444 | + * built-in source that is not in this array, but defined by |
| 2445 | + * ResourceLoader::__construct() so that it cannot be unset. |
| 2446 | + * |
| 2447 | + * Example: |
| 2448 | + * $wgResourceLoaderSources['foo'] = array( 'loadScript' => 'http://example.org/w/load.php' ); |
| 2449 | + */ |
| 2450 | +$wgResourceLoaderSources = array(); |
| 2451 | + |
| 2452 | +/** |
2443 | 2453 | * Maximum time in seconds to cache resources served by the resource loader |
2444 | 2454 | */ |
2445 | 2455 | $wgResourceLoaderMaxage = array( |
Index: trunk/phase3/resources/mediawiki/mediawiki.js |
— | — | @@ -299,18 +299,34 @@ |
300 | 300 | * making it impossible to hold back registration of jquery until after |
301 | 301 | * mediawiki. |
302 | 302 | * |
| 303 | + * For exact details on support for script, style and messages, look at |
| 304 | + * mw.loader.implement. |
| 305 | + * |
303 | 306 | * Format: |
304 | 307 | * { |
305 | 308 | * 'moduleName': { |
306 | | - * 'dependencies': ['required module', 'required module', ...], (or) function() {} |
307 | | - * 'state': 'registered', 'loading', 'loaded', 'ready', or 'error' |
308 | | - * 'script': function() {}, |
309 | | - * 'style': 'css code string', |
310 | | - * 'messages': { 'key': 'value' }, |
311 | | - * 'version': ############## (unix timestamp) |
| 309 | + * 'version': ############## (unix timestamp), |
| 310 | + * 'dependencies': ['required.foo', 'bar.also', ...], (or) function() {} |
| 311 | + * 'group': 'somegroup', (or) null, |
| 312 | + * 'source': 'local', 'someforeignwiki', (or) null |
| 313 | + * 'state': 'registered', 'loading', 'loaded', 'ready', or 'error' |
| 314 | + * 'script': ..., |
| 315 | + * 'style': ..., |
| 316 | + * 'messages': { 'key': 'value' }, |
| 317 | + * } |
312 | 318 | * } |
313 | 319 | */ |
314 | 320 | var registry = {}, |
| 321 | + /** |
| 322 | + * Mapping of sources, keyed by source-id, values are objects. |
| 323 | + * Format: |
| 324 | + * { |
| 325 | + * 'sourceId': { |
| 326 | + * 'loadScript': 'http://foo.bar/w/load.php' |
| 327 | + * } |
| 328 | + * } |
| 329 | + */ |
| 330 | + sources = {}, |
315 | 331 | // List of modules which will be loaded as when ready |
316 | 332 | batch = [], |
317 | 333 | // List of modules to be loaded |
— | — | @@ -724,24 +740,41 @@ |
725 | 741 | } |
726 | 742 | } |
727 | 743 | |
| 744 | + /** |
| 745 | + * Asynchronously append a script tag to the end of the body |
| 746 | + * that invokes load.php |
| 747 | + * @param moduleMap {Object}: Module map, see buildModulesString() |
| 748 | + * @param currReqBase {Object}: Object with other parameters (other than 'modules') to use in the request |
| 749 | + * @param sourceLoadScript {String}: URL of load.php |
| 750 | + */ |
| 751 | + function doRequest( moduleMap, currReqBase, sourceLoadScript ) { |
| 752 | + var request = $.extend( |
| 753 | + { 'modules': buildModulesString( moduleMap ) }, |
| 754 | + currReqBase |
| 755 | + ); |
| 756 | + request = sortQuery( request ); |
| 757 | + // Asynchronously append a script tag to the end of the body |
| 758 | + // Append &* to avoid triggering the IE6 extension check |
| 759 | + addScript( sourceLoadScript + '?' + $.param( request ) + '&*' ); |
| 760 | + } |
| 761 | + |
728 | 762 | /* Public Methods */ |
729 | 763 | |
730 | 764 | /** |
731 | 765 | * Requests dependencies from server, loading and executing when things when ready. |
732 | 766 | */ |
733 | 767 | this.work = function() { |
734 | | - // Build a list of request parameters |
735 | | - var base = { |
736 | | - 'skin': mw.config.get( 'skin' ), |
737 | | - 'lang': mw.config.get( 'wgUserLanguage' ), |
738 | | - 'debug': mw.config.get( 'debug' ) |
| 768 | + // Build a list of request parameters common to all requests. |
| 769 | + var reqBase = { |
| 770 | + skin: mw.config.get( 'skin' ), |
| 771 | + lang: mw.config.get( 'wgUserLanguage' ), |
| 772 | + debug: mw.config.get( 'debug' ) |
739 | 773 | }, |
740 | | - // Extend request parameters with a list of modules in the batch |
741 | | - requests = [], |
742 | | - // Split into groups |
743 | | - groups = {}; |
| 774 | + // Split module batch by source and by group. |
| 775 | + splits = {}, |
| 776 | + maxQueryLength = mw.config.get( 'wgResourceLoaderMaxQueryLength', -1 ); |
744 | 777 | |
745 | | - // Appends a list of modules to the batch |
| 778 | + // Appends a list of modules from the queue to the batch |
746 | 779 | for ( var q = 0; q < queue.length; q++ ) { |
747 | 780 | // Only request modules which are undefined or registered |
748 | 781 | if ( !( queue[q] in registry ) || registry[queue[q]].state === 'registered' ) { |
— | — | @@ -755,85 +788,125 @@ |
756 | 789 | } |
757 | 790 | } |
758 | 791 | } |
759 | | - // Early exit if there's nothing to load |
| 792 | + // Early exit if there's nothing to load... |
760 | 793 | if ( !batch.length ) { |
761 | 794 | return; |
762 | 795 | } |
763 | | - // Clean up the queue |
| 796 | + |
| 797 | + // The queue has been processed into the batch, clear up the queue. |
764 | 798 | queue = []; |
| 799 | + |
765 | 800 | // Always order modules alphabetically to help reduce cache |
766 | | - // misses for otherwise identical content |
| 801 | + // misses for otherwise identical content. |
767 | 802 | batch.sort(); |
| 803 | + |
| 804 | + // Split batch by source and by group. |
768 | 805 | for ( var b = 0; b < batch.length; b++ ) { |
769 | | - var bGroup = registry[batch[b]].group; |
770 | | - if ( !( bGroup in groups ) ) { |
771 | | - groups[bGroup] = []; |
| 806 | + var bSource = registry[batch[b]].source, |
| 807 | + bGroup = registry[batch[b]].group; |
| 808 | + if ( !( bSource in splits ) ) { |
| 809 | + splits[bSource] = {}; |
772 | 810 | } |
773 | | - groups[bGroup][groups[bGroup].length] = batch[b]; |
| 811 | + if ( !( bGroup in splits[bSource] ) ) { |
| 812 | + splits[bSource][bGroup] = []; |
| 813 | + } |
| 814 | + var bSourceGroup = splits[bSource][bGroup]; |
| 815 | + bSourceGroup[bSourceGroup.length] = batch[b]; |
774 | 816 | } |
775 | | - for ( var group in groups ) { |
776 | | - // Calculate the highest timestamp |
777 | | - var version = 0; |
778 | | - for ( var g = 0; g < groups[group].length; g++ ) { |
779 | | - if ( registry[groups[group][g]].version > version ) { |
780 | | - version = registry[groups[group][g]].version; |
| 817 | + |
| 818 | + // Clear the batch - this MUST happen before we append any |
| 819 | + // script elements to the body or it's possible that a script |
| 820 | + // will be locally cached, instantly load, and work the batch |
| 821 | + // again, all before we've cleared it causing each request to |
| 822 | + // include modules which are already loaded. |
| 823 | + batch = []; |
| 824 | + |
| 825 | + var source, group, modules, maxVersion, sourceLoadScript; |
| 826 | + |
| 827 | + for ( source in splits ) { |
| 828 | + |
| 829 | + sourceLoadScript = sources[source].loadScript; |
| 830 | + |
| 831 | + for ( group in splits[source] ) { |
| 832 | + |
| 833 | + // Cache access to currently selected list of |
| 834 | + // modules for this group from this source. |
| 835 | + modules = splits[source][group]; |
| 836 | + |
| 837 | + // Calculate the highest timestamp |
| 838 | + maxVersion = 0; |
| 839 | + for ( var g = 0; g < modules.length; g++ ) { |
| 840 | + if ( registry[modules[g]].version > maxVersion ) { |
| 841 | + maxVersion = registry[modules[g]].version; |
| 842 | + } |
781 | 843 | } |
782 | | - } |
783 | 844 | |
784 | | - var reqBase = $.extend( { 'version': formatVersionNumber( version ) }, base ), |
785 | | - reqBaseLength = $.param( reqBase ).length, |
786 | | - reqs = [], |
787 | | - limit = mw.config.get( 'wgResourceLoaderMaxQueryLength', -1 ), |
788 | | - // We may need to split up the request to honor the query string length limit, |
789 | | - // so build it piece by piece. |
790 | | - l = reqBaseLength + 9, // '&modules='.length == 9 |
791 | | - r = 0; |
| 845 | + var currReqBase = $.extend( { 'version': formatVersionNumber( maxVersion ) }, reqBase ), |
| 846 | + currReqBaseLength = $.param( currReqBase ).length, |
| 847 | + moduleMap = {}, |
| 848 | + // We may need to split up the request to honor the query string length limit, |
| 849 | + // so build it piece by piece. |
| 850 | + l = currReqBaseLength + 9; // '&modules='.length == 9 |
792 | 851 | |
793 | | - reqs[0] = {}; // { prefix: [ suffixes ] } |
| 852 | + moduleMap = {}; // { prefix: [ suffixes ] } |
794 | 853 | |
795 | | - for ( var i = 0; i < groups[group].length; i++ ) { |
796 | | - // Determine how many bytes this module would add to the query string |
797 | | - var lastDotIndex = groups[group][i].lastIndexOf( '.' ), |
798 | | - // Note that these substr() calls work even if lastDotIndex == -1 |
799 | | - prefix = groups[group][i].substr( 0, lastDotIndex ), |
800 | | - suffix = groups[group][i].substr( lastDotIndex + 1 ), |
801 | | - bytesAdded = prefix in reqs[r] |
802 | | - ? suffix.length + 3 // '%2C'.length == 3 |
803 | | - : groups[group][i].length + 3; // '%7C'.length == 3 |
| 854 | + for ( var i = 0; i < modules.length; i++ ) { |
| 855 | + // Determine how many bytes this module would add to the query string |
| 856 | + var lastDotIndex = modules[i].lastIndexOf( '.' ), |
| 857 | + // Note that these substr() calls work even if lastDotIndex == -1 |
| 858 | + prefix = modules[i].substr( 0, lastDotIndex ), |
| 859 | + suffix = modules[i].substr( lastDotIndex + 1 ), |
| 860 | + bytesAdded = prefix in moduleMap |
| 861 | + ? suffix.length + 3 // '%2C'.length == 3 |
| 862 | + : modules[i].length + 3; // '%7C'.length == 3 |
804 | 863 | |
805 | | - // If the request would become too long, create a new one, |
806 | | - // but don't create empty requests |
807 | | - if ( limit > 0 && !$.isEmptyObject( reqs[r] ) && l + bytesAdded > limit ) { |
808 | | - // This request would become too long, create a new one |
809 | | - r++; |
810 | | - reqs[r] = {}; |
811 | | - l = reqBaseLength + 9; |
| 864 | + // If the request would become too long, create a new one, |
| 865 | + // but don't create empty requests |
| 866 | + if ( maxQueryLength > 0 && !$.isEmptyObject( moduleMap ) && l + bytesAdded > maxQueryLength ) { |
| 867 | + // This request would become too long, create a new one |
| 868 | + // and fire off the old one |
| 869 | + doRequest( moduleMap, currReqBase, sourceLoadScript ); |
| 870 | + moduleMap = {}; |
| 871 | + l = currReqBaseLength + 9; |
| 872 | + } |
| 873 | + if ( !( prefix in moduleMap ) ) { |
| 874 | + moduleMap[prefix] = []; |
| 875 | + } |
| 876 | + moduleMap[prefix].push( suffix ); |
| 877 | + l += bytesAdded; |
812 | 878 | } |
813 | | - if ( !( prefix in reqs[r] ) ) { |
814 | | - reqs[r][prefix] = []; |
| 879 | + // If there's anything left in moduleMap, request that too |
| 880 | + if ( !$.isEmptyObject( moduleMap ) ) { |
| 881 | + doRequest( moduleMap, currReqBase, sourceLoadScript ); |
815 | 882 | } |
816 | | - reqs[r][prefix].push( suffix ); |
817 | | - l += bytesAdded; |
818 | 883 | } |
819 | | - for ( var r = 0; r < reqs.length; r++ ) { |
820 | | - requests[requests.length] = $.extend( |
821 | | - { 'modules': buildModulesString( reqs[r] ) }, reqBase |
822 | | - ); |
| 884 | + } |
| 885 | + }; |
| 886 | + |
| 887 | + /** |
| 888 | + * Register a source. |
| 889 | + * |
| 890 | + * @param id {String}: Short lowercase a-Z string representing a source, only used internally. |
| 891 | + * @param props {Object}: Object containing only the loadScript property which is a url to |
| 892 | + * the load.php location of the source. |
| 893 | + * @return {Boolean} |
| 894 | + */ |
| 895 | + this.addSource = function( id, props ) { |
| 896 | + // Allow multiple additions |
| 897 | + if ( typeof id === 'object' ) { |
| 898 | + for ( var source in id ) { |
| 899 | + mw.loader.addSource( source, id[source] ); |
823 | 900 | } |
| 901 | + return true; |
824 | 902 | } |
825 | | - // Clear the batch - this MUST happen before we append the |
826 | | - // script element to the body or it's possible that the script |
827 | | - // will be locally cached, instantly load, and work the batch |
828 | | - // again, all before we've cleared it causing each request to |
829 | | - // include modules which are already loaded |
830 | | - batch = []; |
831 | | - // Asynchronously append a script tag to the end of the body |
832 | | - for ( var r = 0; r < requests.length; r++ ) { |
833 | | - requests[r] = sortQuery( requests[r] ); |
834 | | - // Append &* to avoid triggering the IE6 extension check |
835 | | - var src = mw.config.get( 'wgLoadScript' ) + '?' + $.param( requests[r] ) + '&*'; |
836 | | - addScript( src ); |
| 903 | + |
| 904 | + if ( sources[id] !== undefined ) { |
| 905 | + throw new Error( 'source already registered: ' + id ); |
837 | 906 | } |
| 907 | + |
| 908 | + sources[id] = props; |
| 909 | + |
| 910 | + return true; |
838 | 911 | }; |
839 | 912 | |
840 | 913 | /** |
— | — | @@ -845,13 +918,16 @@ |
846 | 919 | * @param dependencies {String|Array|Function}: One string or array of strings of module |
847 | 920 | * names on which this module depends, or a function that returns that array. |
848 | 921 | * @param group {String}: Group which the module is in (optional, defaults to null) |
| 922 | + * @param source {String}: Name of the source. Defaults to local. |
849 | 923 | */ |
850 | | - this.register = function( module, version, dependencies, group ) { |
| 924 | + this.register = function( module, version, dependencies, group, source ) { |
851 | 925 | // Allow multiple registration |
852 | 926 | if ( typeof module === 'object' ) { |
853 | 927 | for ( var m = 0; m < module.length; m++ ) { |
| 928 | + // module is an array of module names |
854 | 929 | if ( typeof module[m] === 'string' ) { |
855 | 930 | mw.loader.register( module[m] ); |
| 931 | + // module is an array of arrays |
856 | 932 | } else if ( typeof module[m] === 'object' ) { |
857 | 933 | mw.loader.register.apply( mw.loader, module[m] ); |
858 | 934 | } |
— | — | @@ -867,10 +943,11 @@ |
868 | 944 | } |
869 | 945 | // List the module as registered |
870 | 946 | registry[module] = { |
871 | | - 'state': 'registered', |
| 947 | + 'version': version !== undefined ? parseInt( version, 10 ) : 0, |
| 948 | + 'dependencies': [], |
872 | 949 | 'group': typeof group === 'string' ? group : null, |
873 | | - 'dependencies': [], |
874 | | - 'version': version !== undefined ? parseInt( version, 10 ) : 0 |
| 950 | + 'source': typeof source === 'string' ? source: 'local', |
| 951 | + 'state': 'registered' |
875 | 952 | }; |
876 | 953 | if ( typeof dependencies === 'string' ) { |
877 | 954 | // Allow dependencies to be given as a single module name |