Wanikani Levels Overview Plus

Improves the levels overview popup with progress indications

目前為 2019-12-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name          Wanikani Levels Overview Plus
// @namespace     Mercieral
// @description   Improves the levels overview popup with progress indications
// @include       https://www.wanikani.com/*
// @version       1.0.1
// @run-at        document-end
// @grant         none
// ==/UserScript==

/* global $ */

(function() {
    // Reqire the WK open resource
    if (!window.wkof) {
        alert('"Wanikani Levels Overview Plus" 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;
    }

    // Initialize the level stage data array
    const levelCounts = [];
    for (let i = 0; i < 61; i++) {
        levelCounts.push({
            locked: 0,
            apprentice: 0,
            guru: 0,
            master: 0,
            enlighten: 0,
            burn: 0
        });
    }
    window.levelCounts = levelCounts;

    window.wkof.include('ItemData');
    window.wkof.ready('document,ItemData').then(startup).catch(loadError);

    /**
     * Parse the data once the document and WKOF data is ready
     */
    function startup () {
        initCSS();
        if (window.wkof.ItemData != null) {
            const config = {
                wk_items: {
                    options: {assignments: true}
                }
            };
            window.wkof.ItemData.get_items(config).then(function (processItems) {
                if (processItems != null) {
                    for (let i = 0, itemsLen = processItems.length; i < itemsLen; i++) {
                        const item = processItems[i];
                        const srsLevel = item && item.assignments && item.assignments.srs_stage;
                        const level = item && item.data && item.data.level;
                        const levelStageCounts = levelCounts[level];
                        if (levelStageCounts == null) {
                            // skip this item...
                            continue;
                        }

                        // Incremement the appropriate level's stage counter for this item
                        if (srsLevel == null || srsLevel === 0) {
                            levelStageCounts.locked++;
                        } else if (srsLevel < 5) {
                            levelStageCounts.apprentice++;
                        } else if (srsLevel < 7) {
                            levelStageCounts.guru++;
                        } else if (srsLevel === 7) {
                            levelStageCounts.master++;
                        } else if (srsLevel === 8) {
                            levelStageCounts.enlighten++;
                        } else if (srsLevel === 9) {
                            levelStageCounts.burn++;
                        }
                    }
                    finishUI();
                }
                else {
                    loadError();
                }
            }).catch(loadError);
        }
        else {
            loadError();
        }
    };

    /**
     * Create the UI once the data has been parsed into the data array
     */
    function finishUI () {
        // Get the HTML square elements for each level in the popout
        const levelBlocks = $('.sitemap__expandable-chunk--levels > .sitemap__grouped-pages > ol > li > a');
        for (let levelBlock of levelBlocks) {
            levelBlock = $(levelBlock);
            const originalLevelText = levelBlock.text();
            const levelStageCounts = levelCounts[Number(originalLevelText)];
            const levelTotal = levelStageCounts.locked + levelStageCounts.apprentice + levelStageCounts.guru + levelStageCounts.master + levelStageCounts.enlighten + levelStageCounts.burn;

            // Create the overlay elements
            const levelText = `<span class="level-block-text">${originalLevelText}</span>`;
            const lockedDiv = `<div class="level-block-item level-block-locked" style="width:${levelStageCounts.locked/levelTotal*100}%"></div>`;
            const apprenticeDiv = `<div class="level-block-item level-block-apprentice" style="width:${levelStageCounts.apprentice/levelTotal*100}%"></div>`;
            const guruDiv = `<div class="level-block-item level-block-guru" style="width:${levelStageCounts.guru/levelTotal*100}%"></div>`;
            const masterDiv = `<div class="level-block-item level-block-master" style="width:${levelStageCounts.master/levelTotal*100}%"></div>`;
            const enlightenedDiv = `<div class="level-block-item level-block-enlightened" style="width:${levelStageCounts.enlighten/levelTotal*100}%"></div>`;
            const burnDiv = `<div class="level-block-item level-block-burn" style="width:${levelStageCounts.burn/levelTotal*100}%"></div>`;

            if (levelStageCounts.burn === levelTotal) {
                // Fully burned level, add the checkbox to the div
                levelBlock.addClass('level-block-complete');
            }

            if (levelStageCounts.locked === levelTotal) {
                // Fully locked level, add the padlock to the div
                levelBlock.addClass('level-block-full-locked');
            }

            // Rewrite the level block's HTML with the custom elements
            levelBlock.html(`<div class="level-block-container">${apprenticeDiv}${guruDiv}${masterDiv}${enlightenedDiv}${burnDiv}${lockedDiv}</div>${levelText}`);
        }

        // Append a "+" to the level nav text to indicate script success
        $('.navigation > .sitemap > li:first-child > .sitemap__section-header').children().append('+');
    };

    /**
     * Create the CSS style sheet and append it to the document
     */
    function initCSS() {
        $('head').append(`
        <style>
            .sitemap__expandable-chunk--levels > .sitemap__grouped-pages > ol > li > a {
                position: relative;
                height: 46px;
                overflow: hidden;
                border: 1px solid black;
            }

            .level-block-text {
                width: 100%;
                position: absolute;
                top: 0;
                left: 0;
                text-align: center;
                line-height: 46px;
            }

            .sitemap__page--current-level > a > span{
                line-height: 42px !important;
            }

            .sitemap__pages--levels .sitemap__page--current-level a {
                border: 2px solid black !important;
            }

            .sitemap__pages--levels .sitemap__page a:hover {
                background-color: rgba(255,255,255,0.5);
            }

            .level-block-container {
                height: 100%;
                width: 100%;
                position: absolute;
                top: 0;
                left: 0;
            }

            .level-block-complete {
                background-position: center !important;
                background-repeat: no-repeat !important;
                background-size: 34px !important;
                background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE2LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgd2lkdGg9IjM1Mi42MnB4IiBoZWlnaHQ9IjM1Mi42MnB4IiB2aWV3Qm94PSIwIDAgMzUyLjYyIDM1Mi42MiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzUyLjYyIDM1Mi42MjsiDQoJIHhtbDpzcGFjZT0icHJlc2VydmUiIGZpbGw9IiMwMDY2MDAiIHN0cm9rZT0iIzAwNjYwMCI+DQo8Zz4NCgk8cGF0aCBkPSJNMzM3LjIyMiwyMi45NTJjLTE1LjkxMi04LjU2OC0zMy42Niw3Ljk1Ni00NC4wNjQsMTcuNzQ4Yy0yMy44NjcsMjMuMjU2LTQ0LjA2Myw1MC4xODQtNjYuNzA4LDc0LjY2NA0KCQljLTI1LjA5MiwyNi45MjgtNDguMzQ4LDUzLjg1Ni03NC4wNTIsODAuMTczYy0xNC42ODgsMTQuNjg4LTMwLjYsMzAuNi00MC4zOTIsNDguOTZjLTIyLjAzMi0yMS40MjEtNDEuMDA0LTQ0LjY3Ny02NS40ODQtNjMuNjQ4DQoJCWMtMTcuNzQ4LTEzLjQ2NC00Ny4xMjQtMjMuMjU2LTQ2LjUxMiw5LjE4YzEuMjI0LDQyLjIyOSwzOC41NTYsODcuNTE3LDY2LjA5NiwxMTYuMjhjMTEuNjI4LDEyLjI0LDI2LjkyOCwyNS4wOTIsNDQuNjc2LDI1LjcwNA0KCQljMjEuNDIsMS4yMjQsNDMuNDUyLTI0LjQ4LDU2LjMwNC0zOC41NTZjMjIuNjQ1LTI0LjQ4LDQxLjAwNS01Mi4wMjEsNjEuODEyLTc3LjExMmMyNi45MjgtMzMuMDQ4LDU0LjQ2OC02NS40ODUsODAuNzg0LTk5LjE0NQ0KCQlDMzI2LjIwNiw5Ni4zOTIsMzc4LjIyNiw0NC45ODMsMzM3LjIyMiwyMi45NTJ6IE0yNi45MzcsMTg3LjU4MWMtMC42MTIsMC0xLjIyNCwwLTIuNDQ4LDAuNjExDQoJCWMtMi40NDgtMC42MTEtNC4yODQtMS4yMjQtNi43MzItMi40NDhsMCwwQzE5LjU5MywxODQuNTIsMjIuNjUzLDE4NS4xMzIsMjYuOTM3LDE4Ny41ODF6Ii8+DQo8L2c+DQo8L3N2Zz4NCg==') !important;
            }

            .level-block-full-locked {
                background-position: center !important;
                background-repeat: no-repeat !important;
                background-size: 36px !important;
                background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE2LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgd2lkdGg9IjM0cHgiIGhlaWdodD0iMzRweCIgdmlld0JveD0iMCAwIDM0IDM0IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAzNCAzNDsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHN0cm9rZT0iIzY2NjY2NiIgZmlsbD0iIzY2NjY2NiI+DQo8Zz4NCgk8cGF0aCBkPSJNMjYuODMzLDEzLjMzM1Y5YzAtNC45NjItNC4wMzgtOS05LTloLTEuNjY2Yy00Ljk2MiwwLTksNC4wMzgtOSw5djQuMzMzSDQuMzMzVjM0aDI1LjMzNFYxMy4zMzNIMjYuODMzeiBNMTEuMTY3LDkNCgkJYzAtMi43NTcsMi4yNDMtNSw1LTVoMS42NjZjMi43NTcsMCw1LDIuMjQzLDUsNXY0LjMzM0gxMS4xNjdWOXoiLz4NCjwvZz4NCjwvc3ZnPg0K') !important;
            }

            .level-block-item {
                height: 100%;
                display: inline-block;
            }

            .level-block-locked {
                background-color: rgba(0, 0, 0, 0.3);
            }

            .level-block-apprentice {
                background-color: rgba(221, 0, 147, 0.4);
            }

            .level-block-guru {
                background-color: rgba(136, 45, 158, 0.4);
            }

            .level-block-master {
                background-color: rgba(41, 77, 219, 0.4);
            }

            .level-block-enlightened {
                background-color: rgba(0, 147, 221, 0.4);
            }

            .level-block-burn {
                background-color: rgba(251, 192, 66, 0.4);
            }

        </style>`);
    }

    /**
     * log an error if any part of the wkof data request failed
     * @param {*} [e] - The error to log if it exists
     */
    function loadError (e) {
        console.error('Failed to load data from WKOF for "Wanikani Levels Overview Plus"', e);
    };
})();

QingJ © 2025

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