WaniKani Quick Info KT

Shows available information while waiting for the server response. Originally by Ethan, Modified.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name          WaniKani Quick Info KT
// @namespace     https://www.wanikani.com
// @description   Shows available information while waiting for the server response. Originally by Ethan, Modified.
// @version       0.1.4
// @include       http*://www.wanikani.com/review/session*
// @run-at        document-end
// @grant         none
// ==/UserScript==

/*global $, console, additionalContent, Notes, UserSynonyms, setTimeout*/
/*jslint plusplus: true */

(function () {
    'use strict';
    additionalContent.itemInfo = function () { return; }; // replace with empty function. will prevent execute on firefox, do nothing on chrome.
    function newItemInfo() {
        // takes in a string and returns an array containing only the kanji characters in the string.
        function getComponents(vocab) {
            var c, components = [];
            for (c = 0; c < vocab.length; c++) {
                if (/^[\u4e00-\u9faf]+$/.test(vocab[c])) {
                    components.push(vocab[c]);
                }
            }
            return components;
        }
        function regex(key) {
            switch (key) {
            case "radical":
                return (/\[(?:radical)\]/gi);
            case "kanji":
                return (/\[(?:kanji)\]/gi);
            case "vocabulary":
                return (/\[(?:vocabulary)\]/gi);
            case "meaning":
                return (/\[(?:meaning)\]/gi);
            case "reading":
                return (/\[(?:reading)\]/gi);
            case "ja":
                return (/\[(?:ja)\]/gi);
            case "closeTagSpan":
                return (/\[\/(?:radical|kanji|vocabulary|meaning|reading|ja)\]/gi);
            }
        }
        function htmlify(str) {
            var i, keys;
            str = str.replace("\r\n", "<br><br>");
            keys = [ "radical", "kanji", "vocabulary", "meaning", "reading", "ja", "closeTagSpan" ];
            function filter(str, key) {
                switch (key) {
                case "ja":
                    return str.replace(regex(key), '<span lang="ja">');
                case "closeTagSpan":
                    return str.replace(regex(key), "</span>");
                default:
                    return str.replace(regex(key), '<span class="highlight-' + key + '">');
                }
            }
            for (i = 0; i < keys.length; i++) {
                str = filter(str, keys[i]);
            }
            return str;
        }
        function getRkvId(item) {
            var rkvType = item.rad ? "r" : item.kan ? "k" : item.voc ? "v" : undefined;
            return (rkvType + item.id);
        }
        function checkCurItem(readMeanType1, rkvId1) {
            var readMeanType2, rkvId2;
            readMeanType2 = $.jStorage.get("questionType");
            rkvId2 = getRkvId($.jStorage.get("currentItem"));
            return (readMeanType1 === readMeanType2 && rkvId1 === rkvId2);
        }
        // normal meaning/name always start with a Capital letter, but injected synonyms are always lowercase only
        // answerChecker injects cur.syn into cur.en when validating a 'meaning' question
        function meaningSynFilterFmt(meaningList) {
            return $.grep(meaningList, function (str) {
                return str.toLowerCase() !== str;
            }).join(", ");
        }
        $("#option-item-info").off("click"); // delete all click handlers for this element
        $("#option-item-info").click(function () {
            // replace the main button toggler code
            if ($("#option-item-info").hasClass("active")) { // if info button .active
                $("#additional-content li").removeClass("active"); // deactivate all buttons
                $("#information").hide().children().hide(); // hide "#information" and child "#item-info"
                $("html, body").animate({ scrollTop: 0 }, 200); // scroll up
            } else if ($("#user-response").is(":disabled")) { // info button NOT .active and not waiting for answer
                $("#information").show().children().hide(); // show "#information" and hide any visible children
                $("html, body").animate({ scrollTop: $("#user-response").offset().top - 10 }, 200); // scroll down
                $("#additional-content li").removeClass("active"); // deactivate all buttons
                $("#option-item-info").addClass("active"); // make info button active
                $("#item-info").fadeIn(300); // show #item-info
            }
            // this replaces another click handler that we deleted
            if ($("#user-response").is(":disabled")) {
                $("#answer-exception").remove();
            }
            var rkvId, itemInfoCol1, itemInfoCol2, cur, readMeanType, itemInfo, loading;
            itemInfo = $("#item-info");
            cur = $.jStorage.get("currentItem");
            readMeanType = $.jStorage.get("questionType");
            rkvId = getRkvId(cur);
            if (itemInfo.is(":visible") && (itemInfo.data("question-type") !== readMeanType || itemInfo.data("id") !== rkvId)) {
                itemInfoCol1 = $("#item-info-col1");
                itemInfoCol2 = $("#item-info-col2");
                itemInfoCol1.empty();
                itemInfoCol2.empty();
                loading = '<img height="40px" src="https://cdn-staging.wanikani.com/assets/v03/loading-100x100-3f623bf48901ac45b1f40168644dbc901efd41b633b9f6d95a5d3ecda3f2111d.gif">'; //put crabigator here
                if (cur.rad) {
                    itemInfoCol1.html('<section id="item-info-name"><h2>Name</h2>' + meaningSynFilterFmt(cur.en) + '</section><section class="user-synonyms"><h2>User Synonyms</h2></section>');
                    itemInfoCol2.html('<section id="item-info-mnemonic"><h2>Mnemonics</h2>' + loading + '</section><section id="note-meaning"></section>');
                    UserSynonyms.load("radical", cur.syn, cur.id, true);
                    $("#all-info").hide();
                    $.getJSON("/json/radical/" + cur.id, function (radJSON) {
                        if (!checkCurItem(readMeanType, rkvId)) {
                            return; // item has since changed, ignore this data.
                        }
                        radJSON.mnemonic = htmlify(radJSON.mnemonic);
                        itemInfoCol1.find("section#item-info-name").html("<h2>Name</h2>" + radJSON.en);
                        itemInfoCol2.find("section#item-info-mnemonic").html("<h2>Mnemonics</h2>" + radJSON.mnemonic);
                        itemInfoCol2.find("section#note-meaning").html("<h2>Name Note</h2>");
                        Notes.add("radical", "meaning", cur.id, radJSON.meaning_note, itemInfoCol2.find("section#note-meaning"));
                        itemInfo.data("id", rkvId);
                        itemInfo.data("question-type", readMeanType);
                    }).fail(function () {
                        $("#information-offline").show();
                    });
                } else if (cur.kan) {
                    (function () {
                        var emphReading;
                        emphReading = cur.emph === "onyomi" ? cur.on : cur.kun;
                        itemInfoCol1.html('<section id="item-info-meaning"><h2>Meanings</h2>' + meaningSynFilterFmt(cur.en) + '</section><section class="user-synonyms"><h2>User Synonyms</h2></section><section id="item-info-reading"><h2>Important Readings (' + cur.emph + ")</h2>" + emphReading + '</section><section id="related-items"><h2>Radical Combination</h2>' + loading + "</section>");
                        itemInfoCol2.html('<section id="item-info-meaning-mnemonic"><h2>Meaning Mnemonic</h2>' + loading + '</section><section id="note-meaning"></section><section id="item-info-reading-mnemonic"><h2>Reading Mnemonic</h2>' + loading + '</section><section id="note-reading"></section>');
                        UserSynonyms.load("kanji", cur.syn, cur.id, true);
                        if (readMeanType === "meaning") {
                            $("#item-info-reading, #item-info-reading-mnemonic, #note-reading").hide();
                        } else {
                            $("#item-info-meaning, #item-info-meaning-mnemonic, #note-meaning, .user-synonyms").hide();
                        }
                        $("#all-info").show();
                    }());
                    $.getJSON("/json/kanji/" + cur.id, function (kanJSON) {
                        if (!checkCurItem(readMeanType, rkvId)) {
                            return; // item has since changed, ignore this data.
                        }
                        var relatedRadChar, relatedRad, i, relatedRadStr, relatedRadList;
                        kanJSON.meaning_mnemonic = htmlify(kanJSON.meaning_mnemonic);
                        kanJSON.reading_mnemonic = htmlify(kanJSON.reading_mnemonic);
                        kanJSON.meaning_hint = htmlify(kanJSON.meaning_hint);
                        kanJSON.reading_hint = htmlify(kanJSON.reading_hint);
                        relatedRadStr = "";
                        relatedRadList = kanJSON.related;
                        for (i = 0; i < relatedRadList.length; i++) {
                            relatedRad = relatedRadList[i];
                            if (relatedRad.custom_font_name) {
                                relatedRadChar = '<i class="radical-' + relatedRad.custom_font_name + '"></i>';
                            } else if (/\.png/i.test(relatedRad.rad)) {
                                relatedRadChar = '<img src="https://s3.amazonaws.com/s3.wanikani.com/images/radicals/' + relatedRad.rad + '">';
                            } else {
                                relatedRadChar = relatedRad.rad;
                            }
                            relatedRadStr += '<li><a title="View radical information page" target="_blank" href="/radicals/' + relatedRad.slug + '"><span class="radical" lang="ja">' + relatedRadChar + "</span> " + relatedRad.en.split(",")[0] + "</li>";
                        }
                        itemInfoCol1.find("section#item-info-meaning").html("<h2>Meanings</h2>" + kanJSON.en);
                        itemInfoCol1.find("section#related-items").html('<h2>Radical Combination</h2><ul class="radical">' + relatedRadStr + "</ul>");
                        itemInfoCol2.find("section#item-info-meaning-mnemonic").html('<h2>Meaning Mnemonic</h2>' + kanJSON.meaning_mnemonic + '<blockquote><h3><i class="icon-question-sign"></i> HINT</h3>' + kanJSON.meaning_hint + '</blockquote>');
                        itemInfoCol2.find("section#item-info-reading-mnemonic").html('<h2>Reading Mnemonic</h2>' + kanJSON.reading_mnemonic + '<blockquote><h3><i class="icon-question-sign"></i> HINT</h3>' + kanJSON.reading_hint + '</blockquote>');
                        itemInfoCol2.find("section#note-meaning").html("<h2>Meaning Note</h2>");
                        Notes.add("kanji", "meaning", cur.id, kanJSON.meaning_note, itemInfoCol2.find("section#note-meaning"));
                        itemInfoCol2.find("section#note-reading").html("<h2>Reading Note</h2>");
                        Notes.add("kanji", "reading", cur.id, kanJSON.reading_note, itemInfoCol2.find("section#note-reading"));
                        itemInfo.data("id", rkvId);
                        itemInfo.data("question-type", readMeanType);
                    }).fail(function () {
                        $("#information-offline").show();
                    });
                } else if (cur.voc) {
                    (function () {
                        var i, relatedKanStr, relatedKanList, relatedKan;
                        relatedKanStr = "";
                        relatedKanList = getComponents(cur.voc);
                        for (i = 0; i < relatedKanList.length; i++) {
                            relatedKan = relatedKanList[i];
                            relatedKanStr += '<li><a title="View kanji information page" target="_blank" href="/kanji/' + relatedKan + '"><span class="kanji" lang="ja">' + relatedKan + "</span></a></li>";
                        }
                        itemInfoCol1.html('<section id="item-info-meaning"><h2>Meanings</h2>' + meaningSynFilterFmt(cur.en) + '</section><section class="user-synonyms"><h2>User Synonyms</h2></section><section id="item-info-reading"><h2>Reading</h2>' + cur.kana.join(", ") + '</section><section id="part-of-speech"><h2>Part of Speech</h2>' + loading + '</section><section id="related-items"><h2>Related Kanji</h2><ul class="kanji">' + relatedKanStr + "</ul></section>");
                        itemInfoCol2.html('<section id="item-info-meaning-mnemonic"><h2>Meaning Explanation</h2>' + loading + '</section><section id="note-meaning"></section><section id="item-info-reading-mnemonic"><h2>Reading Explanation</h2>' + loading + '</section><section id="note-reading"></section><section id="item-info-context-sentences"><h2>Context Sentence</h2>' + loading + "</section>");
                        UserSynonyms.load("vocabulary", cur.syn, cur.id, true);
                        if (readMeanType === "meaning") {
                            $("#item-info-reading, #item-info-reading-mnemonic, #note-reading").hide();
                        } else {
                            $("#item-info-meaning, #item-info-meaning-mnemonic, #note-meaning, .user-synonyms").hide();
                        }
                        $("#all-info").show();
                    }());
                    $.getJSON("/json/vocabulary/" + cur.id, function (vocJSON) {
                        if (!checkCurItem(readMeanType, rkvId)) {
                            return; // item has since changed, ignore this data.
                        }
                        var sentenceStr, relatedKan, i, relatedKanStr, relatedKanList;
                        vocJSON.meaning_explanation = htmlify(vocJSON.meaning_explanation);
                        vocJSON.reading_explanation = htmlify(vocJSON.reading_explanation);
                        relatedKanStr = "";
                        relatedKanList = vocJSON.related;
                        for (i = 0; i < relatedKanList.length; i++) {
                            relatedKan = relatedKanList[i];
                            relatedKanStr += '<li><a title="View kanji information page" target="_blank" href="/kanji/' + relatedKan.slug + '"><span class="kanji" lang="ja">' + relatedKan.kan + "</span> " + relatedKan.en + "</a></li>";
                        }
                        if (vocJSON.sentences.length === 0) {
                            sentenceStr = "<p>N/A</p>";
                        } else {
                            sentenceStr = "<p>" + vocJSON.sentences[0][0] + "</p><p>" + vocJSON.sentences[0][1] + "</p>";
                        }
                        itemInfoCol1.find("section#item-info-meaning").html("<h2>Meanings</h2>" + vocJSON.en);
                        itemInfoCol1.find("section#item-info-reading").html("<h2>Reading</h2>" + vocJSON.kana);
                        itemInfoCol1.find("section#part-of-speech").html("<h2>Part of Speech</h2>" + vocJSON.part_of_speech);
                        itemInfoCol1.find("section#related-items").html('<h2>Related Kanji</h2><ul class="kanji">' + relatedKanStr + "</ul>");
                        itemInfoCol2.find("section#item-info-meaning-mnemonic").html('<h2>Meaning Explanation</h2>' + vocJSON.meaning_explanation);
                        itemInfoCol2.find("section#item-info-reading-mnemonic").html('<h2>Reading Explanation</h2>' + vocJSON.reading_explanation);
                        itemInfoCol2.find("section#note-meaning").html("<h2>Meaning Note</h2>");
                        Notes.add("vocabulary", "meaning", cur.id, vocJSON.meaning_note, itemInfoCol2.find("section#note-meaning"));
                        itemInfoCol2.find("section#note-reading").html("<h2>Reading Note</h2>");
                        Notes.add("vocabulary", "reading", cur.id, vocJSON.reading_note, itemInfoCol2.find("section#note-reading"));
                        itemInfoCol2.find("section#item-info-context-sentences").html("<h2>Context Sentence</h2>" + sentenceStr);
                        itemInfo.data("id", rkvId);
                        itemInfo.data("question-type", readMeanType);
                    }).fail(function () {
                        $("#information-offline").show();
                    });
                }
            }
        });
    }
    // delayed load to make sure this loads after (and so can replace) the original
    setTimeout(function () {
        additionalContent.itemInfo = newItemInfo; // replace empty function with new function
        additionalContent.itemInfo(); // execute it
        console.log('WaniKani Quick Info KT: loaded replacement function');
    }, 100);
    console.log('WaniKani Quick Info KT: script load end');
}());