WaniKani Markdown Notes

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

  1. // ==UserScript==
  2. // @name WaniKani Markdown Notes
  3. // @namespace rfindley
  4. // @description Allows you to write Markdown in the notes, which will be rendered as HTML when the page loads.
  5. // @version 1.4
  6. // @require https://cdnjs.cloudflare.com/ajax/libs/showdown/1.3.0/showdown.min.js
  7. // @include https://www.wanikani.com/level*
  8. // @include https://www.wanikani.com/radical*
  9. // @include https://www.wanikani.com/kanji*
  10. // @include https://www.wanikani.com/vocabulary*
  11. // @include https://www.wanikani.com/review/session*
  12. // @copyright 2013, Jeshua
  13. // @run-at document-end
  14. // @grant none
  15. // ==/UserScript==
  16.  
  17. wkmdnotes = {};
  18.  
  19. (function() {
  20. /**
  21. * Render the given markdown text.
  22. */
  23. function render(text) {
  24. // Do some custom replacements.
  25. text = text.replace(/#kan#/g, '<span class="kanji-highlight highlight-kanji" rel="tooltip" data-original-title="Kanji">');
  26. text = text.replace(/#\/kan#/g, '</span>');
  27.  
  28. text = text.replace(/#rad#/g, '<span class="radical-highlight highlight-radical" rel="tooltip" data-original-title="Radical">');
  29. text = text.replace(/#\/rad#/g, '</span>');
  30.  
  31. text = text.replace(/#read#/g, '<span class="reading-highlight highlight-reading" rel="tooltip" data-original-title="Reading">');
  32. text = text.replace(/#\/read#/g, '</span>');
  33.  
  34. text = text.replace(/#voc#/g, '<span class="vocabulary-highlight highlight-vocabulary" rel="tooltip" data-original-title="Vocabulary">');
  35. text = text.replace(/#\/voc#/g, '</span>');
  36.  
  37. // Render the rest as markdown.
  38. return (new showdown.Converter()).makeHtml(text);
  39. }
  40.  
  41. /**
  42. * Find all of the tooltips in the given container and tooltipify them.
  43. */
  44. function activateTooltips(container) {
  45. if (container.tooltip) {
  46. container.find('span[rel="tooltip"]').tooltip();
  47. }
  48. }
  49.  
  50. /**
  51. * Setup the given note field with the required callbacks.
  52. */
  53. function setupNoteField(note) {
  54. // Save the markdown and render the content.
  55. var html = note.html();
  56. if (typeof html === 'undefined') html = '';
  57. note.data('noteContent', html.replace(/<br>/g,'\n'));
  58. note.html(render(html));
  59. activateTooltips(note);
  60.  
  61. note.click(function(e) {
  62. if (e.target.tagName.toLowerCase() === 'textarea') {
  63. return;
  64. }
  65.  
  66. // If the target is the div, they are going from display --> edit.
  67. if (e.target.tagName.toLowerCase() !== 'button') {
  68. var interval = setInterval(function() {
  69. // If we can find a textarea, they must have clicked to edit the text field.
  70. // So, we want to display the markdown content.
  71. if (note.find('textarea')) {
  72. clearInterval(interval);
  73. if (note.data('noteContent') === 'Click to add note') {
  74. note.find('textarea').val('');
  75. } else {
  76. note.find('textarea').val(note.data('noteContent'));
  77. }
  78. }
  79. }, 50);
  80. }
  81.  
  82. // Otherwise, they are going from edit --> display.
  83. else {
  84. var textarea = note.find('textarea');
  85. var str = textarea.val().replace(/\n/g,'\n');
  86. textarea.html(str);
  87. var interval = setInterval(function() {
  88. // Keep waiting until there is no text area. Then, save the changed markdown
  89. // value to the data. Also re-render the note.
  90. if (note.find('textarea').length === 0) {
  91. clearInterval(interval);
  92. note.data('noteContent', note.html().replace(/<br>/g,'\n'));
  93. note.html(render(note.html()));
  94. activateTooltips(note);
  95. }
  96. }, 50);
  97. }
  98. });
  99. }
  100. function main() {
  101. // Convert the text in the meaning note.
  102. var noteFields = ['.note-meaning', '.note-reading'];
  103. $.each(noteFields, function(i, noteSelector) {
  104. // During reviews, we have to wait for the field to be added to the dom first.
  105. // Then, we can add a listener to the note selector.
  106. $('#option-item-info').click(function() {
  107. var interval = setInterval(function() {
  108. if ($(noteSelector).length !== 0) {
  109. clearInterval(interval);
  110. setupNoteField($(noteSelector));
  111. }
  112. }, 50);
  113. });
  114.  
  115. // Setup the note field if it is on the page already.
  116. setupNoteField($(noteSelector));
  117. });
  118. }
  119. // Run startup() after window.onload event.
  120. if (document.readyState === 'complete')
  121. main();
  122. else
  123. window.addEventListener("load", main, false);
  124. })(wkmdnotes);