Greasy Fork镜像 还支持 简体中文。

Show Total Lesson Count - WaniKani

Changes the count of lessons on the Today's Lessons tile to show the total number of available lessons in addition to the number selected for you

目前為 2024-08-07 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Show Total Lesson Count - WaniKani
// @namespace    http://tampermonkey.net/
// @version      0.5.2
// @description  Changes the count of lessons on the Today's Lessons tile to show the total number of available lessons in addition to the number selected for you
// @license      MIT
// @author       LupoMikti
// @match        https://www.wanikani.com/*
// @grant        none
// @supportURL   https://community.wanikani.com/t/userscript-show-total-lesson-count/66776
// ==/UserScript==

(async function() {
    'use strict';

    /* global wkof */

    let scriptId = 'show_total_lesson_count';
    let scriptName = 'Show Total Lesson Count';
    let initial_load = true;
    let todaysLessonsCount;
    let settings;
    let stateStarting = false;
    let todaysLessonsFrameLoaded = false;
    let navBarCountFrameLoaded = false;

    let debugLogText = `START: ${scriptName} Debug Log:\n`;
    let hasOutputLog = false;
    let mainSource = '';
    const INTERNAL_DEBUG_TURBO_HANDLING = false;

    function addToDebugLog(message) {
        debugLogText += `${new Date().toISOString()}: ${message}\n`;
    }

    function printDebugLog(force = false) {
        if (!hasOutputLog || force) { console.log(`${scriptName}: Outputting a debug log to console.debug()`); console.debug(debugLogText); }
        if (!force) hasOutputLog = true;
        debugLogText = `START: ${scriptName} Debug Log:\n`;
    }

    if (!window.wkof) {
        if (confirm(scriptName + ' requires Wanikani Open Framework.\nDo you want to be forwarded to the installation instructions?')) {
            window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
        }
        return;
    }

    const wkofTurboEventsScriptUrl = 'https://update.gf.qytechs.cn/scripts/501980/1423213/Wanikani%20Open%20Framework%20Turbo%20Events.user.js';
    addToDebugLog(`Attempting to load the TurboEvents library script...`)
    await wkof.load_script(wkofTurboEventsScriptUrl, /* use_cache */ true);
    addToDebugLog(`Checking if TurboEvents library script is loaded in...`)
    let injectedDependency = document.head.querySelector('script[uid*="Turbo"]');
    addToDebugLog(`Turbo Events library ${injectedDependency ? 'is' : 'is NOT' } loaded.`);

    if (INTERNAL_DEBUG_TURBO_HANDLING) {
        window.addEventListener('turbo:load', () => { console.log(`DEBUG: turbo:load has fired`); });
        window.addEventListener('turbo:before-frame-render', (e) => { console.log(`DEBUG: turbo:before-frame-render has fired for '#${e.target.id}'`); });
        window.addEventListener('turbo:frame-load', (e) => { console.log(`DEBUG: turbo:frame-load has fired for '#${e.target.id}'`); });
    }

    const _init = (source) => {
        if (stateStarting) { addToDebugLog(`We are already in the starting state, no need to initialize, returning...`); return; }
        addToDebugLog(`SOURCE = "${source}" | Setting globals and calling _start()`);
        initial_load = stateStarting = true;
        hasOutputLog = todaysLessonsFrameLoaded = navBarCountFrameLoaded = false;
        _start();
    };

    wkof.ready('TurboEvents').then(() => {
        addToDebugLog(`Start of TurboEvents ready callback`)
        let urlList = [wkof.turbo.common.locations.dashboard, wkof.turbo.common.locations.items_pages, /^https:\/\/www\.wanikani\.com\/(settings|level|radicals|kanji|vocabulary)(\/|\?difficulty=).+\/?$/];

        wkof.turbo.on.event.load((e) => { _init('turbo:load'); }, { urls: urlList });

        wkof.turbo.on.event.before_frame_render((e) => {
            const frameId = e.target.id;
            if (initial_load && !stateStarting) { addToDebugLog(`initial_load is true (no frames were previously retrieved) and we are not already in starting state, starting initialization sequence...`); _init('turbo:before-frame-render'); }
            if (frameId === 'todays-lessons-frame') {
                addToDebugLog(`turbo:before-frame-render has fired for "#${frameId}"`);
                todaysLessonsFrameLoaded = false;
            }
            else if (frameId === 'lesson-and-review-count-frame') {
                addToDebugLog(`turbo:before-frame-render has fired for "#${frameId}"`);
                navBarCountFrameLoaded = false;
            }
            else {
                addToDebugLog(`turbo:before-frame-render was fired for "#${frameId}", doing nothing...`);
            }
        }, { urls: urlList });

        wkof.turbo.on.event.frame_load(async (e) => {
            const frameId = e.target.id;
            if (frameId === 'todays-lessons-frame') {
                addToDebugLog(`turbo:frame-load was fired for "#${frameId}, calling main function"`);
                todaysLessonsFrameLoaded = true;
                mainSource = `turbo:frame-load for "#${frameId}"`;
                await main();
            }
            else if (frameId === 'lesson-and-review-count-frame') {
                addToDebugLog(`turbo:frame-load was fired for "#${frameId}, calling main function"`);
                navBarCountFrameLoaded = true;
                mainSource = `turbo:frame-load for "#${frameId}"`;
                await main();
            }
            else {
                addToDebugLog(`turbo:frame-load was fired for "#${frameId}", doing nothing...`);
            }
            mainSource = '';
        }, { urls: urlList });

        addToDebugLog(`All turbo callbacks have been sent to TurboEvents library to be registered`);
    }).catch((err) => { addToDebugLog(`TurboEvents library rejected with error: ${err}`); })
      .finally(() => { if (INTERNAL_DEBUG_TURBO_HANDLING) { addToDebugLog(`SOURCE: turbo ready finally`); printDebugLog(INTERNAL_DEBUG_TURBO_HANDLING); } });

    function _start() {
        addToDebugLog(`Starting...`);
        wkof.include('Settings, Menu, Apiv2');
        wkof.ready('Settings, Menu, Apiv2').then(loadSettings).then(insertMenu).then(main)
            .catch((err) => { addToDebugLog(`wkof.ready() rejected with error: ${err}`); })
            .finally(() => { if (INTERNAL_DEBUG_TURBO_HANDLING) { addToDebugLog(`SOURCE: wkof modules ready finally`); printDebugLog(INTERNAL_DEBUG_TURBO_HANDLING); } });
    }

    function loadSettings() {
        addToDebugLog(`Loading settings...`);

        let defaults = {
            showTotalOnly: false,
            setOwnPreferredDaily: false,
            preferredDailyAmount: wkof.user.preferences.lessons_batch_size * 3,
            enableDebugging: true,
        };

        return wkof.Settings.load(scriptId, defaults).then(function(wkof_settings) {settings = wkof_settings;});
    }

    function insertMenu() {
        addToDebugLog(`Inserting menu...`);
        let config = {
            name: scriptId,
            submenu: 'Settings',
            title: scriptName,
            on_click: openSettings
        };

        wkof.Menu.insert_script_link(config);
        mainSource = `_start() -> loadSettings() -> insertMenu()`;
    }

    async function saveSettings(wkof_settings) {
        hasOutputLog = false;
        addToDebugLog(`Save button was clicked on settings, calling main() with new settings...`);
        mainSource = 'wkof.Settings.save()';
        settings = wkof_settings;
        await main();
    }

    function openSettings() {
        let config = {
            script_id: scriptId,
            title: scriptName,
            on_save: saveSettings,
            content: {
                showTotalOnly: {
                    type: 'checkbox',
                    label: 'Show Only Total Lesson Count',
                    hover_tip: `Changes display between "<today's lesson count> / <total lesson count>" and just "<total lesson count>"`,
                    default: false,
                },
                setOwnPreferredDaily: {
                    type: 'checkbox',
                    label: '(DEPRECATED 1)',
                    hover_tip: `THIS SETTING HAS BEEN DEPRECATED DUE TO THE OFFICIAL SETTING FROM WANIKANI.`,
                    default: false,
                },
                preferredDailyAmount: {
                    type: 'number',
                    label: '(DEPRECATED 2)',
                    hover_tip: `THIS SETTING HAS BEEN DEPRECATED DUE TO THE OFFICIAL SETTING FROM WANIKANI.`,
                    min: 0,
                    max: 100,
                },
                enableDebugging: {
                    type: 'checkbox',
                    label: 'Enable console debugging',
                    hover_tip: `Enable output of debugging info to console.debug()`,
                    default: true,
                }
            }
        };

        let dialog = new wkof.Settings(config);
        dialog.open();
    }

    function getCountContainers() {
        let dashboardTileCountContainer = document.querySelector('.todays-lessons__count-text .count-bubble');
        let navBarCountContainer = document.querySelector('.lesson-and-review-count__count');

        if (initial_load && (dashboardTileCountContainer || navBarCountContainer)) {
            let container = dashboardTileCountContainer ?? navBarCountContainer;
            todaysLessonsCount = parseInt(container.textContent);
            initial_load = false;
        }

        return [dashboardTileCountContainer, navBarCountContainer];
    }

    async function main() {
        addToDebugLog(`Main function is executing... source of start = ${mainSource}`);
        if (!settings) {
            addToDebugLog(`We do not have settings, setting timeout on _start()`);
            if (!stateStarting) { setTimeout(_start, 50); }
            else addToDebugLog(`Did not set timeout due to already being in starting state`);
            addToDebugLog(`Main function has finished executing`);
            return;
        }
        addToDebugLog(`We have settings`);
        addToDebugLog(`Retrieving summary data via await of the enpoint...`);
        let summary_data = await wkof.Apiv2.get_endpoint('summary');
        addToDebugLog(`Summary data has been retrieved`);
        let totalLessonCount = summary_data.lessons[0].subject_ids.length;
        let lessonCountContainers;
        if (todaysLessonsFrameLoaded || navBarCountFrameLoaded) {
            addToDebugLog(`Frame(s) loaded, retrieving containers in frames containing the counts...`);
            lessonCountContainers = getCountContainers();
            addToDebugLog(`Count containers have been retrieved`);
        }
        else {
            addToDebugLog(`No frames loaded`);
            addToDebugLog(`Main function has finished executing`);
            //if (settings.enableDebugging) printDebugLog(INTERNAL_DEBUG_TURBO_HANDLING);
            return;
        }
        let todaysCountForDisplay = todaysLessonsCount;

        if (lessonCountContainers.every(node => node == null)) {
            addToDebugLog(`No nodes in containers`);
            addToDebugLog(`Main function has finished executing`);
            //if (settings.enableDebugging) printDebugLog(INTERNAL_DEBUG_TURBO_HANDLING);
            return;
        }
        addToDebugLog(`At least one container exists`);
        stateStarting = false;

        if (isNaN(todaysLessonsCount)) todaysCountForDisplay = 0;

        if (lessonCountContainers[0]) {
            lessonCountContainers[0].textContent = settings.showTotalOnly ? totalLessonCount : todaysCountForDisplay + ' / ' + totalLessonCount;
            addToDebugLog(`Setting display amount for Today's Lessons tile, set to ${lessonCountContainers[0].textContent}`);
        }

        if (lessonCountContainers[1]) {
            lessonCountContainers[1].textContent = settings.showTotalOnly ? totalLessonCount : todaysCountForDisplay;
            addToDebugLog(`Setting display amount for navigation bar, set to ${lessonCountContainers[1].textContent}`);
        }

        if (todaysCountForDisplay === 0) {
            addToDebugLog(`Hiding start button due to having 0 lessons with configured count source`);
            // hide the start button if it is not already, TODO: disable nav bar button if it is not already
            let startButton = document.querySelector('.todays-lessons-button--start')
            if (startButton && startButton.checkVisibility()) {
                startButton.style.display = 'none';
            }
        }

        // hide "Today's" subtitle
        let lessonSubtitle = document.querySelector('.todays-lessons__subtitle');

        if (lessonSubtitle && lessonSubtitle.checkVisibility()) {
            addToDebugLog(`Hiding the "Today's" subtitle on the lesson tile`);
            lessonSubtitle.style.display = 'none';
        }

        addToDebugLog(`Main function has successfully executed`);

        if (settings.enableDebugging) printDebugLog(INTERNAL_DEBUG_TURBO_HANDLING);
    }
})();

QingJ © 2025

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