+ var i, j, deps;
+ function resolveIndex( dep ) {
+ return typeof dep === 'number' ? modules[ dep ][ 0 ] : dep;
+ }
+ for ( i = 0; i < modules.length; i++ ) {
+ deps = modules[ i ][ 2 ];
+ if ( deps ) {
+ for ( j = 0; j < deps.length; j++ ) {
+ deps[ j ] = resolveIndex( deps[ j ] );
+ }
+ }
+ }
+ }
+
+ /**
+ * Create network requests for a batch of modules.
+ *
+ * This is an internal method for #work(). This must not be called directly
+ * unless the modules are already registered, and no request is in progress,
+ * and the module state has already been set to `loading`.
+ *
+ * @private
+ * @param {string[]} batch
+ */
+ function batchRequest( batch ) {
+ var reqBase, splits, maxQueryLength, b, bSource, bGroup, bSourceGroup,
+ source, group, i, modules, sourceLoadScript,
+ currReqBase, currReqBaseLength, moduleMap, l,
+ lastDotIndex, prefix, suffix, bytesAdded;
+
+ if ( !batch.length ) {
+ return;
+ }
+
+ // Always order modules alphabetically to help reduce cache
+ // misses for otherwise identical content.
+ batch.sort();
+
+ // Build a list of query parameters common to all requests
+ reqBase = {
+ skin: mw.config.get( 'skin' ),
+ lang: mw.config.get( 'wgUserLanguage' ),
+ debug: mw.config.get( 'debug' )
+ };
+ maxQueryLength = mw.config.get( 'wgResourceLoaderMaxQueryLength', 2000 );
+
+ // Split module list by source and by group.
+ splits = {};
+ for ( b = 0; b < batch.length; b++ ) {
+ bSource = registry[ batch[ b ] ].source;
+ bGroup = registry[ batch[ b ] ].group;
+ if ( !hasOwn.call( splits, bSource ) ) {
+ splits[ bSource ] = {};
+ }
+ if ( !hasOwn.call( splits[ bSource ], bGroup ) ) {
+ splits[ bSource ][ bGroup ] = [];
+ }
+ bSourceGroup = splits[ bSource ][ bGroup ];
+ bSourceGroup.push( batch[ b ] );
+ }
+
+ for ( source in splits ) {
+
+ sourceLoadScript = sources[ source ];
+
+ for ( group in splits[ source ] ) {
+
+ // Cache access to currently selected list of
+ // modules for this group from this source.
+ modules = splits[ source ][ group ];
+
+ currReqBase = $.extend( {
+ version: getCombinedVersion( modules )
+ }, reqBase );
+ // For user modules append a user name to the query string.
+ if ( group === 'user' && mw.config.get( 'wgUserName' ) !== null ) {
+ currReqBase.user = mw.config.get( 'wgUserName' );
+ }
+ currReqBaseLength = $.param( currReqBase ).length;
+ // We may need to split up the request to honor the query string length limit,
+ // so build it piece by piece.
+ l = currReqBaseLength + 9; // '&modules='.length == 9
+
+ moduleMap = {}; // { prefix: [ suffixes ] }
+
+ for ( i = 0; i < modules.length; i++ ) {
+ // Determine how many bytes this module would add to the query string
+ lastDotIndex = modules[ i ].lastIndexOf( '.' );
+
+ // If lastDotIndex is -1, substr() returns an empty string
+ prefix = modules[ i ].substr( 0, lastDotIndex );
+ suffix = modules[ i ].slice( lastDotIndex + 1 );
+
+ bytesAdded = hasOwn.call( moduleMap, prefix )
+ ? suffix.length + 3 // '%2C'.length == 3
+ : modules[ i ].length + 3; // '%7C'.length == 3
+
+ // If the url would become too long, create a new one,
+ // but don't create empty requests
+ if ( maxQueryLength > 0 && !$.isEmptyObject( moduleMap ) && l + bytesAdded > maxQueryLength ) {
+ // This url would become too long, create a new one, and start the old one
+ doRequest( moduleMap, currReqBase, sourceLoadScript );
+ moduleMap = {};
+ l = currReqBaseLength + 9;
+ mw.track( 'resourceloader.splitRequest', { maxQueryLength: maxQueryLength } );
+ }
+ if ( !hasOwn.call( moduleMap, prefix ) ) {
+ moduleMap[ prefix ] = [];
+ }
+ moduleMap[ prefix ].push( suffix );
+ l += bytesAdded;
+ }
+ // If there's anything left in moduleMap, request that too
+ if ( !$.isEmptyObject( moduleMap ) ) {
+ doRequest( moduleMap, currReqBase, sourceLoadScript );
+ }
+ }
+ }
+ }
+
+ /**
+ * Evaluate a batch of load.php responses retrieved from mw.loader.store.
+ *
+ * @private
+ * @param {string[]} implementations Array containing pieces of JavaScript code in the
+ * form of calls to mw.loader#implement().
+ * @param {Function} cb Callback in case of failure
+ * @param {Error} cb.err
+ */
+ function batchEval( implementations, cb ) {
+ if ( !implementations.length ) {
+ return;
+ }
+ mw.requestIdleCallback( function iterate( deadline ) {
+ while ( implementations[ 0 ] && deadline.timeRemaining() > 5 ) {
+ try {
+ $.globalEval( implementations.shift() );
+ } catch ( err ) {
+ cb( err );
+ return;
+ }
+ }
+ if ( implementations[ 0 ] ) {
+ mw.requestIdleCallback( iterate );