Last active
November 2, 2019 20:50
-
-
Save TomsJensen/183eaccda7ef980ac17522c806599a5a to your computer and use it in GitHub Desktop.
A WaniKani userscript that shows WaniKani's example sentences as context for kanji.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name WaniKani Context | |
// @description Shows WaniKani's example sentences as context for kanji. | |
// @namespace wkcontext | |
// @include https://www.wanikani.com/review/session* | |
// @run-at document-end | |
// @version 0.1.9 | |
// @author Toms Jensen | |
// @license MIT; http://opensource.org/licenses/MIT | |
// ==/UserScript== | |
var character; | |
var context; | |
var questionType; | |
var verbConjugations = [ | |
// Plain | |
'な', | |
'ない', | |
'た', | |
'なかった', | |
// Polite | |
'ます', | |
'ません', | |
'ました', | |
'ませんでした', | |
'ましょう', | |
// Progressive | |
'て', 'で', 'って', | |
'てる', 'でる', 'ってる', | |
'ている', 'でいる', 'っている', | |
]; | |
// Sort verbs by descending length. | |
verbConjugations = verbConjugations.sort(function(a, b) { return b.length - a.length }); | |
window.addEventListener("load", onLoad); | |
// Runs on load. | |
function onLoad() { | |
// Find the character node. | |
var characterDiv = document.getElementById('character'); | |
// Find the node that contains our vocab. | |
character = characterDiv.firstElementChild; | |
// Copy the span for our own nefarious purposes. | |
context = character.cloneNode(); | |
context.classList.add('context'); | |
context.show = function(fade) { | |
character.style.display = 'none'; | |
context.style.display = ''; | |
context.style.opacity = fade; | |
}; | |
context.hide = function() { | |
character.style.display = ''; | |
context.style.display = 'none'; | |
context.style.opacity = 0; | |
}; | |
// Add the copied element to the character div. | |
characterDiv.appendChild(context); | |
// Create styles. | |
// height: <#character.vocab>.line-height / font-size (i.e. 3.21em / 0.4em = 8.025em) | |
createStyle('.context { display: table; position: relative; width: 100%; height: 8.025em; font-size: 0.4em; line-height: 1; transition: opacity 0.5s }'); | |
createStyle('.context span { display: table-cell; vertical-align: middle; padding: 0% 2% }'); | |
createStyle('.context mark { display: inline-block; font-size: 150%; color: unset; background-color: rgba(0, 255, 255, 0.5); }'); | |
// Hide the context. | |
context.hide(); | |
// Create character observer. | |
var observer = new MutationObserver(onCharacterChanged); | |
observer.observe(character, { childList: true }); | |
if (character.innerText != '') | |
onCharacterChanged(); | |
} | |
// Runs when a mutation is observed in the character element. | |
function onCharacterChanged() { | |
context.innerText = ''; // Remove the text that was added by WK's system. | |
context.style.fontFamily = character.style.fontFamily; // Match font set by font randomizers. | |
// Thanks obskyr! (https://community.wanikani.com/t/wanikani-jstorage-documentation/18885) | |
var currentItem = $.jStorage.get("currentItem"); | |
var currentVocab = currentItem.voc; | |
// Remove style and break if current item isn't vocab (i.e. radical or kanji). | |
if (currentVocab) | |
context.show(0); | |
else { | |
context.hide(); | |
return; | |
} | |
// Remove squiggly thing if it's there. | |
if (currentVocab.charAt(0) == '〜') | |
currentVocab = currentVocab.substr(1); | |
$.getJSON("/json/vocabulary/" + currentItem.id, function(json) { | |
var currentSentence = getRandomElement(json.sentences)[0]; | |
var vocabIndex = currentSentence.indexOf(currentVocab); | |
var vocabLength = currentVocab.length; | |
// If the raw vocab wasn't found we need to attempt to find the conjugated version. | |
if (vocabIndex < 0) { | |
var verbMatch = currentVocab.match(/([-ゟ一-龿]+?)([うくぐすずつづぬふぶぷむゆる]|する)/); // i.e. 食べる | |
if (verbMatch == null) | |
{ | |
context.hide(); | |
console.log(currentVocab); | |
console.log(currentSentence); | |
return; | |
} | |
var verbRoot = verbMatch[1]; // i.e. 食 | |
var verbStem = verbMatch[2]; // i.e. べる | |
// Find the index of the verb root. | |
vocabIndex = currentSentence.indexOf(verbRoot); | |
// Find the index of the verb conjugation. | |
for (var i = 0; i < verbConjugations.length; ++i) { | |
var conjugation = verbConjugations[i]; | |
var conjugationIndex = currentSentence.indexOf(conjugation, vocabIndex); | |
if (conjugationIndex < 0) | |
continue; | |
var conjugationDistance = conjugationIndex - vocabIndex; | |
// Found a valid conjugation! | |
if (conjugationDistance <= vocabLength) { | |
vocabLength = conjugationDistance + conjugation.length; | |
break; | |
} | |
} | |
} | |
// Build the context html. | |
context.show(1); | |
context.innerHTML = | |
'<span>' + | |
currentSentence.substr(0, vocabIndex) + | |
'<mark>' + currentSentence.substr(vocabIndex, vocabLength) + '</mark>' + | |
currentSentence.substr(vocabIndex + vocabLength) + | |
'</span>'; | |
}); | |
} | |
// Utility functions. | |
function createStyle(css) { | |
var head = document.getElementsByTagName('head')[0]; | |
var style = document.createElement('style'); | |
style.type = 'text/css'; | |
style.innerHTML = css; | |
head.appendChild(style) | |
}; | |
var random = Math.random; // Shouldn't have to do this, but other scripts can canabalize the random function. | |
function getRandomElement(array) { | |
var index = Math.floor(random() * array.length); | |
return array[index]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment