注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
/***************************************************************************************************
 extra.js --- by Evad37
 > Common helper functions, stored in the window.extraJs object.
 > Version 2.0.0
----------------------------------------------------------------------------------------------------
 Take care to load approriate resource loader modules, as specified for each function. Or just load
 all that may be required, like this:
 
 mw.loader.using( ['mediawiki.util', 'mediawiki.api', 'mediawiki.Title', 'mediawiki.RegExp',
	'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows'], function () {

 // ... your code goes here...

 });
 
***************************************************************************************************/
// <nowiki>

window.extraJs = {};

/**
 * makeApiErrorMsg
 *
 * Makes an error message, suitable for displaying to a user, from the values
 * that the MediaWiki Api passes to the failure callback function, e.g.
 * `new mw.Api.get(queryObject}).done(successCallback).fail(failureCallback)`
 *
 * @param {string} code
 *  First paramater passed to failure callback function.
 * @param {jQuery.jqXHR} jqxhr
 *  Second paramater passed to failure callback function.
 * @return {string} Error message details, with in a format like
 *  "(API|HTTP) error: details"
 */
extraJs.makeErrorMsg = function(code, jqxhr) {
	var details = '';
	if ( code === 'http' && jqxhr.textStatus === 'error' ) {
		details = 'HTTP error ' + jqxhr.xhr.status;
	} else if ( code === 'http' ) {
		details = 'HTTP error: ' + jqxhr.textStatus;
	} else if ( code === 'ok-but-empty' ) {
		details = 'Error: Got an empty response from the server';
	} else {
		details = 'API error: ' + code;
	}
	return details;
};

/**
 * makeLink
 *
 * Makes a link to a en.Wikipedia page that opens in a new tab/window.
 * 
 * @requires {Module} mediawiki.util
 * @param {string} linktarget
 *  The target page.
 * @param {string} linktext
 *  Text to display in the link. Optional, if not provided the target will be used.
 * @return {jQuery} jQuery object containing the `<a>` element.
 */
extraJs.makeLink = function(linktarget, linktext) {
	if ( linktext == null ) {
		linktext = linktarget;
	}
	return $('<a>').attr({
		'href':'https://en.wikipedia.org/wiki/'+mw.util.wikiUrlencode(linktarget),
		'target':'_blank'
	}).text(linktext);
};

/**
 * makeTooltip
 *
 * Make a question mark in a circle that shows a 'tooltip' when hovered over
 *
 * @param {string} tipText
 *  The text for the tooltip.
 * @return {jQuery} jQuery object containing the tooltip element
 */
extraJs.makeTooltip = function(tipText) {
	// Add css rule, if not already added
	if ( !extraJs.tooltipStyle ) {
		var s = mw.loader.addStyleTag('.ejs-tooltip { border:1px solid #33a; border-radius:10px; '+
		'font-weight:bold; font-size:80%; color:#22a; padding:0px; cursor:help }');
		extraJs.tooltipStyle = s.sheet || s.styleSheet || s;
	}
	return $('<span>').attr({'title':tipText, 'class':'ejs-tooltip'}).html('&thinsp;?&thinsp;');
};

/**
 * multiButtonConfirm
 *
 * Uses OOjs UI to create a multi-button confirm dialogue.
 *
 * @requires {Modules} oojs-ui-core, oojs-ui-windows, mediawiki.util
 * @param {string} title
 *  Title for the dialogue.
 * @param {string} message
 *  Message for the dialogue. Certain HTML tags can be escaped, newline characters are ignored.
 * @param {object[]} buttons
 *  Array of objects which each represent a buttons to show at the bottom of the prompt.
 *  Each object can be of the form
 *  `{
 *    label: {string},
 *    action: {string|null},
 *    flags: {string|null},
 *   }`
 *  where `label` is the label to show on the button, `action` is the value passed to the callback
 *  function if that button is selected by the user, and `flags` is one of "safe", "primary",
 *  "progressive", or "destructive", per https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
 * @param {function} callback
 *  callback function executed after a button is selected (passed that button's `action` value)
 * @param {object} options
 *  Display options:
 *   @param {boolean} options.verbose
 *    Display verbose message formatting (left- instead of center-aligned)
 *   @param {boolean} options.unescape
 *    Unescape the following simple HTML tags (no attributes) in the message:
 *    <br>, <p>, <ul>, <li>, <pre> (and matching closing tags or self-closed tags).
 *    Tags within <pre> tags will not be unescaped.
 *   @param {boolean} options.wikilinks
 *    Convert [[wikilinks]] into actual links. If `options.unescape` is set to `true`, wikilinks
 *    within <pre> tags will not be converted.
 */
