* 'group': 'somegroup', (or) null
* 'source': 'local', (or) 'anotherwiki'
* 'skip': 'return !!window.Example', (or) null
+ * 'module': export Object
*
* // Set from execute() or mw.loader.state()
* 'state': 'registered', 'loaded', 'loading', 'ready', 'error', or 'missing'
// List of modules which will be loaded as when ready
batch = [],
+ // Pending queueModuleScript() requests
+ handlingPendingRequests = false,
+ pendingRequests = [],
+
// List of modules to be loaded
queue = [],
} );
}
+ /**
+ * Queue the loading and execution of a script for a particular module.
+ *
+ * @private
+ * @param {string} src URL of the script
+ * @param {string} [moduleName] Name of currently executing module
+ * @return {jQuery.Promise}
+ */
+ function queueModuleScript( src ) {
+ var r = $.Deferred();
+
+ pendingRequests.push( function () {
+ addScript( src ).always( function () {
+ r.resolve();
+
+ // Start the next one (if any)
+ if ( pendingRequests[ 0 ] ) {
+ pendingRequests.shift()();
+ } else {
+ handlingPendingRequests = false;
+ }
+ } );
+ } );
+ if ( !handlingPendingRequests && pendingRequests[ 0 ] ) {
+ handlingPendingRequests = true;
+ pendingRequests.shift()();
+ }
+ return r.promise();
+ }
+
/**
* Utility function for execute()
*
handlePending( module );
};
nestedAddScript = function ( arr, callback, i ) {
- // Recursively call addScript() in its own callback
+ // Recursively call queueModuleScript() in its own callback
// for each element of arr.
if ( i >= arr.length ) {
// We're at the end of the array
return;
}
- addScript( arr[ i ] ).always( function () {
+ queueModuleScript( arr[ i ], module ).always( function () {
nestedAddScript( arr, callback, i + 1 );
} );
};
} else if ( $.isFunction( script ) ) {
// Pass jQuery twice so that the signature of the closure which wraps
// the script can bind both '$' and 'jQuery'.
- script( $, $ );
+ script( $, $, mw.loader.require, registry[ module ].module );
markModuleReady();
+
} else if ( typeof script === 'string' ) {
// Site and user modules are legacy scripts that run in the global scope.
// This is transported as a string instead of a function to avoid needing
}
// List the module as registered
registry[ module ] = {
+ // Exposed to execute() for mw.loader.implement() closures.
+ // Import happens via require().
+ module: {
+ exports: {}
+ },
version: version !== undefined ? String( version ) : '',
dependencies: [],
group: typeof group === 'string' ? group : null,
} );
},
+ /**
+ * Get the exported value of a module.
+ *
+ * Module provide this value via their local `module.exports`.
+ *
+ * @since 1.27
+ * @return {Array}
+ */
+ require: function ( moduleName ) {
+ var state = mw.loader.getState( moduleName );
+
+ // Only ready mudules can be required
+ if ( state !== 'ready' ) {
+ // Module may've forgotten to declare a dependency
+ throw new Error( 'Module "' + moduleName + '" is not loaded.' );
+ }
+
+ return registry[ moduleName ].module.exports;
+ },
+
/**
* @inheritdoc mw.inspect#runReports
* @method