您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Additional Search filters for the WaniKani Open Framework
// ==UserScript== // @name WaniKani Open Framework Search Filters // @namespace https://www.wanikani.com // @description Additional Search filters for the WaniKani Open Framework // @author prouleau // @version 1.4.1 // @match https://www.wanikani.com/* // @license MIT; http://opensource.org/licenses/MIT // @grant none // ==/UserScript== (function(wkof) { 'use strict'; var wkofMinimumVersion = '1.0.52'; if (!wkof) { var response = confirm('WaniKani Open Framework Date 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; } var settingsDialog; var settingsScriptId = 'searchFilters'; var settingsTitle = 'Search Filters'; var needToRegisterFilters = true; var filterNamePrefix = 'searchFilters_'; var globalSearchFilterName = filterNamePrefix + 'globalSearch'; var exactSearchFilterName = filterNamePrefix + 'exactSearch'; var kunSearchFilterName = filterNamePrefix + 'kunSearch'; var onSearchFilterName = filterNamePrefix + 'onSearch'; var componentsSearchFilterName = filterNamePrefix + 'componentsSearch'; var usedInSearchFilterName = filterNamePrefix + 'usedInSearch'; var visSimSearchFilterName = filterNamePrefix + 'visSimSearch'; var allowSearchFilterName = filterNamePrefix + 'allowSearch'; var blockSearchFilterName = filterNamePrefix + 'blockSearch'; var ctxSenSearchFilterName = filterNamePrefix + 'ctxSenkSearch'; var mnemonicsSearchFilterName = filterNamePrefix + 'mnemonicsSearch'; var supportedFilters = [globalSearchFilterName, exactSearchFilterName, kunSearchFilterName, onSearchFilterName, componentsSearchFilterName, usedInSearchFilterName, visSimSearchFilterName, allowSearchFilterName, blockSearchFilterName, ctxSenSearchFilterName, mnemonicsSearchFilterName, ]; function prepareSubjectIndexThenRegisterFilters(){ wkof.set_state(settingsScriptId, 'loading'); var config = {wk_items: {filters:{}, options:{'subjects': true,}}}; wkof.include('ItemData'); wkof.ready('ItemData') .then(function(){return wkof.ItemData.get_items(config)}) .then(prepareIndex) .then(updateFiltersWhenReady) } var componentIndex = {}; var usedInIndex = {}; var visuallySimilarIndex = {}; function prepareIndex(items){ for (var item of items){ if (item.object !== 'radical'){ componentIndex[item.data.slug] = componentIndex[item.data.slug] || []; componentIndex[item.data.slug] = componentIndex[item.data.slug].concat(item.data.component_subject_ids); } if (item.object !== 'vocabulary'){ usedInIndex[item.data.slug] = usedInIndex[item.data.slug] || []; usedInIndex[item.data.slug] = usedInIndex[item.data.slug].concat(item.data.amalgamation_subject_ids); if (item.object === 'radical' && item.data.characters !== null){ usedInIndex[item.data.characters] = usedInIndex[item.data.characters] || []; usedInIndex[item.data.characters] = usedInIndex[item.data.characters].concat(item.data.amalgamation_subject_ids); } } if (item.object === 'kanji'){ visuallySimilarIndex[item.data.slug] = visuallySimilarIndex[item.data.slug] || []; visuallySimilarIndex[item.data.slug] = visuallySimilarIndex[item.data.slug].concat(item.data.visually_similar_subject_ids); } } } function updateFiltersWhenReady() { needToRegisterFilters = true; return waitForItemDataRegistry().then(registerFilters); } function waitForItemDataRegistry() { return wkof.wait_state('wkof.ItemData.registry', 'ready'); } function registerFilters() { if (!needToRegisterFilters) { return; } supportedFilters.forEach(function(filterName) { delete wkof.ItemData.registry.sources.wk_items.filters[filterName]; }); registerSearchFilter(); registerExactSearchFilter(); registerKunSearchFilter(); registerOnSearchFilter(); registerComponentsSearchFilter(); registerUsedInSearchFilter(); registerVisSimSearchFilter(); registerAllowSearchFilter(); registerBlockSearchFilter(); registerCtxSenSearchFilter(); registerMnemonicsSearchFilter(); needToRegisterFilters = false; wkof.set_state(settingsScriptId, 'ready'); } function split_list(str) {return str.replace(/、/g,',').replace(/[\s ]+/g,' ').trim().replace(/ *, */g, ',').toLowerCase().split(',').filter(function(name) {return (name.length > 0);});} function makeComponentPairs(str){ return split_list(str).reduce((acc, item) =>{return acc.concat(componentIndex[item] || [] )}, []); } function makeUsedInPairs(str){ return split_list(str).reduce((acc, item) =>{return acc.concat(usedInIndex[item.replace(' ', '-')]) || [] }, []); } function makeVisuallySimilarPairs(str){ return split_list(str).reduce((acc, item) =>{return acc.concat(visuallySimilarIndex[item] || [] )}, []); } // BEGIN Global Search let searchHover_tip = 'Enter a search term for meaning, reading or kanji.\nAll approximate matches will be found.\nYou may use latin, kana and kanji.\nMultiple terms separated by commas are allowed.'; function registerSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[globalSearchFilterName] = { type: 'text', label: 'Global Search', placeholder: 'gambler, 力士', default: '', filter_func: searchFilter, filter_value_map: split_list, set_options: function(options) { options.subjects = true; }, hover_tip: searchHover_tip, }; } function searchFilter(filterValue, item) { if (item.data === undefined) { return false; }; for (var searchTerm of filterValue){ if (item.data.characters !== null) if (item.data.characters.indexOf(searchTerm) >= 0){return true}; if (item.data.slug.indexOf(searchTerm.replace(' ', '-')) >= 0){return true}; for (var meaning of item.data.meanings){ if (meaning.accepted_answer && meaning.meaning.toLowerCase().indexOf(searchTerm) >= 0){return true}; }; if (item.object !== 'radical' && item.object !== 'kana_vocabulary'){ for (let reading of item.data.readings){ if (reading.reading.indexOf(searchTerm) >= 0) {return true;}; }; }; }; return false; } // END Global Search // BEGIN Exact Search let exactSearchHover_tip = 'Enter a search term for meaning, reading or kanji.\nReturns only exact matches.\nYou may use latin, kana and kanji.\nMultiple terms separated by commas are allowed.'; function registerExactSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[exactSearchFilterName] = { type: 'text', label: 'Exact Search', default: '', placeholder: 'gambler, 力士', filter_func: exactSearchFilter, filter_value_map: split_list, set_options: function(options) { options.subjects = true; }, hover_tip: exactSearchHover_tip, }; } function exactSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; for (var searchTerm of filterValue){ if (item.data.characters !== null) if (searchTerm === item.data.characters) {return true}; if (searchTerm.replace(' ', '-') === item.data.slug){return true}; for (var meaning of item.data.meanings){ if (!meaning.accepted_answer) break; let term = meaning.meaning.trim().toLowerCase(); if (searchTerm === term){return true}; let words = term.split(' ').filter(function(name) {return (name.length > 0);}) for (var word of words) { if (searchTerm === word){return true}; } }; if (item.object !== 'radical' && item.object !== 'kana_vocabulary'){ for (let reading of item.data.readings){ if (searchTerm === reading.reading) {return true;}; }; }; }; return false; } // END Exact Search // BEGIN Kunyomi Search let kunSearchHover_tip = 'Enter a search term for kunyomi reading.\nReturns only exact matches.\nMultiple terms separated by commas are allowed.'; function registerKunSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[kunSearchFilterName] = { type: 'text', label: 'Kunyomi Search', default: '', placeholder: 'し、のう', filter_func: kunSearchFilter, filter_value_map: split_list, set_options: function(options) { options.subjects = true; }, hover_tip: kunSearchHover_tip, }; } function kunSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; for (var searchTerm of filterValue){ if (item.object === 'kanji'){ for (var reading of item.data.readings){ if (reading.accepted_answer && reading.type === 'kunyomi' && searchTerm === reading.reading){return true}; }; }; }; return false; } // END Kunyomi Search // BEGIN Onyomi Search let onSearchHover_tip = 'Enter a search term for onyomi reading.\nReturns only exact matches.\nMultiple terms separated by commas are allowed.'; function registerOnSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[onSearchFilterName] = { type: 'text', label: 'Onyomi Search', default: '', placeholder: 'し、のう', filter_func: onSearchFilter, filter_value_map: split_list, set_options: function(options) { options.subjects = true; }, hover_tip: onSearchHover_tip, }; } function onSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; for (var searchTerm of filterValue){ if (item.object === 'kanji'){ for (var reading of item.data.readings){ if (reading.accepted_answer && reading.type === 'onyomi' && searchTerm === reading.reading){return true}; }; }; }; return false; } // END Onyomi Search // BEGIN Components Search let componentsSearchHover_tip = 'Looking for components on listed items.\nRadicals for kanji, Kanji for vocabulary.\nMultiple terms separated by commas are allowed.'; function registerComponentsSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[componentsSearchFilterName] = { type: 'text', label: 'Components Search', default: '', placeholder: '能、入り口', filter_func: componentsSearchFilter, filter_value_map: makeComponentPairs, set_options: function(options) { options.subjects = true; }, hover_tip: componentsSearchHover_tip, }; } function componentsSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; return (item.object !== 'vocabulary' || item.object !== 'kana_vocabulary') && (filterValue.indexOf(item.id) >= 0); } // END Components Search // BEGIN Used In Search let usedInSearchHover_tip = 'Looking for where listed items are used.\nKanji for radicals, Vocabulary for kanji.\nMultiple terms separated by commas are allowed.'; function registerUsedInSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[usedInSearchFilterName] = { type: 'text', label: 'Where Used Search', default: '', placeholder: '能、ground', filter_func: usedInSearchFilter, filter_value_map: makeUsedInPairs, set_options: function(options) { options.subjects = true; }, hover_tip: usedInSearchHover_tip, }; } function usedInSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; return (item.object !== 'radical' || item.object !== 'kana_vocabulary') && (filterValue.indexOf(item.id) >= 0) } // END Used In Search // BEGIN Visually Similar Search let visSimSearchHover_tip = 'Looking for kanji visually similar to listed kanji.\naccording to Wanikani.\nMultiple terms separated by commas are allowed.'; function registerVisSimSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[visSimSearchFilterName] = { type: 'text', label: 'WK Visually Similar', default: '', placeholder: '能、人', filter_func: visSimSearchFilter, filter_value_map: makeVisuallySimilarPairs, set_options: function(options) { options.subjects = true; }, hover_tip: visSimSearchHover_tip, }; } function visSimSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; return (item.object === 'kanji') && (filterValue.indexOf(item.id) >= 0); } // END Visually Similar Search // BEGIN Allow List Search let allowSearchHover_tip = 'Enter a search term for the allow list.\nAll approximate matches will be found.\n# will match empty allow lists.\n* will match non empty allow lists.\nMultiple terms separated by commas are allowed.'; function registerAllowSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[allowSearchFilterName] = { type: 'text', label: 'Allow List Search', default: '', placeholder: 'pure, conflict', filter_func: allowSearchFilter, filter_value_map: split_list, set_options: function(options) { options.subjects = true; }, hover_tip: allowSearchHover_tip, }; } function allowSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; var list = item.data.auxiliary_meanings.filter((entry)=> entry.type === 'whitelist'); for (var searchTerm of filterValue){ if (searchTerm === '#' && list.length === 0) return true; if (searchTerm === '*' && list.length !== 0) return true; for (var allowTerm of list){ if (allowTerm.meaning.indexOf(searchTerm) >= 0) return true; }; }; return false; } // END Allow List Search // BEGIN Block List Search let blockSearchHover_tip = 'Enter a search term for the block list.\nAll approximate matches will be found.\n# will match empty block lists.\n* will match non empty block lists.\nMultiple terms separated by commas are allowed.'; function registerBlockSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[blockSearchFilterName] = { type: 'text', label: 'Block List Search', default: '', placeholder: 'pure, conflict', filter_func: blockSearchFilter, filter_value_map: split_list, set_options: function(options) { options.subjects = true; }, hover_tip: blockSearchHover_tip, }; } function blockSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; var list = item.data.auxiliary_meanings.filter((entry)=> entry.type === 'blacklist'); for (var searchTerm of filterValue){ if (searchTerm === '#' && list.length === 0) return true; if (searchTerm === '*' && list.length !== 0) return true; for (var allowTerm of list){ if (allowTerm.meaning.indexOf(searchTerm) >= 0) return true; }; }; return false; } // END Block List Search // BEGIN Context Sentences Search let ctxSenSearchHover_tip = 'Enter a search term for the context sentences.\nAll approximate matches will be found.\n# will match empty empty context sentences lists.\n* will match non empty empty context sentences lists.\nMultiple terms separated by commas are allowed.'; function registerCtxSenSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[ctxSenSearchFilterName] = { type: 'text', label: 'Context Sentences Search', default: '', placeholder: 'pure, conflict', filter_func: ctxSenSearchFilter, filter_value_map: split_list, set_options: function(options) { options.subjects = true; }, hover_tip: ctxSenSearchHover_tip, }; } function ctxSenSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; if (item.object !== 'vocabulary' && item.object !== 'kana_vocabulary') { return false; }; var list = item.data.context_sentences; for (var searchTerm of filterValue){ if (searchTerm === '#' && list.length === 0) return true; if (searchTerm === '*' && list.length !== 0) return true; for (var sentences of list){ if (sentences.ja.indexOf(searchTerm) >= 0) return true; if (sentences.en.indexOf(searchTerm) >= 0) return true; }; }; return false; } // END Context Sentences Search // BEGIN Mnemonics and Notes Search let mnemonicsSearchHover_tip = 'Enter a search term for the mnemonics, hints or user notes.\nAll approximate matches will be found.\nMultiple terms separated by commas are allowed.'; function registerMnemonicsSearchFilter() { wkof.ItemData.registry.sources.wk_items.filters[mnemonicsSearchFilterName] = { type: 'text', label: 'Mnemonics/Notes Search', default: '', placeholder: 'pure, conflict', filter_func: mnemonicsSearchFilter, filter_value_map: split_list, set_options: function(options) { options.subjects = true; options.study_materials = true; }, hover_tip: mnemonicsSearchHover_tip, }; } function mnemonicsSearchFilter(filterValue, item) { if (item.data === undefined) { return false; }; for (var searchTerm of filterValue){ if (item.data.meaning_mnemonic.indexOf(searchTerm) >= 0) return true; if (item.data.reading_mnemonic && item.data.reading_mnemonic.indexOf(searchTerm) >= 0) return true; if (item.data.meaning_hint && item.data.meaning_hint.indexOf(searchTerm) >= 0) return true; if (item.data.reading_hint && item.data.reading_hint.indexOf(searchTerm) >= 0) return true; if (item.study_materials && item.study_materials.meaning_note && item.study_materials.meaning_note.indexOf(searchTerm) >= 0) return true; if (item.study_materials && item.study_materials.reading_note && item.study_materials.reading_note.indexOf(searchTerm) >= 0) return true; }; return false; } // END Mnemonics and Notes Search prepareSubjectIndexThenRegisterFilters(); })(window.wkof);