/**
* Write a message the console's warning channel.
- * Also logs a stacktrace for easier debugging.
* Actions not supported by the browser console are silently ignored.
*
* @param {string...} msg Messages to output to console
var console = window.console;
if ( console && console.warn && console.warn.apply ) {
console.warn.apply( console, arguments );
- if ( console.trace ) {
- console.trace();
- }
}
};
function addEmbeddedCSS( cssText, callback ) {
var $style, styleEl;
+ function fireCallbacks() {
+ var oldCallbacks = cssCallbacks;
+ // Reset cssCallbacks variable so it's not polluted by any calls to
+ // addEmbeddedCSS() from one of the callbacks (T105973)
+ cssCallbacks = $.Callbacks();
+ oldCallbacks.fire().empty();
+ }
+
if ( callback ) {
cssCallbacks.add( callback );
}
} else {
styleEl.appendChild( document.createTextNode( cssText ) );
}
- cssCallbacks.fire().empty();
+ fireCallbacks();
return;
}
}
$( newStyleTag( cssText, getMarker() ) ).data( 'ResourceLoaderDynamicStyleTag', true );
- cssCallbacks.fire().empty();
+ fireCallbacks();
}
/**
}
/**
- * Adds a script tag to the DOM, either using document.write or low-level DOM manipulation,
- * depending on whether document-ready has occurred yet and whether we are in async mode.
+ * Load and execute a script with callback.
*
* @private
* @param {string} src URL to script, will be used as the src attribute in the script tag
* @param {Function} [callback] Callback which will be run when the script is done
- * @param {boolean} [async=false] Whether to load modules asynchronously.
- * Ignored (and defaulted to `true`) if the document-ready event has already occurred.
*/
- function addScript( src, callback, async ) {
- // Using isReady directly instead of storing it locally from a $().ready callback (bug 31895)
- if ( $.isReady || async ) {
- $.ajax( {
- url: src,
- dataType: 'script',
- // Force jQuery behaviour to be for crossDomain. Otherwise jQuery would use
- // XHR for a same domain request instead of <script>, which changes the request
- // headers (potentially missing a cache hit), and reduces caching in general
- // since browsers cache XHR much less (if at all). And XHR means we retreive
- // text, so we'd need to $.globalEval, which then messes up line numbers.
- crossDomain: true,
- cache: true,
- async: true
- } ).always( callback );
- } else {
- /*jshint evil:true */
- document.write( mw.html.element( 'script', { 'src': src }, '' ) );
- if ( callback ) {
- // Document.write is synchronous, so this is called when it's done.
- // FIXME: That's a lie. doc.write isn't actually synchronous.
- callback();
- }
- }
+ function addScript( src, callback ) {
+ $.ajax( {
+ url: src,
+ dataType: 'script',
+ // Force jQuery behaviour to be for crossDomain. Otherwise jQuery would use
+ // XHR for a same domain request instead of <script>, which changes the request
+ // headers (potentially missing a cache hit), and reduces caching in general
+ // since browsers cache XHR much less (if at all). And XHR means we retreive
+ // text, so we'd need to $.globalEval, which then messes up line numbers.
+ crossDomain: true,
+ cache: true
+ } ).always( callback );
}
/**
if ( !hasOwn.call( registry, module ) ) {
throw new Error( 'Module has not been registered yet: ' + module );
- } else if ( registry[module].state === 'registered' ) {
+ }
+ if ( registry[module].state === 'registered' ) {
throw new Error( 'Module has not been requested from the server yet: ' + module );
- } else if ( registry[module].state === 'loading' ) {
+ }
+ if ( registry[module].state === 'loading' ) {
throw new Error( 'Module has not completed loading yet: ' + module );
- } else if ( registry[module].state === 'ready' ) {
+ }
+ if ( registry[module].state === 'ready' ) {
throw new Error( 'Module has already been executed: ' + module );
}
registry[module].state = 'ready';
handlePending( module );
};
- nestedAddScript = function ( arr, callback, async, i ) {
+ nestedAddScript = function ( arr, callback, i ) {
// Recursively call addScript() in its own callback
// for each element of arr.
if ( i >= arr.length ) {
}
addScript( arr[i], function () {
- nestedAddScript( arr, callback, async, i + 1 );
- }, async );
+ nestedAddScript( arr, callback, i + 1 );
+ } );
};
if ( $.isArray( script ) ) {
- nestedAddScript( script, markModuleReady, registry[module].async, 0 );
+ nestedAddScript( script, markModuleReady, 0 );
} else if ( $.isFunction( script ) ) {
- registry[module].state = 'ready';
// Pass jQuery twice so that the signature of the closure which wraps
// the script can bind both '$' and 'jQuery'.
+ registry[module].state = 'ready';
script( $, $ );
handlePending( module );
+ } else if ( typeof script === 'string' ) {
+ // Site and user modules are a legacy scripts that run in the global scope.
+ // This is transported as a string instead of a function to avoid needing
+ // to use string manipulation to undo the function wrapper.
+ if ( module === 'user' ) {
+ // Implicit dependency on the site module. Not real dependency because
+ // it should run after 'site' regardless of whether it succeeds or fails.
+ mw.loader.using( 'site' ).always( function () {
+ registry[module].state = 'ready';
+ $.globalEval( script );
+ handlePending( module );
+ } );
+ } else {
+ registry[module].state = 'ready';
+ $.globalEval( script );
+ handlePending( module );
+ }
}
} catch ( e ) {
// This needs to NOT use mw.log because these errors are common in production mode
mw.templates.set( module, registry[module].templates );
}
- if ( $.isReady || registry[module].async ) {
- // Make sure we don't run the scripts until all (potentially asynchronous)
- // stylesheet insertions have completed.
- ( function () {
- var pending = 0;
- checkCssHandles = function () {
- // cssHandlesRegistered ensures we don't take off too soon, e.g. when
- // one of the cssHandles is fired while we're still creating more handles.
- if ( cssHandlesRegistered && pending === 0 && runScript ) {
- runScript();
- runScript = undefined; // Revoke
+ // Make sure we don't run the scripts until all stylesheet insertions have completed.
+ ( function () {
+ var pending = 0;
+ checkCssHandles = function () {
+ // cssHandlesRegistered ensures we don't take off too soon, e.g. when
+ // one of the cssHandles is fired while we're still creating more handles.
+ if ( cssHandlesRegistered && pending === 0 && runScript ) {
+ runScript();
+ runScript = undefined; // Revoke
+ }
+ };
+ cssHandle = function () {
+ var check = checkCssHandles;
+ pending++;
+ return function () {
+ if ( check ) {
+ pending--;
+ check();
+ check = undefined; // Revoke
}
};
- cssHandle = function () {
- var check = checkCssHandles;
- pending++;
- return function () {
- if ( check ) {
- pending--;
- check();
- check = undefined; // Revoke
- }
- };
- };
- }() );
- } else {
- // We are in blocking mode, and so we can't afford to wait for CSS
- cssHandle = function () {};
- // Run immediately
- checkCssHandles = runScript;
- }
+ };
+ }() );
// Process styles (see also mw.loader.implement)
// * back-compat: { <media>: css }
* @param {string|string[]} dependencies Module name or array of string module names
* @param {Function} [ready] Callback to execute when all dependencies are ready
* @param {Function} [error] Callback to execute when any dependency fails
- * @param {boolean} [async=false] Whether to load modules asynchronously.
- * Ignored (and defaulted to `true`) if the document-ready event has already occurred.
*/
- function request( dependencies, ready, error, async ) {
+ function request( dependencies, ready, error ) {
// Allow calling by single module name
if ( typeof dependencies === 'string' ) {
dependencies = [dependencies];
return;
}
queue.push( module );
- if ( async ) {
- registry[module].async = true;
- }
}
} );
}
/**
- * Asynchronously append a script tag to the end of the body
- * that invokes load.php
+ * Load modules from load.php
* @private
* @param {Object} moduleMap Module map, see #buildModulesString
* @param {Object} currReqBase Object with other parameters (other than 'modules') to use in the request
* @param {string} sourceLoadScript URL of load.php
- * @param {boolean} async Whether to load modules asynchronously.
- * Ignored (and defaulted to `true`) if the document-ready event has already occurred.
*/
- function doRequest( moduleMap, currReqBase, sourceLoadScript, async ) {
+ function doRequest( moduleMap, currReqBase, sourceLoadScript ) {
var request = $.extend(
{ modules: buildModulesString( moduleMap ) },
currReqBase
);
request = sortQuery( request );
// Support: IE6
- // Append &* to satisfy load.php's WebRequest::checkUrlExtension test. This script
- // isn't actually used in IE6, but MediaWiki enforces it in general.
- addScript( sourceLoadScript + '?' + $.param( request ) + '&*', null, async );
+ // Append &* to satisfy load.php's WebRequest::checkUrlExtension test.
+ // This script isn't actually used in IE6, but MediaWiki enforces it in general.
+ addScript( sourceLoadScript + '?' + $.param( request ) + '&*' );
}
/**
var reqBase, splits, maxQueryLength, q, b, bSource, bGroup, bSourceGroup,
source, concatSource, origBatch, group, i, modules, sourceLoadScript,
currReqBase, currReqBaseLength, moduleMap, l,
- lastDotIndex, prefix, suffix, bytesAdded, async;
+ lastDotIndex, prefix, suffix, bytesAdded;
// Build a list of request parameters common to all requests.
reqBase = {
currReqBase.user = mw.config.get( 'wgUserName' );
}
currReqBaseLength = $.param( currReqBase ).length;
- async = true;
// 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
if ( maxQueryLength > 0 && !$.isEmptyObject( moduleMap ) && l + bytesAdded > maxQueryLength ) {
// This request would become too long, create a new one
// and fire off the old one
- doRequest( moduleMap, currReqBase, sourceLoadScript, async );
+ doRequest( moduleMap, currReqBase, sourceLoadScript );
moduleMap = {};
- async = true;
l = currReqBaseLength + 9;
mw.track( 'resourceloader.splitRequest', { maxQueryLength: maxQueryLength } );
}
moduleMap[prefix] = [];
}
moduleMap[prefix].push( suffix );
- if ( !registry[modules[i]].async ) {
- // If this module is blocking, make the entire request blocking
- // This is slightly suboptimal, but in practice mixing of blocking
- // and async modules will only occur in debug mode.
- async = false;
- }
l += bytesAdded;
}
// If there's anything left in moduleMap, request that too
if ( !$.isEmptyObject( moduleMap ) ) {
- doRequest( moduleMap, currReqBase, sourceLoadScript, async );
+ doRequest( moduleMap, currReqBase, sourceLoadScript );
}
}
}
if ( typeof module !== 'string' ) {
throw new Error( 'module must be of type string, not ' + typeof module );
}
- if ( script && !$.isFunction( script ) && !$.isArray( script ) ) {
- throw new Error( 'script must be of type function or array, not ' + typeof script );
+ if ( script && !$.isFunction( script ) && !$.isArray( script ) && typeof script !== 'string' ) {
+ throw new Error( 'script must be of type function, array, or script; not ' + typeof script );
}
if ( style && !$.isPlainObject( style ) ) {
throw new Error( 'style must be of type object, not ' + typeof style );
* @param {string} [type='text/javascript'] MIME type to use if calling with a URL of an
* external script or style; acceptable values are "text/css" and
* "text/javascript"; if no type is provided, text/javascript is assumed.
- * @param {boolean} [async] Whether to load modules asynchronously.
- * Ignored (and defaulted to `true`) if the document-ready event has already occurred.
- * Defaults to `true` if loading a URL, `false` otherwise.
*/
- load: function ( modules, type, async ) {
+ load: function ( modules, type ) {
var filtered, l;
// Validate input
// Allow calling with an external url or single dependency as a string
if ( typeof modules === 'string' ) {
if ( /^(https?:)?\/\//.test( modules ) ) {
- if ( async === undefined ) {
- // Assume async for bug 34542
- async = true;
- }
if ( type === 'text/css' ) {
// Support: IE 7-8
// Use properties instead of attributes as IE throws security
return;
}
if ( type === 'text/javascript' || type === undefined ) {
- addScript( modules, null, async );
+ addScript( modules );
return;
}
// Unknown type
return;
}
// Since some modules are not yet ready, queue up a request.
- request( filtered, undefined, undefined, async );
+ request( filtered, undefined, undefined );
},
/**
// Module failed to load
descriptor.state !== 'ready' ||
// Unversioned, private, or site-/user-specific
- ( !descriptor.version || $.inArray( descriptor.group, [ 'private', 'user', 'site' ] ) !== -1 ) ||
+ ( !descriptor.version || $.inArray( descriptor.group, [ 'private', 'user' ] ) !== -1 ) ||
// Partial descriptor
$.inArray( undefined, [ descriptor.script, descriptor.style,
descriptor.messages, descriptor.templates ] ) !== -1