MediaWiki:Gadget-DocTabs.js

From Linguifex
Jump to navigation Jump to search

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)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// Idea: add or remove portlet links as required, using an API query to check what needs to be redlinked beforehand.
// The following page types are unsupported: `Template_talk:XYZ/documentation`, `Module_talk:XYZ/documentation`, `Citations_talk:XYZ`
// as it is not clear whether these pages should exist at all, and if so, what portlets should be shown on them.
// <nowiki>

let portletBar = document.querySelector("#p-associated-pages, #p-namespaces, #p-cactions");

if (portletBar) {
	let actionAPI = new mw.Api({ajax: {headers: {"Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]"}}});
	let ns = mw.config.values.wgNamespaceNumber;
	let title = mw.config.values.wgTitle.replaceAll(" ", "_");
	let isDocumentationPage = (ns === 10 || ns === 11 || ns === 828 || ns === 829) && title.endsWith("/documentation");

	// Remove /documentation suffix if necessary.
	let mainTitle = isDocumentationPage ? title.slice(0, -14) : title;

	// Page title, display text, ID, tooltip, access key.
	let portletData = {
		"mainspace": [mainTitle, "Entry", "ca-nstab-main", "View the content page", "c"],
		"talk": [`Talk:${mainTitle}`, "Discussion", "ca-talk", "Discussion about the content page", "t"],
		"citations": [`Citations:${mainTitle}`, "Citations", "ca-nstab-citations", "View the citations page", "3"],
		"template": [`Template:${mainTitle}`, "Template", "ca-nstab-template", "View the template", "c"],
		"template_talk": [`Template_talk:${mainTitle}`, "Discussion", "ca-talk", "Discussion about the content page", "t"],
		"template_documentation": [`Template:${mainTitle}/documentation`, "Documentation", "ca-nstab-docs", "View the documentation", "3"],
		"module": [`Module:${mainTitle}`, "Module", "ca-nstab-module", "View the module page", "c"],
		"module_talk": [`Module_talk:${mainTitle}`, "Discussion", "ca-talk", "Discussion about the content page", "t"],
		"module_documentation": [`Module:${mainTitle}/documentation`, "Documentation", "ca-nstab-docs", "View the documentation", "3"]
	};

	// Given a list of portlet types (see above), return a filtered set of portlets which need to be highlighted as redlinks.
	async function checkRedlinkPortlets(...portletList) {
		let titleList = portletList.map(portlet => portletData[portlet][0]);

		// Check whether the page titles exist.
		let response = await actionAPI.get({
			action: "query",
			titles: titleList.join("|"),
			format: "json"
		});

		// Get page results which have a negative ID (i.e., do not exist).
		let redlinks = new Set();
		for (let [pageKey, pageData] of Object.entries(response.query.pages)) {
			if (pageKey < 0)
				redlinks.add(pageData.title.replaceAll(" ", "_"));
		}
		return new Set(portletList.filter(portlet => redlinks.has(portletData[portlet][0])));
	}

	// Adds a namespace link of the specified type (see above) on the portlet bar.
	// It uses the `redlinkPortlets` list generated by checkRedlinkPortlets().
	// `location` should be set to either "beginning" or "end".
	// There appears to be no way to create a portlet redlink, so this implementation tries to emulate the default behaviour.
	// Note: adding two identical portlets is currently unsupported, as it results in duplicate IDs in the DOM.
	// More info: https://doc.wikimedia.org/mediawiki-core/REL1_29/js/#!/api/mw.util-method-addPortletLink
	function addNamespaceLink(portletType, redlinkPortlets, location, isSelected) {
		let data = portletData[portletType];

		// Certain skins that have unrelated portlets in the same bar which we should always be before.
		// If none of the selectors match an element, the portlet is placed at the very end as desired.
		// If `location` is "beginning", the selector always matches the first element.
		let before = location === "beginning" ? "li" : "#ca-edit, #ca-view, #ca-watch";

		let isRedlink = redlinkPortlets.has(portletType);
		let tooltip = data[3];
		let href = `/wiki/${data[0]}`;

		if (isRedlink) {
			tooltip += " (page does not exist)";
			href = `/w/index.php?title=${data[0]}&action=edit&redlink=1`;
		}

		mw.util.addPortletLink(portletBar.id, href, data[1], data[2], tooltip, data[4], portletBar.querySelector(before));

		let portletElem = document.getElementById(data[2]);
		if (isSelected) {
			portletElem.classList.add("selected");
		} else if (isRedlink) {
			portletElem.classList.add("new");
			portletElem.querySelector("a").classList.add("new");
		}
	}

	(async () => {
		if (ns === 0 || ns === 1) { // Mainspace and Talk
			// Add a link to the citations page.
			let redlinkPortlets = await checkRedlinkPortlets("citations");
			addNamespaceLink("citations", redlinkPortlets, "end");

		} else if (ns === 114) { // Citations
			// Add links to mainspace and talk and remove link to citations talk.
			let redlinkPortlets = await checkRedlinkPortlets("mainspace", "talk");

			// Remove link to Citations talk.
			portletBar.querySelectorAll("li")[1].remove();

			// Set access key to "3".
			let citationsLink = portletBar.querySelector("li > a");
			citationsLink.setAttribute("accesskey", 3);
			citationsLink.setAttribute("title", "View the citations page [alt-shift-3]");

			addNamespaceLink("talk", redlinkPortlets, "beginning");
			addNamespaceLink("mainspace", redlinkPortlets, "beginning");

		} else if ((ns === 10 || ns === 11) && !isDocumentationPage) { // Template and Template_talk
			// Add a link to the documentation.
			let redlinkPortlets = await checkRedlinkPortlets("template_documentation");
			addNamespaceLink("template_documentation", redlinkPortlets, "end");

		} else if (ns === 10 && isDocumentationPage) { // Template documentation
			let redlinkPortlets = await checkRedlinkPortlets("template", "template_talk", "template_documentation");

			// Remove existing two portlet links.
			portletBar.querySelector("li").remove();
			portletBar.querySelector("li").remove();

			// Add links to the template, discussion, and current documentation pages.
			addNamespaceLink("template", redlinkPortlets, "end");
			addNamespaceLink("template_talk", redlinkPortlets, "end");
			addNamespaceLink("template_documentation", redlinkPortlets, "end", true);

		} else if ((ns === 828 || ns === 829) && !isDocumentationPage) { // Module and Module_talk
			// Add a link to the documentation.
			let redlinkPortlets = await checkRedlinkPortlets("module_documentation");
			addNamespaceLink("module_documentation", redlinkPortlets, "end");

		} else if (ns === 828 && isDocumentationPage) { // Module documentation
			let redlinkPortlets = await checkRedlinkPortlets("module", "module_talk", "module_documentation");

			// Remove existing two portlet links.
			portletBar.querySelector("li").remove();
			portletBar.querySelector("li").remove();

			// Add links to the module, discussion, and current documentation pages.
			addNamespaceLink("module", redlinkPortlets, "end");
			addNamespaceLink("module_talk", redlinkPortlets, "end");
			addNamespaceLink("module_documentation", redlinkPortlets, "end", true);
		}
	})();
}
// </nowiki>