Merge "Almost invisible change on UI colors to align with Wikimedia color palette"
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.js
index 6280c95..fceeb64 100644 (file)
@@ -7,7 +7,9 @@
  * @alternateClassName mediaWiki
  * @singleton
  */
-/*jshint latedef:false */
+
+/* eslint-disable no-use-before-define */
+
 ( function ( $ ) {
        'use strict';
 
@@ -31,7 +33,7 @@
         * @return {string} hash as an seven-character base 36 string
         */
        function fnv132( str ) {
-               /*jshint bitwise:false */
+               /* eslint-disable no-bitwise */
                var hash = 0x811C9DC5,
                        i;
 
@@ -46,6 +48,7 @@
                }
 
                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