您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Displays additional examples sentences for the given vocabulary.
// ==UserScript== // @name WaniKani Example Sentences // @version 3.2 // @description Displays additional examples sentences for the given vocabulary. // @require https://greasyfork.org/scripts/34539-wanikani-api/code/WaniKani%20API.js?version=226222 // @match https://www.wanikani.com/settings/account* // @match https://www.wanikani.com/vocabulary/* // @match https://www.wanikani.com/review/session* // @match https://www.wanikani.com/lesson/session* // @run-at document-end // @copyright 2017 jeshuam // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_addStyle // @namespace https://greasyfork.org/users/27794 // ==/UserScript== ////////////////////////// /////// CSS Styles /////// ////////////////////////// GM_addStyle(` div#example-sentences { max-height: 400px; overflow: auto; } .example-sentence-unlearned-vocab { display: none; } #example-sentences-toggle-display { margin-bottom: 20px; }`); ///////////////////////// /////// Constants /////// ///////////////////////// // The amount of time before the learned item cache is updated. let CACHE_EXPIRATION_TIMER_MS = 24 * 60 * 60 * 1000; // (1 day) ///////////////////////////////// /////// Utility Functions /////// ///////////////////////////////// // // Determine whether the given Japanese word ends in an 'u' sound (i.e. is a verb). // function EndsInUSound(japaneseWord) { return japaneseWord.match(/[るゆむふぬつすくう]/) !== null; } // // Function to check whether a Japanese word only contains kana (hira- or katakana). // function OnlyContainsKanaOrPunctuation(japaneseWord) { return japaneseWord.match(/^[\u3000-\u30FF]+$/) !== null; } ///////////////////////////// /////// Main Function /////// ///////////////////////////// function DisplayExampleSentences(known_vocab) { // // Extract the Kanji from the current page. This will have a switch for each type of page, and // will do something different for each. This will only include vocabulary pages, reviews and // lessons. // function GetVocabularyKanjiFromPage() { // Vocabulary information page. if (document.URL.indexOf('vocabulary') != -1) { return document.querySelector('header span.vocabulary-icon span').innerText.trim(); } // Review page. else if (document.URL.indexOf('review/session') != -1) { return document.querySelector('div.vocabulary span').innerText.trim(); } // Lesson page. else if (document.URL.indexOf('lesson/session') != -1) { return document.querySelector('div#main-info div#character').innerText.trim(); } // Not on a valid page. else { return null; } } // // Get the data from the remote URL for the given vocabulary. // function GetExampleSentencesForVocabulary(vocabulary, complete) { WaniKaniAPI.load('https://jeshuam.pythonanywhere.com/wanikani-sentences/' + vocabulary, complete); } // // Generate the DOM required to display the example sentences. This will be consistent over the // various pages this script runs on. // function GetSectionWithExamplesSentences(known_vocab, sentences) { // Make the initial section. let section = document.createElement('section'); section.id = 'examples-sentences-section'; section.innerHTML = ` <h2>More Context Sentences</h2> <button id="example-sentences-toggle-display">Show All Sentences</button> <div id="example-sentences"></div> `; // When the button is pressed, show/hide the example sentences with unlearned vocab. section.children[1].onclick = function() { if (this.innerText === 'Show All Sentences') { for (let element of document.querySelectorAll('.example-sentence-unlearned-vocab')) { element.style.display = 'block'; } this.innerText = 'Show Only Known Vocab'; } else { for (let element of document.querySelectorAll('.example-sentence-unlearned-vocab')) { element.style.display = 'none'; } this.innerText = 'Show All Sentences'; } }; // Add each sentence to the section. for (let sentence of sentences) { // Check if this sentence has any words we don't know. If it does, add an extra class to it. let extra_class = ''; let kanji = GetVocabularyKanjiFromPage(); for (let word of sentence.jpn) { if (word != kanji && !OnlyContainsKanaOrPunctuation(word) && known_vocab[word] === undefined) { extra_class = 'example-sentence-unlearned-vocab'; break; } } // Make the HTML for this sentence. let japanese_html = ''; for (let word of sentence.jpn) { // Highlight the current word. if (word == kanji) { japanese_html += '<span class="vocabulary-highlight highlight-vocabulary">' + kanji + '</span>'; } // Insert a link to the WaniKani page for learned vocabulary. else if (known_vocab[word] !== undefined) { japanese_html += '<span><a href="https://www.wanikani.com/vocabulary/' + known_vocab[word] + '">' + word + '</a></span>'; } // Otherwise, just put the word into the text. else { japanese_html += '<span>' + word + '</span>'; } } let sentence_html = document.createElement('div'); sentence_html.className = `context-sentence-group ${extra_class}`; sentence_html.innerHTML = `<p lang="ja">${japanese_html}</p><p>${sentence.eng}</p>`; section.children[2].appendChild(sentence_html); } return section; } // Process the vocabulary page. if (document.URL.indexOf('vocabulary') >= 0) { let vocab = GetVocabularyKanjiFromPage(); GetExampleSentencesForVocabulary(vocab, function(data) { if (data.length === 0) { return; } let section = GetSectionWithExamplesSentences(known_vocab, data); let insertion_section = document.querySelector('section.context-sentence'); insertion_section.parentNode.insertBefore(section, insertion_section.nextSibling); }); } // Process the review page. TODO(jeshua): test this. else if (document.URL.indexOf('review/session') >= 0) { // If the 'all-info' button is pressed, then display it. document.querySelector('div#all-info').onclick = function() { let vocab = GetVocabularyKanjiFromPage(); GetExampleSentencesForVocabulary(vocab, function(data) { if (data.length === 0) { return; } // Remove the old section. let section_to_remove = document.querySelector('#examples-sentences-section'); if (section_to_remove !== null) { section_to_remove.parentNode.removeChild(section_to_remove); } let section = GetSectionWithExamplesSentences(known_vocab, data); let insertion_section = document.querySelector('section#item-info-context-sentences'); insertion_section.parentNode.appendChild(section); }); }; } // Process the lesson page. else if (document.URL.indexOf('lesson/session') >= 0) { let observer = new MutationObserver(function() { let vocab = GetVocabularyKanjiFromPage(); GetExampleSentencesForVocabulary(vocab, function(data) { if (data.length === 0) { return; } // Remove the old section. let section_to_remove = document.querySelector('#examples-sentences-section'); if (section_to_remove !== null) { section_to_remove.parentNode.removeChild(section_to_remove); } // Add the new section. let section = GetSectionWithExamplesSentences(known_vocab, data); let insertion_section = document.querySelector('div#supplement-voc-context-sentence'); insertion_section.parentNode.insertBefore(section, insertion_section.nextSibling); }); }); observer.observe(document.querySelector('div#main-info'), {subtree: true, childList: true}); } } ////////////////////////////// /////// Start Function /////// ////////////////////////////// document.addEventListener('DOMContentLoaded', function() { // Get their API key. If we are on the account page, go no further. if (WaniKaniAPI.getAPIKey() === undefined) { console.log('EXAMPLE-SENTENCES: No WaniKani API key found!'); return; } // Load the unlocked vocab, initializing to an empty object. let unlockedVocab = JSON.parse(GM_getValue('wanikani-sentences-learned-cache', '{"__cache-time": 0}')); // If cache expired, update first then run main. let currentTime = (new Date().getTime()); if ((currentTime - unlockedVocab['__cache-time']) > CACHE_EXPIRATION_TIMER_MS) { console.log('EXAMPLE-SENTENCES: Cache expired, refreshing known vocabulary.'); WaniKaniAPI.load(WaniKaniAPI.apiURL('vocabulary'), function(data) { for (let vocab of data.requested_information.general) { if (vocab.user_specific !== null) { // Remove any preceding ~ characters (as they aren't part of the word). let kanji = vocab.character.replace(/〜/g, ''); unlockedVocab[kanji] = kanji; // For verbs, remove the U sound at the end. This should do a decent job of showing // the verb even if it is conjugated (it won't be perfect, but better than nothing). if (EndsInUSound(kanji)) { unlockedVocab[kanji.substr(0, kanji.length - 1)] = kanji; } } } // Cache time in milliseconds since the epoch. unlockedVocab['__cache-time'] = (new Date().getTime()); // Save the cache, then keep going with the main program. GM_setValue('wanikani-sentences-learned-cache', JSON.stringify(unlockedVocab)); DisplayExampleSentences(unlockedVocab); }); } else { DisplayExampleSentences(unlockedVocab); } });