User:Erutuon/scripts/watchlistScriptTagging.js

From Wiktionary, the free dictionary
Jump to navigation Jump to search

Note – after saving, you may have to bypass your browser’s cache to see the changes.

  • Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
  • Konqueror and Chrome: click Reload or press F5;
  • Opera: clear the cache in Tools → Preferences;
  • Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.
/*
Script-tags links in your watchlist and the Recent Changes and Related Changes
pages, using [[User:Erutuon/scripts/scriptRecognition.js]] to detect the script.

Uses ES6, so will not work on some older browsers.
*/

// <nowiki>
/* jshint eqeqeq: true, undef: true, unused: true, loopfunc: true */
/* globals mw, $, isCombining */

"use strict";

$(() => {
const basePageName = mw.config.get("wgPageName").match(/^[^/]+|^\//)[0];

if (![
	"Special:Watchlist",
	"Special:RecentChanges",
	"Special:RecentChangesLinked",
	"Special:Contributions",
	"Special:WhatLinksHere"
].includes(basePageName)) {
	return;
}

// from https://api.jquery.com/jQuery.getScript
$.cachedScript = function (url, options) {
	options = $.extend(options || {}, {
		dataType: "script",
		cache: true,
		url: url
	});
 
	return $.ajax(options);
};

$.when($.cachedScript(
	"//en.wiktionary.org/w/index.php?action=raw&title=User:Erutuon/scripts/scriptRecognition.js&ctype=text/javascript"),
$.cachedScript(
	"//en.wiktionary.org/w/index.php?action=raw&title=User:Erutuon/scripts/combiningCharacters.js&ctype=text/javascript"),
	mw.loader.using("mediawiki.Title"))
.done(() => {
	function addTagger(toTagSelector, mutationObserverTarget) {
		const getScriptCache = Object.create(null);
		const cachedGetScript = function (str) {
			if (str in getScriptCache) {
				return getScriptCache[str];
			} else {
				const script = window.getScript(str);
				getScriptCache[str] = script;
				return script;
			}
		};
		
		function addDottedCircle(str) {
			if (isCombining(str.codePointAt(0)))
				str = '\u25CC' + str;
			return str;
		}
		
		function scriptTagLinks(parent, selector) {
			if (!(!parent || parent instanceof window.Element
			|| parent instanceof window.HTMLDocument)) {
				throw new TypeError("Expected Element or HTMLDocument or falsy value");
			}
			parent = parent || document;
			
			for (const titleElement of parent.querySelectorAll(selector)) {
				const content = titleElement.textContent;
				
				// Skip unless title is in "Rhymes" or "Rhymes talk" namespaces
				// or contains characters above U+0370 (could contain non-Latin).
				if (!/^Rhymes(?: talk)?:|[\u0370-\uFFFF]/.test(content))
					continue;
				
				const title = new mw.Title(content);
				if (!title)
					continue;
				
				switch (title.getNamespaceId()) {
					case 0: { // main
						// Avoid tagging translations and derived terms subpage names.
						titleElement.innerHTML = content.replace(/^(.+?)(\/(?:translations|derived terms))?$/,
							(wholeMatch, base, postfix) => {
								const script = cachedGetScript(base);
								return script
									? '<span class="' + script + '">'
										+ addDottedCircle(base) + '</span>'
										+ (postfix || '')
									: wholeMatch;
								
							});
						break;
					}
					// Add script class to the last portion of Talk namespace links.
					case 1: // Talk
						titleElement.innerHTML = content.replace(/^(Talk:)(.+)$/,
							(wholeMatch, namespace, unPrefixed) => {
								const script = cachedGetScript(unPrefixed);
								return script
									? namespace + '<span class="' + script + '">'
										+ addDottedCircle(unPrefixed) + '</span>'
									: wholeMatch;
								
							});
						break;
					case 106: case 107: // Rhymes, Rhymes talk
						titleElement.innerHTML = content.replace(/^(Rhymes(?: talk)?:(.+?)\/)(.+)$/,
							function (wholeMatch, rootPageName, languageName, rhyme) {
								return rootPageName
									+ (languageName === "Telugu"
										? '<span class="Telu" lang="te">'
										: '<span class="IPA">')
									+ rhyme
									+ '</span>';
							});
						break;
				}
			}
		}
		
		// Document loading and back and forward events.
		$(() => scriptTagLinks(document, toTagSelector));
		$(window).on("popstate", () => scriptTagLinks(document, toTagSelector));
		
		// Use MutationObserver to script-tag links when
		// "View newest changes" button is clicked.
		const target = mutationObserverTarget;
		if (!target)
			 return;
		
		const observer = new MutationObserver((mutationList) => {
			for (const mutation of mutationList) {
				/*
				* The div element containing watchlist entries should have a first
				* element with the class mw-changeslist-line.
				*/
				for (const node of mutation.addedNodes) {
					if (node.nodeType === 1
							&& node.querySelector(toTagSelector)) {
						scriptTagLinks(node, toTagSelector);
					}
				}
			}
		});
		
		observer.observe(target, { childList: true });
		
		return observer;
	}
	
	switch (basePageName) {
		case "Special:Watchlist":
		case "Special:RecentChanges":
		case "Special:RecentChangesLinked":
			addTagger(".mw-changeslist-title", document.getElementsByClassName("mw-changeslist")[0]);
			break;
		case "Special:Contributions":
			addTagger(".mw-contributions-title", document.getElementsByClassName("mw-contributions-list")[0]);
			break;
		case "Special:WhatLinksHere":
			addTagger("#mw-whatlinkshere-list li > a", document.getElementById("mw-whatlinkshere-list"));
			break;
	}
});

});

// </nowiki>