* @alternateClassName mediaWiki
* @singleton
*/
-/*jshint latedef:false */
+
+/* eslint-disable no-use-before-define */
+
( function ( $ ) {
'use strict';
* @return {string} hash as an seven-character base 36 string
*/
function fnv132( str ) {
- /*jshint bitwise:false */
+ /* eslint-disable no-bitwise */
var hash = 0x811C9DC5,
i;
}
return hash;
+ /* eslint-enable no-bitwise */
}
StringSet = window.Set || ( function () {
return mw.format.apply( null, [ this.map.get( this.key ) ].concat( this.parameters ) );
},
+ // eslint-disable-next-line valid-jsdoc
/**
* Add (does not replace) parameters for `$N` placeholder values.
*
var text;
if ( !this.exists() ) {
- // Use <key> as text if key does not exist
- if ( this.format === 'escaped' || this.format === 'parse' ) {
- // format 'escaped' and 'parse' need to have the brackets and key html escaped
- return mw.html.escape( '<' + this.key + '>' );
- }
- return '<' + this.key + '>';
+ // Use ⧼key⧽ as text if key does not exist
+ // Err on the side of safety, ensure that the output
+ // is always html safe in the event the message key is
+ // missing, since in that case its highly likely the
+ // message key is user-controlled.
+ // '⧼' is used instead of '<' to side-step any
+ // double-escaping issues.
+ // (Keep synchronised with Message::toString() in PHP.)
+ return '⧼' + mw.html.escape( this.key ) + '⧽';
}
if ( this.format === 'plain' || this.format === 'text' || this.format === 'parse' ) {
}
};
+ /* eslint-disable no-console */
log = ( function () {
// Also update the restoration of methods in mediawiki.log.js
// when adding or removing methods here.
log.deprecate = !Object.defineProperty ? function ( obj, key, val ) {
obj[ key ] = val;
} : function ( obj, key, val, msg, logName ) {
+ var logged = new StringSet();
logName = logName || key;
msg = 'Use of "' + logName + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' );
- var logged = new StringSet();
function uniqueTrace() {
var trace = new Error().stack;
if ( logged.has( trace ) ) {
return log;
}() );
+ /* eslint-enable no-console */
/**
* @class mw
}
if ( registry[ module ].skip !== null ) {
- /*jshint evil:true */
+ // eslint-disable-next-line no-new-func
skip = new Function( registry[ module ].skip );
registry[ module ].skip = null;
if ( skip() ) {
) );
}
- unresolved.add( module );
+ unresolved.add( module );
sortDependencies( deps[ i ], resolved, unresolved );
}
}
* @private
* @param {string[]} modules Array of string module names
* @return {Array} List of dependencies, including 'module'.
+ * @throws {Error} If an unregistered module or a dependency loop is encountered
*/
function resolve( modules ) {
- var resolved = [];
- $.each( modules, function ( idx, module ) {
- sortDependencies( module, resolved );
- } );
+ var i, resolved = [];
+ for ( i = 0; i < modules.length; i++ ) {
+ sortDependencies( modules[ i ], resolved );
+ }
return resolved;
}
* Utility function for execute()
*
* @ignore
+ * @param {string} [media] Media attribute
+ * @param {string} url URL
*/
function addLink( media, url ) {
var el = document.createElement( 'link' );
} );
};
- implicitDependencies = ( $.inArray( module, legacyModules ) !== -1 )
- ? []
- : legacyModules;
+ implicitDependencies = ( $.inArray( module, legacyModules ) !== -1 ) ?
+ [] :
+ legacyModules;
if ( module === 'user' ) {
// Implicit dependency on the site module. Not real dependency because
implicitDependencies.push( 'site' );
}
- legacyWait = implicitDependencies.length
- ? mw.loader.using( implicitDependencies )
- : $.Deferred().resolve();
+ legacyWait = implicitDependencies.length ?
+ mw.loader.using( implicitDependencies ) :
+ $.Deferred().resolve();
legacyWait.always( function () {
try {
* to a query string of the form foo.bar,baz|bar.baz,quux
*
* @private
+ * @param {Object} moduleMap Module map
+ * @return {string} Module query string
*/
function buildModulesString( moduleMap ) {
var p, prefix,
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
+ 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
return true;
} );
asyncEval( implementations, function ( err ) {
+ var failed;
// Not good, the cached mw.loader.implement calls failed! This should
// never happen, barring ResourceLoader bugs, browser bugs and PEBKACs.
// Depending on how corrupt the string is, it is likely that some
// modules' implement() succeeded while the ones after the error will
// never run and leave their modules in the 'loading' state forever.
+ mw.loader.store.stats.failed++;
// Since this is an error not caused by an individual module but by
// something that infected the implement call itself, don't take any
mw.track( 'resourceloader.exception', { exception: err, source: 'store-eval' } );
// Re-add the failed ones that are still pending back to the batch
- var failed = $.grep( sourceModules, function ( module ) {
+ failed = $.grep( sourceModules, function ( module ) {
return registry[ module ].state === 'loading';
} );
batchRequest( failed );
deferred.fail( error );
}
- // Resolve entire dependency map
- dependencies = resolve( dependencies );
+ try {
+ // Resolve entire dependency map
+ dependencies = resolve( dependencies );
+ } catch ( e ) {
+ return deferred.reject( e ).promise();
+ }
if ( allReady( dependencies ) ) {
// Run ready immediately
deferred.resolve( mw.loader.require );
*
* @protected
* @since 1.27
+ * @param {string} moduleName Module name
+ * @return {Mixed} Exported value
*/
require: function ( moduleName ) {
var state = mw.loader.getState( moduleName );
items: {},
// Cache hit stats
- stats: { hits: 0, misses: 0, expired: 0 },
+ stats: { hits: 0, misses: 0, expired: 0, failed: 0 },
/**
* Construct a JSON-serializable object representing the content of the store.
*
* @param {string} module Module name
* @param {Object} descriptor The module's descriptor as set in the registry
+ * @return {boolean} Module was set
*/
set: function ( module, descriptor ) {
var args, key, src;
// Partial descriptor
// (e.g. skipped module, or style module with state=ready)
$.inArray( undefined, [ descriptor.script, descriptor.style,
- descriptor.messages, descriptor.templates ] ) !== -1
+ descriptor.messages, descriptor.templates ] ) !== -1
) {
// Decline to store
return false;
}
} catch ( e ) {
mw.track( 'resourceloader.exception', { exception: e, source: 'store-localstorage-json' } );
- return;
+ return false;
}
src = 'mw.loader.implement(' + args.join( ',' ) + ');';
}
mw.loader.store.items[ key ] = src;
mw.loader.store.update();
+ return true;
},
/**
* Iterate through the module store, removing any item that does not correspond
* (in name and version) to an item in the module registry.
+ *
+ * @return {boolean} Store was pruned
*/
prune: function () {
var key, module;
delete mw.loader.store.items[ key ];
}
}
+ return true;
},
/**
* - this.Raw: The raw value is directly included.
* - this.Cdata: The raw value is directly included. An exception is
* thrown if it contains any illegal ETAGO delimiter.
- * See <http://www.w3.org/TR/html401/appendix/notes.html#h-B.3.2>.
+ * See <https://www.w3.org/TR/html401/appendix/notes.html#h-B.3.2>.
* @return {string} HTML
*/
element: function ( name, attrs, contents ) {
* Wrapper object for raw HTML passed to mw.html.element().
*
* @class mw.html.Raw
+ * @constructor
+ * @param {string} value
*/
Raw: function ( value ) {
this.value = value;
* Wrapper object for CDATA element contents passed to mw.html.element()
*
* @class mw.html.Cdata
+ * @constructor
+ * @param {string} value
*/
Cdata: function ( value ) {
this.value = value;
*/
remove: list.remove,
+ // eslint-disable-next-line valid-jsdoc
/**
* Run a hook.
*
* @param {string} [data.module] Name of module which caused the error
*/
function logError( topic, data ) {
+ /* eslint-disable no-console */
var msg,
e = data.exception,
source = data.source,
console.error( String( e ), e );
}
}
+ /* eslint-enable no-console */
}
// Subscribe to error streams