您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Show when next review will become available on hover and automatically enable review button
当前为
// ==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.1.2 // @include /^https://(www|preview).wanikani.com/ // @grant none // ==/UserScript== (function(wkof, $) { "use strict"; if (!wkof) { const response = confirm("WaniKani Review Button Auto Enable and 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 todayForecastSectionSelector = "section[data-react-class^='ReviewForecast'] table > tbody"; const dashboardClassPrefix = "lessons-and-reviews__reviews-button--"; const dashboardClassReviewCounts = [0, 1, 50, 100, 250, 500, 1000]; const dashboardButtonPopoverOffset = 0; const popoverStyles = ` .review-button-aehd.popover { border-radius: 5px; border: 5px solid rgba(75,75,75,0.8); box-shadow: none; width: fit-content; } .review-button-aehd.popover > .arrow { border-bottom-color: rgba(75,75,75,0.8); border-width: 9px; top: -14px; border-top-width: 0; margin-left: -9px; } .review-button-aehd.popover > .arrow:after { border-color: transparent; } .review-button-aehd.popover > .popover-content { text-shadow: 0 1px 0 #fff; text-align: center; } `; const popoverTemplate = ` <div class="popover review-button-aehd"> <div class="arrow"></div> <div class="popover-content"></div> </div> `; const popoverConfig = { html: true, animation: false, placement: "bottom", trigger: "hover", template: popoverTemplate, content: () => popoverText, }; let nextReviewCount; let nextReviewDate; 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; nextReviewDate = new Date(next_reviews_at); setupPopovers(); if (nextReviewAvailable()) { updatePopover(); } else { updateInterval = setInterval(updatePeriodically, 1000); } } function setupPopovers() { const elements = $([menuReviewButtonSelector, dashboardReviewButtonSelector]); if (elements.length > 0) { $("head").append(`<style>${popoverStyles}</style>`); elements.popover(popoverConfig); } } function nextReviewAvailable() { return nextReviewDate < new Date(); } function updatePeriodically() { updatePopover(); if (nextReviewAvailable()) { updateButtons(); updateForecast(); clearInterval(updateInterval); } } function updatePopover() { if (nextReviewAvailable()) { popoverText = `<strong>${nextReviewCount}</strong> available now`; } else { popoverText = `+<strong>${nextReviewCount}</strong> ${relativeTimeToNextReview()}`; } } function updateButtons() { 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); } } function updateForecast() { let todayForecastSection = $(todayForecastSectionSelector).first(); let hourRows = todayForecastSection.find(".review-forecast__hour"); if (hourRows.length === 0) { todayForecastSection.remove(); todayForecastSection = $(todayForecastSectionSelector).first(); hourRows = todayForecastSection.find(".review-forecast__hour"); todayForecastSection.find(".review-forecast__day-header time").text("Today"); } const additionalReviewsToday = todayForecastSection.find("td").first().contents().last(); if (hourRows.length === 1) { const todayForecastSectionHeader = todayForecastSection.find("tr").first(); if (!todayForecastSection.hasClass("is-collapsed")) { todayForecastSectionHeader.click(); } todayForecastSectionHeader.removeClass("cursor-pointer").addClass("cursor-default"); todayForecastSection.on("click", false); additionalReviewsToday.replaceWith("0"); todayForecastSection.find("td").eq(1).text(nextReviewCount); todayForecastSection.find("i").addClass("text-gray-400"); } else { hourRows.first().remove(); additionalReviewsToday.replaceWith(+additionalReviewsToday.text() - nextReviewCount); } } function relativeTimeToNextReview() { const seconds = Math.round((nextReviewDate - new Date()) / 1000); if (seconds < 45) { return "in a few seconds"; } const stages = [ { unit: "minute", factor: 60, threshold: 0.25 }, { unit: "hour", factor: 60, threshold: 0.25 }, { unit: "day", factor: 24, threshold: 0.1 }, { unit: "month", factor: 30, threshold: 0.15 }, { unit: "year", factor: 12, threshold: 0.1 }, ]; let measure = seconds; let unit; for (const stage of stages) { stage.measure = measure / stage.factor; stage.measure += stage.measure < 1 ? stage.threshold : 0.5; stage.measure = Math.floor(stage.measure); if (stage.measure === 0) { break; } measure = stage.measure; unit = stage.unit; } let time = "in "; if (measure === 1) { time += `a${unit.startsWith("h") ? "n" : ""} ${unit}`; } else { time += `${measure} ${unit}s`; } return time; } })(window.wkof, window.jQuery);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址