").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
;
/**
* Base library for MediaWiki.
*
* @class mw
* @alternateClassName mediaWiki
* @singleton
*/
var mw = ( function ( $, undefined ) {
'use strict';
/* Private Members */
var hasOwn = Object.prototype.hasOwnProperty,
slice = Array.prototype.slice;
/**
* Log a message to window.console, if possible. Useful to force logging of some
* errors that are otherwise hard to detect (I.e., this logs also in production mode).
* Gets console references in each invocation, so that delayed debugging tools work
* fine. No need for optimization here, which would only result in losing logs.
*
* @private
* @method log_
* @param {string} msg text for the log entry.
* @param {Error} [e]
*/
function log( msg, e ) {
var console = window.console;
if ( console && console.log ) {
console.log( msg );
// If we have an exception object, log it through .error() to trigger
// proper stacktraces in browsers that support it. There are no (known)
// browsers that don't support .error(), that do support .log() and
// have useful exception handling through .log().
if ( e && console.error ) {
console.error( String( e ), e );
}
}
}
/* Object constructors */
/**
* Creates an object that can be read from or written to from prototype functions
* that allow both single and multiple variables at once.
*
* @example
*
* var addies, wanted, results;
*
* // Create your address book
* addies = new mw.Map();
*
* // This data could be coming from an external source (eg. API/AJAX)
* addies.set( {
* 'John Doe' : '10 Wall Street, New York, USA',
* 'Jane Jackson' : '21 Oxford St, London, UK',
* 'Dominique van Halen' : 'Kalverstraat 7, Amsterdam, NL'
* } );
*
* wanted = ['Dominique van Halen', 'George Johnson', 'Jane Jackson'];
*
* // You can detect missing keys first
* if ( !addies.exists( wanted ) ) {
* // One or more are missing (in this case: "George Johnson")
* mw.log( 'One or more names were not found in your address book' );
* }
*
* // Or just let it give you what it can
* results = addies.get( wanted, 'Middle of Nowhere, Alaska, US' );
* mw.log( results['Jane Jackson'] ); // "21 Oxford St, London, UK"
* mw.log( results['George Johnson'] ); // "Middle of Nowhere, Alaska, US"
*
* @class mw.Map
*
* @constructor
* @param {boolean} [global=false] Whether to store the values in the global window
* object or a exclusively in the object property 'values'.
*/
function Map( global ) {
this.values = global === true ? window : {};
return this;
}
Map.prototype = {
/**
* Get the value of one or multiple a keys.
*
* If called with no arguments, all values will be returned.
*
* @param {string|Array} selection String key or array of keys to get values for.
* @param {Mixed} [fallback] Value to use in case key(s) do not exist.
* @return mixed If selection was a string returns the value or null,
* If selection was an array, returns an object of key/values (value is null if not found),
* If selection was not passed or invalid, will return the 'values' object member (be careful as
* objects are always passed by reference in JavaScript!).
* @return {string|Object|null} Values as a string or object, null if invalid/inexistant.
*/
get: function ( selection, fallback ) {
var results, i;
// If we only do this in the `return` block, it'll fail for the
// call to get() from the mutli-selection block.
fallback = arguments.length > 1 ? fallback : null;
if ( $.isArray( selection ) ) {
selection = slice.call( selection );
results = {};
for ( i = 0; i < selection.length; i++ ) {
results[selection[i]] = this.get( selection[i], fallback );
}
return results;
}
if ( typeof selection === 'string' ) {
if ( !hasOwn.call( this.values, selection ) ) {
return fallback;
}
return this.values[selection];
}
if ( selection === undefined ) {
return this.values;
}
// invalid selection key
return null;
},
/**
* Sets one or multiple key/value pairs.
*
* @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.
*/
set: function ( selection, value ) {
var s;
if ( $.isPlainObject( selection ) ) {
for ( s in selection ) {
this.values[s] = selection[s];
}
return true;
}
if ( typeof selection === 'string' && arguments.length > 1 ) {
this.values[selection] = value;
return true;
}
return false;
},
/**
* Checks if one or multiple keys exist.
*
* @param {Mixed} selection String key or array of keys to check
* @return {boolean} Existence of key(s)
*/
exists: function ( selection ) {
var s;
if ( $.isArray( selection ) ) {
for ( s = 0; s < selection.length; s++ ) {
if ( typeof selection[s] !== 'string' || !hasOwn.call( this.values, selection[s] ) ) {
return false;
}
}
return true;
}
return typeof selection === 'string' && hasOwn.call( this.values, selection );
}
};
/**
* Object constructor for messages.
*
* Similar to the Message class in MediaWiki PHP.
*
* Format defaults to 'text'.
*
* @class mw.Message
*
* @constructor
* @param {mw.Map} map Message storage
* @param {string} key
* @param {Array} [parameters]
*/
function Message( map, key, parameters ) {
this.format = 'text';
this.map = map;
this.key = key;
this.parameters = parameters === undefined ? [] : slice.call( parameters );
return this;
}
Message.prototype = {
/**
* Simple message parser, does $N replacement and nothing else.
*
* This may be overridden to provide a more complex message parser.
*
* The primary override is in mediawiki.jqueryMsg.
*
* 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;
} );
},
/**
* Appends (does not replace) parameters for replacement to the .parameters property.
*
* @param {Array} parameters
* @chainable
*/
params: function ( parameters ) {
var i;
for ( i = 0; i < parameters.length; i += 1 ) {
this.parameters.push( parameters[i] );
}
return this;
},
/**
* Converts message object to it's string form based on the state of format.
*
* @return {string} Message as a string in the current form or `` if key does not exist.
*/
toString: function () {
var text;
if ( !this.exists() ) {
// Use 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 + '>';
}
if ( this.format === 'plain' || this.format === 'text' || this.format === 'parse' ) {
text = this.parser();
}
if ( this.format === 'escaped' ) {
text = this.parser();
text = mw.html.escape( text );
}
return text;
},
/**
* Changes format to 'parse' and converts message to string
*
* If jqueryMsg is loaded, this parses the message text from wikitext
* (where supported) to HTML
*
* Otherwise, it is equivalent to plain.
*
* @return {string} String form of parsed message
*/
parse: function () {
this.format = 'parse';
return this.toString();
},
/**
* Changes format to 'plain' and converts message to string
*
* This substitutes parameters, but otherwise does not change the
* message text.
*
* @return {string} String form of plain message
*/
plain: function () {
this.format = 'plain';
return this.toString();
},
/**
* Changes format to 'text' and converts message to string
*
* If jqueryMsg is loaded, {{-transformation is done where supported
* (such as {{plural:}}, {{gender:}}, {{int:}}).
*
* Otherwise, it is equivalent to plain.
*/
text: function () {
this.format = 'text';
return this.toString();
},
/**
* Changes the format to 'escaped' and converts message to string
*
* This is equivalent to using the 'text' format (see text method), then
* HTML-escaping the output.
*
* @return {string} String form of html escaped message
*/
escaped: function () {
this.format = 'escaped';
return this.toString();
},
/**
* Checks if message exists
*
* @see mw.Map#exists
* @return {boolean}
*/
exists: function () {
return this.map.exists( this.key );
}
};
/**
* @class mw
*/
return {
/* Public Members */
/**
* Dummy placeholder for {@link mw.log}
* @method
*/
log: ( function () {
var log = function () {};
log.warn = function () {};
log.deprecate = function ( obj, key, val ) {
obj[key] = val;
};
return log;
}() ),
// Make the Map constructor publicly available.
Map: Map,
// Make the Message constructor publicly available.
Message: Message,
/**
* Map of configuration values
*
* Check out [the complete list of configuration values](https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#mw.config)
* on MediaWiki.org.
*
* If `$wgLegacyJavaScriptGlobals` is true, this Map will put its values in the
* global window object.
*
* @property {mw.Map} config
*/
// Dummy placeholder. Re-assigned in ResourceLoaderStartupModule with an instance of `mw.Map`.
config: null,
/**
* Empty object that plugins can be installed in.
* @property
*/
libs: {},
/**
* Access container for deprecated functionality that can be moved from
* from their legacy location and attached to this object (e.g. a global
* function that is deprecated and as stop-gap can be exposed through here).
*
* This was reserved for future use but never ended up being used.
*
* @deprecated since 1.22: Let deprecated identifiers keep their original name
* and use mw.log#deprecate to create an access container for tracking.
* @property
*/
legacy: {},
/**
* Localization system
* @property {mw.Map}
*/
messages: new Map(),
/* Public Methods */
/**
* Get a message object.
*
* Similar to wfMessage() in MediaWiki PHP.
*
* @param {string} key Key of message to get
* @param {Mixed...} parameters Parameters for the $N replacements in messages.
* @return {mw.Message}
*/
message: function ( key ) {
// Variadic arguments
var parameters = slice.call( arguments, 1 );
return new Message( mw.messages, key, parameters );
},
/**
* Get a message string using 'text' format.
*
* Similar to wfMsg() in MediaWiki PHP.
*
* @see mw.Message
* @param {string} key Key of message to get
* @param {Mixed...} parameters Parameters for the $N replacements in messages.
* @return {string}
*/
msg: function () {
return mw.message.apply( mw.message, arguments ).toString();
},
/**
* Client-side module loader which integrates with the MediaWiki ResourceLoader
* @class mw.loader
* @singleton
*/
loader: ( function () {
/* Private Members */
/**
* Mapping of registered modules
*
* The jquery module is pre-registered, because it must have already
* been provided for this object to have been built, and in debug mode
* jquery would have been provided through a unique loader request,
* making it impossible to hold back registration of jquery until after
* mediawiki.
*
* For exact details on support for script, style and messages, look at
* mw.loader.implement.
*
* Format:
* {
* 'moduleName': {
* 'version': ############## (unix timestamp),
* 'dependencies': ['required.foo', 'bar.also', ...], (or) function () {}
* 'group': 'somegroup', (or) null,
* 'source': 'local', 'someforeignwiki', (or) null
* 'state': 'registered', 'loaded', 'loading', 'ready', 'error' or 'missing'
* 'script': ...,
* 'style': ...,
* 'messages': { 'key': 'value' },
* }
* }
*
* @property
* @private
*/
var registry = {},
//
// Mapping of sources, keyed by source-id, values are objects.
// Format:
// {
// 'sourceId': {
// 'loadScript': 'http://foo.bar/w/load.php'
// }
// }
//
sources = {},
// List of modules which will be loaded as when ready
batch = [],
// List of modules to be loaded
queue = [],
// List of callback functions waiting for modules to be ready to be called
jobs = [],
// Selector cache for the marker element. Use getMarker() to get/use the marker!
$marker = null,
// Buffer for addEmbeddedCSS.
cssBuffer = '',
// Callbacks for addEmbeddedCSS.
cssCallbacks = $.Callbacks();
/* Private methods */
function getMarker() {
// Cached ?
if ( $marker ) {
return $marker;
}
$marker = $( 'meta[name="ResourceLoaderDynamicStyles"]' );
if ( $marker.length ) {
return $marker;
}
mw.log( 'getMarker> No found, inserting dynamically.' );
$marker = $( '' ).attr( 'name', 'ResourceLoaderDynamicStyles' ).appendTo( 'head' );
return $marker;
}
/**
* Create a new style tag and add it to the DOM.
*
* @private
* @param {string} text CSS text
* @param {HTMLElement|jQuery} [nextnode=document.head] The element where the style tag should be
* inserted before. Otherwise it will be appended to ``.
* @return {HTMLElement} Reference to the created `