r93247 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r93246‎ | r93247 | r93248 >
Date:21:10, 26 July 2011
Author:krinkle
Status:ok (Comments)
Tags:
Comment:
[ResourceLoader 2]: Add support for multiple loadScript sources
Front-end:
* New mw.loader method: addSource(). Call with two arguments or an object as first argument for multiple registrations
* New property in module registry: "source". Optional for local modules (falls back to 'local'). When loading/using one or more modules, the worker will group the request by source and make separate requests to the sources as needed.
* Re-arranging object properties in mw.loader.register to match the same order all other code parts use.
* Adding documentation for 'source' and where missing updating it to include 'group' as well.
* Refactor of mw.loader.work() by Roan Kattouw and Timo Tijhof:'
-- Additional splitting layer by source (in addition to splitting by group), renamed 'groups' to 'splits'
-- Clean up of the loop, and removing a no longer needed loop after the for-in-loop
-- Much more function documentation in mw.loader.work()
-- Moved caching of wgResourceLoaderMaxQueryLength out of the loop and renamed 'limit' to 'maxQueryLength

Back-end changed provided through patch by Roan Kattouw (to avoid broken code between commits):
* New method in ResourceLoader: addSource(). During construction of ResourceLoader this will be called by default for 'local' with loadScript property set to $wgLoadScript. Additional sources can be registered through $wgResourceLoaderSources (empty array by default)
* Calling mw.loader.addSource from the startup module
* Passing source to mw.loader.register from startup module
* Some new static helper methods

Use:
* By default nothing should change in core, all modules simply default to 'local'. This info originates from the getSource()-method of the ResourceLoaderModule class, which is inherited to all core ResourceLoaderModule-implementations (none override it)
* Third-party users and/or extensions can create new classes extending ResourceLoaderModule, re-implementing the getSource-method to return something else.

Basic example:
$wgResourceLoaderSources['mywiki'] = array( 'loadScript' => 'http://example.org/w/load.php' );
class MyCentralWikiModule extends ResourceLoaderModule {
function getSource(){
return 'mywiki';
}
}
$wgResourceModules['cool.stuff'] => array( 'class' => 'MyCentralWikiModule' );

More complicated example
// imagine some stuff with a ForeignGadgetRepo class, putting stuff in $wgResourceLoaderSources in the __construct() method
class ForeignGadgetRepoGadget extends ResourceLoaderModule {
function getSource(){
return $this->source;
}
}

Loading:
Loading is completely transparent, stuff like $wgOut->addModules() or mw.loader.loader/using both take it as any other module and load from the right source accordingly.


--
This commit is part of the ResourceLoader 2 project.
Modified paths:
  • /trunk/phase3/includes/DefaultSettings.php (modified) (history)
  • /trunk/phase3/includes/resourceloader/ResourceLoader.php (modified) (history)
  • /trunk/phase3/includes/resourceloader/ResourceLoaderModule.php (modified) (history)
  • /trunk/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php (modified) (history)
  • /trunk/phase3/resources/mediawiki/mediawiki.js (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/resourceloader/ResourceLoader.php
@@ -30,12 +30,17 @@
3131
3232 /* Protected Static Members */
3333 protected static $filterCacheVersion = 4;
 34+ protected static $requiredSourceProperties = array( 'loadScript' );
3435
3536 /** Array: List of module name/ResourceLoaderModule object pairs */
3637 protected $modules = array();
 38+
3739 /** Associative array mapping module name to info associative array */
3840 protected $moduleInfos = array();
3941
 42+ /** array( 'source-id' => array( 'loadScript' => 'http://.../load.php' ) ) **/
 43+ protected $sources = array();
 44+
4045 /* Protected Methods */
4146
4247 /**
@@ -178,10 +183,16 @@
179184 * Registers core modules and runs registration hooks.
180185 */
181186 public function __construct() {
182 - global $IP, $wgResourceModules;
 187+ global $IP, $wgResourceModules, $wgResourceLoaderSources, $wgLoadScript;
183188
184189 wfProfileIn( __METHOD__ );
185190
 191+ // Add 'local' source first
 192+ $this->addSource( 'local', array( 'loadScript' => $wgLoadScript ) );
 193+
 194+ // Add other sources
 195+ $this->addSource( $wgResourceLoaderSources );
 196+
186197 // Register core modules
187198 $this->register( include( "$IP/resources/Resources.php" ) );
188199 // Register extension modules
@@ -250,7 +261,43 @@
251262 wfProfileOut( __METHOD__ );
252263 }
253264
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+ /**
255302 * Get a list of module names
256303 *
257304 * @return Array: List of module names
@@ -292,6 +339,15 @@
293340 }
294341
295342 /**
 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+ /**
296352 * Outputs a response to a resource load-request, including a content-type header.
297353 *
298354 * @param $context ResourceLoaderContext: Context in which a response should be formed
@@ -660,30 +716,31 @@
661717 * @param $version Integer: Module version number as a timestamp
662718 * @param $dependencies Array: List of module names on which this module depends
663719 * @param $group String: Group which the module is in.
 720+ * @param $source String: Source of the module, or 'local' if not foreign.
664721 * @param $script String: JavaScript code
665722 *
666723 * @return string
667724 */
668 - public static function makeCustomLoaderScript( $name, $version, $dependencies, $group, $script ) {
 725+ public static function makeCustomLoaderScript( $name, $version, $dependencies, $group, $source, $script ) {
669726 $script = str_replace( "\n", "\n\t", trim( $script ) );
670727 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 ) );
673730 }
674731
675732 /**
676733 * Returns JS code which calls mw.loader.register with the given
677734 * parameters. Has three calling conventions:
678735 *
679 - * - ResourceLoader::makeLoaderRegisterScript( $name, $version, $dependencies, $group ):
 736+ * - ResourceLoader::makeLoaderRegisterScript( $name, $version, $dependencies, $group, $source ):
680737 * Register a single module.
681738 *
682739 * - ResourceLoader::makeLoaderRegisterScript( array( $name1, $name2 ) ):
683740 * Register modules with the given names.
684741 *
685742 * - 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 ),
688745 * ...
689746 * ) ):
690747 * Registers modules with the given names and parameters.
@@ -692,22 +749,44 @@
693750 * @param $version Integer: Module version number as a timestamp
694751 * @param $dependencies Array: List of module names on which this module depends
695752 * @param $group String: group which the module is in.
 753+ * @param $source String: source of the module, or 'local' if not foreign
696754 *
697755 * @return string
698756 */
699757 public static function makeLoaderRegisterScript( $name, $version = null,
700 - $dependencies = null, $group = null )
 758+ $dependencies = null, $group = null, $source = null )
