WaniKani Review Button Auto Enable and Hover Details

Show when next review will become available on hover and automatically enable review button

目前为 2020-03-16 提交的版本。查看 最新版本

// ==UserScript==
// @name          WaniKani Review Button Auto Enable and Hover Details
// @description   Show when next review will become available on hover and automatically enable review button
// @author        Nekosuki
// @namespace     https://www.wanikani.com/users/Nekosuki
// @version       1.0.1
// @include       /^https://(www|preview).wanikani.com/
// @require       https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js
// @grant         none
// ==/UserScript==

(function(wkof, $, moment) {
    "use strict";

    if (!wkof) {
        const response = confirm("WaniKani Lesson Hover Details script requires WaniKani Open Framework.\n Click 'OK' to be forwarded to installation instructions.");
        if (response) {
            window.location.href = "https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549";
        }
        return;
    }

    const menuReviewButtonSelector = ".navigation .navigation-shortcut--reviews";
    const dashboardReviewButtonSelector = "a.lessons-and-reviews__reviews-button";
    const dashboardClassPrefix = "lessons-and-reviews__reviews-button--";
    const dashboardClassReviewCounts = [0, 1, 50, 100, 250, 500, 1000];
    const dashboardButtonPopoverOffset = 12;

    const popoverConfig = {
        html: true,
        animation: false,
        placement: "bottom",
        trigger: "hover",
        template: "<div class='popover review-time' style='transform: translate(0, 100px)'><div class='arrow'></div><div class='popover-content'></div></div>",
        content: () => popoverText,
    };

    let nextReviewCount;
    let nextReviewTime;
    let popoverText;
    let updateInterval;

    offsetDashboardButtonPopover();
    fetchData();

    function offsetDashboardButtonPopover() {
        const dashboardButton = $(dashboardReviewButtonSelector);
        if (dashboardButton.length === 0) {
            return;
        }
        const popoverParent = dashboardButton.parent().get(0);
        const dashboardReviewMutationObserver = new MutationObserver(mutations => {
            const popover = mutations[0].addedNodes[0];
            if (popover !== undefined) {
                popover.style.top = `${popover.offsetTop + dashboardButtonPopoverOffset}px`;
            }
        });
        dashboardReviewMutationObserver.observe(popoverParent, { childList: true });
    }

    function fetchData() {
        wkof.include("Apiv2");
        wkof.ready("Apiv2").then(() => {
            wkof.Apiv2.get_endpoint("summary").then(processData);
        });
    }

    function processData(summary) {
        const { next_reviews_at, reviews } = summary;
        nextReviewCount = reviews.find(review => review.available_at === next_reviews_at).subject_ids.length;
        nextReviewTime = moment(next_reviews_at);
        updateInterval = setInterval(updatePeriodically, 1000);
        setupPopovers();
    }

    function setupPopovers() {
        const elements = $([menuReviewButtonSelector, dashboardReviewButtonSelector]);
        if (elements.length > 0) {
            elements.popover(popoverConfig);
        } else {
            clearInterval(updateInterval);
        }
    }

    function updatePeriodically() {
        const nextReviewAvailable = nextReviewTime.isBefore();
        updatePopover(nextReviewAvailable);
        updateButtons(nextReviewAvailable);
        if (nextReviewAvailable) {
            clearInterval(updateInterval);
        }
    }

    function updatePopover(reviewAvailable) {
        if (reviewAvailable) {
            popoverText = `<strong>${nextReviewCount}</strong> available now`;
        } else {
            popoverText = `+<strong>${nextReviewCount}</strong> ${nextReviewTime.fromNow()}`;
        }
    }

    function updateButtons(reviewAvailable) {
        if (!reviewAvailable) {
            return;
        }
        const dashboardButton = $(dashboardReviewButtonSelector);
        if (dashboardButton.length > 0 && dashboardButton.hasClass(`${dashboardClassPrefix}0`)) {
            dashboardButton.removeClass(`${dashboardClassPrefix}0`);
            const dashboardClassReviewCount = dashboardClassReviewCounts.reverse().find(c => c <= nextReviewCount);
            dashboardButton.addClass(`${dashboardClassPrefix}${dashboardClassReviewCount}`);
            dashboardButton.find("span").text(nextReviewCount);
        }
        const menuButton = $(menuReviewButtonSelector);
        if (menuButton.length > 0 && +menuButton.attr("data-count") === 0) {
            menuButton.attr("data-count", nextReviewCount);
            menuButton.find("span").text(nextReviewCount);
        }
    }
})(window.wkof, window.jQuery, this.moment);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址