WaniKani Hint Button

Button that shows a part of the answer when hovered and shows more when clicked.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

Bạn sẽ cần cài đặt một tiện ích mở rộng như Tampermonkey hoặc Violentmonkey để cài đặt kịch bản này.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         WaniKani Hint Button
// @namespace    wk-showhint
// @version      1.0
// @description  Button that shows a part of the answer when hovered and shows more when clicked.
// @author       Jiftoo
// @include      http://www.wanikani.com/review/session*
// @include      https://www.wanikani.com/review/session*
// @grant        GM_addStyle
// @grant        unsafeWindow
// ==/UserScript==

/* jshint esversion: 6 */
(function () {
	"use strict";

	const $ = unsafeWindow.$;
	if (!$) {
		throw new Error("jquеry's $ is not defined. Hint button is disabled.");
	}
	if (!CSS.supports("display", "flex")) {
		throw new Error("Flexbox is not supported. Hint button is disabled.");
	}

	const buttonList = document.querySelector("#additional-content > ul");

	const textSpan = document.createElement("span");
	textSpan.id = "hint-option-span";
	textSpan.innerText = "Hover for hint";
	textSpan.title = "pog";
	const setHint = (str) => {
		textSpan.title = str;
	};

	const templateItem = buttonList.children[0].cloneNode();
	templateItem.id = "option-hint";
	templateItem.appendChild(textSpan);
	const hintCallback = (ev) => {
		// 'text-span' doesn't work; currentTarget only works when called as callback.
		const hintEl = textSpan; //document.getElementById("hint-option-span");

		const advanceHint = (hint) => {
			// Increment hint advance by one or set it to one
			// Attribute converted to number using +(...)
			// 'data-adv' can't be null because it's set to '0' in 'resetHintSpan'
			console.assert(hintEl.getAttribute("data-adv") !== null);
			const nextAdv = +hintEl.getAttribute("data-adv") + 1;
			hintEl.setAttribute("data-adv", nextAdv);

			const hintSlice = hint.slice(0, nextAdv);
			setHint(`${hintSlice}${hintSlice.length !== hint.length ? "..." : ""}`);
		};
		// Wanikani uses it internally.
		const {rad, kan, voc, en, kana, emph, on, kun} = $.jStorage.get("currentItem");
		const firstEn = en[0];
		const type = $.jStorage.get("questionType");
		if (type === "reading") {
			if (rad) {
				advanceHint(rad);
			} else if (kan) {
				if (emph === "onyomi") {
					advanceHint(on[0]);
				} else {
					// kunyomi
					advanceHint(kun[0]);
				}
			} else if (voc) {
				advanceHint(kana[0]);
			}
		} else {
			// It must be 'meaning'
			advanceHint(firstEn);
		}
	};
	const resetHintSpan = () => {
		textSpan.setAttribute("data-adv", 0);
		hintCallback();
	};

	templateItem.onclick = hintCallback;
	buttonList.appendChild(templateItem);

	// Reset hint when submitting
	// Set a small timeout to let the old item update before getting values for hint
	const resetCallback = (ev) => {
		if (ev === undefined || ev.key === "Enter") {
			setTimeout(() => resetHintSpan(), 50);
		}
	};
	const keyListenerFix = ({code}) => {
		if (code === "Enter") {
			resetCallback();
		}
	};
	document.querySelector("#user-response ~ button").addEventListener(
		"click",
		() => {
			resetCallback();
		},
		true
	);
	$(document).on("keydown.reviewScreen", resetCallback);

	GM_addStyle(`#additional-content ul {display: flex; justify-content: space-around;}`);
	GM_addStyle(`#additional-content ul li {width: 0 !important; flex-grow: 1;}`);

	// Observe loading screen style to detect loading
	let loadingScreen = document.getElementById("loading");
	let observer = new MutationObserver((mutations) => {
		for (let mutation of mutations) {
			if (mutation.type == "attributes" && mutation.target.style.display === "none") {
				console.log("finished loading!");

				// Initialise first hint.
				resetHintSpan();

				observer.disconnect();
				observer = undefined;
				break;
			}
		}
	});
	observer.observe(loadingScreen, {
		attributes: true,
	});
})();