User:Erutuon/scripts/sandbox.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Erutuon/scripts/sandbox. |
/*
A script that makes it easier to create scripts
that perform repetitive and tedious editing tasks.
It inserts buttons above the text box, if certain conditions are fulfilled.
Pressing the button executes the function.
A variable tracks how many times a change is made,
then the function adds an edit summary if the change was done;
if not, it keeps the original edit summary.
*/
/* globals $, mw */
// <nowiki>
"use strict";
var mwValues = mw.config.values;
var action = mwValues.wgAction;
var namespaceNumber = mwValues.wgNamespaceNumber;
var contentModel = mwValues.wgPageContentModel;
var pageName = mwValues.wgPageName;
// mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Joeytje50/JWB.js/load.js&action=raw&ctype=text/javascript');
// Notify about footnote errors (such as undefined named footnotes).
var citeErrors = document.getElementsByClassName("mw-ext-cite-error");
if (citeErrors.length > 0)
mw.notify(Array.from(citeErrors).map(element => element.innerText).join("\n"));
if ( action === "edit" && contentModel === "wikitext"
// not edit conflict
&& document.getElementsByClassName("mw-twocolconflict-page").length === 0 )
{
var wikitext = $("#wpTextbox1").val();
var startsAndEndsWithWhitespace = /^\s+.+?\s+$/;
// Notify about template errors (such as more than one value for a parameter).
var previewNote = document.getElementsByClassName("previewnote");
if (previewNote.length > 0) {
var notes = previewNote[0].children;
for (const note of notes) {
const innerText = note.innerText;
if (innerText.includes("Template:"))
mw.notify(innerText);
}
}
var match;
var regex = /\{\{lang\|([^|]+)\|([^}]+)\}\}/g;
var langs = [];
var nocat_langs = [];
while ((match = regex.exec(wikitext)) !== null) {
if (match[2] && match[2].includes("nocat")) {
if (!nocat_langs.includes(match[1]))
nocat_langs.push(match[1]);
}
else if (!langs.includes(match[1])) {
langs.push(match[1]);
}
}
var failed_cat = [];
for (var lang of nocat_langs) {
if (!langs.includes(lang))
failed_cat.push(lang);
}
if (failed_cat.length > 0)
mw.notify(`Categorization might be failing for the following languages: ${failed_cat.join(", ")}.`,
{ autoHide: false });
var notifyReplacements = function(count)
{
if ( typeof count === "number" )
mw.notify(`${ count === 0 && "No" || count } replacement${ count !== 1 && "s" || "" } made.`);
else
console.log("The function notifyReplacements failed because its argument is not a number.");
};
var addSummary = function(count, summary)
{
if ( typeof count !== "number" )
console.log("Error: count argument to addSummary is not a number.");
if ( count > 0 )
{
$("#wpSummary").val(
function(index, content)
{
var afterSectionName = content.match(/^(?:\/\*[^\*]+\*\/)?\s*(.*?)$/);
var scriptMention = ", with the help of [[User:Erutuon/scripts/cleanup.js|JavaScript]]";
var addition;
if ( afterSectionName && afterSectionName[1].length > 0 )
{
if ( content.includes(scriptMention) )
{
content = content.replace(scriptMention, "");
addition = " and " + summary;
}
else
addition = "; " + summary;
}
else
addition = summary;
if ( ( !afterSectionName || !content.includes(summary) ) )
content += addition;
if ( content.includes(summary) && !content.includes(scriptMention) )
content += scriptMention;
return content;
}
);
}
};
var cleanupFunctions = [
// Template:
{
condition: false,
textBoxIncludes: "",
button: {
text: "blah",
},
minorEdit: false,
"function":
function(content)
{
return content;
}
},
{
textBoxIncludes: /binomial|sectio/,
condition: function (wikitext) {
return /{{[Ss]peciesbox/.test(wikitext)
&& (wikitext.includes("binomial") || wikitext.includes("sectio"));
},
button: {
text: "fix {{speciesbox}}",
},
minorEdit: true,
"function":
function(content)
{
var taxon;
var pageNameWithSpaces = pageName.replace(/_/g, " ");
var origContent = content;
content = content.replace(
/{{speciesbox(?:[^{}]+|{{[^}]+}})+}}/g,
function (template) {
return template
.replace(/\|}}$/, "}}")
.replace(
/(\n*\|\s*)([\w_]+)(\s*=\s*)(.*?)(?=\s*[|}])/g,
function (wholeMatch, before, paramName, between, paramValue) {
// console.log([ wholeMatch, before, paramName, between, paramValue ].map(str => '"' + str + '"').join("\t"));
switch (paramName) {
case "binomial": case "taxon": // include taxon because of code below
paramName = "taxon";
paramValue = paramValue.replace(/''/g, "");
taxon = paramValue; break;
case "binomial_authority":
paramName = "authority"; break;
case "subgenus": case "sectio":
case "subsectio": case "series":
paramName = "parent"; break;
case "name": {
// Delete unnecessary |name= parameter.
if (paramValue.replace(/''/g, "") === pageNameWithSpaces)
return "";
break;
}
default:
return wholeMatch;
}
return before + paramName + between + paramValue;
});
});
// mw.notify([ "taxon", taxon, "pageName", pageName.replace(/_/g, " ") ].join(', '));
var summary = "fix parameters of [[Template:speciesbox]]";
if (taxon === pageNameWithSpaces) {
var oldContent = content;
content = content.replace(/{{[Ii]talic ?title}}\n*/g, "");
if (content !== oldContent)
summary += " and remove unnecessary [[Template:italic title]]";
}
if (content === origContent) return;
addSummary(1, summary);
return content;
}
},
{
textBoxIncludes: /\{\{[Tt]axobox\s*[\||\}\}]/,
button: {
text: "{{taxobox}} to {{speciesbox}}",
},
"function":
function(content)
{
var disallowedParams = new Set(["classis", "divisio",
"familia", "genus", "ordo", "regnum", "species",
"subfamilia", "tribus", "unranked_classis",
"unranked_divisio", "unranked_ordo"]);
content = content.replace(
/\{\{[Tt]axobox(?:[^\{\}]+|\{\{[^}]+\}\})+\}\}/g,
function (template) {
var whitespace = /^\s+$/;
var taxon;
template = template.replace(
/\|\s*([^=\|\}]+?)(\s*=\s*)((?:\[\[[^\]]+\]\]|\[[^\]]+\]|\{\{[^\}]+\}\}|[^\|\}\[\]]+)+)/g,
function (wholeMatch, paramName, between, paramValue) {
console.log(wholeMatch, paramName, paramValue);
if (disallowedParams.has(paramName))
return "";
else {
var ref = "";
if (paramName === "name") {
// Remove italics and compare with page name.
if (paramValue.trim().replace(/''/g, "")
=== pageName.replace(/_/g, " "))
return "";
}
if (paramName === "binomial") {
paramName = "taxon";
paramValue = paramValue.replace(
/''(.+?)''/g, '$1'); // Remove italics.
} else if (paramName === "binomial_authority")
paramName = "authority";
else if (paramName === "subgenus" || paramName === "sectio"
|| paramName === "subsectio" || paramName === "series")
paramName = "parent";
else if (paramName === "synonyms" && paramValue.includes('<br')) {
var match = paramValue.match(/^(.+?)\s*(<ref.+)$/);
if (match) {
ref = match[1], paramValue = match[2];
// Remove any text from the list of refs.
var refRegex = /<ref(?: [^>]+)?>.+?<\/ref>/g;
var refs = [];
var refMatch;
while ((refMatch = refRegex.exec(ref))) {
refs.push(refMatch[0]);
}
ref = refs.join("");
}
paramValue = paramValue
.split(/(?:,?\s*<br\s*\/?>\n*)+/)
.map(item => whitespace.test(item) ? "" : "\n* " + item)
.join("")
.trim();
}
if (paramName === "taxon")
taxon = paramValue;
if (!startsAndEndsWithWhitespace.test(between))
between = " = ";
return "| " + paramName + between + paramValue + ref;
}
});
template = template
.replace(/[Tt]axobox/, "speciesbox")
.replace(/\|\}\}$/, "}}");
return template;
});
content = content.replace(/{{[Ii]talic ?title}}\n*/, "");
addSummary(1, "switched to [[Template:speciesbox]]");
return content;
}
},
{
textBoxIncludes: /<ref|[^\[]\[[^\[]+\](?!\])/i,
button: {
text: "specific-source templates",
},
"function":
function(content)
{
var count1 = 0, count2 = 0;
var regexes = {};
regexes.BONAP = /https?:\/\/bonap\.net\/(?:MapGallery|[Nn][Aa][Pp][Aa]\/TaxonMaps\/Genus)\/([cC]ounty|[sS]tate)\/(\w+)(?:%20(.*?)\.png)?/g;
regexes.PLANTS = /https?:\/\/plants\.usda\.gov\/(?:core|java)\/profile\?symbol=([a-zA-Z]+\d*)/;
regexes.eFloras = /https?:\/\/(?:www\.)?efloras\.org\/florataxon\.aspx\?flora_id=(\d+)&taxon_id=(\d+)/;
regexes.wildflowerDotOrg = /https?:\/\/(?:www\.)?wildflower\.org\/plants\/result\.php\?id_plant=([a-zA-Z]+\d*)/;
regexes.FEIS = /https?:\/\/(?:www\.)?fs\.fed\.us\/database\/feis\/[a-z]+\/([a-z]+)\/[a-z]+\/all\.html/;
regexes.ThePlantList = /http:\/\/(?:www\.)?theplantlist\.org\/tpl1\.1\/record\/([a-z0-9-]+)/;
regexes.MissouriPlants = /https?:\/\/(?:www\.)?missouriplants\.com\/(\w+?)(opp|alt)\/([A-Za-z]+)_([A-Za-z]+)_page\.html/;
regexes.CalPhotos = /https?:\/\/(?:www\.)?calphotos\.berkeley\.edu\/cgi\/img_query\?(?:query_src=photos_index&)?where-taxon=([\w-]+)(?:\+|%20)([\w-]+)/;
regexes.Tropicos = /http:\/\/(?:www\.)?tropicos\.org\/[Nn]ame\/(\d+)\/?(?:\?projectid=(\d+))?/;
regexes.EOL = /https?:\/\/(?:www\.)?eol\.org\/pages\/(\d+)(?:\/overview)?/;
regexes.ITIS = /https?:\/\/(?:www\.)?itis\.gov\/servlet\/SingleRpt\/SingleRpt\?search_topic=TSN&search_value=(\d+)/;
regexes.GoBotany = /https?:\/\/(?:www\.)?gobotany\.newenglandwild\.org\/(?:genus|species)\/(\w+)(?:\/(\w+))?/;
regexes.GoOrchids = /https?:\/\/(?:www\.)?goorchids\.northamericanorchidcenter\.org\/\w+\/(\w+)(?:\/(\w+))/;
regexes.IllinoisWildflowers = /https?:\/\/(?:www\.)?illinoiswildflowers\.info\/([a-z\._\/]+)\.htm/;
regexes.MinnesotaWildflowers = /https?:\/\/(?:www\.)?minnesotawildflowers\.info\/([a-z\.\/-]+)/;
regexes.WCSP = /https?:\/\/(?:www\.)?apps\.kew\.org\/wcsp\/namedetail\.do\?name_id=(\d+)/;
regexes.KansasWildflowers = /https?:\/\/(?:www\.)?kswildflower\.org\/([a-z]+)_details\.php\?[a-z]+ID=(\d+)/;
// http://ucjeps.berkeley.edu/cgi-bin/get_IJM.pl?tid=80590
// http://ucjeps.berkeley.edu/eflora/eflora_display.php?tid=80590
// cgi-bin/get_IJM eflora/eflora
regexes.Jepson = /https?:\/\/(?:www\.)?ucjeps\.berkeley\.edu\/(?:eflora\/eflora_display\.php|cgi-bin\/get_IJM\.pl)\?(tid|key)=(\d+)/;
//http://ucjeps.berkeley.edu/cgi-bin/get_JM_treatment.pl?8738,8858,8875
regexes.JepsonManual = /https?:\/\/(?:www\.)?ucjeps\.berkeley\.edu\/cgi-bin\/get_JM_treatment\.pl\?([\d,]+)/;
// http://www.calflora.org/cgi-bin/species_query.cgi?where-taxon=Sambucus+racemosa+var.+melanocarpa
regexes.Calflora = /https?:\/\/(?:www\.)?calflora\.org\/cgi-bin\/species_query\.cgi\?where-taxon=([A-Za-z.+-]+)/;
regexes.Gymnosperm = /https?:\/\/(?:www\.)?conifers\.org\/([a-z][a-z])\/([A-Z][a-z]+)(?:_([a-z]+)(?:_([a-z]+))?)?\.php/;
regexes.IPNI = /https?:\/\/(?:www\.)?ipni\.org\/ipni\/idPlantNameSearch\.do\?id=([\d-]+)/;
// https://michiganflora.net/species.aspx?id=2796
// https://michiganflora.net/genus.aspx?id=Viola
// https://michiganflora.net/family.aspx?id=Violaceae
regexes.MichiganFlora = /https?:\/\/(?:www\.)?michiganflora\.net\/(family|genus|species)\.aspx\?id=(\w+)/;
regexes.ConnecticutPlants = /https?:\/\/(?:www\.)?ct-botanical-society\.org\/Plants\/view\/(\d+)/;
regexes.FloraOfWisconsin = /https?:\/\/(?:www\.)?wisflora\.herbarium\.wisc\.edu\/taxa\/index\.php\?taxon=(\d+)/;
var paramValueRegex = /\s*=\s*([^|]+[^|\s])/;
var pageNameWithSpaces = pageName.replace(/_/g, " ");
content = content.replace(
/(<ref(?:\s+name\s*=\s*[^\/<>]+)?>)([^\0]*?)(<\/ref>)/gi,
function (wholeMatch, openRefTag, contents, closeRefTag) {
if (!(contents.includes("http://") | contents.includes("https://"))) {
return wholeMatch;
}
var match, taxon, accessDate;
++count1;
taxon = contents.match(/title\s*=\s*([^\|\}]*[^\s\|\}])/)
|| contents.match(/''(.+?)''/); // only species and below
taxon = taxon ? taxon[1] : pageNameWithSpaces;
accessDate = contents.match(/(?:[Rr]etrieved|[Aa]ccessed|access-?date\s*=)\s*([A-Za-z\d-\/, ]+)/)
|| contents.match(/([A-Z][a-z]+ \d\d?,? \d\d\d\d)/);
var accessDateParam = accessDate ? ` |access-date=${accessDate[1]}` : '';
if (contents.includes("bonap.net")) {
match = regexes.BONAP.exec(contents);
const year = contents.match(/201\d/);
if (match && match[1]) {
return `${openRefTag}{{BONAP |genus=${match[2]}`
+ (match[3] ? ` |species=${match[3]}` : '')
+ (match[1].toLowerCase() == "state" ? " |state=1" : "")
+ (year ? ` |date=${year[0]}` : '')
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("plants.usda.gov")) { // ever another capitalization?
match = regexes.PLANTS.exec(contents);
if (match) {
return `${openRefTag}{{PLANTS |symbol=${match[1].toUpperCase()}`
+ (taxon ? ` |taxon=${taxon}` : '')
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("efloras.org")) {
match = regexes.eFloras.exec(contents);
if (match) {
return `${openRefTag}{{eFloras|${match[1]}`
+ `|${match[2]}`
+ (taxon ? `|${taxon.replace(/'''?/g, "")}` : '')
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("kswildflower.org")) {
match = regexes.KansasWildflowers.exec(contents);
if (match) {
return `${openRefTag}{{Kansas Wildflowers|${match[2]}|${pageNameWithSpaces}`
+ (match[1] === "flower" ? '' : match[1])
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("wildflower.org")) {
match = regexes.wildflowerDotOrg.exec(contents);
if (match) {
return `${openRefTag}{{NPIN|${match[1].toUpperCase()}`
+ `|${taxon}`
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("fs.fed.us/database/feis")) {
match = regexes.FEIS.exec(contents);
var genusSpecies = taxon.split(/\s+/);
if (genusSpecies.length !== 2)
genusSpecies = pageNameWithSpaces.split(' ');
if (match && genusSpecies.length === 2) {
var params = new Map();
for (let i = 0, paramName1 = "first", paramName2 = "last";
contents.includes(paramName1);
++i, paramName1 = "first" + i, paramName2 = "last" + i) {
const index1 = contents.indexOf(paramName1);
if (index1 !== -1) {
const value = contents.substring(index1 + paramName1.length).match(paramValueRegex);
if (value)
params.set(paramName1, value[1]);
}
const index2 = contents.indexOf(paramName2);
if (index2 !== -1) {
const value = contents.substring(index2 + paramName2.length).match(paramValueRegex);
if (value)
params.set(paramName2, value[1]);
}
}
{
const index = contents.indexOf("date");
if (index !== -1) {
const dateMatch = contents.substring(index + "date".length).match(paramValueRegex);
if (dateMatch)
params.set("date", dateMatch[1]);
}
}
var printedParams = [];
for (const [ key, value ] of params) {
printedParams.push(` |${key}=${value}`);
}
return `${openRefTag}{{FEIS |genus=${genusSpecies[0]} |species=${genusSpecies[1]}`
+ ` |type=${match[1]}`
+ printedParams.join('')
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("theplantlist.org")) {
match = regexes.ThePlantList.exec(contents);
if (match) {
return `${openRefTag}{{ThePlantList |id=${match[1]}`
+ ` |taxon=${taxon} |authority=`
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("tropicos.org")) {
match = regexes.Tropicos.exec(contents);
if (match) {
return `${openRefTag}{{Tropicos|${match[2] ? match[2] + "|" : ""}${match[1]}`
+ `|${taxon}`
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("eol.org")) {
match = regexes.EOL.exec(contents);
if (match) {
return `${openRefTag}{{EOL|${match[1]}`
+ `|${taxon}`
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("itis.gov")) {
match = regexes.ITIS.exec(contents);
if (match) {
return `${openRefTag}{{ITIS |id=${match[1]}`
+ ` |taxon=${taxon}` + accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("ipni.org")) {
match = regexes.IPNI.exec(contents);
if (match) {
return `${openRefTag}{{IPNI |id=${match[1]}`
+ ` |taxon=${taxon}`
+ accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("gobotany.newenglandwild.org")) {
match = regexes.GoBotany.exec(contents);
if (match) {
match[1] = match[1].replace(/^./, (char) => char.toUpperCase());
if (match[2]) {
return `${openRefTag}{{Go Botany |genus=${match[1]}`
+ ` |species=${match[2]}` + accessDateParam
+ `}}${closeRefTag}`;
}
else {
return `${openRefTag}{{Go Botany `
+ `|genus=${match[1]}`
+ accessDateParam + `}}${closeRefTag}`;
}
}
}
else if (contents.includes("goorchids.northamericanorchidcenter.org")) {
match = regexes.GoOrchids.exec(contents);
if (match) {
match[1] = match[1].replace(/^./, (char) => char.toUpperCase()); // genus
if (match[2]) {
return `${openRefTag}{{Go Orchids |genus=${match[1]}`
+ ` |species=${match[2]}` + accessDateParam
+ `}}${closeRefTag}`;
}
else {
return `${openRefTag}{{Go Orchids`
+ ` |genus=${match[1]}`
+ accessDateParam + `}}${closeRefTag}`;
}
}
}
else if (contents.includes("illinoiswildflowers.info")) {
match = regexes.IllinoisWildflowers.exec(contents);
if (match) {
return `${openRefTag}{{Illinois Wildflowers|${match[1]}`
+ `|${taxon}` + accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("minnesotawildflowers.info")) {
match = regexes.MinnesotaWildflowers.exec(contents);
if (match) {
return `${openRefTag}{{Minnesota Wildflowers|${match[1]}`
+ `|${taxon}` + accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("pfaf.org")) {
// No regex needed.
return `${openRefTag}{{PFAF` + accessDateParam
+ `}}${closeRefTag}`;
}
else if (contents.includes("apps.kew.org/wcsp")) {
match = regexes.WCSP.exec(contents);
if (match) {
return `${openRefTag}{{WCSP|${match[1]}`
+ `|${taxon}` + accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("ucjeps.berkeley.edu/cgi-bin/get_IJM")
|| contents.includes("ucjeps.berkeley.edu/eflora/eflora")) {
match = regexes.Jepson.exec(contents);
if (match) {
return `${openRefTag}{{Jepson eFlora|${match[2]}`
+ `|${taxon}`
+ (match[1] === "key" ? " |type=key" : "")
+ accessDateParam + `}}${closeRefTag}`;
}
}
else if (contents.includes("ucjeps.berkeley.edu/cgi-bin/get_JM_treatment")) {
match = regexes.JepsonManual.exec(contents);
if (match) {
return `${openRefTag}{{Jepson Manual |id=${match[1]}`
+ ` |taxon=${taxon}` + accessDateParam
+ `}}${closeRefTag}`;
}
}
else if (contents.includes("calflora.org")) {
match = regexes.Calflora.exec(contents);
if (match) {
return `${openRefTag}{{Calflora|${match[1].replace(/\+/g, ' ')}`
+ `${accessDateParam}}}${closeRefTag}`;
}
}
else if (contents.includes("conifers.org")) {
match = regexes.Gymnosperm.exec(contents);
var families = {
ar: "Araucariaceae", cu: "Cupressaceae",
cy: "Cycadaceae", ep: "Ephedraceae",
gi: "Ginkgoaceae", gn: "Gnetaceae",
pi: "Pinaceae", po: "Podocarpaceae",
sc: "Sciadopityaceae", ta: "Taxaceae",
we: "Welwitschiaceae",
};
if (match) {
if (families[match[1]])
return `${openRefTag}{{Gymnosperm Database`
+ ` |family=${families[match[1]]}`
+ (match[2] ? ` |genus=${match[2]}` : '')
+ (match[3] ? ` |species=${match[3]}` : '')
+ (match[4] ? ` |subspecies=${match[4]}` : '')
+ `${accessDateParam}}}${closeRefTag}`;
else
mw.notify(`Family ${match[1]} in ${wholeMatch} not recognized.`);
}
}
else if (contents.includes("michiganflora.net")) {
match = regexes.MichiganFlora.exec(contents);
if (match) {
let params;
switch (match[1]) {
case "family": case "genus":
params = ` |${match[1]}=${match[2]}`; break;
case "species": {
params = ` |id=${match[2]}`;
let genusAndSpecies;
if (taxon) {
genusAndSpecies = taxon.split(" ");
params += ` |genus=${genusAndSpecies[0]} |species=${genusAndSpecies[1]}`;
}
break;
}
}
return `${openRefTag}{{Michigan Flora${params}${accessDateParam}}}${closeRefTag}`;
}
}
else if (contents.includes("ct-botanical-society.org")) {
match = regexes.ConnecticutPlants.exec(contents);
if (match) {
return `${openRefTag}{{Connecticut Plants|${match[1]}|${taxon}`
+ `${accessDateParam}}}${closeRefTag}`;
}
}
else if (contents.includes("missouriplants.com")) {
match = regexes.MissouriPlants.exec(contents);
// captures: color, leaf, genus, species
if (match) {
return `${openRefTag}{{Missouri Plants |color=${match[1]} `
+ `|leaf=${match[2]} |genus=${match[3]} `
+ `|species=${match[4]}}}${closeRefTag}`;
}
}
else if (contents.includes("wisflora.herbarium.wisc.edu")) {
match = regexes.FloraOfWisconsin.exec(contents);
if (match) {
return `${openRefTag}{{Flora of Wisconsin`
+ `|${match[1]}|${taxon}}}${closeRefTag}`;
}
}
--count1;
return wholeMatch;
});
// Look over external links.
content = content.replace(
/(^|[^\[])\[([^\[][^ ]+)(\s+[^\]]+)\](?=[^\]]|$)/g,
function (wholeMatch, before, URL, text) {
var match;
++count2;
if (URL.includes("missouriplants.com")) {
match = regexes.MissouriPlants.exec(URL);
// captures: color, leaf, genus, species
if (match) {
return `${before}{{Missouri Plants |color=${match[1]} `
+ `|leaf=${match[2]} |genus=${match[3]} `
+ `|species=${match[4]} |link=1}}`;
}
}
else if (URL.includes("calphotos.berkeley.edu")) {
match = regexes.CalPhotos.exec(URL);
// captures: genus, species
if (match) {
return `${before}{{CalPhotos|${match[1]}|${match[2]}}}`;
}
}
else if (URL.includes("kswildflower.org")) {
match = regexes.KansasWildflowers.exec(URL);
if (match) {
return `${before}{{Kansas Wildflowers|${match[2]}|`
+ (match[1] === "flower" ? '' : match[1])
+ ' |link=1}}';
}
}
else if (URL.includes("illinoiswildflowers.info")) {
match = regexes.IllinoisWildflowers.exec(URL);
if (match) {
return `${before}{{Illinois Wildflowers|${match[1]} |link=1}}`;
}
}
else if (URL.includes("minnesotawildflowers.info")) {
match = regexes.MinnesotaWildflowers.exec(URL);
if (match) {
return `${before}{{Minnesota Wildflowers|${match[1]} |link=1}}`;
}
}
else if (URL.includes("wildflower.org")) {
match = regexes.wildflowerDotOrg.exec(URL);
if (match) {
return `${before}{{NPIN|${match[1].toUpperCase()} |link=1}}`;
}
}
else if (URL.includes("ucjeps.berkeley.edu/cgi-bin/get_JM_treatment")) {
match = regexes.JepsonManual.exec(URL);
if (match) {
return `${before}{{Jepson Manual |id=${match[1]} |link=1}}`;
}
}
else if (URL.includes("ucjeps.berkeley.edu")) { // See also more specific condition above.
match = regexes.Jepson.exec(URL);
if (match && match[1] !== "key") {
return `${before}{{Jepson eFlora|${match[2]} |link=1}}`;
}
}
else if (URL.includes("calflora.org")) {
match = regexes.Calflora.exec(URL);
if (match) {
return `${before}{{Calflora|${match[1].replace(/\+/g, " ")} |link=1}}`;
}
}
else if (URL.includes("gobotany.newenglandwild.org")) {
match = regexes.GoBotany.exec(URL);
if (match) {
match[1] = match[1].replace(/^./, (char) => char.toUpperCase());
if (match[2]) {
return `${before}{{Go Botany |genus=${match[1]}`
+ ` |species=${match[2]}`
+ ` |link=1}}`;
}
else {
return `${before}{{Go Botany `
+ `|genus=${match[1]} |link=1}}`;
}
}
}
else if (URL.includes("goorchids.northamericanorchidcenter.org")) {
match = regexes.GoOrchids.exec(URL);
if (match) {
match[1] = match[1].replace(/^./, (char) => char.toUpperCase()); // genus
if (match[2]) {
return `${before}{{Go Orchids |genus=${match[1]}`
+ ` |species=${match[2]}`
+ ` |link=1}}`;
}
else {
return `${before}{{Go Orchids `
+ `|genus=${match[1]} |link=1}}`;
}
}
}
--count2;
return wholeMatch;
});
var whatWasDone = [];
if (count1 > 0) whatWasDone.push("citation" + (count1 === 1 ? "" : "s"));
if (count2 > 0) whatWasDone.push("external link" + (count2 === 1 ? "" : "s"));
addSummary(count1 + count2, `templatized ${whatWasDone.join(" and ")}`);
if (count1 === 0 && count2 === 0)
return;
return content;
}
},
{
textBoxIncludes: "access_date",
button: {
text: "|access_date= → |access-date=",
},
minorEdit: true,
"function":
function(content)
{
content = content.replace(/access_date/g, "access-date");
addSummary(1, "|access_date= → |access-date=");
return content;
}
},
{
textBoxIncludes: /^[\*#;:]+(?=[^\s\*#;:])/m,
button: {
text: "spaces after list syntax",
},
"function":
function(content)
{
content = content.replace(/^([\*#;:]+)\s*(?=[^\s\*#;:])/gm,
'$1 ');
return content;
}
},
{
condition: false,
textBoxIncludes: /(^|\D)\d\d-\d\d-\d\d\d\d(?=\D|$)/,
button: {
text: "MM-DD-YYYY → YYYY-MM-DD",
},
minorEdit: false,
"function":
function(content)
{
var count = 0;
content = content.replace(
/(^|\D)(\d\d-\d\d)-(\d\d\d\d)(?=\D|$)/,
function (wholeMatch, before, monthDay, year) {
++count;
return before + year + "-" + monthDay;
});
mw.notify(`${count} numerical date${count === 1 ? "" : "s"} fixed.`);
return content;
}
},
{
textBoxIncludes: /\{\{[Cc]onvert\|[^}]+\|\s*abbr\s*=\s*on/,
button: {
text: "{{convert}} → {{cvt}}",
},
minorEdit: true,
"function":
function(content)
{
content = content.replace(/\{\{[Cc]onvert(\|[^}]+)\|\s*abbr\s*=\s*on\s*/g,
"{{cvt$1");
addSummary(1, "[[Template:convert]] with |abbr=on → [[Template:cvt]] for brevity");
return content;
}
},
{
// condition: namespaceNumber === 0,
textBoxIncludes: /\[\[([^\|\]]+)\|\1[^\]]+\]\]/,
button: {
text: "fix piped links",
},
minorEdit: true,
"function":
function(content)
{
let count = 0;
content = content.replace(
/\[\[([^|]+)\|(\1)([^\]]*)\]\]/gi,
function (wholeMatch, _, linkText, suffix) {
++count;
return `[[${linkText}]]${suffix}`;
});
addSummary(count, `fixed piped link${count !== 1 ? 's' : ''}`);
return content;
}
},
{
textBoxIncludes: /\[\[([^\|]+)\|\1\]\]/,
button: {
text: "fix piped links",
},
minorEdit: true,
"function":
function(content)
{
var count = 0;
content = content.replace(
/\[\[([^\|]+)\|\1\]\]/g,
function(wholeMatch, target)
{
count++;
return "[[" + target + "]]";
}
);
notifyReplacements(count);
addSummary(count, "fixed piped links");
return content;
}
},
{
textBoxIncludes: /[‹›⟨⟩]/,
button: {
text: "add {{angbr}}",
},
minorEdit: true,
"function":
function(content)
{
var count = 0;
content = content.replace(
/⟨([^⟩]+)⟩|‹([^›]+)›/g,
function(wholematch, insideBrackets1, insideBrackets2)
{
count++;
return "{{angbr|" + ( insideBrackets1 || insideBrackets2 ) + "}}";
}
);
notifyReplacements(count);
addSummary(count, "replaced literal angle brackets with [[Template:angbr]]");
return content;
}
},
{
textBoxIncludes: "{{IPA|",
button: {
text: "' → ˈ",
},
minorEdit: true,
"function":
function(content)
{
var count = 0;
content = content.replace(
/{{IPA\|([^\}\n]+)}}/g,
function(wholematch, transcription)
{
if ( transcription.includes("'") )
{
count++;
return "{{IPA|" + transcription.replace(/'/g, "ˈ") + "}}";
}
else
return wholematch;
}
);
notifyReplacements(count);
addSummary(count, "replaced apostrophe with length mark in IPA transcriptions");
return content;
}
},
{
textBoxIncludes: /(\/[^\/]+\/|\[[^\]]+\])(?=[\s\.\,\:\;\)\&\-<])/,
button: {
text: "add {{IPA}}",
},
minorEdit: true,
"function":
function(content)
{
var count = 0;
var escaped = [];
var i = 0;
var escape = function(text, regexString)
{
var regex = new RegExp(regexString, "g");
text = text.replace(
regex,
function(match)
{
escaped[i] = match;
var replacement = "%%" + i + "%%";
i += 1;
return replacement;
}
);
return text;
};
content = escape(content, "(?:https?:)\\/\\/[^\\s\\|]+");
content = escape(content, "<\\/?[a-z][^>\n]+>");
content = escape(content, "\\{\\{IPA(?:[^\\{\\}\n]+|\\{\\{[^\\}\\n]+\\}\\})+\\}\\}");
var numberRegEx = /^\d+$/;
content = content.replace(
/(?:\/[^\/\|%\n]+\/|\[[^\]\|%\n]+\])(?=[\s\.\,\:\;\)\&\-<%])/g,
function(wholematch)
{
count++;
if ( wholematch.includes("http") || wholematch.includes("...")
|| wholematch.match(numberRegEx))
{
count--;
return wholematch;
}
else
return "{{IPA|" + wholematch + "}}";
}
);
content = content.replace(
/%%(\d+)%%/g,
function(wholematch, number) {
number = Number(number);
return escaped[number];
}
);
content = content.replace(
/%%(\d+)%%/g,
function(wholematch, number) {
number = Number(number);
return escaped[number];
}
);
notifyReplacements(count);
addSummary(count, "added [[Template:IPA]]");
return content;
}
},
{
textBoxIncludes: /[\/\[]\{\{IPA/,
button: {
text: "clean up IPA templates",
},
minorEdit: true,
watch: false,
"function":
function(content)
{
var count1 = 0;
var count2 = 0;
content = content.replace(
/([\/\[])\{\{IPA\|\[\[([^\}]+?)\]\]\|*\}\}([\/\]])/g,
function(wholematch, openingBracket, transcription, closingBracket)
{
count2++;
var template;
if ( openingBracket === "/" )
template = "IPAslink";
else if ( openingBracket === "[" )
template = "IPAblink";
if ( template )
return "{{" + template + "|" + transcription + "}}";
else
{
count2--;
return wholematch;
}
}
);
content = content.replace(
/([\/\[])\{\{IPA\|([^\}]+?)\|*\}\}([\/\]])/g,
function(wholematch, openingBracket, transcription, closingBracket)
{
count1++;
return "{{IPA|" + openingBracket + transcription + closingBracket + "}}";
}
);
/*
content = content.replace(
/([\/\[])\{\{IPA\|/g,
function(wholematch, openingBracket)
{
count++;
return "{{IPA|" + openingBracket;
}
);
*/
content = content.replace(
/(?:\{\{IPA\|)?([\/\[])\{\{IPA ?link\|([^\}]+)\}\}([\/\]])(?:\}\})?/g,
function(wholematch, openingBracket, transcription, closingBracket)
{
count2++;
var template;
if ( openingBracket === "/" )
template = "IPAslink";
else if ( openingBracket === "[" )
template = "IPAblink";
if ( template )
return "{{" + template + "|" + transcription + "}}";
else
{
count2--;
return wholematch;
}
}
);
addSummary(count1, "moved brackets inside [[Template:IPA]]");
addSummary(count2, "replaced [[Template:IPA link]] with [[Template:IPAslink]] or [[Template:IPAblink]]");
notifyReplacements(count1 + count2);
return content;
}
},
{
textBoxIncludes: /[ʦʣʧʤʨʥ]/,
button: {
text: "update deprecated IPA",
},
minorEdit: true,
"function":
function(content)
{
var count = 0;
var update = new Map([
[ "ʧ", "t͡ʃ" ],
[ "ʤ", "d͡ʒ" ],
[ "ʦ", "t͡s" ],
[ "ʣ", "d͡z" ],
[ "ʨ", "t͡ɕ" ],
[ "ʥ", "d͡ʑ" ]
]);
update.forEach(
function(value, key)
{
var regex = new RegExp(key, "g");
content = content.replace(
regex,
function(wholematch)
{
count++;
return value;
}
);
}
);
addSummary(count, "updated deprecated IPA");
return content;
}
},
// From [[User:Erutuon/footnoteCleanup.js]].
{
condition: [ 0, 12 ].includes(namespaceNumber),
button: {
text: "clean up footnotes",
id: "footnote-cleanup"
},
minorEdit: true,
"function":
function(content)
{
var oldContent = content;
var escaped = [];
var i = 0;
var replacements = [];
var count = 0;
var escape = function(text, regexString)
{
var regex = new RegExp(regexString, "g");
text = text.replace(
regex,
function(match)
{
escaped[i] = match;
var replacement = "%%" + i + "%%";
i += 1;
return replacement;
}
);
return text;
};
var puncRegex = /((?:%%\d+%%)+)([\.\,\;\:\"]{1,3})/g;
var reorder = function(match, capture1, capture2)
{
count += 1;
var replacement = capture2 + capture1;
replacements.push(replacement);
return replacement;
};
var fixPunctuationPlacement = function(text)
{
while ( puncRegex.test(text) )
text = text.replace(
/\s*((?:%%\d+%%)+)\s*([\.\,\;\:\"]{1,3})/g,
reorder
);
return text;
};
/* Escape various things:
ref tags */
content = escape(
content,
"<ref[^\\/<>]+\\/>"
);
content = escape(
content,
"<ref[^>]*>[^<]+<\\/ref>"
);
// citation needed
content = escape(
content,
"\\{\\{(?:[Cc]itation needed|[Cc]n|[Ff]act|[Cc]b|[Cc]tn|[Rr]ef\\?)\\|[^\}]+\\}\\}"
);
// "dubious"
content = escape(
content,
"\\{\\{(?:[Dd]ubious)\\|[^\}]+\\}\\}"
);
content = fixPunctuationPlacement(content);
// footnote templates
/* Handles up to one level of nested templates.
Any more, and there may be problems. */
content = escape(
content,
"\\{\\{(?:sfn|efn|rfn)\\|(?:[^\\}]*?(?:\\{\\{[^\\}]+\\}\\})?)+\\}\\}"
);
if ( i > 0 )
mw.notify(i + " refs or tagging templates found.");
content = fixPunctuationPlacement(content);
if ( count === 0 )
mw.notify("No misplaced refs or tagging templates were found.");
else
mw.notify(count + " correction" + ( ( count > 1 && "s" ) || "" ) + " made: " + replacements.join());
/* Unescape the various things escaped above.
This has to be done twice, since escaping was done twice. */
content = content.replace(
/%%(\d+)%%/g,
function(wholematch, number) {
number = Number(number);
return escaped[number];
}
);
content = content.replace(
/%%(\d+)%%/g,
function(wholematch, number) {
number = Number(number);
return escaped[number];
}
);
addSummary(count, "made sure refs are after punctuation as prescribed by [[WP:REFPUNC]]");
return content;
}
},
// From [[User:Erutuon/scripts/imageSize.js]].
{
textBoxIncludes: /[=|]\s*\d+px/,
button: {
text: "convert pixel to scaling size",
},
minorEdit: true,
watch: false,
"function":
function(content)
{
var count1 = 0;
var count2 = 0;
// earlier regex: /\[\[(?:[Ff]ile|[Ii]mage)(?:[^\]]+|\[+[^\]]+\]+)+\]\]/g
// improved version: /\[\[(?:[Ff]ile|[Ii]mage)(?:[^\[\]\n]+|\[+[^\[\[\n]+\]+)+\]\]/g
// version less prone to backtracking errors: /\[\[(?:[Ff]ile|[Ii]mage).+/g
content = content.replace(
/\[\[(?:[Ff]ile|[Ii]mage).+/g,
function(wholematch)
{
var convertedPixels = false;
wholematch = wholematch.replace(
/\|\s*(\d+)px\s*(?=[\|\]])/g,
function(match, number)
{
// Convert string to number.
number = Number(number);
if ( number > 99 )
{
// Convert to upright value.
number = number / 220;
// Round to nearest hundredth.
number = Math.floor( number * 100 ) / 100;
count1++;
if ( number === 1 )
return "";
else {
convertedPixels = true;
return "|upright=" + number;
}
}
// Do not convert pixel value if it is less than 100 pixels.
else
return match;
}
);
if ( convertedPixels && !wholematch.includes("thumb") )
wholematch = wholematch.replace(
/\]\]/g,
function()
{
count2++;
return "|frameless]]";
}
);
return wholematch;
}
);
content = content.replace(
/\{\{(?:(?:[Aa]utomatic )?[Tt]axo|[Ss]pecies)box(?:[^\{\}]+|\{\{[^}]+\}\})+\}\}/g,
function (template) {
var taxon;
template = template.replace(
/\|\s*([^=\|\}]+?)(\s*=\s*)((?:\[\[[^\]]+\]\]|\[[^\]]+\]|\{\{[^\}]+\}\}|[^\|\}\[\]]+)+)/g,
function (wholeMatch, paramName, between, paramValue) {
if (!paramName.includes("width"))
return wholeMatch;
var match = paramValue.match(/(\d+)px/);
// Convert string to number.
var number = Number(match[1]);
if ( number > 99 )
{
// Convert to upright value.
number = number / 220;
// Round to nearest hundredth.
number = Math.floor( number * 100 ) / 100;
count1++;
if ( number === 1 )
return ""; // Parameter is unnecessary.
else
paramValue = number + "\n";
}
if (!startsAndEndsWithWhitespace.test(between))
between = " = ";
paramName = paramName.replace("width", "upright");
return "| " + paramName + between + paramValue;
})
.replace(/\|\}\}$/, "}}");
return template;
});
notifyReplacements(count1);
var summary = "converted image sizes in pixels to scaling values, as prescribed by [[WP:IMGSIZE]]";
if ( count2 > 0 )
summary += ", changing images to frameless as needed";
addSummary(count1, summary);
return content;
}
},
];
// Add cleanup button wrapper if it doesn't exist.
if ( !$("#wikitext-cleanup-button-wrapper").length )
$("#editform").prepend('<div id="wikitext-cleanup-button-wrapper"></div>');
mw.loader.load("//en.wiktionary.org/w/index.php?title=User:Erutuon/styles/wikitext-cleanup.css&action=raw&ctype=text/css", "text/css");
var buttonCount = 0;
cleanupFunctions.forEach(
function(element, index)
{
var condition1;
var condition2;
var condition;
if ( typeof (element.condition) === "boolean" )
condition1 = element.condition;
else if ( typeof (element.condition) === "function" )
condition1 = element.condition($("#wpTextbox1").val());
if ( element.textBoxIncludes )
{
if ( typeof (element.textBoxIncludes) === "string" )
condition2 = $("#wpTextbox1").val().includes(element.textBoxIncludes);
else if ( element.textBoxIncludes instanceof RegExp )
condition2 = element.textBoxIncludes.test($("#wpTextbox1").val());
else
console.log(`The ${["first", "second", "third", "fourth", "fifth"][index]} function has a textBoxIncludes value that is not recognized.`);
}
if ( typeof (condition1) === "boolean" )
{
if ( typeof (condition2) === "boolean" )
condition = ( condition1 && condition2 );
else
condition = condition1;
}
else if ( typeof (condition2) === "boolean" )
condition = condition2;
else
condition = false;
if ( condition === true )
{
buttonCount += 1;
if ( !element.button.id )
element.button.id = "button-id-" + index;
if ( !element.functionName )
element.functionName = "cleanup" + ( index + 1 );
if ( !element.button.text )
element.button.text = "button " + ( index + 1 );
$("#wikitext-cleanup-button-wrapper")
.append(`<div id="${element.button.id}" class="wikitext-cleanup-button">${element.button.text}</div>`);
if ( typeof element.function === "function" )
$("#" + element.button.id).click(
function()
{
var oldContent = $("#wpTextbox1").val();
var newContent = element.function(oldContent);
if ( newContent === oldContent || typeof newContent !== "string" )
return;
$("#wpTextbox1").val(newContent);
if ( element.minorEdit === true )
$("#wpMinoredit").prop("checked", true);
if ( element.watch === false )
{
/*
Uncheck the "watch this page" button
if there is not a "unwatch" button at the top of the page:
that is, if the page is not currently watched.
This is the only way to check if the page is on the watchlist
if "watch this page" is automatically checked when you edit.
*/
if ( !$("#ca-unwatch").length )
$("#wpWatchthis").prop("checked", false);
}
}
);
else
console.log(`The function called "${element.functionName}" is ${element.function && "a " + typeof element.function + ': "' + element.function + '"' || typeof element.function}.`);
}
}
);
console.log(`${buttonCount === 0 && "No" || buttonCount} ${buttonCount === 1 && "button" || "buttons"} added.`);
if ( !$("#wikitext-cleanup-button-wrapper").children().length )
$("#wikitext-cleanup-button-wrapper").remove();
}
// </nowiki>