🏠 Home 

WaniKani Markdown Notes

Allows you to write Markdown in the notes, which will be rendered as HTML when the page loads.

// ==UserScript==
// @name         WaniKani Markdown Notes
// @namespace    rfindley
// @description  Allows you to write Markdown in the notes, which will be rendered as HTML when the page loads.
// @version      1.4
// @require      https://cdnjs.cloudflare.com/ajax/libs/showdown/1.3.0/showdown.min.js
// @include      https://www.wanikani.com/level*
// @include      https://www.wanikani.com/radical*
// @include      https://www.wanikani.com/kanji*
// @include      https://www.wanikani.com/vocabulary*
// @include      https://www.wanikani.com/review/session*
// @copyright    2013, Jeshua
// @run-at       document-end
// @grant        none
// ==/UserScript==
wkmdnotes = {};
(function() {
/**
* Render the given markdown text.
*/
function render(text) {
// Do some custom replacements.
text = text.replace(/#kan#/g, '<span class="kanji-highlight highlight-kanji" rel="tooltip" data-original-title="Kanji">');
text = text.replace(/#\/kan#/g, '</span>');
text = text.replace(/#rad#/g, '<span class="radical-highlight highlight-radical" rel="tooltip" data-original-title="Radical">');
text = text.replace(/#\/rad#/g, '</span>');
text = text.replace(/#read#/g, '<span class="reading-highlight highlight-reading" rel="tooltip" data-original-title="Reading">');
text = text.replace(/#\/read#/g, '</span>');
text = text.replace(/#voc#/g, '<span class="vocabulary-highlight highlight-vocabulary" rel="tooltip" data-original-title="Vocabulary">');
text = text.replace(/#\/voc#/g, '</span>');
// Render the rest as markdown.
return (new showdown.Converter()).makeHtml(text);
}
/**
* Find all of the tooltips in the given container and tooltipify them.
*/
function activateTooltips(container) {
if (container.tooltip) {
container.find('span[rel="tooltip"]').tooltip();
}
}
/**
* Setup the given note field with the required callbacks.
*/
function setupNoteField(note) {
// Save the markdown and render the content.
var html = note.html();
if (typeof html === 'undefined') html = '';
note.data('noteContent', html.replace(/<br>/g,'\n'));
note.html(render(html));
activateTooltips(note);
note.click(function(e) {
if (e.target.tagName.toLowerCase() === 'textarea') {
return;
}
// If the target is the div, they are going from display --> edit.
if (e.target.tagName.toLowerCase() !== 'button') {
var interval = setInterval(function() {
// If we can find a textarea, they must have clicked to edit the text field.
// So, we want to display the markdown content.
if (note.find('textarea')) {
clearInterval(interval);
if (note.data('noteContent') === 'Click to add note') {
note.find('textarea').val('');
} else {
note.find('textarea').val(note.data('noteContent'));
}
}
}, 50);
}
// Otherwise, they are going from edit --> display.
else {
var textarea = note.find('textarea');
var str = textarea.val().replace(/\n/g,'\n');
textarea.html(str);
var interval = setInterval(function() {
// Keep waiting until there is no text area. Then, save the changed markdown
// value to the data. Also re-render the note.
if (note.find('textarea').length === 0) {
clearInterval(interval);
note.data('noteContent', note.html().replace(/<br>/g,'\n'));
note.html(render(note.html()));
activateTooltips(note);
}
}, 50);
}
});
}
function main() {
// Convert the text in the meaning note.
var noteFields = ['.note-meaning', '.note-reading'];
$.each(noteFields, function(i, noteSelector) {
// During reviews, we have to wait for the field to be added to the dom first.
// Then, we can add a listener to the note selector.
$('#option-item-info').click(function() {
var interval = setInterval(function() {
if ($(noteSelector).length !== 0) {
clearInterval(interval);
setupNoteField($(noteSelector));
}
}, 50);
});
// Setup the note field if it is on the page already.
setupNoteField($(noteSelector));
});
}
// Run startup() after window.onload event.
if (document.readyState === 'complete')
main();
else
window.addEventListener("load", main, false);
})(wkmdnotes);