Wanikani: Progress Percentages

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

目前為 2021-07-20 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Wanikani: Progress Percentages
// @namespace    http://tampermonkey.net/
// @version      1.2.7
// @description  Calculates the percentage of known kanji for each JLPT level, Joyo grade, Frequency bracket, and various other sources.
// @author       Kumirei
// @include      /^https://(www|preview).wanikani.com/(dashboard)?$/
// @require      https://gf.qytechs.cn/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) {
        for (var key in percentages) {
            for (var level in percentages[key]) {
                var stage = (key == "jlpt" ? 6-level : level);
                var elem = $('#'+key+'_'+stage)[0];
                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;
    }
    .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
    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'
        $('#search-bar').before(section);
    }
    else if (wkof.settings.progress_percentages.position == "below_srs") $(".srs-progress").append(section);
    else $('#search-bar').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;
}
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址