[Userscript] WaniKani Similar Kanji

mahum said... Hey sorry if it's been mentioned but I was using the script today for the first time and noticed this: 



I’m not sure why it’s doing that, but thought I would point it out. :]

 That sort of annoyed me as well. It happens usually when two meanings of a kanji are provided. I made a little fix that makes sure you only see the first meaning. Makes it look a little better ;).

On line 216, place the following line of code: var allMeanings = similar[i].meaning.split(", ");
On line 223, replace similar[i].meaning for: allMeanings[0]

Anyway: thanks OP! I’m enjoying this script a lot!

Kanji for set up (構) and side (横) are very similar and confuses the heck out of me. Neither of them shows up in either kanji. Please add.

I’ve been using this for a few days and it’s really good! 

tomboy said…(Also a tiny little sidenote: if someone wants to show a totally different set of similar kanji, I added the option to supply your on API url.)
 ^ That quote is 10 months old so maybe no one can answer this, but I still can’t figure out how to supply an API url. Maybe because I’m on Firefox? Anyway, it would be nice to be able to choose which kanji to add myself.  I’m not sure if that’s exactly what’s meant by “showing a different set of similar kanji”, but it’d be great.

Gorgophone said... I've been using this for a few days and it's really good! 

tomboy said...(Also a tiny little sidenote: if someone wants to show a totally different set of similar kanji, I added the option to supply your on API url.)
 ^ That quote is 10 months old so maybe no one can answer this, but I still can't figure out how to supply an API url. Maybe because I'm on Firefox? Anyway, it would be nice to be able to choose which kanji to add myself.  I'm not sure if that's exactly what's meant by "showing a different set of similar kanji", but it'd be great.
 For chrome there is a tamermonkey menu point for supplying your own API url. If that doesn't work in the browser you're using, you can always edit the script itself, the relevant line is at the top of the script:

var API = localStorage.getItem("WKSimilarKanjiAPIUrl") || "
http://wk-similarkanji.rhcloud.com/kanji";

You'd have to host your own api though which respond with the same json structure as the original.
So it's not as easy as adding items manually, sorry about that, I'm pretty bad at javascript, so I can't do any fancy stuff.

Thanks tomboy! I’ll give it a shot. And yeah i already poked around in the greasemonkey menus but it only gives the option of editing the script.

I may eventually just switch to Chrome for WK since there’s another script I can’t get to work in Firefox either!

THIS SCRIPT. THANK YOU.

I was JUST lamenting about how I have a difficult time distinguishing particular kanji from one another. Now I can look in once place. Cheers.

mimiha said... THIS SCRIPT. THANK YOU.

I was JUST lamenting about how I have a difficult time distinguishing particular kanji from one another. Now I can look in once place. Cheers.
 I'm glad you're finding it useful :)

Sorry, this is completely unrelated… Have you reset your account, tomboy?!

I keep mixing up 麦 and 夏.  They’re both crushing winter. I know they’re not super close, but I keep doing it! If any one else has requested this too, would you add it to the similar list? They get introduced close enough to each other, but far enough away… that I keep making the mistake. Usually with 麦. Maybe only with 麦. I have to keep reminding myself that it’s “a hundred degrees” in summer, crushing winter away. Not just “that winter is being crushed” (which is my usual train of thought, for some reason).

“both” and “drawing” keep confusing me.
drawing says it doesn’t have any lookalikes… I think that would be a good one.

AnimeCanuck said... I keep mixing up 麦 and 夏.  They're both crushing winter. I know they're not super close, but I keep doing it! If any one else has requested this too, would you add it to the similar list? They get introduced close enough to each other, but far enough away... that I keep making the mistake. Usually with 麦. Maybe only with 麦. I have to keep reminding myself that it's "a hundred degrees" in summer, crushing winter away. Not just "that winter is being crushed" (which is my usual train of thought, for some reason).
 I second that - I confused them quite a few times too.

困 Distressed

因 Cause



区 district
図 diagram

The kanji for simple (単, Lesson 15) and nest (巣, Lesson 31) are similar looking enough to confuse me a lot.


賞 Prize
覚 Memorize

I have added a few lines of code to the script.  Similar kanjis are shown in the learning session.  I would be happy to share if told how to do so.  

@andreask, please share! :smiley:

andreask said... I have added a few lines of code to the script.  Similar kanjis are shown in the learning session.  I would be happy to share if told how to do so.  
 How?

Either paste the code here, or post it on greasyfork if you have an account there (or any pastebin style website).
Seems there has been a hickup with the line feed before.  Now, it looks a bit better.  The changes to tomboy's script are highlighted in bold.