extraJs.multiButtonConfirm = function(title, message, buttons, callback, options) {
	// If needed, create array of deferreds
	if ( !extraJs.multiButtonConfirm.dfd ) {
		extraJs.multiButtonConfirm.dfd = [$.Deferred().resolve()];
	}
	
	// Add Deferred, to be resoved when previous window is done
	var dfdIndex = extraJs.multiButtonConfirm.dfd.push($.Deferred()) - 1;
	
	// Open window when previous window's Deferred is resolved
	extraJs.multiButtonConfirm.dfd[dfdIndex-1].done(function(){
		extraJs.multiButtonConfirm.windowManager = new OO.ui.WindowManager();
		var windowManager = extraJs.multiButtonConfirm.windowManager;
		var messageDialog = new OO.ui.MessageDialog();
		$('body').append( windowManager.$element );
		windowManager.addWindows( [ messageDialog ] );
		var instance = windowManager.openWindow( messageDialog, {
			title: title,
			message: message,
			verbose: options && options.verbose,
			actions: buttons
		} );
		instance.opened.then( function() {
			if ( options && ( options.unescape || options.wikilinks ) ) {
				// Escaped message text
				var msg = $('label.oo-ui-messageDialog-message')
					.filter(':visible')
					.text();
				
				// Unescape escaped html - pre tags			
				if ( options.unescape ) {
					// Process pre tags first (this way the only unescaped `<`s will be for pre tags)
					msg = msg.replace(/&lt;(\/?pre\s?\/?)&gt;/g,'<$1>');
				}
				
				// Make wikilinks into real links
				if ( options.wikilinks ) {
					var path = 'https:' + mw.config.get('wgServer') + '/wiki/';
					// First piped links, then simple links (unless inside <pre> tags)
					msg = msg.replace(
						/\[\[([^\|\]]*?)\|([^\|\]]*?)\]\](?![^<]*?<\/pre>)/g,
						'<a href="' + path + mw.util.wikiUrlencode('$1') + '" target="_blank">$2</a>'
					).replace(
						/\[\[([^\|\]]+?)]\](?![^<]*?<\/pre>)/g,
						'<a href="' + path + mw.util.wikiUrlencode('$1') + '" target="_blank">$1</a>'
					);
				}
				
				// Unescape escaped html - other tags			
				if ( options.unescape ) {
					// Process other tags, unless inside <pre> tags
					msg = msg.replace(/&lt;(\/?(?:br|p|ul|li)\s?\/?)&gt;(?![^<]*?<\/pre>)/g,'<$1>');
				}
				
				// Replace message
				$('label.oo-ui-messageDialog-message')
				.filter(':visible')
				.html(msg);
				
				// Resize dialogue to fit
				messageDialog.updateSize();
			}
		} );
		instance.closed.then( function ( data ) {
			if ( data && data.action ) {
				callback(data.action);
			} else {
				callback(null);
			}
			windowManager.destroy();
			// Resolve this window's Deferred
			extraJs.multiButtonConfirm.dfd[dfdIndex].resolve();
		} );
	});
};

/**
 * toSentenceCase
 *
 * Capitalises the first letter of a string.
 *
 * @param {String} input
 *  The string to be transformed.
 * @param {Boolean} lc
 *  Transform the characters following the first character to lowercase
 * @returns {String} Transformed string.
 */
extraJs.toSentenceCase = function(input, lc) {
	return input.slice(0,1).toUpperCase() +
		(( lc ) ? input.slice(1).toLowerCase() : input.slice(1));
};

/**
 * uniqueArray
 *
 * Filters out possible duplicate values from an array.
 *
 * @param {array} a
 *  Array to be filtered.
 * @return {array} Filtered array.
 */
extraJs.uniqueArray = function(a) {
	return a.filter(function(val, i, arr){ return arr.indexOf(val) === i; });
};

/**
 * unlink
 *
 * Function to unlink and/or remove links and file usages from a block of wikitext.
 * Derived from XFDcloser < https://en.wikipedia.org/wiki/User:Evad37/XFDcloser.js >
 *
 * @requires {Module} mediawiki.RegExp
 * @param {string} wikitext
 *  Wikitext in which to search for links or file usages.
 * @param {string[]} unlinkThese
 *  Array of page titles to be unlinked.
 * @param {number} ns
 *  Number of the namespace which the wikitext is in.
 * @param {boolean} isDab
 *  Wikitext is of a disambiguation page.
 * @return {string} Updated wikitext. If no links or file usages were found, this will be
 *  the same as the input wikitext.
 */
