}
}
+ // String format helper. Replaces $1, $2 .. $N placeholders with positional
+ // args. Used by Message.prototype.parser() and exported as mw.format().
+ function format( formatString ) {
+ var parameters = slice.call( arguments, 1 );
+ return formatString.replace( /\$(\d+)/g, function ( str, match ) {
+ var index = parseInt( match, 10 ) - 1;
+ return parameters[index] !== undefined ? parameters[index] : '$' + match;
+ } );
+ }
+
/* Object constructors */
/**
* @class mw.Map
*
* @constructor
- * @param {Object|boolean} [values] Value-bearing object to map, or boolean
- * true to map over the global object. Defaults to an empty object.
+ * @param {Object|boolean} [values] Value-bearing object to map, defaults to an empty object.
+ * For backwards-compatibility with mw.config, this can also be `true` in which case values
+ * will be copied to the Window object as global variables (T72470). Values are copied in one
+ * direction only. Changes to globals are not reflected in the map.
*/
function Map( values ) {
- this.values = values === true ? window : ( values || {} );
- return this;
+ if ( values === true ) {
+ this.values = {};
+
+ // Override #set to also set the global variable
+ this.set = function ( selection, value ) {
+ var s;
+
+ if ( $.isPlainObject( selection ) ) {
+ for ( s in selection ) {
+ setGlobalMapValue( this, s, selection[s] );
+ }
+ return true;
+ }
+ if ( typeof selection === 'string' && arguments.length ) {
+ setGlobalMapValue( this, selection, value );
+ return true;
+ }
+ return false;
+ };
+
+ return;
+ }
+
+ this.values = values || {};
+ }
+
+ /**
+ * Alias property to the global object.
+ *
+ * @private
+ * @static
+ * @param {mw.Map} map
+ * @param {string} key
+ * @param {Mixed} value
+ */
+ function setGlobalMapValue( map, key, value ) {
+ map.values[key] = value;
+ mw.log.deprecate(
+ window,
+ key,
+ value,
+ // Deprecation notice for mw.config globals (T58550, T72470)
+ map === mw.config && 'Use mw.config instead.'
+ );
}
Map.prototype = {
*
* @param {string|Object} selection String key to set value for, or object mapping keys to values.
* @param {Mixed} [value] Value to set (optional, only in use when key is a string)
- * @return {Boolean} This returns true on success, false on failure.
+ * @return {boolean} This returns true on success, false on failure.
*/
set: function ( selection, value ) {
var s;
}
return true;
}
- if ( typeof selection === 'string' && arguments.length > 1 ) {
+ if ( typeof selection === 'string' && arguments.length ) {
this.values[selection] = value;
return true;
}
* This function will not be called for nonexistent messages.
*/
parser: function () {
- var parameters = this.parameters;
- return this.map.get( this.key ).replace( /\$(\d+)/g, function ( str, match ) {
- var index = parseInt( match, 10 ) - 1;
- return parameters[index] !== undefined ? parameters[index] : '$' + match;
- } );
+ return format.apply( null, [ this.map.get( this.key ) ].concat( this.parameters ) );
},
/**
function () { return +new Date(); };
}() ),
+ /**
+ * Format a string. Replace $1, $2 ... $N with positional arguments.
+ *
+ * @method
+ * @since 1.25
+ * @param {string} fmt Format string
+ * @param {Mixed...} parameters Substitutions for $N placeholders.
+ * @return {string} Formatted string
+ */
+ format: format,
+
/**
* Track an analytic event.
*
*
* @property {mw.Map} config
*/
- // Dummy placeholder. Re-assigned in ResourceLoaderStartupModule to an instance of `mw.Map`.
+ // Dummy placeholder. Re-assigned in ResourceLoaderStartUpModule to an instance of `mw.Map`.
config: null,
/**
} );
} catch ( err ) {
// IE8 can throw on Object.defineProperty
+ // Create a copy of the value to the object.
obj[key] = val;
}
};
if ( 'documentMode' in document && document.documentMode <= 9 ) {
$style = getMarker().prev();
- // Verify that the the element before Marker actually is a
+ // Verify that the element before Marker actually is a
// <style> tag and one that came from ResourceLoader
// (not some other style tag or even a `<meta>` or `<script>`).
if ( $style.data( 'ResourceLoaderDynamicStyleTag' ) === true ) {
* @param {string} module Name of module
* @param {Function|Array} script Function with module code or Array of URLs to
* be used as the src attribute of a new `<script>` tag.
- * @param {Object} style Should follow one of the following patterns:
+ * @param {Object} [style] Should follow one of the following patterns:
*
* { "css": [css, ..] }
* { "url": { <media>: [url, ..] } }
* The reason css strings are not concatenated anymore is bug 31676. We now check
* 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} [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, templates ) {
// Validate input
if ( typeof module !== 'string' ) {
- throw new Error( 'module must be a string, not a ' + typeof module );
+ throw new Error( 'module must be of type string, not ' + typeof module );
}
if ( !$.isFunction( script ) && !$.isArray( script ) ) {
- throw new Error( 'script must be a function or an array, not a ' + typeof script );
+ throw new Error( 'script must be of type function or array, not ' + typeof script );
}
- if ( !$.isPlainObject( style ) ) {
- throw new Error( 'style must be an object, not a ' + typeof style );
+ if ( style && !$.isPlainObject( style ) ) {
+ throw new Error( 'style must be of type object, not ' + typeof style );
}
- if ( !$.isPlainObject( msgs ) ) {
- throw new Error( 'msgs must be an object, not a ' + typeof msgs );
+ if ( msgs && !$.isPlainObject( msgs ) ) {
+ throw new Error( 'msgs must be of type object, not a ' + typeof msgs );
}
- if ( templates !== undefined && !$.isPlainObject( templates ) ) {
- throw new Error( 'templates must be an object, not a ' + typeof templates );
+ if ( templates && !$.isPlainObject( templates ) ) {
+ throw new Error( 'templates must be of type object, not a ' + typeof templates );
}
// Automatically register module
if ( !hasOwn.call( registry, module ) ) {
}
// Attach components
registry[module].script = script;
- registry[module].style = style;
- registry[module].messages = msgs;
+ 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
* Get the state of a module.
*
* @param {string} module Name of module
- * @return {string|null} The state, or null if the module (or its version) is not
+ * @return {string|null} The state, or null if the module (or its state) is not
* in the registry.
*/
getState: function ( module ) {