您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Additional filters for the WaniKani Open Framework
当前为
// ==UserScript== // @name WaniKani Open Framework Additional Filters // @namespace https://www.wanikani.com // @description Additional filters for the WaniKani Open Framework // @author seanblue // @version 1.3.1 // @include https://www.wanikani.com/* // @grant none // ==/UserScript== (function(wkof) { 'use strict'; var wkofMinimumVersion = '1.0.18'; if (!wkof) { var response = confirm('WaniKani Open Framework Additional Filters 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; } if (!wkof.version || wkof.version.compare_to(wkofMinimumVersion) === 'older') { alert('WaniKani Open Framework Additional Filters requires at least version ' + wkofMinimumVersion + ' of WaniKani Open Framework.'); return; } var settingsDialog; var settingsScriptId = 'additionalFilters'; var settingsTitle = 'Additional Filters'; var needToRegisterFilters = true; var settingsLoadedPromise = promise(); var filterNamePrefix = 'additionalFilters_'; var recentLessonsFilterName = filterNamePrefix + 'recentLessons'; var leechTrainingFilterName = filterNamePrefix + 'leechTraining'; var timeUntilReviewFilterName = filterNamePrefix + 'timeUntilReview'; var failedLastReviewName = filterNamePrefix + 'failedLastReview'; var relatedItemsName = filterNamePrefix + 'relatedItems'; var supportedFilters = [recentLessonsFilterName, leechTrainingFilterName, timeUntilReviewFilterName, failedLastReviewName, relatedItemsName]; var defaultSettings = {}; defaultSettings[recentLessonsFilterName] = true; defaultSettings[leechTrainingFilterName] = true; defaultSettings[timeUntilReviewFilterName] = true; defaultSettings[failedLastReviewName] = true; defaultSettings[relatedItemsName] = true; var recentLessonsHoverTip = 'Only include lessons taken in the last X hours.'; var leechesSummaryHoverTip = 'Only include leeches. Formula: incorrect / currentStreak^1.5.'; var leechesHoverTip = leechesSummaryHoverTip + '\n * The higher the value, the fewer items will be included as leeches.\n * Setting the value to 1 will include items that have just been answered incorrectly for the first time.\n * Setting the value to 1.01 will exclude items that have just been answered incorrectly for the first time.'; var timeUntilReviewSummaryHoverTip = 'Only include items that have at least X% of their SRS interval remaining.'; var timeUntilReviewHoverTip = timeUntilReviewSummaryHoverTip + '\nValid values are from 0 to 100. Examples:\n "75": At least 75% of an item\'s SRS interval must be remaining.'; var failedLastReviewSummaryHoverTip = 'Only include items where the most recent review was failed.'; var failedLastReviewHoverTip = failedLastReviewSummaryHoverTip + '\nOnly look at items whose most recent review was in the last X hours.'; var relatedItemsSummaryHoverTip = 'Only include items that contain at least one of the given kanji.'; var relatedItemsHoverTip = relatedItemsSummaryHoverTip + ' Examples:\n "金": All items containing the kanji 金.\n "金髪 -曜": All items containing the kanji 金 or 髪, but not 曜.'; var msPerHour = 3600000; var nowForTimeUntilReview; var nowForFailedLastReview; var regularSrsIntervals = [0, 4, 8, 23, 47, 167, 335, 719, 2879]; var acceleratedSrsIntervals = [0, 2, 4, 8, 23, 167, 335, 719, 2879]; var acceleratedLevels = [1, 2]; function promise(){var a,b,c=new Promise(function(d,e){a=d;b=e;});c.resolve=a;c.reject=b;return c;} function getSrsIntervalInHours(srsStage, level) { var srsInvervals = acceleratedLevels.includes(level) ? acceleratedSrsIntervals : regularSrsIntervals; return srsInvervals[srsStage]; } wkof.include('Menu, Settings'); wkof.ready('Menu').then(installMenu); waitForItemDataRegistry().then(installSettings); function waitForItemDataRegistry() { return wkof.wait_state('wkof.ItemData.registry', 'ready'); } function installMenu() { loadSettings().then(function() { addMenuItem(); }); } function addMenuItem() { wkof.Menu.insert_script_link({ script_id: settingsScriptId, submenu: 'Settings', title: settingsTitle, on_click: function() { settingsDialog.open(); } }); } function installSettings() { wkof.ItemData.pause_ready_event(true); loadSettings().then(function() { wkof.ItemData.pause_ready_event(false); }); } function loadSettings(postLoadAction) { wkof.ready('Settings').then(function() { if (settingsDialog) { return; } var settings = {}; settings[recentLessonsFilterName] = { type: 'checkbox', label: 'Recent Lessons', hover_tip: recentLessonsHoverTip }; settings[leechTrainingFilterName] = { type: 'checkbox', label: 'Leech Training', hover_tip: leechesSummaryHoverTip }; settings[timeUntilReviewFilterName] = { type: 'checkbox', label: 'Time Until Review', hover_tip: timeUntilReviewSummaryHoverTip }; settings[failedLastReviewName] = { type: 'checkbox', label: 'Failed Last Review', hover_tip: failedLastReviewSummaryHoverTip }; settings[relatedItemsName] = { type: 'checkbox', label: 'Related Items', hover_tip: relatedItemsSummaryHoverTip }; settingsDialog = new wkof.Settings({ script_id: settingsScriptId, title: settingsTitle, on_save: saveSettings, settings: settings }); settingsDialog.load(defaultSettings).then(function() { updateFiltersWhenReady(); settingsLoadedPromise.resolve(); }); }); return settingsLoadedPromise; } function saveSettings(){ settingsDialog.save().then(function() { updateFiltersWhenReady(); }); } function updateFiltersWhenReady() { needToRegisterFilters = true; waitForItemDataRegistry().then(registerFilters); } function registerFilters() { if (!needToRegisterFilters) { return; } supportedFilters.forEach(function(filterName) { delete wkof.ItemData.registry.sources.wk_items.filters[filterName]; }); if (wkof.settings[settingsScriptId][recentLessonsFilterName]) { registerRecentLessonsFilter(); } if (wkof.settings[settingsScriptId][leechTrainingFilterName]) { registerLeechTrainingFilter(); } if (wkof.settings[settingsScriptId][timeUntilReviewFilterName]) { registerTimeUntilReviewFilter(); } if (wkof.settings[settingsScriptId][failedLastReviewName]) { registerFailedLastReviewFilter(); } if (wkof.settings[settingsScriptId][relatedItemsName]) { registerRelatedItemsFilter(); } needToRegisterFilters = false; } // BEGIN Recent Lessons function registerRecentLessonsFilter() { wkof.ItemData.registry.sources.wk_items.filters[recentLessonsFilterName] = { type: 'number', label: 'Recent Lessons', default: 24, placeholder: '24', filter_func: recentLessonsFilter, set_options: function(options) { options.assignments = true; }, hover_tip: recentLessonsHoverTip }; } function recentLessonsFilter(filterValue, item) { if (item.assignments === undefined) { return false; } var startedAt = item.assignments.started_at; if (startedAt === null || startedAt === undefined) { return false; } var startedAtDate = new Date(startedAt); var timeSinceStart = Date.now() - startedAtDate; return (timeSinceStart / msPerHour) < filterValue; } // END Recent Lessons // BEGIN Leeches function registerLeechTrainingFilter() { wkof.ItemData.registry.sources.wk_items.filters[leechTrainingFilterName] = { type: 'number', label: 'Leech Training', default: 1, placeholder: '1', filter_func: leechTrainingFilter, set_options: function(options) { options.review_statistics = true; }, hover_tip: leechesHoverTip }; } function leechTrainingFilter(filterValue, item) { if (item.review_statistics === undefined) { return false; } var reviewStats = item.review_statistics; var meaningScore = getLeechScore(reviewStats.meaning_incorrect, reviewStats.meaning_current_streak); var readingScore = getLeechScore(reviewStats.reading_incorrect, reviewStats.reading_current_streak); return meaningScore >= filterValue || readingScore >= filterValue; } function getLeechScore(incorrect, currentStreak) { return incorrect / Math.pow((currentStreak || 0.5), 1.5); } // END Leeches // BEGIN Time Until Review function registerTimeUntilReviewFilter() { wkof.ItemData.registry.sources.wk_items.filters[timeUntilReviewFilterName] = { type: 'number', label: 'Time Until Review', default: 50, placeholder: '50', prepare: timeUntilReviewPrepare, filter_value_map: convertPercentageToDecimal, filter_func: timeUntilReviewFilter, set_options: function(options) { options.assignments = true; }, hover_tip: timeUntilReviewHoverTip }; } function timeUntilReviewPrepare() { // Only set "now" once so that all items use the same value when filtering. nowForTimeUntilReview = Date.now(); } function convertPercentageToDecimal(percentage) { if (percentage < 0) { return 0; } if (percentage > 100) { return 1; } return percentage / 100; } function timeUntilReviewFilter(decimal, item) { if (item.assignments === undefined) { return false; } var srsStage = item.assignments.srs_stage; if (srsStage === 0) { return false; } if (srsStage === 9) { return true; } var level = item.assignments.level; var reviewAvailableAt = item.assignments.available_at; var srsInvervalInHours = getSrsIntervalInHours(srsStage, level); return isAtLeastMinimumHoursUntilReview(srsInvervalInHours, reviewAvailableAt, decimal); } function isAtLeastMinimumHoursUntilReview(srsInvervalInHours, reviewAvailableAt, decimal) { var hoursUntilReview = (Date.parse(reviewAvailableAt) - nowForTimeUntilReview) / msPerHour; var minimumHoursUntilReview = srsInvervalInHours * decimal; return minimumHoursUntilReview <= hoursUntilReview; } // END Time Until Review // BEGIN Failed Last Review function registerFailedLastReviewFilter() { wkof.ItemData.registry.sources.wk_items.filters[failedLastReviewName] = { type: 'number', label: 'Failed Last Review', default: 24, placeholder: '24', prepare: failedLastReviewPrepare, filter_func: failedLastReviewFilter, set_options: function(options) { options.review_statistics = true; options.assignments = true; }, hover_tip: failedLastReviewHoverTip }; } function failedLastReviewPrepare() { // Only set "now" once so that all items use the same value when filtering. nowForFailedLastReview = Date.now(); } function failedLastReviewFilter(filterValue, item) { // review_statistics is undefined for new lessons. if (item.assignments === undefined || item.review_statistics === undefined) { return false; } var assignments = item.assignments; var srsStage = assignments.srs_stage; if (srsStage === 0) { return false; } if (srsStage === 9) { return false; } if (!failedLastReview(item.review_statistics)) { return false; } var srsInvervalInHours = getSrsIntervalInHours(srsStage, assignments.level); var lastReviewTimeInMs = getLastReviewTimeInMs(srsInvervalInHours, assignments.available_at); var hoursSinceLastReview = (nowForFailedLastReview - lastReviewTimeInMs) / msPerHour; return hoursSinceLastReview <= filterValue; } function failedLastReview(reviewStats) { return failedLastReviewOfType(reviewStats.meaning_incorrect, reviewStats.meaning_current_streak) || failedLastReviewOfType(reviewStats.reading_incorrect, reviewStats.reading_current_streak); } function failedLastReviewOfType(totalIncorrect, currentStreak) { return totalIncorrect > 0 && currentStreak === 1; } function getLastReviewTimeInMs(srsInvervalInHours, reviewAvailableAt) { var srsIntervalInMs = srsInvervalInHours * msPerHour; return Date.parse(reviewAvailableAt) - srsIntervalInMs; } // END Failed Last Review // BEGIN Related Items function registerRelatedItemsFilter() { wkof.ItemData.registry.sources.wk_items.filters[relatedItemsName] = { type: 'text', label: 'Related Items', default: '', placeholder: '入力', filter_value_map: relatedItemsMap, filter_func: relatedItemsFilter, hover_tip: relatedItemsHoverTip }; } function relatedItemsMap(kanjiString) { var parts = kanjiString.split(' '); var includeList = []; var excludeList = []; for (var i = 0; i < parts.length; i++) { var part = parts[i]; if (part.startsWith('-')) { concat(excludeList, part.substr(1).split('')); } else { concat(includeList, part.split('')); } } return { include: includeList, exclude: excludeList }; } function concat(array1, array2) { Array.prototype.push.apply(array1, array2); } function relatedItemsFilter(filterValue, item) { var characters = item.data.characters; if (characters === null || characters === undefined) { return false; } var itemCharacterArray = characters.split(''); return containsAny(filterValue.include, itemCharacterArray) && !containsAny(filterValue.exclude, itemCharacterArray); } function containsAny(filterValueArray, itemCharacterArray) { return itemCharacterArray.some(function(itemCharacter) { return filterValueArray.includes(itemCharacter); }); } // END Related Items })(window.wkof);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址