extraJs.unlink = function(wikitext, unlinkThese, ns, isDab) {
	// Remove image/file usages, if any titles are files
	var unlinkFiles = unlinkThese.filter(function(t){ return /^File:/i.test(t); });
	if ( unlinkFiles.length > 0 ) {
		// Start building regex strings
		var normal_regex_str = "(";
		var gallery_regex_str = "(";
		var free_regex_str = "(";
		for ( var i=0; i<unlinkFiles.length; i++ ) {
			// Take off namespace prefix
			filename = unlinkFiles[i].replace(/^.*?:/, "");
			// For regex matching: first character can be either upper or lower case, special
			// characters need to be escaped, spaces/underscores can be either spaces or underscores
			filename_regex_str = "[" + mw.RegExp.escape(filename.slice(0, 1).toUpperCase()) +
			mw.RegExp.escape(filename.slice(0, 1).toLowerCase()) + "]" +
			mw.RegExp.escape(filename.slice(1)).replace(/(?: |_)/g, "[ _]");
			// Add to regex strings
			normal_regex_str += "\\[\\[\\s*(?:[Ii]mage|[Ff]ile)\\s*:\\s*" + filename_regex_str +
			"\\s*\\|?.*?(?:(?:\\[\\[.*?\\]\\]).*?)*\\]\\]";
			gallery_regex_str += "^\\s*(?:[Ii]mage|[Ff]ile):\\s*" + filename_regex_str + ".*?$";
			free_regex_str += "\\|\\s*(?:[\\w\\s]+\\=)?\\s*(?:(?:[Ii]mage|[Ff]ile):\\s*)?" +
			filename_regex_str;
			
			if ( i+1 !== unlinkFiles.length ) {
				normal_regex_str += "|";
				gallery_regex_str += "|";
				free_regex_str += "|";				
			}
		}
		// Close off regex strings
		normal_regex_str += ")(?![^<]*?-->)";
		gallery_regex_str += ")(?![^<]*?-->)";
		free_regex_str += ")(?![^<]*?-->)";

		// Check for normal file usage, i.e. [[File:Foobar.png|...]]
		var normal_regex = new RegExp( normal_regex_str, "g");
		wikitext = wikitext.replace(normal_regex, "");
		
		// Check for gallery usage, i.e. instances that must start on a new line, eventually
		// preceded with some space, and must include File: or Image: prefix
		var gallery_regex = new RegExp( gallery_regex_str, "mg" );
		wikitext = wikitext.replace(gallery_regex, "");
		
		// Check for free usages, for example as template argument, might have the File: or Image:
		// prefix excluded, but must be preceeded by an |
		var free_regex = new RegExp( free_regex_str, "mg" );
		wikitext = wikitext.replace(free_regex, "");
	}
	
	// Remove links
	// Start building regex strings
	var simple_regex_str = "\\[\\[\\s*:?\\s*(";
	var named_regex_str = "\\[\\[\\s*:?\\s*(?:";
	for ( var ii=0; ii<unlinkThese.length; ii++ ) {
		// For regex matching: first character can be either upper or lower case, special
		// characters need to be escaped, spaces/underscores can be either spaces or underscores
		var unlink_regex_str = "[" + mw.RegExp.escape(unlinkThese[ii].slice(0, 1).toUpperCase()) +
			mw.RegExp.escape(unlinkThese[ii].slice(0, 1).toLowerCase()) + "]" +
			mw.RegExp.escape(unlinkThese[ii].slice(1)).replace(/(?: |_)/g, "[ _]");
		// Add to regex strings
		simple_regex_str += unlink_regex_str;
		named_regex_str += unlink_regex_str;
		if ( ii+1 !== unlinkThese.length ) {
			simple_regex_str += "|";
			named_regex_str += "|";			
		}
	}
	// Close off regex strings
	simple_regex_str += ")(?:#[^\\|\\]]*?)?\\s*\\]\\](?![^<]*?-->)";
	named_regex_str += ")(?:#[^\\|\\]]*?)?\\s*\\|([^\\[\\]\\n\\r]+?)\\]\\](?![^<]*?-->)";
	var simple_regex = new RegExp( simple_regex_str, "g" );
	var named_regex = new RegExp( named_regex_str, "g" );
	
	// Set index articles for names, which should be treated like disambiguation pages, will contain
	// one of these templates
	var name_set_index_regex = /\{\{\s*(?:[Gg]iven[ _]name|[Ss]urnames?|[Nn]ickname|[Ff]irst[ _]name|[Ff]orename|[Dd]isambigN(?:ame|m)?)\s*(?:\|.*?)*?\}\}/g;
	if ( name_set_index_regex.test(wikitext) ) {
		isDab = true;
	}
	
	// List items removals:
	if ( ns === 10 ) {
		//Within navbox templates, remove links entirely, including the preceding *'s and the following newline
		var navbox_regex = new RegExp("\\{\\{[Nn]avbox(?: with collapsible groups| with columns)?\\s*\\|" +
			"(?:.|\\n)*?(?:(?:\\{\\{" +			// accounts for templates within the navbox
				"(?:.|\\n)*?(?:(?:\\{\\{" +		// accounts for templates within templates within the navbox
					"(?:.|\\n)*?" +
				"\\}\\})(?:.|\\n)*?)*?" +
			"\\}\\})(?:.|\\n)*?)*" +
		"\\}\\}", "g");
		var navbox_simple_regex = new RegExp( "\\*+\\s*" + simple_regex_str + "[\\r\\t\\f\\v ]*\\n", "g" );
		var navbox_named_regex = new RegExp( "\\*+\\s*" + named_regex_str + "[\\r\\t\\f\\v ]*\\n", "g" );
		//Find navbox templates
		var navboxes = wikitext.match(navbox_regex);
		if ( navboxes ) {
			// remove regex matches from wikitext
			for ( var jj=0; jj<navboxes.length; jj++ ) {
				replacement = navboxes[jj].replace(navbox_simple_regex, "").replace(navbox_named_regex, "");
				wikitext = wikitext.replace(navboxes[jj], replacement);
			}
		}
	} else if ( isDab ) {
		// For disambiguation pages, entirely remove list items containing a backlink, including the
		// preceding *'s and the following newline (but skiping list items with multiple links)
		var dab_simple_regex = new RegExp( "\\*+[^\\[\\]\\n\\r]*" + simple_regex_str + "[^\\[\\]\\n\\r]*\\n", "g" );
		var dab_named_regex = new RegExp( "\\*+[^\\[\\]\\n\\r]*" + named_regex_str + "[^\\[\\]\\n\\r]*\\n", "g" );	
		wikitext = wikitext.replace(dab_simple_regex, "").replace(dab_named_regex, "");
	} else {
		// For See also sections, entirely remove list items containing a backlink, including the
		// preceding *'s and the following newline (but skiping list items with multiple links)
		var seealso_regex = /==+\s*[Ss]ee [Aa]lso\s*==+\n+(?:^.*\n*)*?(?:(?===+)|$)/gm;
		var seealso_simple_regex = new RegExp( "\\*+[^\\[\\]\\n\\r]*" + simple_regex_str + "[^\\[\\]\\n\\r]*\\n", "g" );
		var seealso_named_regex = new RegExp( "\\*+[^\\[\\]\\n\\r]*" + named_regex_str + "[^\\[\\]\\n\\r]*\\n", "g" );
		var seealso = wikitext.match(seealso_regex);
		if ( seealso ) {
			// remove regex matches from wikitext
			for ( var kk=0; kk<seealso.length; kk++ ) {
				replacement = (seealso[kk]+"\n").replace(seealso_simple_regex, "").replace(seealso_named_regex, "");
				wikitext = wikitext.replace(seealso[kk].trim(), replacement.trim());
			}
		}
		// For other lists, entirely remove list items if the only content is a single backlink,
		// including the preceding *'s and the following newline
		var list_simple_regex = new RegExp( "\\*+[\\t ]*" + simple_regex_str + "[\\t ]*\\n", "g" );
		var list_named_regex = new RegExp( "\\*+[\\t ]*" + named_regex_str + "[\\t ]*\\n", "g" );	
		wikitext = wikitext.replace(list_simple_regex, "").replace(list_named_regex, "");
	}
	// Mark any other list items with backlinks for manual review, using {{subst:void}}
	var manual_review_regex = new RegExp( '^\\*+.*(?:' + simple_regex_str + '|' +
		named_regex_str + ').*$', 'gm' );
	wikitext = wikitext.replace(manual_review_regex, '{{subst:void}}$&');

	// For all other links, replace with unlinked text
	wikitext = wikitext.replace(simple_regex, "$1").replace(named_regex, "$1");

	return wikitext;
};

/**
 * val2key
 *
 * For an object `obj` with key:value pairs, return a value's corresponding key.
 *
 * @param {string|number} val
 *  Value to seach for.
 * @param {object} obj
 *  Object to search in.
 * @return {string|number} Key corresponding to the input value.
 */
extraJs.val2key = function(val, obj) {
    for ( var k in obj ) {
        if ( obj[k] === val ) {
            return k;
        }
    }
};

// </nowiki>