User:Mesidast/Tidy citations.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <nowiki>
// Forked from [[User:Meteor sandwich yum/Tidy citations.js]] by [[User:Meteor sandwich yum]]
// Forked by [[User:Mesidast]]. For full list of changes see [[User:Mesidast/Tidy citations]]
// Version 2.3.2

// Retrieve and Set Global variables
if (gTidyCiteEditSum == null) var gTidyCiteEditSum = true; //generate a short summary
if (gTidyCiteShowDiff == null) var gTidyCiteShowDiff = true; //show diff after
if (gTidyCiteMarkMinor == null) var gTidyCiteMarkMinor = true; //mark edit as minor
if (gTidyCiteReplaceParams == null) var gTidyCiteReplaceParams = true; //replace deprecated parameters
if (gTidyCiteRemoveLive == null) var gTidyCiteRemoveLive = true; //remove url-status=live on unarchived refs
if (gTidyCiteReplacementParams == null) var gTidyCiteReplacementParams = {}; //initialise empty object if no user values found

mw.loader.using("mediawiki.util", function () {
	// Only run script if user is editing an article
	if (!document.forms.editform || (mw.config.get("wgAction") !== "edit" && mw.config.get("wgAction") !== "submit")) {
		return;
	}

	// Deprecated parameters to replace
	var deprecatedParams = { //gTidyCiteReplacementParams
		"accessdate": "access-date",
		"archiveurl": "archive-url",
		"archivedate": "archive-date",
		"urlstatus": "url-status",
		"urlaccess": "url-access",
		"transcripturl": "transcript-url",
		"authorlink": "author-link"
	};
	// If custom user list exists, append to and supersede the default param replace list
	deprecatedParams = Object.assign(deprecatedParams, gTidyCiteReplacementParams);

	function TidyCitations(mode) {
		// WikEd compatibility
		const useWikEd = window.wikEd && window.wikEd.useWikEd;
		if (useWikEd) {
			window.wikEd.UpdateTextarea();
		}

		// Get and check textbox and summary field
		var textbox = $("#wpTextbox1");
		var summary = $("#wpSummary");
		if (!textbox) {
			mw.notify("Textbox not found | Tidy Citations");
			return false;
		}
		if (!summary) {
			mw.notify("Summary box not found | Tidy Citations");
			return false;
		}
		var txt = textbox.val();
		const original = txt;
		var hyphenToggle = false;
		var statusToggle = false;

		const paramRegex = / *\| *([a-zA-Z1-9-]+) *= */g;
		var customRegex;
		if (mode === "custom") {
			customRegex = prompt("Please enter your custom format, using the following examples: ' |$1=' or '|$1=' or ' | $1 = '");
			console.log(customRegex);
			if ((customRegex.match(/["']?(\s*\|\s*\$1\s*=\s*)["']?/g) || []).length !== 1) {
				mw.notify("Invalid custom format entered | Tidy Citations");
				return false;
			}
			customRegex = customRegex.replace(/["']/g, "");
		}

		// Fill an array with one entry per each recognized citation template
		var oldTemplates = txt.match(/{{[_\s]*[Cc]it(?:ation|e[A-Za-z_ ]*)[_\s]*\|(?:{{[^}]*}}|[^}])+}}/g) || [];
		var newTemplates = oldTemplates.slice();
		//var archiveTemplates = txt.match(/{{[_\s]*[Ww]eb(?:\.|[_ ]*)archive[_\s]*\|(?:{{[^}]*}}|[^}])+}}/g) || []; //TODO : Tie in
		//var allReferences = txt.match(/<ref(?:\s+name\s*=.+?)?\s*>.*?<\/ref\s*>|<ref\s+name\s*=.+?\/>/gsi); // Get full text within ref tags
		// /{{[_\s]*[Ll]ondon[_ ]*[Gg]azette[_\s]*\|	(London Gazette)

		for (var i = 0; i < oldTemplates.length; i++) {

			// Replace deprecated non-hyphenated parameters
			if (gTidyCiteReplaceParams) {
				for (let key in deprecatedParams) {
					if (deprecatedParams.hasOwnProperty(key)) {
						let deprecatedRegex = new RegExp("\\| *" + key + " *=", "gi");
						if (deprecatedRegex.test(newTemplates[i])) {
							newTemplates[i] = newTemplates[i].replace(deprecatedRegex, "|" + deprecatedParams[key] + "=");
							hyphenToggle = true;
						}
					}
				}
			}

			//Remove "|url-status=live" if there is no archive link
			if (gTidyCiteRemoveLive && /\|\s*url-?status\s*=\s*live/.test(newTemplates[i])) { //Fix .test
				if (!(/\|\s*archive-?url\s*=\s*http/.test(newTemplates[i]))) {
					newTemplates[i] = newTemplates[i].replace(/\|\s*url-?status\s*=\s*live/,"");
					statusToggle = true;
				}
			}

			// standard, crammed, roomy & custom
			if (mode !== "vertical") {
				// Remove newlines
				newTemplates[i] = newTemplates[i].replace(/\n/g, "");
				
				switch (mode) { // Normalize spaces around the pipes and equal signs
					case "standard":
						newTemplates[i] = newTemplates[i].replace(paramRegex, " |$1=");
						break;
					case "crammed":
						newTemplates[i] = newTemplates[i].replace(paramRegex, "|$1=");
						break;
					case "roomy":
						newTemplates[i] = newTemplates[i].replace(paramRegex, " | $1 = ");
						break;
					case "custom":
						newTemplates[i] = newTemplates[i].replace(paramRegex, customRegex);
						break;
				}
				// Remove potential extra spaces before template ends
				newTemplates[i] = newTemplates[i].replace(/\s*}}$/, "}}");
				txt = txt.replace(oldTemplates[i], newTemplates[i]);
				// vertical
			} else {
				// Fill an array with one entry per each parameter for this citation template
				var oldParams = oldTemplates[i].match(/ *\n? *\| *\n? *([a-zA-Z1-9-]+) *= */g);
				var newParams = [];
				var maxWidth = 0;
				for (var j = 0; j < oldParams.length; j++) {
					// Get rid of the delimiters and spaces, keep only the parameter string
					newParams[j] = oldParams[j].match(/[a-zA-Z1-9-]+/)[0];
					// Calculate the length of the longest parameter
					maxWidth = (newParams[j].length > maxWidth) ? newParams[j].length : maxWidth;
				}
				maxWidth++; // We need an extra one because Array(n).join(' ') will produce a string with n-1 chars

				// Generate the aligned versions of the parameters (with padding before the equal signs)
				for (var k = 0; k < oldParams.length; k++) {
					var numSpaces = maxWidth - newParams[k].length;
					var alignedParam = "\n  | " + newParams[k] + new Array(numSpaces).join(" ") + " = ";
					// Replace the original parameters with the tweakes ones
					newTemplates[i] = newTemplates[i].replace(oldParams[k], alignedParam);
				}

				// Also align the }}
				newTemplates[i] = newTemplates[i].replace(/ *\n? *}}/g, "\n}}");
				// Replace the original templates with the tweaked versions
				txt = txt.replace(oldTemplates[i], newTemplates[i]);
			}
		}
		// Only insert the text if something has changed
		if (txt !== original) {
			textbox.val(txt); //update text
			if (useWikEd) wikEd.UpdateFrame(); //wikEd compatibility
			if (gTidyCiteEditSum) summary.val(EditSummary(summary.val(), hyphenToggle, statusToggle)); //edit summary
			if (gTidyCiteMarkMinor) document.editform.wpMinoredit.checked = true; //mark as minor
			if (gTidyCiteShowDiff) $("#wpDiff").click(); //show changes
		} else {
			mw.notify("No changes made | Tidy Citations");
		}
	}

	// Generate a random alphanumeric ID of set length
	function GetUniqueID(length) {
		const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
		var result = "";
		for (let i = 0; i < length; i++) {
			result += chars[Math.floor(Math.random() * chars.length)];
		}
		return result;
	}

	// Substitute comments with placeholders
	function RemoveComments(citeTemplate) {
		const commentText = [];
		const commentMatches = citeTemplate.match(/<!--.*?-->/gs); //Get all comments
		for (let i = 0; i < commentMatches.length; i++) {
			var subText = GetUniqueID(10);
			while (citeTemplate.indexOf(subText) !== -1) {
				subText = GetUniqueID(10);
			}
			commentText.push([subText, commentMatches[i]]);
			citeTemplate = citeTemplate.replace(commentMatches[i], subText);
		}
		return { commentText, citeTemplate };
	}
	// a = RemoveComments("Test")
	// b = a.commentText
	// c = a.newTemplates

	// Restore comments from placeholders
	function RestoreComments(citeTemplate, commentText) {
		for (let key in commentText) {
			if (commentText.hasOwnProperty(key)) {
				citeTemplate = citeTemplate.replace(key, commentText[key]);
			}
		}
		return citeTemplate;
	}

	// Get array of parameters split by "|" and then split by "="
	function SplitParams(citeTemplate) {
		var paramList = citeTemplate
			.split("|")
			.map(element => element.split(/=(.*)/s)
				.map(element => element.trim())
				.filter(element => element !== ""));
		return paramList;
	}

	// Append custom message unto the end of the current edit dummary
	function EditSummary(sum, hyphenToggle, statusToggle) {
		// Define edit summary messages
		const spaceSum = "Standardise citation spacing";
		const hyphenSum = " replace deprecated fields";
		const statusSum = " remove 'url-status=live' from non-archived refs";
		const linkSum = " using [[User:Mesidast/Tidy citations|a script]]";
		
		var repeatSummary = sum.indexOf(linkSum) !== -1;
		var currentRegex;
		if (repeatSummary) {
			// Merge toggles from previous edit summary
			currentRegex = new RegExp(spaceSum + ".*?" + linkSum.replace(/([\[\]\/\|])/g, "\\$1"), "gs");
			var currentSum = sum.match(currentRegex) || [""];
			hyphenToggle = hyphenToggle || currentSum[0].indexOf(hyphenSum) !== -1;
			statusToggle = statusToggle || currentSum[0].indexOf(statusSum) !== -1;
		}
		
		// Build edit summary
		var firstAppend = true;
		var appendSum = linkSum;
		if (statusToggle === true) {
			appendSum = " and" + statusSum + appendSum;
			firstAppend = false;
		}
		if (hyphenToggle === true) {
			appendSum = (firstAppend ? " and" : ",") + hyphenSum + appendSum;
			firstAppend = false;
		}
		appendSum = spaceSum + appendSum;
		
		// Merge script summary and existing summary
		if (repeatSummary) {
			// Script re-run
			sum = sum.replace(currentRegex, appendSum);
		} else {
			// First time
			if (/[^\*\/\s][^\/\s]?\s*$/.test(sum)) {
				sum += " | " + appendSum;
			} else {
				sum = appendSum;
			}
		}
		return sum;
	}

	var tidyButton1 = mw.util.addPortletLink("p-tb", "#", "{{Tidy}}", "t-tidyCiteDefault", "Format citations: tidy whitespace");
	var tidyButton2 = mw.util.addPortletLink("p-tb", "#", "{{Tidy}} (Vertical)", "t-tidyCiteVertical", "Formats citations & tidy whitespace (vertically)");
	var tidyButton3 = mw.util.addPortletLink("p-tb", "#", "{{Crammed}}", "t-tidyCiteCrammed", "Formats citations without any whitespace whatsoever");
	var tidyButton4 = mw.util.addPortletLink("p-tb", "#", "{{Roomy}}", "t-tidyCiteRoomy", "Formats citations with a lot of whitespace");
	var tidyButton5 = mw.util.addPortletLink("p-tb", "#", "{{Custom}}", "t-tidyCiteCustom", "Formats citations with custom format");
	$(tidyButton1).click(function () { TidyCitations("standard"); });
	$(tidyButton2).click(function () { TidyCitations("vertical"); });
	$(tidyButton3).click(function () { TidyCitations("crammed"); });
	$(tidyButton4).click(function () { TidyCitations("roomy"); });
	$(tidyButton5).click(function () { TidyCitations("custom"); });

});
// </nowiki>