MediaWiki:Gadget-U2693.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/* jshint undef:true, latedef:true, shadow:true, boss:true, scripturl:true */
/* global mw, jQuery */
// <nowiki>

"use strict";
mw.loader.using(["mediawiki.util"]).then(() =>
	$(function() {
		var curRevisionId = mw.config.get("wgCurRevisionId");

		function createAnchorLink(label, href, wikilink, title) {
			var span = document.createElement("span");
			var link = document.createElement("a");

			link.href = href;
			link.addEventListener(
				"click",
				function(ev) {
					ev.preventDefault();
					// IE
					if (window.clipboardData && window.clipboardData.setData) {
						window.clipboardData.setData("text", wikilink);
						mw.util.jsMessage("Wikitext copied to the clipboard.");
						return;
					}
					// XXX: there is a brand new shiny ClipboardEvent API, but I could not get it to work on my browser.
					// plus, there is no graceful fallback if it fails to work. going back to good ol' prompt().
					prompt("Hit Ctrl-C to copy this to your clipboard", wikilink);
				},
				false
			);

			link.appendChild(document.createTextNode(label));
			link.title = title;

			span.appendChild(document.createTextNode("["));
			span.appendChild(link);
			span.appendChild(document.createTextNode("]"));
			span.className = "mw-editsection-like anchor-gadget-link";

			return span;
		}

		// XXX: not ideal; it may over-unescape things, but it usually works as intended
		function unescapeAnchor(anchor) {
			anchor = anchor.replace(/_/g, " ");
			try {
				return decodeURIComponent(
					anchor.replace(/\.([0-9A-F][0-9A-F])/g, "%$1")
				);
			} catch (e) {
				return anchor;
			}
		}

		function forEachHeader(callback) {
			var headers = document.querySelectorAll(
				".mw-heading > h2, .mw-heading > h3, .mw-heading > h4, .mw-heading > h5, .mw-heading > h6, .mw-headline"
			);

			for (var i = 0; i < headers.length; ++i) {
				var links = headers[i].parentNode.getElementsByTagName("a");
				var title = null;
				for (var j = 0; j < links.length; ++j) {
					try {
						var u = new URL(links[j].href);
						if (
							u.hostname === location.host &&
							u.pathname === mw.config.get("wgScript") &&
							u.searchParams.get("action") === "edit" &&
							!u.searchParams.get("redlink")
						) {
							title = new mw.Title(u.searchParams.get("title"));
						}
					} catch (e) {
						/* screw it */
					}
				}
				if (!title) continue;

				callback(headers[i], title);
			}
		}

		function getRevInfo(oldid, callback) {
			// XXX: of course I have it right on the page, but I cannot rely on it being in any particular format.
			// hence wasting bandwidth on an API request.

			mw.loader.using(["mediawiki.api", "mediawiki.util"], function() {
				var api = new mw.Api();

				api
					.get({
						action: "query",
						pageids: mw.config.get("wgArticleId"),
						prop: "revisions",
						rvprop: "user|timestamp",
						rvstartid: oldid,
						rvlimit: 1,
					})
					.then(function(result) {
						var revobj =
							result.query.pages[mw.config.get("wgArticleId")].revisions[0];

						callback(revobj);
					});
			});
		}

		function createUnsignedLink(revobj) {
			var userpart =
				(mw.util.isIPv4Address(revobj.user) ||
					mw.util.isIPv6Address(revobj.user) ?
					"{{unsigned-ip|" :
					"{{unsigned|") + revobj.user;
			var datepart = "}}";

			var m;
			if (
				(m = /^(\d+)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/.exec(
					revobj.timestamp
				))
			) {
				var monthNames = [
					"January",
					"February",
					"March",
					"April",
					"May",
					"June",
					"July",
					"August",
					"September",
					"October",
					"November",
					"December",
				];
				datepart =
					"|" +
					m[4] +
					":" +
					m[5] +
					", " +
					m[3].replace(/^0+/, "") +
					" " +
					monthNames[parseInt(m[2], 10) - 1] +
					" " +
					m[1] +
					" (UTC)}}";
			}

			return createAnchorLink(
				"✎",
				"javascript:void window.warranty",
				userpart + datepart,
				"{{unsigned}}"
			);
		}

		if (mw.config.get("wgAction") !== "view") return;

		mw.util.addCSS(
			".anchor-gadget-link { -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none; }"
		);

		if (mw.util.getParamValue("diff")) {
			// viewing a diff
			var oldid, newid;
			var ot = document.getElementById("mw-diff-otitle1");
			var nt = document.getElementById("mw-diff-ntitle1");
			if (ot) {
				ot = new URL(ot.getElementsByTagName("a")[0].href);
				oldid = ot.searchParams.get("oldid");
			}
			if (nt) {
				nt = new URL(nt.getElementsByTagName("a")[0].href);
				newid = nt.searchParams.get("oldid");
			}

			document
				.getElementById("firstHeading")
				.appendChild(
					createAnchorLink(
						"⚓",
						mw.config.get("wgScript") + "?oldid=" + oldid + "&diff=" + newid,
						"[[Special:Diff/" + oldid + "/" + newid + "]]",
						"Link to this diff"
					)
				);

			if (
				document.getElementsByClassName("diffmulti").length === 0 &&
				mw.config.get("wgArticleId")
			) {
				getRevInfo(newid, function(revobj) {
					document
						.getElementById("firstHeading")
						.appendChild(createUnsignedLink(revobj));
				});
			}
		} else if (mw.util.getParamValue("oldid") || curRevisionId) {
			// viewing an old page revision
			var oldid = mw.util.getParamValue("oldid") || curRevisionId;

			document
				.getElementById("firstHeading")
				.appendChild(
					createAnchorLink(
						"⚓",
						mw.config.get("wgScript") + "?oldid=" + oldid,
						"[[Special:Permalink/" + oldid + "]]",
						"Link to this revision"
					)
				);

			getRevInfo(oldid, function(revobj) {
				document
					.getElementById("firstHeading")
					.appendChild(createUnsignedLink(revobj));
			});
		}

		if (
			mw.config.get("wgRevisionId") === curRevisionId &&
			// 100 = appendix, 114 = citations
			[0, 100, 114].indexOf(mw.config.get("wgNamespaceNumber")) === -1
		) {
			forEachHeader(function(header, title) {
				var anchorLink = createAnchorLink(
					"⚓",
					mw.util.getUrl(title.getPrefixedDb()) + "#" + header.id,
					"[[" +
					title.getPrefixedText() +
					"#" +
					unescapeAnchor(header.id) +
					"]]"
				);
				var editSectionLink =
					header.parentNode.querySelector(".mw-editsection");
				if (editSectionLink) {
					header.parentNode.insertBefore(
						anchorLink,
						editSectionLink.nextSibling
					);
				} else {
					header.parentNode.appendChild(anchorLink);
				}
			});
		}
	})
);

// </nowiki>