701759 {
702760 if ( is_array( $name ) ) {
703761 return Xml::encodeJsCall( 'mw.loader.register', array( $name ) );
704762 } else {
705763 $version = (int) $version > 1 ? (int) $version : 1;
706764 return Xml::encodeJsCall( 'mw.loader.register',
707 - array( $name, $version, $dependencies, $group ) );
 765+ array( $name, $version, $dependencies, $group, $source ) );
708766 }
709767 }
710768
711769 /**
 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+ /**
712791 * Returns JS code which runs given JS code if the client-side framework is
713792 * present.
714793 *
Index: trunk/phase3/includes/resourceloader/ResourceLoaderModule.php
@@ -159,6 +159,16 @@
160160 // Stub, override expected
161161 return null;
162162 }
 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+ }
163173
164174 /**
165175 * Where on the HTML page should this module's JS be loaded?
Index: trunk/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php
@@ -137,6 +137,11 @@
138138 $out = '';
139139 $registrations = array();
140140 $resourceLoader = $context->getResourceLoader();
 141+
 142+ // Register sources
 143+ $out .= ResourceLoader::makeLoaderSourcesScript( $resourceLoader->getSources() );
 144+
 145+ // Register modules
141146 foreach ( $resourceLoader->getModuleNames() as $name ) {
142147 $module = $resourceLoader->getModule( $name );
143148 // Support module loader scripts
@@ -144,9 +149,10 @@
145150 if ( $loader !== false ) {
146151 $deps = $module->getDependencies();
147152 $group = $module->getGroup();
 153+ $source = $module->getSource();
148154 $version = wfTimestamp( TS_ISO_8601_BASIC,
149155 $module->getModifiedTime( $context ) );
150 - $out .= ResourceLoader::makeCustomLoaderScript( $name, $version, $deps, $group, $loader );
 156+ $out .= ResourceLoader::makeCustomLoaderScript( $name, $version, $deps, $group, $source, $loader );
151157 }
152158 // Automatically register module
153159 else {
@@ -154,23 +160,29 @@
155161 // seem to do that, and custom implementations might forget. Coerce it to TS_UNIX
156162 $moduleMtime = wfTimestamp( TS_UNIX, $module->getModifiedTime( $context ) );
157163 $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
159165 // mw.loader.register()
160 - if ( !count( $module->getDependencies() && $module->getGroup() === null ) ) {
 166+ if ( !count( $module->getDependencies() && $module->getGroup() === null && $module->getSource() === 'local' ) ) {
161167 $registrations[] = array( $name, $mtime );
162168 }
163 - // Modules with dependencies but no group pass three arguments
 169+ // Modules with dependencies but no group or foreign source pass three arguments
164170 // (name, timestamp, dependencies) to mw.loader.register()
165 - elseif ( $module->getGroup() === null ) {
 171+ elseif ( $module->getGroup() === null && $module->getSource() === 'local' ) {
166172 $registrations[] = array(
167173 $name, $mtime, $module->getDependencies() );
168174 }
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)
170176 // to mw.loader.register()
171 - else {
 177+ else if ( $module->getSource() === 'local' ) {
172178 $registrations[] = array(
173179 $name, $mtime, $module->getDependencies(), $module->getGroup() );
174180 }
 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+ }
175187 }
176188 }
177189 $out .= ResourceLoader::makeLoaderRegisterScript( $registrations );
Index: trunk/phase3/includes/DefaultSettings.php
@@ -2439,6 +2439,16 @@
24402440 $wgResourceModules = array();
24412441
24422442 /**
 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+/**
24432453 * Maximum time in seconds to cache resources served by the resource loader
24442454 */
24452455 $wgResourceLoaderMaxage = array(
Index: trunk/phase3/resources/mediawiki/mediawiki.js
@@ -299,18 +299,34 @@
300300 * making it impossible to hold back registration of jquery until after
301301 * mediawiki.
302302 *
 303+ * For exact details on support for script, style and messages, look at
 304+ * mw.loader.implement.
 305+ *
303306 * Format:
304307 * {
305308 * '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+ * }
312318 * }
313319 */
314320 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 = {},
315331 // List of modules which will be loaded as when ready
316332 batch = [],
317333 // List of modules to be loaded
@@ -724,24 +740,41 @@
725741 }
726742 }
727743
 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+
