میڈیاویکی:Gadget-libExtraUtil.js
یاد دہانی: محفوظ کرنے کے بعد ان تبدیلیوں کو دیکھنے کے لیے آپ کو اپنے براؤزر کا کیش (cache) صاف کرنا ہوگا۔
- فائرفاکس/ سفاری: جب Reload پر کلک کریں تو Shift دباکر رکھیں، یا Ctrl-F5 یا Ctrl-R دبائیں (Mac پر R-⌘)
- گوگل کروم: Ctrl-Shift-R دبائیں (Mac پر Shift-R-⌘)
- انٹرنیٹ ایکسپلورر: جب Refresh پر کلک کریں تو Ctrl یا Ctrl-F5 دبائیں
- اوپیرا: Tools → Preferences میں جائیں اور کیش (cache) صاف کریں
/* _____________________________________________________________________________
* | |
* | === WARNING: GLOBAL GADGET FILE === |
* | Changes to this page affect many users. |
* | Please discuss changes on the talk page or on [[WT:Gadget]] before editing. |
* |_____________________________________________________________________________|
*
* ###############################################################################
* # #
* # This script has unit tests on its /testcases.js subpage. Please install #
* # the WikiUnit script [[User:Evad37/WikiUnit]] and run tests before saving. #
* # #
* ###############################################################################
*
* Imported from version 924857650 as of 14 November 2019 from [[User:Evad37/extra.js]]
* Common helper functions (created for XFDcloser, but may be useful for other scripts)
* Author: Evad37
*
* Take care to load approriate resource loader modules, as specified for each function.
* See < https://www.mediawiki.org/wiki/mw.loader >
*/
/* jshint esversion: 5, laxbreak: true, undef: true, maxerr: 999*/
/* globals window, $, mw, OO, extraJs */
// <nowiki>
window.extraJs = { version: '2.3.0' };
/* ========== Polyfills to support IE11 and Edge<14 ============================================= */
// Array.prototype.includes polyfill
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement /*, fromIndex*/) {
return this.indexOf(searchElement) !== -1;
};
}
// String.prototype.includes polyfill, per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
/* ============================================================================================== */
/**
* 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': mw.util.getUrl(linktarget),
'target':'_blank'
}).text(linktext);
};
/**
* makeTooltip
*
* Make a question mark in a circle that shows a 'tooltip' when hovered over
*
* @deprecated Use addTooltip instead.
*
* @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(' ? ');
};
/**
* addTooltip
*
* Appends a question mark in a circle to an element, and shows a
* 'tooltip' when the element is hovered over
*
* @param {jQuery} $el jQuery element to add the tooltip to.
* @param {string} tipText The text for the tooltip.
* @return {jQuery} jQuery element witj the tooltip
*/
extraJs.addTooltip = function($el, tipText) {
// Add css rule, if not already added
if ( !extraJs.tooltipStyles ) {
var s = mw.loader.addStyleTag(
".ejs-tooltips { display: inline-block; border-bottom: 1px dotted black; cursor:help; } "+
".ejs-tooltipstext-container { visibility: hidden; position: absolute; z-index: 2000; width:auto; max-width:500px; padding:2px 0 0 10px; white-space:normal; } "+
".ejs-tooltips:hover .ejs-tooltipstext-container { visibility: visible; } "+
".ejs-tooltips .ejs-tooltipstext { display:inline-block; background-color: #333; color: #ddd; text-align: center; border-radius: 5px; padding: 5px; } "+
".ejs-tooltips-symbol { border:1px solid #33a; border-radius:20px; font-weight:bold; font-size:80%; color:#22a; padding:0px; cursor:help }"
);
extraJs.tooltipStyles = s.sheet || s.styleSheet || s;
}
return $el.addClass("ejs-tooltips").append(
$("<span class='ejs-tooltips-symbol'> ? </span>"),
$("<div class='ejs-tooltipstext-container'>").append(
$("<span class='ejs-tooltipstext'>").text(tipText)
)
);
};
/**
* 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(/<(\/?pre\s?\/?)>/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(/<(\/?(?:br|p|ul|li)\s?\/?)>(?![^<]*?<\/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();
} );
});
};
/** Template
*
* @class
* Represents the wikitext of template transclusion. Used by #parseTemplates.
* @prop {String} name Name of the template
* @prop {String} wikitext Full wikitext of the transclusion
* @prop {Object[]} parameters Parameters used in the translcusion, in order, of form:
{
name: {String|Number} parameter name, or position for unnamed parameters,
value: {String} Wikitext passed to the parameter (whitespace trimmed),
wikitext: {String} Full wikitext (including leading pipe, parameter name/equals sign (if applicable), value, and any whitespace)
}
* @constructor
* @param {String} wikitext Wikitext of a template transclusion, starting with '{{' and ending with '}}'.
*/
extraJs.Template = function(wikitext) {
this.wikitext = wikitext;
this.parameters = [];
};
extraJs.Template.constructor = extraJs.Template;
extraJs.Template.prototype.addParam = function(name, val, wikitext) {
this.parameters.push({
'name': name,
'value': val,
'wikitext': '|' + wikitext
});
};
/**
* Get a parameter data by parameter name
*/
extraJs.Template.prototype.getParam = function(paramName) {
return this.parameters.find(function(p) { return p.name == paramName; });
};
extraJs.Template.prototype.setName = function(name) {
this.name = name.trim();
};
/**
* parseTemplates
*
* Parses templates from wikitext.
* Based on SD0001's version at <https://en.wikipedia.org/wiki/User:SD0001/parseAllTemplates.js>.
* Returns an array containing the template details:
* var templates = parseTemplates("Hello {{foo |Bar|baz=qux |2=loremipsum|3=}} world");
* console.log(templates[0]); // --> object
{
name: "foo",
wikitext:"{{foo |Bar|baz=qux | 2 = loremipsum |3=}}",
parameters: [
{
name: 1,
value: 'Bar',
wikitext: '|Bar'
},
{
name: 'baz',
value: 'qux',
wikitext: '|baz=qux '
},
{
name: '2',
value: 'loremipsum',
wikitext: '| 2 = loremipsum '
},
{
name: '3',
value: '',
wikitext: '|3='
}
],
getParam: function(paramName) {
return this.parameters.find(function(p) { return p.name == paramName; });
}
}
*
*
* @param {String} wikitext
* @param {Boolean} recursive Set to `true` to also parse templates that occur within other templates,
* rather than just top-level templates.
* @return object[]
*/
extraJs.parseTemplates = function(wikitext, recursive) {
var strReplaceAt = function(string, index, char) {
return string.slice(0,index) + char + string.slice(index + 1);
};
var result = [];
var processTemplateText = function (startIdx, endIdx) {
var text = wikitext.slice(startIdx, endIdx);
var template = new extraJs.Template('{{' + text.replace(/\1/g,'|') + '}}');
// swap out pipe in links with \1 control character
// [[File: ]] can have multiple pipes, so might need multiple passes
while ( /(\[\[[^\]]*?)\|(.*?\]\])/g.test(text) ) {
text = text.replace(/(\[\[[^\]]*?)\|(.*?\]\])/g, '$1\1$2');
}
var chunks = text.split('|').map(function(chunk) {
// change '\1' control characters back to pipes
return chunk.replace(/\1/g,'|');
});
template.setName(chunks[0]);
var parameterChunks = chunks.slice(1);
var unnamedIdx = 1;
parameterChunks.forEach(function(chunk) {
var indexOfEqualTo = chunk.indexOf('=');
var indexOfOpenBraces = chunk.indexOf('{{');
var isWithoutEquals = !chunk.includes('=');
var hasBracesBeforeEquals = chunk.includes('{{') && indexOfOpenBraces < indexOfEqualTo;
var isUnnamedParam = ( isWithoutEquals || hasBracesBeforeEquals );
var pName, pNum, pVal;
if ( isUnnamedParam ) {
// Get the next number not already used by either an unnamed parameter, or by a
// named parameter like `|1=val`
while ( template.getParam(unnamedIdx) ) {
unnamedIdx++;
}
pNum = unnamedIdx;
pVal = chunk.trim();
} else {
pName = chunk.slice(0, indexOfEqualTo).trim();
pVal = chunk.slice(indexOfEqualTo + 1).trim();
}
template.addParam(pName || pNum, pVal, chunk);
});
result.push(template);
};
var n = wikitext.length;
// number of unclosed braces
var numUnclosed = 0;
// are we inside a comment, or between nowiki tags, or in a {{{parameter}}}?
var inComment = false;
var inNowiki = false;
var inParameter = false;
var startIdx, endIdx;
for (var i=0; i<n; i++) {
if ( !inComment && !inNowiki && !inParameter ) {
if (wikitext[i] === '{' && wikitext[i+1] === '{' && wikitext[i+2] === '{' && wikitext[i+3] !== '{') {
inParameter = true;
i += 2;
} else if (wikitext[i] === '{' && wikitext[i+1] === '{') {
if (numUnclosed === 0) {
startIdx = i+2;
}
numUnclosed += 2;
i++;
} else if (wikitext[i] === '}' && wikitext[i+1] === '}') {
if (numUnclosed === 2) {
endIdx = i;
processTemplateText(startIdx, endIdx);
}
numUnclosed -= 2;
i++;
} else if (wikitext[i] === '|' && numUnclosed > 2) {
// swap out pipes in nested templates with \1 character
wikitext = strReplaceAt(wikitext, i,'\1');
} else if ( /^<!--/.test(wikitext.slice(i, i + 4)) ) {
inComment = true;
i += 3;
} else if ( /^<nowiki ?>/.test(wikitext.slice(i, i + 9)) ) {
inNowiki = true;
i += 7;
}
} else { // we are in a comment or nowiki or {{{parameter}}}
if (wikitext[i] === '|') {
// swap out pipes with \1 character
wikitext = strReplaceAt(wikitext, i,'\1');
} else if (/^-->/.test(wikitext.slice(i, i + 3))) {
inComment = false;
i += 2;
} else if (/^<\/nowiki ?>/.test(wikitext.slice(i, i + 10))) {
inNowiki = false;
i += 8;
} else if (wikitext[i] === '}' && wikitext[i+1] === '}' && wikitext[i+2] === '}') {
inParameter = false;
i += 2;
}
}
}
if ( recursive ) {
var subtemplates = result.map(function(template) {
return template.wikitext.slice(2,-2);
})
.filter(function(templateWikitext) {
return /\{\{(?:.|\n)*\}\}/.test(templateWikitext);
})
.map(function(templateWikitext) {
return extraJs.parseTemplates(templateWikitext, true);
});
return result.concat.apply(result, subtemplates);
}
return result;
};
/**
* 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 of strings.
*
* @param {String[]} a Array to be filtered.
* @return {String[]} Filtered array.
*/
extraJs.uniqueArray = function(a) {
var table = {};
a.forEach(function(e) {
table[e] = true;
});
return Object.keys(table);
};
/**
* 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.util, User:SD0001/parseTemplate.js
* @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 = "(";
var filename, filename_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.util.escapeRegExp(filename.slice(0, 1).toUpperCase()) +
mw.util.escapeRegExp(filename.slice(0, 1).toLowerCase()) + "]" +
mw.util.escapeRegExp(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 portal links/templates, if there are any
var unlinkPortals = unlinkThese.filter(function(t){ return /^Portal:/i.test(t); });
if ( unlinkPortals.length > 0 ) {
// Build regex string
var portal_regex_str = "(" +
unlinkPortals.map(function(portal) {
// Take off namespace prefix
var portalname = portal.replace("Portal:", "");
// 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
return "[" + mw.util.escapeRegExp(portalname.slice(0, 1).toUpperCase()) +
mw.util.escapeRegExp(portalname.slice(0, 1).toLowerCase()) + "]" +
mw.util.escapeRegExp(portalname.slice(1)).replace(/(?: |_)/g, "[ _]");
}).join('|') +
")(?![^<]*?-->)"; // Close off regex string
var portal_regex = new RegExp(portal_regex_str);
// Find templates to remove parameters from, or remove entirely
var templatesInWikitext = extraJs.parseTemplates(wikitext, true);
// templates using numbered/unnamed parameters, e.g.{{Portal|Foo|Bar}}
var numberedParameterTemplates = [
// {{Portal}} and its redirects:
'portal', 'portalpar', 'portal box', 'ports', 'portal-2',
// {{Portal-inline}} and its redirects:
'portal-inline', 'portal inline', 'portal frameless', 'portal-inline-template',
// {{Portal bar}} and its redirects:
'portal bar', 'portalbar'
];
// templates using named parameters, e.g. {{Subject bar |portal=Foo |portal2=Bar}}
var namedParameterTemplates = ['subject bar'];
// helper functions for filtering/mapping
var isNumberedParameter = function(param) {
return !isNaN(Number(param.name));
};
var isNamedPortalParameter = function(param) {
return /portal\d*/.test(param.name);
};
/**
* @param {TemplateObject[]} existingTemplates Subset of TemplateObjects from extraJs.parseTemplates
* @param {Function(ParamObject)=>boolean} paramTypeFilter Function that returns `true` if
* the passed in parameter might contain a portal, and `false` otherwise
* @param {Function(ParamObject[])=>boolean} keepFilter Function that returns `true` if the
* template should be kept (and edited), or `false` if the template should just be removed
* @sideEffect modifies variable `wikitext`
*/
var editOrRemoveTemplates = function( existingTemplates, paramTypeFilter, keepFilter ) {
existingTemplates.forEach(function(template) {
var paramsToKeep = template.parameters.filter(function(param) {
return !paramTypeFilter(param) || !portal_regex.test(param.value);
});
if ( paramsToKeep.length === template.parameters.length ) {
// No changes needed
return;
}
if ( keepFilter(paramsToKeep) ) {
var updatedTemplateWikitext = template.wikitext.replace(/\|(.|\n)*/, '') +
paramsToKeep.map(function(p) { return p.wikitext; }).join('') +
'}}';
wikitext = wikitext.replace(template.wikitext, updatedTemplateWikitext);
} else {
// Remove template wikitext, including any preceding * or : characters:
// - if on it's own line, remove a linebreak
wikitext = wikitext.replace(
new RegExp('\\n[\\*\\:]*[\\t ]*' + mw.util.escapeRegExp(template.wikitext) + '\\n'),
'\n'
)
// - if something else is on the line, leave the linebreaks alone
.replace(
new RegExp('[\\*\\:]*[\\t ]*' + mw.util.escapeRegExp(template.wikitext)),
''
);
}
});
};
// Deal with numbered-parameter templates
editOrRemoveTemplates(
templatesInWikitext.filter(function(template) {
var name = template.name.toLowerCase().replace(/_/g, ' ');
return numberedParameterTemplates.includes(name);
}),
isNumberedParameter,
function(params) { return params.some(isNumberedParameter); }
);
// Deal with named parameter templates
editOrRemoveTemplates(
templatesInWikitext.filter(function(template) {
var name = template.name.toLowerCase().replace(/_/g, ' ');
return namedParameterTemplates.includes(name);
}),
isNamedPortalParameter,
function(params) { return params.length > 0; }
);
// Remove any "See also" sections that are now empty
var seeAlsoSection = /(==+)\s*[Ss]ee [Aa]lso\s*==+([.\n]*?)(?:(==+)|$)/g.exec(wikitext);
if ( seeAlsoSection ) {
var hasSubsection = seeAlsoSection[1] && seeAlsoSection[3] && seeAlsoSection[3].length > seeAlsoSection[1].length;
var isEmpty = seeAlsoSection[2].trim() === '';
if ( isEmpty && !hasSubsection ) {
wikitext = wikitext.replace(seeAlsoSection[0], seeAlsoSection[3]);
}
}
}
// 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.util.escapeRegExp(unlinkThese[ii].slice(0, 1).toUpperCase()) +
mw.util.escapeRegExp(unlinkThese[ii].slice(0, 1).toLowerCase()) + "]" +
mw.util.escapeRegExp(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
var replacement;
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
var replacement_seealso;
for ( var kk=0; kk<seealso.length; kk++ ) {
replacement_seealso = (seealso[kk]+"\n").replace(seealso_simple_regex, "").replace(seealso_named_regex, "");
wikitext = wikitext.replace(seealso[kk].trim(), replacement_seealso.trim());
}
}
// Other lists need manual review, in case the item should be retained unlinked (e.g. complete lists per [[WP:CSC]])
}
// Mark any other list items with backlinks for manual review, using {{subst:void}}
var manual_review_regex = new RegExp(
'^(\\*+.*)(' +
simple_regex_str.replace(/([^\\])\(([^?])/g, "$1(?:$2") + // replace capturing groups with non-capturing groups
'|' +
named_regex_str.replace(/([^\\])\(([^?])/g, "$1(?:$2") + // replace capturing groups with non-capturing groups
')(.*)$', 'gm' );
wikitext = wikitext.replace(manual_review_regex, '{{subst:void}}$1{{subst:^|<strong>}}$2{{subst:^|</strong>}}$3');
// 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>