User:Erutuon/scripts/moduleDocumentation.js

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

Note: You may have to bypass your browser’s cache to see the changes. In addition, after saving a sitewide CSS file such as MediaWiki:Common.css, it will take 5-10 minutes before the changes take effect, even if you clear your cache.

  • 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.

Shows language names for the submodules of Module:accel and Module:number list.

Ideas:

  • Add language search dropdown to "create new module" textbox.

/* jshint boss: true, eqeqeq: true, esversion: 6, undef: true, unused: true, varstmt: true */
/* globals $, apiWrapper, mw, OO */

// <nowiki>

(function subpageLinkIIFE() {
"use strict";

const subpageLinkSelector = ".mw-prefixindex-list a";

if (!document.querySelector(subpageLinkSelector))
	return;

mw.loader.using([
	"mediawiki.storage", "mediawiki.Title", "mediawiki.util"
]).done(() => {
	const baseText = mw.config.get("wgPageName").match(/^(?:[^\/]+|\/)/)[0].replace(/_/g, " ");
	
	if (!(baseText === "Module:accel" || baseText === "Module:number list")) {
		console.info("Wrong page");
		return;
	}
	
	const baseTitle = mw.Title.newFromText(baseText);
	const baseTitleRegExp = new RegExp("^" + mw.util.escapeRegExp(baseTitle.getMainText() + "/") + "?");
	
	function getSubpage(unprefixedPageName) {
		return unprefixedPageName.replace(baseTitleRegExp, "");
	}
	
	function removeChildren(elem) {
		let child;
		while (child = elem.firstChild)
			elem.removeChild(child);
	}
	
	function appendLangNode(elem, languageCodeToName, langCode) {
		const langCodeNode = document.createElement("code");
		langCodeNode.textContent = langCode;
		for (const child of [
			document.createTextNode(languageCodeToName[langCode] + " ("),
			langCodeNode,
			document.createTextNode(")")
		])
			elem.appendChild(child);
	}
	
	// Must match the following (minus the namespace):
	// [[Module:number list/data/en]]
	// [[Module:accel/en]]
	function getLangCode(subpageName) {
		const match = subpageName.match(/^(?:data\/)?([a-z]{2,3}(?:-[a-z]{2,3}){0,2})$/);
		return match && match[1];
	}
	
	function processPrefixIndex(languageCodeToName) {
		Array.prototype.forEach.call($(subpageLinkSelector), subpageLink => {
			const text = subpageLink.textContent;
			const subpage = getSubpage(text);
			subpageLink.textContent = subpage;
			
			const langCode = getLangCode(subpage);
			if (!languageCodeToName[langCode]) {
				if (langCode)
					console.info("invalid language code: ", langCode);
				else
					console.info("no language code in ", subpage);
				subpageLink.parentNode.remove(); // Remove list items containing non-language modules.
				return;
			}
			
			removeChildren(subpageLink);
			
			appendLangNode(subpageLink, languageCodeToName, langCode);
		});
	}
	
	/*
	 * Create an object that loads JSON data with a template on demand and
	 * saves it in localStorage.
	 * It provides a doWithData option that takes a callback that requires
	 * the data, and returns a new function that when called, calls the callback,
	 * supplying the data to it as the first parameter.
	 */
	function LanguageData(storageKey, template) {
		if (!(typeof storageKey === "string" && typeof template === "string"))
			throw new TypeError("Expected string");
		this.storageKey = storageKey;
		this.template = template;
	}
	
	LanguageData.prototype = {
		get storage() {
			return mw.storage.get(this.storageKey);
		},
		set storage(val) {
			if (val === null || val === undefined)
				mw.storage.remove(this.storageKey);
			else if (typeof val === "object")
				mw.storage.set(this.storageKey, val);
			else
				throw new TypeError("Expected object, null, or undefined");
		},
		get data() {
			return window[this.storageKey];
		},
		set data(val) {
			window[this.storageKey] = val;
		},
	};
	
	LanguageData.prototype.loadJSON = function loadJSON(JSONData) {
		try {
			if (JSONData) {
				const data = JSON.parse(JSONData);

				if (typeof data !== "object")
					throw new TypeError("Object expected");

				this.data = data;
				
				return true;
			}
		} catch (error) {
			console.error(error);
		}
	};
	
	LanguageData.prototype.doWithData = function doWithData(callback, thisValue) {
		return () => {
			if (this.data || this.loadJSON(this.storage)) {
				callback.call(thisValue, this.data);
				return;
			}
			
			$.getScript("//en.wiktionary.org/w/index.php?title=User:Erutuon/scripts/apiWrapper.js&action=raw")
				.done(() => {
				apiWrapper.expandTemplates(
					this.template,
					(JSONData) => {
						this.storage = JSONData;
						if (this.loadJSON(JSONData))
							callback.call(thisValue, this.data);
						else
							console.error("Fail");
					});
			});
		};
	};
	
	const codeToCanonical = new LanguageData(
		"WiktionaryCodeToName",
		"{{#invoke:languages/javascript-interface|AllCodeToCanonical}}"
	);
	
	$(codeToCanonical.doWithData(processPrefixIndex));
	
	mw.loader.using("oojs-ui").done(function () {
		const $box = $("#create-new input[type=text]");
		
		const popupContent = $("<p>");
		const popup = new OO.ui.PopupWidget({
			$content: popupContent,
			padded: true,
			position: 'above',
		});
		$box.before(popup.$element);
		popup.toggle(false); // hidden initially
	
	
		function langCatLink(langName) {
			const pageName = "Category:" + (langName.indexOf("Language") === -1
				? langName + " language" : langName);
			return $("<a>").attr("href", mw.util.getUrl(pageName)).html(langName);
		}
		
		$box.on("input", codeToCanonical.doWithData(function (codeToName) {
			if (this.val() === "" || codeToName[this.val()]) {
				this.css({
					"background-color": "",
					"color": "",
					"border-color": "",
				});
				if (this.val() !== "") {
					popupContent.html("language code for ");
					popupContent.append(langCatLink(codeToName[this.val()]));
					popup.toggle(true);
				} else
					popup.toggle(false);
			} else {
				popup.toggle(false);
				this.css({
					"background-color": "pink",
					"color": "red",
					"border-color": "red",
				});
			}
		}, $box));
		$box.trigger("input");
		
		// const enterKey = 0x0D;
		const $form = $box.closest("form");
		codeToCanonical.doWithData(function (codeToName) {
			$form.submit(function keydownKeyup(event) {
				if (!(codeToName[$box.val()])) {
					popupContent.html($box.val() + " isn't a valid language code!");
					popup.toggle(true);
					return false;
				}
			});
		}, $box)();
	});
});

})();

// </nowiki>