Wanikani: Progress Percentages

Calculates the percentage of known kanji for each JLPT level, Joyo grade, Frequency bracket, and various other sources.

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Wanikani: Progress Percentages
// @namespace    https://greasyfork.org/en/users/1364747
// @version      1.2.9
// @description  Calculates the percentage of known kanji for each JLPT level, Joyo grade, Frequency bracket, and various other sources.
// @author       sarmientoF
// @include      /^https://(www|preview).wanikani.com/(dashboard)?$/
// @require      https://greasyfork.org/scripts/377613-wanikani-open-framework-jlpt-joyo-and-frequency-filters/code/Wanikani%20Open%20Framework%20JLPT,%20Joyo,%20and%20Frequency%20filters.user.js
// @license      MIT; http://opensource.org/licenses/MIT
// @grant        none
// ==/UserScript==

(function () {
    // Make sure WKOF is installed
    var wkof = window.wkof;
    if (!wkof) {
        var response = confirm(
            'Wanikani: JLPT Progress requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.'
        );
        if (response)
            window.location.href =
                "https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549";
        return;
    } else {
        // Install menu
        wkof.include("Menu,Settings");
        wkof.ready("Menu,Settings").then(load_settings).then(install_menu);

        // Initiate progress variable
        var progress = {
            jlpt: {
                1: { learned: 0, total: 1232 },
                2: { learned: 0, total: 367 },
                3: { learned: 0, total: 367 },
                4: { learned: 0, total: 166 },
                5: { learned: 0, total: 79 },
            },
            joyo: {
                1: { learned: 0, total: 80 },
                2: { learned: 0, total: 160 },
                3: { learned: 0, total: 200 },
                4: { learned: 0, total: 200 },
                5: { learned: 0, total: 185 },
                6: { learned: 0, total: 181 },
                9: { learned: 0, total: 1130 },
            },
            freq: {
                500: { learned: 0, total: 500 },
                1000: { learned: 0, total: 500 },
                1500: { learned: 0, total: 500 },
                2000: { learned: 0, total: 500 },
                2500: { learned: 0, total: 500 },
            },
            other: {
                nhk: { learned: 0, total: 0 },
                news: { learned: 0, total: 0 },
                aozora: { learned: 0, total: 0 },
                twitter: { learned: 0, total: 0 },
                wikipedia: { learned: 0, total: 0 },
            },
        };

        // Fetch lesson info then process it
        wkof.include("ItemData");
        wkof
            .ready("ItemData")
            .then(update_progress)
            .then(calculate_percentages)
            .then(display_data);
    }

    // Loads settings
    function load_settings() {
        var defaults = { cumulative: false, threshold: 1, position: "top" };
        wkof.Settings.load("progress_percentages", defaults);
    }

    // Installs the options button in the menu
    function install_menu() {
        var config = {
            name: "progress_percentages_settings",
            submenu: "Settings",
            title: "Progress Percentages",
            on_click: open_settings,
        };
        wkof.Menu.insert_script_link(config);
    }

    // Creates the options
    function open_settings(items) {
        var config = {
            script_id: "progress_percentages",
            title: "Progress Percentages",
            content: {
                cumulative: {
                    type: "checkbox",
                    label: "Cumulative percentages",
                    hover_tip: "Eg. N3 = N3 + N4 + N4",
                    default: false,
                },
                threshold: {
                    type: "list",
                    label: "Learned threshold",
                    hover_tip:
                        "Items at or above this SRS level will be counted as learned",
                    multi: false,
                    size: 9,
                    default: "1",
                    content: {
                        1: "Apprentice 1",
                        2: "Apprentice 2",
                        3: "Apprentice 3",
                        4: "Apprentice 4",
                        5: "Guru 1",
                        6: "Guru 2",
                        7: "Master",
                        8: "Enlightened",
                        9: "Burned",
                    },
                },
                position: {
                    type: "dropdown",
                    label: "Position",
                    hover_tip:
                        "Position of the Progress Percentages element on the dashboard",
                    default: "search",
                    content: {
                        top: "Top of page",
                        below_srs: "Below SRS boxes",
                    },
                    on_change: (setting, value) => {
                        let elem = $(".progress_percentages");
                        elem.toggleClass("span12", value == "top");
                        if (value == "top") $("#search-form").before(elem);
                        if (value == "below_srs") $(".srs-progress").append(elem);
                    },
                },
            },
            on_save: () => {
                progress = {
                    jlpt: {
                        1: { learned: 0, total: 1232 },
                        2: { learned: 0, total: 367 },
                        3: { learned: 0, total: 367 },
                        4: { learned: 0, total: 166 },
                        5: { learned: 0, total: 79 },
                    },
                    joyo: {
                        1: { learned: 0, total: 80 },
                        2: { learned: 0, total: 160 },
                        3: { learned: 0, total: 200 },
                        4: { learned: 0, total: 200 },
                        5: { learned: 0, total: 185 },
                        6: { learned: 0, total: 181 },
                        9: { learned: 0, total: 1130 },
                    },
                    freq: {
                        500: { learned: 0, total: 500 },
                        1000: { learned: 0, total: 500 },
                        1500: { learned: 0, total: 500 },
                        2000: { learned: 0, total: 500 },
                        2500: { learned: 0, total: 500 },
                    },
                    other: {
                        nhk: { learned: 0, total: 0 },
                        news: { learned: 0, total: 0 },
                        aozora: { learned: 0, total: 0 },
                        twitter: { learned: 0, total: 0 },
                        wikipedia: { learned: 0, total: 0 },
                    },
                };
                update_progress().then(calculate_percentages).then(update_element);
            },
        };
        var dialog = new wkof.Settings(config);
        dialog.open();
    }

    // Updates element
    function update_element(percentages) {
        console.log("percentages", percentages);
        for (var key in percentages) {
            console.log("key", key);
            for (var level in percentages[key]) {
                console.log("level", level);
                var stage = key == "jlpt" ? 6 - level : level;
                var elem = $("#" + key + "_" + stage)[0];
                console.log("elem", elem);
                elem.title =
                    percentages[key][stage].learned +
                    (key != "other" ? " of " + percentages[key][stage].total : "") +
                    " learned";
                elem.children[1].innerText = percentages[key][stage].percent + "%";
            }
        }
    }

    // Retreives lesson data
    function update_progress() {
        var resolve,
            promise = new Promise((res, rej) => {
                resolve = res;
            });
        var config = {
            wk_items: {
                options: { assignments: true },
                filters: {
                    item_type: "kan",
                    include_jlpt_data: true,
                    include_joyo_data: true,
                    include_frequency_data: true,
                },
            },
        };
        wkof.ItemData.get_items(config).then((data) => {
            for (var key in data) {
                if (data[key].assignments && data[key].assignments.started_at != null) {
                    var keys = [
                        ["jlpt_level", "jlpt"],
                        ["joyo_grade", "joyo"],
                        ["frequency", "freq"],
                        ["nhk_frequency", "nhk"],
                        ["news_frequency", "news"],
                        ["aozora_frequency", "aozora"],
                        ["twitter_frequency", "twitter"],
                        ["wikipedia_frequency", "wikipedia"],
                    ];
                    keys.forEach((val, i) => {
                        var level = data[key][val[0]];
                        if (
                            level &&
                            data[key].assignments.srs_stage >=
                            wkof.settings.progress_percentages.threshold
                        ) {
                            if (level < 1) {
                                progress.other[val[1]].learned++;
                                progress.other[val[1]].total += level;
                            } else progress[val[1]][level].learned++;
                        }
                    });
                }
            }
            resolve();
        });
        return promise;
    }

    function calculate_percentages() {
        var show_cum = wkof.settings.progress_percentages.cumulative;
        var percentages = {
            jlpt: { 1: {}, 2: {}, 3: {}, 4: {}, 5: {} },
            joyo: { 1: {}, 2: {}, 3: {}, 4: {}, 5: {}, 6: {}, 9: {} },
            freq: { 500: {}, 1000: {}, 1500: {}, 2000: {}, 2500: {} },
            other: { nhk: {}, news: {}, aozora: {}, twitter: {}, wikipedia: {} },
        };
        for (var key in percentages) {
            var cumulative = [0, 0];
            for (var level in percentages[key]) {
                var stage = key == "jlpt" ? 6 - level : level;
                var learned = progress[key][stage].learned;
                var total = progress[key][stage].total;
                cumulative[0] += learned;
                cumulative[1] += total;
                let percent;
                if (key != "other")
                    percent = show_cum ? cumulative[0] / cumulative[1] : learned / total;
                else percent = total;
                percent =
                    percent < 0.1
                        ? Math.floor(percent * 1000) / 10
                        : Math.floor(percent * 100);
                percentages[key][stage].percent = percent;
                percentages[key][stage].learned = show_cum ? cumulative[0] : learned;
                percentages[key][stage].total = show_cum ? cumulative[1] : total;
            }
        }
        return percentages;
    }

    function display_data(percentages) {
        // Add css
        $("head").append(`<style id="progress_percentages">
    .progress_percentages {
        display: flex;
        height: 28px;
        background: #434343;
        color: rgb(240, 240, 240);
        line-height: 28px;
        //margin-bottom: 0;
        border-radius: 5px;
        text-align: center;
        grid-row: 1;
        grid-column: 1 / span 2;
        //margin-top: 15px;
    }
    .srs-progress .progress_percentages {
        margin-top: 5px;
    }
    #search-form {
        grid-row: 1;
    }
    .progress_percentages .PPprogress {
        display: flex;
        width: 100%;
        justify-content: space-around;
    }
    .progress_percentages .PPbtn {
        width: 20px;
        height: auto;
        color: rgb(240,240,240);
        padding: 0 5px;
        cursor: pointer;
        font-size: 16px;
        z-index: 10;
    }
    .progress_percentages .level {
        font-weight: bold;
    }
    .progress_percentages .percent {
        font-weight: normal !important;
    }
    .progress_percentages span {
        font-size: 16px !important;
        display: inline !important;
    }
    </style>`);
        if (is_dark_theme()) {
            $("head").append(`<style id="progress_percentages_dark">
    .progress_percentages {
        box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7), 2px 2px 2px rgba(0, 0, 0, 0.7);
    }
    .progress_percentages > div {
        background: #232629 !important;
    }
    </style>`);
        }
        // Add elements
        console.log("percentages", percentages);
        var section = document.createElement("section");
        section.className = "progress_percentages";

        var active_set =
            localStorage.getItem("WKProgressPercentagesActiveSet") || "jlpt";
        var [next, prev] = get_new_sets(active_set);

        var next_button = document.createElement("div");
        next_button.className = "next PPbtn";
        next_button.innerHTML = '<i class="link icon-chevron-right">></i>';
        next_button.onclick = toggle_percentages;
        next_button.current = active_set;
        next_button.next = next;

        var prev_button = document.createElement("div");
        prev_button.className = "prev PPbtn";
        prev_button.innerHTML = '<i class="link icon-chevron-left"><</i>';
        prev_button.onclick = toggle_percentages;
        prev_button.current = active_set;
        prev_button.next = prev;

        var list = document.createElement("div");
        list.className = "PPprogress";
        for (var key in percentages) {
            for (var level in percentages[key]) {
                var stage = key == "jlpt" ? 6 - level : level;
                var prefix =
                    key == "jlpt" ? "N" : key == "joyo" ? "G" : key == "freq" ? "F" : "";
                var label =
                    key == "other"
                        ? stage == "nhk"
                            ? "NHK"
                            : stage.charAt(0).toUpperCase() + stage.slice(1)
                        : key == "freq"
                            ? stage / 500
                            : stage;
                $(list).append(
                    '<div class="' +
                    key +
                    "_percentages stage " +
                    (key == active_set ? "" : "hidden") +
                    '" id="' +
                    key +
                    "_" +
                    stage +
                    '" title="' +
                    percentages[key][stage].learned +
                    (key != "other" ? " of " + percentages[key][stage].total : "") +
                    ' learned"><span class="level">' +
                    prefix +
                    label +
                    ' </span><span class="percent">' +
                    percentages[key][stage].percent +
                    "%</span></div>"
                );
            }
        }
        section.appendChild(prev_button);
        section.appendChild(list);
        section.appendChild(next_button);
        if (wkof.settings.progress_percentages.position == "top") {
            //section.className += ' span12'
            $(".dashboard").before(section);
        } else if (wkof.settings.progress_percentages.position == "below_srs")
            $(".srs-progress").append(section);
        else $(".srs-progress__stages").before(section);
    }

    // Switches which percentages are showing
    function toggle_percentages(event) {
        var button = event.target;
        if (button.nodeName == "I") button = button.parentElement;
        var current_set = button.current;
        var next_set = button.next;
        $("." + current_set + "_percentages").toggleClass("hidden");
        $("." + next_set + "_percentages").toggleClass("hidden");
        var next_button = $(".progress_percentages .next")[0];
        var prev_button = $(".progress_percentages .prev")[0];
        var [next, prev] = get_new_sets(next_set);
        next_button.next = next;
        next_button.current = next_set;
        prev_button.next = prev;
        prev_button.current = next_set;
        localStorage.setItem("WKProgressPercentagesActiveSet", next_set);
    }

    // Returns the next and previous sets
    function get_new_sets(current_set) {
        var sets = ["jlpt", "joyo", "freq", "other"];
        var current_index = sets.indexOf(current_set);
        return [sets[(current_index + 1) % 4], sets[(current_index + 3) % 4]];
    }

    // Handy little function that rfindley wrote. Checks whether the theme is dark.
    function is_dark_theme() {
        // Grab the <html> background color, average the RGB.  If less than 50% bright, it's dark theme.
        return (
            $("body")
                .css("background-color")
                .match(/\((.*)\)/)[1]
                .split(",")
                .slice(0, 3)
                .map((str) => Number(str))
                .reduce((a, i) => a + i) /
            (255 * 3) <
            0.5
        );
    }
})();