您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Includes Kanjidamage Mnemonics in WaniKani
// ==UserScript== // @name WaniKani Kanjidamage Mnemonics // @namespace https://greasyfork.org/users/649 // @version 2.0.7 // @description Includes Kanjidamage Mnemonics in WaniKani // @author Adrien Pyke // @match *://www.wanikani.com/kanji/* // @match *://www.wanikani.com/level/*/kanji/* // @match *://www.wanikani.com/review/session // @match *://www.wanikani.com/lesson/session // @require https://cdn.jsdelivr.net/gh/fuzetsu/userscripts@ec863aa92cea78a20431f92e80ac0e93262136df/wait-for-elements/wait-for-elements.js // @grant GM_xmlhttpRequest // ==/UserScript== (() => { 'use strict'; const SCRIPT_NAME = 'WaniKani Kanjidamage Mnemonics'; const Util = { log(...args) { args.unshift(`%c${SCRIPT_NAME}:`, 'font-weight: bold;color: #233c7b;'); console.log(...args); }, fromEntries: Object.fromEntries || (iterable => [...iterable].reduce((obj, [key, val]) => ((obj[key] = val), obj), {})), q: (query, context = document) => context.querySelector(query), qq: (query, context = document) => Array.from(context.querySelectorAll(query)), appendAfter: (elem, elemToAppend) => elem.parentNode.insertBefore(elemToAppend, elem.nextElementSibling), makeElem: (type, { classes, ...opts } = {}) => { const node = Object.assign( document.createElement(type), Util.fromEntries( Object.entries(opts).filter(([_, value]) => value != null) ) ); classes && classes.forEach(c => node.classList.add(c)); return node; }, fetch: (url, method = 'GET') => new Promise((resolve, reject) => GM_xmlhttpRequest({ url, method, onload: resolve, onerror: reject }) ), newTabLink: { target: '_blank', rel: 'noopener noreferrer' } }; const App = { cachedKanji: [], getKanjiDamageInfo: async (kanji, inLesson) => { if (App.cachedKanji[kanji]) { Util.log(`${kanji} cached`); return App.cachedKanji[kanji]; } Util.log(`Loading Kanjidamage information for ${kanji}`); try { const response = await Util.fetch( `http://www.kanjidamage.com/kanji/search?q=${kanji}` ); Util.log(`Found Kanjidamage information for ${kanji}`); const tempDiv = Util.makeElem('div', { innerHTML: response.responseText }); const replaceClasses = elem => { if (elem.classList.contains('onyomi')) { elem.classList.remove('onyomi'); elem.classList.add( inLesson ? 'highlight-reading' : 'reading-highlight' ); } if (elem.classList.contains('component')) { elem.classList.remove('component'); elem.classList.add( inLesson ? 'highlight-radical' : 'radical-highlight' ); } if (elem.classList.contains('translation')) { elem.classList.remove('translation'); elem.classList.add( inLesson ? 'highlight-kanji' : 'kanji-highlight' ); } }; const readTableHtml = header => { const section = Util.qq('h2', tempDiv).find(elem => elem.textContent.includes(header) ); if (!section) return; const content = Util.q('td:nth-child(2)', section.nextElementSibling); Util.qq('span', content).forEach(replaceClasses); Util.qq('img', content) .filter(img => img.getAttribute('src').startsWith('/')) .forEach( img => (img.src = 'http://www.kanjidamage.com' + img.getAttribute('src')) ); return content.innerHTML; }; const reading = readTableHtml('Onyomi'); const mnemonic = readTableHtml('Mnemonic'); App.cachedKanji[kanji] = { character: kanji, reading, mnemonic, url: response.finalUrl }; return App.cachedKanji[kanji]; } catch (e) { Util.log(`Could not find Kanjidamage information for ${kanji}`); } }, createH2() { const h2 = Util.makeElem('h2'); const link = Util.makeElem('a', { textContent: 'Kanjidamage', ...Util.newTabLink }); h2.appendChild(link); return { h2, link }; }, createSection(node) { const { h2, link } = App.createH2(); const section = Util.makeElem('section'); if (node) { Util.appendAfter(node, h2); Util.appendAfter(h2, section); } return { h2, link, section }; }, createContainer(sel, selNode) { const container = Util.makeElem('section'); const { h2, link, section } = App.createSection(); container.appendChild(h2); container.appendChild(section); if (typeof sel === 'string') waitForElems({ sel, onmatch: elem => Util.q(selNode).classList.contains('kanji') && Util.appendAfter(elem, container) }); else Util.appendAfter(sel, container); return { container, h2, link, section }; }, getKanjiObjHtml: ({ reading, mnemonic }) => (reading || '') + (mnemonic || ''), initWatch: (sel, selKanji, cb, cbClear) => waitForElems({ context: Util.q(sel), config: { attributes: true, childList: true, characterData: true, subtree: true }, onchange: async () => { cbClear && cbClear(); if (!Util.q(sel).classList.contains('kanji')) return; const kanji = Util.q(selKanji).textContent.trim(); const kanjiObj = await App.getKanjiDamageInfo(kanji, true); kanji === kanjiObj.character && cb && cb(kanjiObj); } }), runOnLesson: () => waitForElems({ sel: '#main-info', stop: true, onmatch() { const { link: meaningLink, section: meaningSection } = App.createSection(Util.q('#supplement-kan-meaning-notes')); const { link: readingLink, section: readingSection } = App.createSection(Util.q('#supplement-kan-reading-notes')); const { link: reviewLink, section: reviewSection } = App.createContainer('#note-reading', '#main-info'); const clearOutput = () => (meaningLink.href = readingLink.href = reviewLink.href = meaningSection.innerHTML = readingSection.innerHTML = reviewSection.innerHTML = ''); const outputKanjidamage = kanjiObj => { meaningLink.href = readingLink.href = reviewLink.href = kanjiObj.url; meaningSection.innerHTML = readingSection.innerHTML = reviewSection.innerHTML = App.getKanjiObjHtml(kanjiObj); }; App.initWatch( '#main-info', '#character', outputKanjidamage, clearOutput ); } }), runOnReview: () => waitForElems({ sel: '#character', onmatch() { const { link, section } = App.createContainer( '#note-reading', '#character' ); const outputKanjidamage = kanjiObj => { link.href = kanjiObj.url; section.innerHTML = App.getKanjiObjHtml(kanjiObj); }; App.initWatch('#character', '#character > span', outputKanjidamage); } }), runOnKanjiPage: async () => { const kanji = Util.q('.kanji-icon').textContent; const kanjiObj = await App.getKanjiDamageInfo(kanji, false); const { link, section } = App.createContainer( Util.q('#note-reading').parentNode ); link.href = kanjiObj.url; section.innerHTML = App.getKanjiObjHtml(kanjiObj); } }; const isLesson = window.location.pathname.includes('/lesson/'); const isReview = window.location.pathname.includes('/review/'); isLesson ? App.runOnLesson() : isReview ? App.runOnReview() : App.runOnKanjiPage(); })();