您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically displays the meanings of the kanji when you get a vocab item wrong
// ==UserScript== // @name Wanikani: Fast Vocab Breakdown // @namespace http://tampermonkey.net/ // @version 1.2.1 // @description Automatically displays the meanings of the kanji when you get a vocab item wrong // @author Kumirei // @match https://www.wanikani.com/* // @match https://preview.wanikani.com/* // @grant none // ==/UserScript== ;(async function (wkof) { let script_name = 'Fast Vocab Breakdown' let script_id = 'fast_vocab_breakdown' let items, elem, answer confirm_wkof() await init() window.addEventListener(`turbo:render`, init) window.addEventListener(`turbo:before-render`, before_render) window.addEventListener(`didAnswerQuestion`, handle_answer) window.addEventListener(`willShowNextQuestion`, hide_breakdown) function before_render(event) { const new_body = event.detail.newBody switch (get_page()) { case 'reviews': case 'extra_study': case 'lesson_quiz': install_element(new_body) break } } function get_page() { const url = window.location.pathname if (/\/subjects\/review/.test(url)) return 'reviews' if (/\/subjects\/extra_study/.test(url)) return 'extra_study' if (/\/subjects\/lesson\/quiz/.test(url)) return 'lesson_quiz' } async function init() { wkof.include('ItemData,Settings,Menu') await wkof.ready('ItemData,Settings,Menu').then(load_settings).then(install_menu).then(fetch_items) install_element(document.body) } function confirm_wkof() { if (!wkof) { if ( confirm( script_name + ' requires Wanikani Open Framework.\nDo you want to be forwarded to the installation instructions?', ) ) { window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549' } return } } // Populates the item database with items indexed by subject id async function fetch_items() { items = wkof.ItemData.get_index(await wkof.ItemData.get_items(), 'subject_id') } // Installs the info element and sets up the page function install_element(body) { install_css() body.querySelector('.character-header__content')?.insertAdjacentHTML( 'beforeend', '<div id="' + script_id + '" class="hidden"></div>', ) elem = body.querySelector(`#${script_id}`) } // Decides what to do when a new answer is submitted function handle_answer(event) { const settings = wkof.settings.fast_vocab_breakdown const item_type = event.detail.subjectWithStats.subject.type if (settings.type !== 'both' && item_type !== settings.type) return if (settings.type === 'both' && item_type !== 'Vocabulary' && item_type !== 'Kanji') return const questionType = event.detail.questionType const passed = event.detail.results.action === 'pass' const is_quest = settings.question == 'both' || settings.question == questionType const is_ans = settings.answer == 'both' || (settings.answer === 'correct') === passed if (is_quest && is_ans) insert_info(event.detail.subjectWithStats.subject.id) else elem.classList.add('hidden') } function hide_breakdown() { elem.classList.add('hidden') } // Finds and inserts the kanji info when an incorrect answer is submitted async function insert_info(itemId) { let meanings = [] for (let componentId of items[itemId].data.component_subject_ids) { const item = items[componentId] for (let meaning of item.data.meanings) { if (meaning.primary) { if (item.data.characters) meanings.push( `<a href="${item.data.document_url}" target="_blank">${item.data.characters}: ${meaning.meaning}</a>`, ) else { const svg = await get_radical_image(item) meanings.push( `<a href="${item.data.document_url}" target="_blank">${svg}: ${meaning.meaning}</a>`, ) } } } } let text = meanings.join(', ') elem.innerHTML = text elem.classList.remove('hidden') } function get_radical_image(item) { return wkof.load_file( item.data.character_images.find((a) => a.content_type == 'image/svg+xml' && a.metadata.inline_styles).url, ) } // Some simple CSS to make things look as they should function install_css() { document.getElementsByTagName('head')[0].insertAdjacentHTML( 'beforeend', `<style id="${script_id}-css"> #${script_id} { position: absolute; font-size: 18px; bottom: 0; padding: 0.5rem; } #${script_id}.hidden {visibility: hidden;} #${script_id} a {color: currentColor; text-decoration: none;} #${script_id} svg {height: 1em; filter: invert(1);} .character-header__srs-container { z-index: 1 } .srs { top: -3em;} </style>`, ) } // Load WKOF settings function load_settings() { let defaults = { question: 'both', answer: 'incorrect', type: 'Vocabulary' } return wkof.Settings.load(script_id, defaults) } // Add settings to the menu function install_menu() { var config = { name: script_id, submenu: 'Settings', title: script_name, on_click: open_settings, } wkof.Menu.insert_script_link(config) } // Define settings menu layout function open_settings(items) { var config = { script_id: script_id, title: script_name, content: { question: { type: 'dropdown', label: 'Show on Question Type', hover_tip: 'Show breakdown when you answer either the meaning or the reading question incorrectly', default: 'both', content: { meaning: 'Meaning', reading: 'Reading', both: 'Meaning & Reading', }, }, answer: { type: 'dropdown', label: 'Show on Answer', hover_tip: 'Show breakdown on either correct or incorrect answers', default: 'incorrect', content: { correct: 'Correct', incorrect: 'Incorrect', both: 'Correct & Incorrect', }, }, type: { type: 'dropdown', label: 'Item Type', hover_tip: 'Show breakdown on either vocab or kanji', default: 'Vocabulary', content: { Vocabulary: 'Vocabulary', Kanji: 'Kanji', both: 'Vocabulary & Kanji', }, }, }, } let dialog = new wkof.Settings(config) dialog.open() } })(window.wkof)