MediaWiki:Gadget-minitoc.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)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
// <nowiki>
// Initialize preferred languages from local storage.
let preferredLanguages = new Set(JSON.parse(localStorage.getItem("minitocLangs")));

// Empty and filled-in bookmark icons.
const unselectedIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="17" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2zm10 14.25-5-3.5-5 3.5V3h10z"></path></g></svg>`;
const selectedIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="17" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2z"></path></g></svg>`;

mw.util.addCSS(`
	.minitoc-icon-selected, .minitoc-icon-unselected {
		cursor: pointer;
		vertical-align: sub;
	}
`);

function displayLanguages() {
	// Clear out previously displayed languages.
	document.querySelectorAll(".minitoc-linked-languages").forEach(elem => elem.remove());

	for (let minitoc of document.querySelectorAll(".minitoc")) {
		let linkedLanguages = document.createElement("span");
		linkedLanguages.className = "minitoc-linked-languages";

		for (let languageLink of minitoc.querySelectorAll(`.NavContent > a:not([href="#catlinks"]`)) {
			if (preferredLanguages.has(languageLink.textContent)) {
				let link = document.createElement("a");
				link.textContent = `→ ${languageLink.textContent}`;
				link.href = languageLink.href;
				link.style.whiteSpace = "nowrap";
				linkedLanguages.append(" ", link);
			}
		}

		if (linkedLanguages.childElementCount)
			minitoc.querySelector(".NavHead").append(linkedLanguages);
	}
}

// Setup.
// Note: the code is generic and can handle multiple miniTOCs on one page, although this probably isn't necessary.
for (let minitoc of document.querySelectorAll(".minitoc")) {
	minitoc.querySelector(".NavContent").insertAdjacentHTML("beforeend",
		`<span style="font-size: 85%" class="minitoc-button-container">
			&#32;
			[<a class="minitoc-button-select" role="button">
				Select preferred languages
			</a>]
			<span style="display: none">
				&#32;
				[<a class="minitoc-button-clear" role="button">
					Clear all
				</a>]
			</span>
		</span>`
		.replace(/[\n\t]/g, "")
	);

	let selectButton = minitoc.querySelector(".minitoc-button-select");
	let clearButton = minitoc.querySelector(".minitoc-button-clear");

	selectButton.addEventListener("click", () => {
		// The button can be in two states: "select" and "save".
		if (selectButton.textContent === "Select preferred languages") {
			clearButton.parentElement.style.display = "";

			// Make sure you can't have multiple miniTOCs in "select" mode simultaneously.
			for (let button of document.querySelectorAll(".minitoc-button-select")) {
				if (button.textContent === "Save preferred languages") {
					// Trigger the saving procedure.
					button.click();
				}
			}

			selectButton.textContent = "Save preferred languages";

			for (let languageLink of minitoc.querySelectorAll(`.NavContent > a:not([href="#catlinks"]`)) {
				let iconContainer = document.createElement("span");
				iconContainer.role = "button";
				languageLink.insertAdjacentElement("beforebegin", iconContainer);

				if (preferredLanguages.has(languageLink.textContent)) {
					iconContainer.className = "minitoc-icon-selected";
					iconContainer.innerHTML = selectedIcon;
				} else {
					iconContainer.className = "minitoc-icon-unselected";
					iconContainer.innerHTML = unselectedIcon;
				}

				iconContainer.addEventListener("click", () => {
					if (preferredLanguages.has(languageLink.textContent)) {
						preferredLanguages.delete(languageLink.textContent);
						iconContainer.className = "minitoc-icon-unselected";
						iconContainer.innerHTML = unselectedIcon;
					} else {
						preferredLanguages.add(languageLink.textContent);
						iconContainer.className = "minitoc-icon-selected";
						iconContainer.innerHTML = selectedIcon;
					}
				});
			}
		} else {
			// Save to local storage.
			localStorage.setItem("minitocLangs", JSON.stringify(Array.from(preferredLanguages)));

			// Reset state.
			minitoc.querySelectorAll(".minitoc-icon-selected, .minitoc-icon-unselected").forEach(elem => elem.remove());
			selectButton.textContent = "Select preferred languages";
			clearButton.parentElement.style.display = "none";
			displayLanguages();
		}
	});

	clearButton.addEventListener("click", () => {
		preferredLanguages.clear();
		for (let iconContainer of minitoc.querySelectorAll(".minitoc-icon-selected")) {
			iconContainer.innerHTML = unselectedIcon;
			iconContainer.className = "minitoc-icon-unselected";
		}
	});
}

displayLanguages();
// </nowiki>