Apply coding conventions for JavaScript
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.js
index 4516e20..8153fe3 100644 (file)
                 */
                messages: new Map(),
 
+               /**
+                * Templates associated with a module
+                * @property {mw.Map}
+                */
+               templates: new Map(),
+
                /* Public Methods */
 
                /**
                        }
 
                        /**
-                        * Generates an ISO8601 "basic" string from a UNIX timestamp
+                        * Convert UNIX timestamp to ISO8601 format
+                        * @param {number} timestamp UNIX timestamp
                         * @private
                         */
                        function formatVersionNumber( timestamp ) {
                        function sortDependencies( module, resolved, unresolved ) {
                                var n, deps, len, skip;
 
-                               if ( registry[module] === undefined ) {
+                               if ( !hasOwn.call( registry, module ) ) {
                                        throw new Error( 'Unknown dependency: ' + module );
                                }
 
                                // Build a list of modules which are in one of the specified states
                                for ( s = 0; s < states.length; s += 1 ) {
                                        for ( m = 0; m < modules.length; m += 1 ) {
-                                               if ( registry[modules[m]] === undefined ) {
+                                               if ( !hasOwn.call( registry, modules[m] ) ) {
                                                        // Module does not exist
                                                        if ( states[s] === 'unregistered' ) {
                                                                // OK, undefined
                                var key, value, media, i, urls, cssHandle, checkCssHandles,
                                        cssHandlesRegistered = false;
 
-                               if ( registry[module] === undefined ) {
+                               if ( !hasOwn.call( registry, module ) ) {
                                        throw new Error( 'Module has not been registered yet: ' + module );
                                } else if ( registry[module].state === 'registered' ) {
                                        throw new Error( 'Module has not been requested from the server yet: ' + module );
                                        mw.messages.set( registry[module].messages );
                                }
 
+                               // Initialise templates
+                               if ( registry[module].templates ) {
+                                       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.
                                                        var check = checkCssHandles;
                                                        pending++;
                                                        return function () {
-                                                               if (check) {
+                                                               if ( check ) {
                                                                        pending--;
                                                                        check();
                                                                        check = undefined; // Revoke
                                addScript( sourceLoadScript + '?' + $.param( request ) + '&*', null, async );
                        }
 
+                       /**
+                        * Resolve indexed dependencies.
+                        *
+                        * ResourceLoader uses an optimization to save space which replaces module names in
+                        * dependency lists with the index of that module within the array of module
+                        * registration data if it exists. The benefit is a significant reduction in the data
+                        * size of the startup module. This function changes those dependency lists back to
+                        * arrays of strings.
+                        *
+                        * @param {Array} modules Modules array
+                        */
+                       function resolveIndexedDependencies( modules ) {
+                               var i, iLen, j, jLen, module, dependency;
+
+                               // Expand indexed dependency names
+                               for ( i = 0, iLen = modules.length; i < iLen; i++ ) {
+                                       module = modules[i];
+                                       if ( module[2] ) {
+                                               for ( j = 0, jLen = module[2].length; j < jLen; j++ ) {
+                                                       dependency = module[2][j];
+                                                       if ( typeof dependency === 'number' ) {
+                                                               module[2][j] = modules[dependency][0];
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
                        /* Public Members */
                        return {
                                /**
                                        // Appends a list of modules from the queue to the batch
                                        for ( q = 0; q < queue.length; q += 1 ) {
                                                // Only request modules which are registered
-                                               if ( registry[queue[q]] !== undefined && registry[queue[q]].state === 'registered' ) {
+                                               if ( hasOwn.call( registry, queue[q] ) && registry[queue[q]].state === 'registered' ) {
                                                        // Prevent duplicate entries
                                                        if ( $.inArray( queue[q], batch ) === -1 ) {
                                                                batch[batch.length] = queue[q];
                                        for ( b = 0; b < batch.length; b += 1 ) {
                                                bSource = registry[batch[b]].source;
                                                bGroup = registry[batch[b]].group;
-                                               if ( splits[bSource] === undefined ) {
+                                               if ( !hasOwn.call( splits, bSource ) ) {
                                                        splits[bSource] = {};
                                                }
-                                               if ( splits[bSource][bGroup] === undefined ) {
+                                               if ( !hasOwn.call( splits[bSource], bGroup ) ) {
                                                        splits[bSource][bGroup] = [];
                                                }
                                                bSourceGroup = splits[bSource][bGroup];
                                                                prefix = modules[i].substr( 0, lastDotIndex );
                                                                suffix = modules[i].slice( lastDotIndex + 1 );
 
-                                                               bytesAdded = moduleMap[prefix] !== undefined
+                                                               bytesAdded = hasOwn.call( moduleMap, prefix )
                                                                        ? suffix.length + 3 // '%2C'.length == 3
                                                                        : modules[i].length + 3; // '%7C'.length == 3
 
                                                                        async = true;
                                                                        l = currReqBaseLength + 9;
                                                                }
-                                                               if ( moduleMap[prefix] === undefined ) {
+                                                               if ( !hasOwn.call( moduleMap, prefix ) ) {
                                                                        moduleMap[prefix] = [];
                                                                }
                                                                moduleMap[prefix].push( suffix );
                                 *
                                 * @param {string} id Short string representing a source wiki, used internally for
                                 *  registered modules to indicate where they should be loaded from (usually lowercase a-z).
-                                * @param {Object|string} loadUrl load.php url, may be an object for backwards-compatability
+                                * @param {Object|string} loadUrl load.php url, may be an object for backwards-compatibility
                                 * @return {boolean}
                                 */
                                addSource: function ( id, loadUrl ) {
                                                return true;
                                        }
 
-                                       if ( sources[id] !== undefined ) {
+                                       if ( hasOwn.call( sources, id ) ) {
                                                throw new Error( 'source already registered: ' + id );
                                        }
 
                                 * Register a module, letting the system know about it and its
                                 * properties. Startup modules contain calls to this function.
                                 *
-                                * @param {string} module Module name
+                                * When using multiple module registration by passing an array, dependencies that
+                                * are specified as references to modules within the array will be resolved before
+                                * the modules are registered.
+                                *
+                                * @param {string|Array} module Module name or array of arrays, each containing
+                                *   a list of arguments compatible with this method
                                 * @param {number} version Module version number as a timestamp (falls backs to 0)
                                 * @param {string|Array|Function} dependencies One string or array of strings of module
                                 *  names on which this module depends, or a function that returns that array.
                                 * @param {string} [skip=null] Script body of the skip function
                                 */
                                register: function ( module, version, dependencies, group, source, skip ) {
-                                       var m;
+                                       var i, len;
                                        // Allow multiple registration
                                        if ( typeof module === 'object' ) {
-                                               for ( m = 0; m < module.length; m += 1 ) {
+                                               resolveIndexedDependencies( module );
+                                               for ( i = 0, len = module.length; i < len; i++ ) {
                                                        // module is an array of module names
-                                                       if ( typeof module[m] === 'string' ) {
-                                                               mw.loader.register( module[m] );
+                                                       if ( typeof module[i] === 'string' ) {
+                                                               mw.loader.register( module[i] );
                                                        // module is an array of arrays
-                                                       } else if ( typeof module[m] === 'object' ) {
-                                                               mw.loader.register.apply( mw.loader, module[m] );
+                                                       } else if ( typeof module[i] === 'object' ) {
+                                                               mw.loader.register.apply( mw.loader, module[i] );
                                                        }
                                                }
                                                return;
                                        if ( typeof module !== 'string' ) {
                                                throw new Error( 'module must be a string, not a ' + typeof module );
                                        }
-                                       if ( registry[module] !== undefined ) {
+                                       if ( hasOwn.call( registry, module ) ) {
                                                throw new Error( 'module already registered: ' + module );
                                        }
                                        // List the module as registered
                                 * whether it's safe to extend the stylesheet (see #canExpandStylesheetWith).
                                 *
                                 * @param {Object} msgs List of key/value pairs to be added to mw#messages.
+                                * @param {Object} [templates] List of key/value pairs to be added to mw#templates.
                                 */
-                               implement: function ( module, script, style, msgs ) {
+                               implement: function ( module, script, style, msgs, templates ) {
                                        // Validate input
                                        if ( typeof module !== 'string' ) {
                                                throw new Error( 'module must be a string, not a ' + typeof module );
                                        if ( !$.isPlainObject( msgs ) ) {
                                                throw new Error( 'msgs must be an object, not a ' + typeof msgs );
                                        }
+                                       if ( templates !== undefined && !$.isPlainObject( templates ) ) {
+                                               throw new Error( 'templates must be an object, not a ' + typeof templates );
+                                       }
                                        // Automatically register module
-                                       if ( registry[module] === undefined ) {
+                                       if ( !hasOwn.call( registry, module ) ) {
                                                mw.loader.register( module );
                                        }
                                        // Check for duplicate implementation
-                                       if ( registry[module] !== undefined && registry[module].script !== undefined ) {
+                                       if ( hasOwn.call( registry, module ) && registry[module].script !== undefined ) {
                                                throw new Error( 'module already implemented: ' + module );
                                        }
                                        // Attach components
                                        registry[module].script = script;
                                        registry[module].style = style;
                                        registry[module].messages = msgs;
+                                       // Templates are optional (for back-compat)
+                                       registry[module].templates = templates || {};
                                        // The module may already have been marked as erroneous
                                        if ( $.inArray( registry[module].state, ['error', 'missing'] ) === -1 ) {
                                                registry[module].state = 'loaded';
                                 * @param {Function} [ready] Callback to execute when all dependencies are ready
                                 * @param {Function} [error] Callback to execute if one or more dependencies failed
                                 * @return {jQuery.Promise}
+                                * @since 1.23 this returns a promise
                                 */
                                using: function ( dependencies, ready, error ) {
                                        var deferred = $.Deferred();
                                        // an array of unrelated modules, whereas the modules passed to
                                        // using() are related and must all be loaded.
                                        for ( filtered = [], m = 0; m < modules.length; m += 1 ) {
-                                               module = registry[modules[m]];
-                                               if ( module !== undefined ) {
+                                               if ( hasOwn.call( registry, modules[m] ) ) {
+                                                       module = registry[modules[m]];
                                                        if ( $.inArray( module.state, ['error', 'missing'] ) === -1 ) {
                                                                filtered[filtered.length] = modules[m];
                                                        }
                                                }
                                                return;
                                        }
-                                       if ( registry[module] === undefined ) {
+                                       if ( !hasOwn.call( registry, module ) ) {
                                                mw.loader.register( module );
                                        }
                                        if ( $.inArray( state, ['ready', 'error', 'missing'] ) !== -1
                                /**
                                 * Get the version of a module.
                                 *
-                                * @param {string} module Name of module to get version for
+                                * @param {string} module Name of module
                                 * @return {string|null} The version, or null if the module (or its version) is not
                                 *  in the registry.
                                 */
                                getVersion: function ( module ) {
-                                       if ( registry[module] !== undefined && registry[module].version !== undefined ) {
-                                               return formatVersionNumber( registry[module].version );
+                                       if ( !hasOwn.call( registry, module ) || registry[module].version === undefined ) {
+                                               return null;
                                        }
-                                       return null;
+                                       return formatVersionNumber( registry[module].version );
                                },
 
                                /**
                                 * Get the state of a module.
                                 *
-                                * @param {string} module Name of module to get state for
+                                * @param {string} module Name of module
+                                * @return {string|null} The state, or null if the module (or its version) is not
+                                *  in the registry.
                                 */
                                getState: function ( module ) {
-                                       if ( registry[module] !== undefined && registry[module].state !== undefined ) {
-                                               return registry[module].state;
+                                       if ( !hasOwn.call( registry, module ) || registry[module].state === undefined ) {
+                                               return null;
                                        }
-                                       return null;
+                                       return registry[module].state;
                                },
 
                                /**
                                         * @return {string|null} Module key or null if module does not exist
                                         */
                                        getModuleKey: function ( module ) {
-                                               return typeof registry[module] === 'object' ?
+                                               return hasOwn.call( registry, module ) ?
                                                        ( module + '@' + registry[module].version ) : null;
                                        },
 
                                                        return;
                                                }
 
-                                               if ( !mw.config.get( 'wgResourceLoaderStorageEnabled' ) || mw.config.get( 'debug' ) ) {
-                                                       // Disabled by configuration, or because debug mode is set
+                                               if ( !mw.config.get( 'wgResourceLoaderStorageEnabled' ) ) {
+                                                       // Disabled by configuration.
+                                                       // Clear any previous store to free up space. (T66721)
+                                                       mw.loader.store.clear();
+                                                       mw.loader.store.enabled = false;
+                                                       return;
+                                               }
+                                               if ( mw.config.get( 'debug' ) ) {
+                                                       // Disable module store in debug mode
                                                        mw.loader.store.enabled = false;
                                                        return;
                                                }
                                                        // Unversioned, private, or site-/user-specific
                                                        ( !descriptor.version || $.inArray( descriptor.group, [ 'private', 'user', 'site' ] ) !== -1 ) ||
                                                        // Partial descriptor
-                                                       $.inArray( undefined, [ descriptor.script, descriptor.style, descriptor.messages ] ) !== -1
+                                                       $.inArray( undefined, [ descriptor.script, descriptor.style,
+                                                                       descriptor.messages, descriptor.templates ] ) !== -1
                                                ) {
                                                        // Decline to store
                                                        return false;
                                                                        String( descriptor.script ) :
                                                                        JSON.stringify( descriptor.script ),
                                                                JSON.stringify( descriptor.style ),
-                                                               JSON.stringify( descriptor.messages )
+                                                               JSON.stringify( descriptor.messages ),
+                                                               JSON.stringify( descriptor.templates )
                                                        ];
                                                        // Attempted workaround for a possible Opera bug (bug 57567).
                                                        // This regex should never match under sane conditions.