// ==UserScript== 
// @name        WaniKani Similar kanji
// @author      tomboy
// @namespace   japanese
// @description Shows similar kanji's for the given kanji on its page.
// @license     GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @include     http*://*wanikani.com/kanji/*
// @include     http*://*wanikani.com/level/*/kanji/*
// @_nclude     http*://*wanikani.com/review/session
// @include     http*://*wanikani.com/lesson/session
// @version     1.4 extended
// @grant       GM_xmlhttpRequest
// @grant       GM_registerMenuCommand
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js
// ==/UserScript==
/*
 * Thanks a lot to ...
 * WaniKani Stroke Order - Userscript
 * The code heavily borrows from that script!
 */
/*
 * Helper Functions/Variables
 */
$ = unsafeWindow.$;
/*
 * Global Variables/Objects/Classes
 */
var Pnum = Object.freeze({ unknown:0, kanji:1, reviews:2, lessons:3 });
var thisPage = Pnum.unknown;
var API = localStorage.getItem("WKSimilarKanjiAPIUrl") || "http://wk-similarkanji.rhcloud.com/kanji";
var WKTopicUrl = "/t/Userscript-WaniKani-Similar-Kanji/8905/1";
var uniqueMessage = "<p>This is a unique kanji that has no similars.</p> \
          <p>Having doubts? Shout them <a href='" + WKTopicUrl + "'>here</a>!</p>"
var errorMessage = "<p>Something unexpected happened while trying to load similar kanji. ごめんなさい。\
          <p>API url: " + API + "</p>\
          </><p>If this bothers you, please drop a line <a href='" + WKTopicUrl + "'>here</a>!</p>"
var showRadicalSpecific = localStorage.getItem("WKSimilarKanjiHideRadicalSpecific") != "false";
var showUserSpecific = localStorage.getItem("WKSimilarKanjiHideUserSpecific") != "false";
function GMsetup() {
  if (GM_registerMenuCommand) {
    GM_registerMenuCommand("WaniKani Similar Kanji: Manually enter API url", function(){
      var apiUrl = prompt("Enter API url for WaniKani Similar Kanji:");
      if(apiUrl){
        localStorage.setItem("WKSimilarKanjiAPIUrl", apiUrl);
      }
      location.reload();
    });
    if (showUserSpecific) {
      GM_registerMenuCommand("WaniKani Similar Kanji: hide user specific kanji", function(){
        localStorage.setItem("WKSimilarKanjiHideUserSpecific", 'false');
        location.reload();
      });
    } else {
      GM_registerMenuCommand("WaniKani Similar Kanji: show user specific kanji", function(){
        localStorage.setItem("WKSimilarKanjiHideUserSpecific", 'true');
        location.reload();
      });
    }
    if (showRadicalSpecific) {
      GM_registerMenuCommand("WaniKani Similar Kanji: hide radical specific kanji", function(){
        localStorage.setItem("WKSimilarKanjiHideRadicalSpecific", 'false');
        location.reload();
      });
    } else {
      GM_registerMenuCommand("WaniKani Similar Kanji: show radical specific kanji", function(){
        localStorage.setItem("WKSimilarKanjiHideRadicalSpecific", 'true');
        location.reload();
      });
    }
  }
}
/*
 * Main
 */
window.addEventListener("load", function (e) {
  GMsetup();
  if (!showRadicalSpecific && !showUserSpecific) {
    return;
  }
  // Determine page type
  if (/\/kanji\/./.test(document.URL)) {
    thisPage = Pnum.kanji;
  } else if (/\/review/.test(document.URL)) {
    thisPage = Pnum.reviews;
  } else if (/\/lesson/.test(document.URL)) {
    thisPage = Pnum.lessons;
  }
  // Create and store the element that will hold the addition
  unsafeWindow.similarKanjiContainer = createSimilarKanjiSection();
  // Register callback for when to addition
  switch (thisPage) {
    case Pnum.kanji:
      loadDiagram();
      break;
    case Pnum.reviews:
      var o = new MutationObserver(function(mutations) {
         // The last one always has 2 mutations, so let's use that
         if (mutations.length != 2)
           return;
         // Reviews dynamically generate the DOM. We always need to re-insert the element
         if (getKanji() !== null) {
         setTimeout(function() {
             var similarKanjiContainer = createSimilarKanjiSection();
             if (similarKanjiContainer !== null && similarKanjiContainer.length > 0) {
               unsafeWindow.similarKanjiContainer = similarKanjiContainer;
               loadDiagram();
             }
           }, 150);
         }
      });
      o.observe(document.getElementById('item-info'), {'attributes' : true});
      break;
    case Pnum.lessons:
      var o = new MutationObserver(loadDiagram);
      o.observe(document.getElementById('supplement-kan'), {'attributes' : true});
      loadDiagram();
      break;
  }
});
/*
 * Returns the current kanji
 */
function getKanji() {
  switch(thisPage) {
    case Pnum.kanji:
      return document.title[document.title.length - 1];
    case Pnum.reviews:
      var curItem = $.jStorage.get("currentItem");
      if("kan" in curItem)
        return curItem.kan.trim();
      else
        return null;
    case Pnum.lessons:
      var kanjiNode = $("#character");
      if(kanjiNode === undefined || kanjiNode === null)
        return null;
      return kanjiNode.text().trim();
  }
  return null;
}
/*
 * Creates a section for the similarKanjiContainer and returns a pointer to its content
 */
