Wanikani Show Level-up Time

Adds the earliest date and time you can level-up to the level progress bar on the front page.

Από την 11/07/2024. Δείτε την τελευταία έκδοση.

// ==UserScript==
// @name         Wanikani Show Level-up Time
// @namespace    http://tampermonkey.net/
// @version      2024-07-10
// @description  Adds the earliest date and time you can level-up to the level progress bar on the front page.
// @author       https://www.wanikani.com/users/ctmf
// @match        https://www.wanikani.com/dashboard
// @match        https://www.wanikani.com/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=wanikani.com
// @license      Don't post me on coding horror websites. Other than that, knock yourself out, this isn't that earth-shattering
// @grant        none
// ==/UserScript==

function TTLU() {
    // time spent on each srs level, in ms
    const TIME_ON_4 = 172800000;
    const TIME_ON_3 = 86400000;
    const TIME_ON_2 = 28800000;
    const TIME_ON_1 = 14400000;

    function fetch_items() {
        var config = {
            wk_items: {
                options: {assignments: true},
                filters: {item_type: 'kan, rad',}
            }};
        wkof.ItemData.get_items(config)
            .then(process_items);
    }

    function process_items(items) {

        function time_until_guru(obj) {
            return obj.assignments.srs_stage >= 5 // item is already guru+
                ? 0
                : obj.assignments.srs_stage > 0 // item is in review stages below guru
                ? add_levels_time(obj.assignments.srs_stage, obj.assignments.available_at)
                : obj.data.component_subject_ids == undefined // item is already a radical, no components
                ? 0
                : add_longest_radical(obj.data.component_subject_ids) + TIME_ON_1 + TIME_ON_2 + TIME_ON_3 + TIME_ON_4;
        }

        function add_levels_time(stage, next_rev) {
            var result = new Date(Date.parse(next_rev)) - new Date(); // time remaining in current stage
            switch (stage) {
                case 1: result += TIME_ON_2; // add the rest of the stages needed for guru
                case 2: result += TIME_ON_3;
                case 3: result += TIME_ON_4;
            }
            return result;
        }

        function add_longest_radical(components) {
            function isInList(element, list) {return !((list.find((e) => e === element)) == undefined);} // for filtering
            return items
                .filter((e) => isInList(e.id, components)) // filter all items to only the ones in the component list
                .map(time_until_guru) // calculate time to guru for each component
                .reduce((a, b) => Math.max(a, b)); // reduce the array to the max value and return it
        }

        // Process items:
        var sorted = items
            .filter((item) => item.object == "kanji" && item.data.level == wkof.user.level)
            .map(time_until_guru) // get the time-to-guru for each kanji on the level
            .sort((a, b) => a - b); // sort the list by how long to guru

        // calculate how many kanji I need to level up (90% of the total number)
        var last_index = Math.ceil(sorted.length * .9) - 1; // don't forget to account for zero-based counting
        var finishtime = new Date(Date.now() + sorted[last_index]); // add time to guru of nth item to now
        finishtime.setMinutes(0); finishtime.setSeconds(0); finishtime.setMilliseconds(0); // round to hour

        // Output to page
        var output_string = "Earliest level-up possible at: " + finishtime.toString();
        const locationC = document.querySelector(".level-progress-bar");
        const locationP = locationC.parentNode;
        const new_element = document.createElement("div");
        const TTLU_output = document.createTextNode(output_string);
        new_element.appendChild(TTLU_output);
        var message_element = locationP.insertBefore(new_element, locationC.nextSibling);
        message_element.setAttribute("style", "margin-top: 1lh;");
    }

    // Check we have wkof or none of this will work
    if (!window.wkof) {
        alert('The TTLU script requires Wanikani Open Framework.\nYou will now be forwarded to installation instructions.');
        window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
        return;
    }

    // Kick off the whole thing when wkof is ready
    wkof.include('ItemData');
    wkof.ready('ItemData').then(fetch_items);
    window.TTLU = TTLU; // so I can re-run it without reloading the whole page

};
TTLU();