WaniKani Exact Review Time

Shows actual time of next review

Устаревшая версия на 06.09.2018. Перейти к последней версии.

// ==UserScript==
// @name        WaniKani Exact Review Time
// @namespace   goldenchrysus.wanikani.exactreviewtime
// @description Shows actual time of next review
// @version     1.0.8
// @include     https://www.wanikani.com/dashboard*
// @require     https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js
// @copyright   2018+, Patrick Golden
// @license     MIT; http://opensource.org/licenses/MIT
// @run-at      document-end
// @grant       none
// ==/UserScript==

(function() {
	"use strict";

	// Establish important variables
	let $time    = $(".timeago");
	let observer = new window.MutationObserver(updateReviewBlock);
	let timeout  = false;

	// Track changes to .timeago in order to override changes created by WaniKani
	observer.observe(
		$time[0],
		{
			characterData : true,
			childList     : true,
			attributes    : true,
			subtree       : true
		}
	);
	updateReviewBlock();

	/**
	 * Updates the time or countdown until the next review
	 */
	function updateReviewBlock() {
		let timestamp        = +$time.attr("datetime");
		let milliseconds     = timestamp * 1000;
		let now_milliseconds = +moment().format("x");
		let in_future        = (milliseconds > now_milliseconds);
		let time             = (!in_future) ? "Available now" : moment(milliseconds).format("h:mm a");
		let difference       = Math.floor((milliseconds - now_milliseconds) / 1000);

		// If time until next review is <= 30 minutes, swap to "# minutes" text
		if (difference > 0 && difference <= 1800) {
			let minutes       = Math.ceil(difference / 60) || 1;
			let plural        = (minutes === 1) ? "" : "s";
			let original_time = time;
			let $parent       = $time.closest(".next");

			time = `${minutes} minute${plural}`;

			// Run this function again in 30 seconds to get updated minute count
			setTimeout(updateReviewBlock, 30000);

			// Maintain exact time feature by injecting a new element adjacent to WaniKani's "Next Review" text
			if (!$parent.find("small.exact").length) {
				let $small = $(`<small class="exact">@ ${original_time}</small>`);

				$parent.append($small);
			}
		}

		// Only update the time HTML if it doesn't match this function's output in order to avoid infinite recursion by MutationObserver
		if ($time.html() !== time) {
			$time.html(time);
		}

		// Set a timeout for 1 millisecond after the next review time in order to generate the "Available now" text
		if (in_future && !timeout) {
			timeout = setTimeout(updateReviewBlock, milliseconds - now_milliseconds + 1);
		}
	}
}());