728762 /* Public Methods */
729763
730764 /**
731765 * Requests dependencies from server, loading and executing when things when ready.
732766 */
733767 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' )
739773 },
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 );
744777
745 - // Appends a list of modules to the batch
 778+ // Appends a list of modules from the queue to the batch
746779 for ( var q = 0; q < queue.length; q++ ) {
747780 // Only request modules which are undefined or registered
748781 if ( !( queue[q] in registry ) || registry[queue[q]].state === 'registered' ) {
@@ -755,85 +788,125 @@
756789 }
757790 }
758791 }
759 - // Early exit if there's nothing to load
 792+ // Early exit if there's nothing to load...
760793 if ( !batch.length ) {
761794 return;
762795 }
763 - // Clean up the queue
 796+
 797+ // The queue has been processed into the batch, clear up the queue.
764798 queue = [];
 799+
765800 // Always order modules alphabetically to help reduce cache
766 - // misses for otherwise identical content
 801+ // misses for otherwise identical content.
767802 batch.sort();
 803+
 804+ // Split batch by source and by group.
768805 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] = {};
772810 }
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];
774816 }
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+ }
781843 }
782 - }
783844
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
792851
793 - reqs[0] = {}; // { prefix: [ suffixes ] }
 852+ moduleMap = {}; // { prefix: [ suffixes ] }
794853
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
804863
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;
812878 }
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 );
815882 }
816 - reqs[r][prefix].push( suffix );
817 - l += bytesAdded;
818883 }
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] );
823900 }
 901+ return true;
824902 }
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 );
837906 }
 907+
 908+ sources[id] = props;
 909+
 910+ return true;
838911 };
839912
840913 /**
@@ -845,13 +918,16 @@
846919 * @param dependencies {String|Array|Function}: One string or array of strings of module
847920 * names on which this module depends, or a function that returns that array.
848921 * @param group {String}: Group which the module is in (optional, defaults to null)
 922+ * @param source {String}: Name of the source. Defaults to local.
849923 */
850 - this.register = function( module, version, dependencies, group ) {
 924+ this.register = function( module, version, dependencies, group, source ) {
851925 // Allow multiple registration
852926 if ( typeof module === 'object' ) {
853927 for ( var m = 0; m < module.length; m++ ) {
 928+ // module is an array of module names
854929 if ( typeof module[m] === 'string' ) {
855930 mw.loader.register( module[m] );
 931+ // module is an array of arrays
856932 } else if ( typeof module[m] === 'object' ) {
857933 mw.loader.register.apply( mw.loader, module[m] );
858934 }
@@ -867,10 +943,11 @@
868944 }
869945 // List the module as registered
870946 registry[module] = {
871 - 'state': 'registered',
 947+ 'version': version !== undefined ? parseInt( version, 10 ) : 0,
 948+ 'dependencies': [],
872949 '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'
875952 };
876953 if ( typeof dependencies === 'string' ) {
877954 // Allow dependencies to be given as a single module name

Follow-up revisions

RevisionCommit summaryAuthorDate
r93248Release notes for r93247.krinkle21:14, 26 July 2011
r97160Followup r93247: add apiScript as a property of a ResourceLoader source.catrope15:40, 15 September 2011
r97173Merged revisions 97087,97091-97092,97094,97096-97098,97100-97101,97103,97136,...dantman16:19, 15 September 2011

Comments

#Comment by Catrope (talk | contribs)   14:57, 3 October 2011

Needs review from someone other than me, because I wrote the PHP code in this commit.

#Comment by 😂 (talk | contribs)   17:24, 18 November 2011

PHP looks ok as of r97160.

#Comment by Catrope (talk | contribs)   16:55, 21 December 2011

Marking OK per Chad's review of the PHP code and my review of the JS code.

Status & tagging log