function createSimilarKanjiSection() {
  // Reviews hack: Only do it once
  if ($('#similar_kanji').length == 0) {
    var sectionHTML = '<section><h2>Similar kanji</h2><p id="similar_kanji">&nbsp;</p></section>';
    switch(thisPage) {
      case Pnum.kanji:
        $(sectionHTML).insertAfter('#information');
        break;
      // reviews and lessons don't load / supress the used css, so it looks ugly
      // case Pnum.reviews:
      //    console.log("prepend");
      //    $('#item-info-col2').prepend(sectionHTML);
      //    break;
      case Pnum.lessons:
          $('#supplement-kan-breakdown .col1').append(sectionHTML);
          break;
    }
  }
  return $('#similar_kanji');
}
/*
 * Adds the similarKanjiContainer section element to the appropriate location
 */
function loadDiagram() {
  if (!unsafeWindow || !unsafeWindow.similarKanjiContainer.length)
    return;
  unsafeWindow.similarKanjiContainer.html("Loading...");
  setTimeout(function() {
    GM_xmlhttpRequest({
      method: "GET",
      url: API + "/" + getKanji(),
      onload: function(xhr) {
        var similarKanjiContainer = unsafeWindow.similarKanjiContainer;
        if (xhr.status == 200) {
          var response = JSON.parse(xhr.responseText);
          
          switch(thisPage){
            case Pnum.kanji:
              if ((similar = response.similar) && (user_similar = response.user_similar) ) {
                if ((similar + user_similar).length > 0) {
                  var similatKanjiList = "";
                  if (showRadicalSpecific) {
                    for (var i=0; i<similar.length; ++i ) {
                      similatKanjiList += "<li class='character-item' id='kanji-custom-" + i + "'> \
                        <span lang='ja' class='item-badge'></span> \
                        <a href='/kanji/" + similar[i].character + "'> \
                          <span class='character' lang='ja'>" + similar[i].character + "</span> \
                            <ul> \
                              <li>" + similar[i].meaning + "</li> \
                            </ul> \
                        </a> \
                      </li>";
                    }
                  }
                  if (localStorage.getItem("WKSimilarKanjiHideUserSpecific") != "false") {
                    for (var i=0; i<user_similar.length; ++i ) {
                      similatKanjiList += "<li class='locked character-item' id='kanji-custom-" + i + "'> \
                        <span lang='ja' class='item-badge'></span> \
                        <a href='/kanji/" + user_similar[i].character + "'> \
                          <span class='character' lang='ja'>" + user_similar[i].character + "</span> \
                            <ul> \
                              <li>" + user_similar[i].meaning + "</li> \
                            </ul> \
                        </a> \
                      </li>";
                    }
                  }
                  similarKanjiContainer.html("<ul class='single-character-grid multi-character-grid-extra-styling-767px'>" + similatKanjiList + "</ul>");
                  return;
                }
                unsafeWindow.similarKanjiContainer.html(uniqueMessage);
                return;
              }
              break;
            
            case Pnum.lessons: 
              if ((similar = response.similar) && (user_similar = response.user_similar) ) {
                if ((similar + user_similar).length > 0) {
                  var similatKanjiList = "";
                  if (showRadicalSpecific) {
                    for (var i=0; i<similar.length; ++i ) {
                      console.log(similatKanjiList);  
                      similatKanjiList += "<li class='pure-u-1-4' id='kanji-custom-" + i + "'> \
                        <a href='/kanji/" + similar[i].character + "'> \
                          <span class='kanji' lang='ja'>" + similar[i].character + "</span> \
                              <div>" + similar[i].meaning + "</div> \
                        </a> \
                      </li>";
                    }
                  }
                  if (localStorage.getItem("WKSimilarKanjiHideUserSpecific") != "false") {
                    for (var i=0; i<user_similar.length; ++i ) {       
                      similatKanjiList += "<li class='pure-u-1-4' id='kanji-custom-" + i + "'> \
                        <a href='/kanji/" + user_similar[i].character + "'> \
                          <span class='kanji' lang='ja'>" + user_similar[i].character + "</span> \
                              <div>" + user_similar[i].meaning + "</div> \
                        </a> \
                      </li>";
                    }
                  }
              
                similarKanjiContainer.html("<ul class='pure-g-r'>" + similatKanjiList + "</ul>");
                return;
              }
                unsafeWindow.similarKanjiContainer.html(uniqueMessage);
                return;
              }
              break;
          }
        }
        unsafeWindow.similarKanjiContainer.html(errorMessage);
      },
      onerror: function(xhr) {
        unsafeWindow.similarKanjiContainer.html(errorMessage);
      }
    });
  }, 0);
}

Really messy formatting… maybe really put it somewhere where the formatting is kept intact.