Wanikani Self-Study Hide Info

Hide item info on the Level and Item screens for self-study.

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name        Wanikani Self-Study Hide Info
// @namespace   rfindley
// @description Hide item info on the Level and Item screens for self-study.
// @version     1.1.2
// @match       https://www.wanikani.com/level/*
// @match       https://www.wanikani.com/radicals*
// @match       https://www.wanikani.com/kanji*
// @match       https://www.wanikani.com/vocabulary*
// @match       https://preview.wanikani.com/level/*
// @match       https://preview.wanikani.com/radicals*
// @match       https://preview.wanikani.com/kanji*
// @match       https://preview.wanikani.com/vocabulary*
// @copyright   2018-2023, Robin Findley
// @license     MIT; http://opensource.org/licenses/MIT
// @run-at      document-end
// @grant       none
// ==/UserScript==

window.ss_hideinfo = {};

(function(gobj) {

    /* globals wkof, ss_quiz */

    //===================================================================
    // Initialization of the Wanikani Open Framework.
    //-------------------------------------------------------------------
    var script_name = 'Self-Study Quiz';
    var wkof_version_needed = '1.0.17';
    if (!window.wkof) {
        if (confirm(script_name+' 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;
    }
    if (wkof.version.compare_to(wkof_version_needed) === 'older') {
        if (confirm(script_name+' requires Wanikani Open Framework version '+wkof_version_needed+'.\nDo you want to be forwarded to the update page?')) {
            window.location.href = 'https://greasyfork.org/en/scripts/38582-wanikani-open-framework';
        }
        return;
    }

    wkof.include('Settings');
    wkof.ready('document, Settings')
    .then(load_settings)
    .then(install_interface);

    //========================================================================
    var btnbar, sections;
    function install_interface() {
        var html =
            '<div class="ss_hideinfo">'+
            '  <label>Self-study:</label>'+
            '  <div class="btn-group">'+
            '    <button class="btn enable" title="Enable/Disable self-study plugin"></button>'+
            '    <button class="btn ssquiz hidden" title="Open the quiz window">Quiz</button>'+
            '    <button class="btn shuffle" title="Shuffle the list of items below">Shuffle</button>'+
            '    <select class="btn mode" title="Select a self-study preset">'+
            '      <option value="jp2en">Japanese to English</option>'+
            '      <option value="en2jp">English to Japanese</option>'+
            '    </select>'+
            '    <select class="btn lockburn" title="Select a self-study preset">'+
            '      <option value="all">Show All Items</option>'+
            '      <option value="hideunlocked">Show Locked Only</option>'+
            '      <option value="hideunburned">Show Burned Only</option>'+
            '      <option value="hidelocked">Hide Locked</option>'+
            '      <option value="hideburned">Hide Burned</option>'+
            '      <option value="hidelockedburned">Hide Locked and Burned</option>'+
            '    </select>'+
            '  </div>'+
            '</div>';

        var css = `
            .ss_hideinfo {margin-left:20px; margin-bottom:10px; position:relative;}
            .ss_hideinfo label {display:inline; vertical-align:middle; padding-right:4px; color:#999; font-size:14px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif; text-shadow:0 1px 0 #fff;}
            .ss_hideinfo .btn-group {display:inline; vertical-align:middle; font-size:0px;}
            .ss_hideinfo select.btn {width:200px;}
            .ss_hideinfo .btn.enable {width:55px;}
            .ss_hideinfo .btn {
                display:inline-block;
                height:30px;
                padding: 4px 12px;
                margin-bottom: 0;
                font-size: 14px;
                line-height: 20px;
                text-align: center;
                vertical-align: middle;
                cursor: pointer;
                text-shadow: 0 1px 1px rgb(255 255 255 / 75%);
                font-family: "Ubuntu", Helvetica, Arial, sans-serif;
                background-color: #f0f0f0;
                background-image: linear-gradient(to bottom, #fff, #e6e6e6);
                background-repeat: repeat-x;
                border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);
                border: 1px solid #ccc;
                border-left: 0;
                border-bottom-color: #b3b3b3;
                box-shadow: inset 0 1px 0 rgb(255 255 255 / 20%), 0 1px 2px rgb(0 0 0 / 5%);
            }
            .ss_hideinfo .btn:hover {
                color: #333;
                text-decoration: none;
                background-position: 0 -15px;
                transition: background-position 0.1s linear;
            }
            .ss_hideinfo .btn:first-child {
                border-left: 1px solid #ccc;
                border-radius: 4px 0 0 4px;
            }
            .ss_hideinfo .btn:last-child {border-radius: 0 4px 4px 0;}

            section.ss_active .ss_hideinfo button.enable {background-color:#b3e6b3; background-image:linear-gradient(to bottom, #ecf9ec, #b3e6b3);}
            section.ss_active .ss_hideinfo button.enable:after {content:"ON";}
            section:not(.ss_active) .ss_hideinfo button.enable:after {content:"OFF";}

            section.ss_active.ss_hidechar .subject-character .subject-character__characters {opacity:0; transition:opacity ease-in-out 0.15s}
            section.ss_active.ss_hideread .subject-character .subject-character__reading {opacity:0; transition:opacity ease-in-out 0.15s}
            section.ss_active.ss_hidemean .subject-character .subject-character__meaning {opacity:0; transition:opacity ease-in-out 0.15s}
            section.ss_active.ss_hideburned .ss_burned {display:none;}
            section.ss_active.ss_hidelocked .ss_locked {display:none;}
            section.ss_active.ss_hideunburned .subject-character-grid__item:not(.ss_burned) {display:none;}
            section.ss_active.ss_hideunlocked .subject-character-grid__item:not(.ss_locked) {display:none;}

            section.ss_active .subject-character:hover span.subject-character__characters {opacity: initial !important; transition:opacity ease-in-out 0.05s !important;}
            section.ss_active .subject-character:hover .subject-character__info span {opacity: initial !important; transition:opacity ease-in-out 0.05s !important;}
            `;

        let css_tag = document.createElement('style');
        css_tag.textContent = css;
        document.head.prepend(css_tag);

        sections = document.querySelectorAll('section.character-grid');

        sections.forEach((section) => {
            section.insertAdjacentHTML('afterbegin',html);
            btnbar = section.querySelector('.ss_hideinfo');
            btnbar.querySelector('button.enable').addEventListener('click', toggle_enable);
            btnbar.querySelector('button.ssquiz').addEventListener('click', open_quiz);
            btnbar.querySelector('button.shuffle').addEventListener('click', shuffle);
            btnbar.querySelector('select.mode').addEventListener('change', mode_changed);
            btnbar.querySelector('select.lockburn').addEventListener('change', lockburn_changed);
        });

        for (let item of document.querySelectorAll('section.character-grid .subject-character--locked')) {
            item.closest('.subject-character-grid__item').classList.add('ss_locked');
        }
        for (let item of document.querySelectorAll('section.character-grid .subject-character--burned')) {
            item.closest('.subject-character-grid__item').classList.add('ss_burned');
        }

        wkof.wait_state('ss_quiz', 'ready').then(function(){
            if (typeof ss_quiz.open === 'function') {
                for (let bar of document.querySelectorAll('.ss_hideinfo button.ssquiz')) {
                    bar.classList.remove('hidden');
                };
            }
        });

        toggle_enable(null, true /* no_toggle */);
        mode_changed();
        lockburn_changed();
        if (settings.enabled) shuffle();
    }

    //========================================================================
    function deep_merge(...objects) {
        let merged = {};
        function recursive_merge(dest, src) {
            for (let prop in src) {
				if (typeof src[prop] === "object" && src[prop] !== null ) {
					if (Array.isArray(src[prop])) {
						dest[prop] = src[prop].slice();
					} else {
						dest[prop] = dest[prop] || {};
						recursive_merge(dest[prop], src[prop]);
					}
				} else {
                    dest[prop] = src[prop];
                }
            }
            return dest;
        }
        for (let obj in objects) {
            recursive_merge(merged, objects[obj]);
        }
        return merged;
    }

    //========================================================================
    var settings;
    function load_settings() {
        var default_settings = {
            enabled: false,
            mode: 'jp2en',
            lockburn: 'all',
        };
        return wkof.Settings.load('ss_hideinfo')
        .then(function(){
            settings = deep_merge(default_settings, wkof.settings.ss_hideinfo);
            settings = wkof.settings.ss_hideinfo;
        });
    }

    //========================================================================
    function save_settings() {
        wkof.Settings.save('ss_hideinfo');
    }

    //========================================================================
    function toggle_enable(e, no_toggle) {
        var enabled = settings.enabled;
        if (no_toggle !== true) enabled = !enabled;
        if (enabled) {
            sections.forEach((section) => section.classList.add('ss_active'));
        } else {
            sections.forEach((section) => section.classList.remove('ss_active'));
        }
        if (enabled !== settings.enabled) {
            settings.enabled = enabled;
            save_settings();
        }
    }

    //========================================================================
    function fisher_yates_shuffle(arr) {
        var i = arr.length, j, temp;
        if (i===0) return arr;
        while (--i) {
            j = Math.floor(Math.random()*(i+1));
            temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;
        }
        return arr;
    }

    //========================================================================
    function shuffle(e) {
        if (e === undefined) {
            // Shuffle all
            for (let section of document.querySelectorAll('.subject-character-grid')) {
                let grid = section.querySelector('.subject-character-grid__items');
                let items = fisher_yates_shuffle(Array.from(grid.children));
                for (let item of items) grid.append(item);
            };
        } else {
            // Shuffle specific group
            let section = e.currentTarget.closest('section.character-grid');
            let grid = section.querySelector('.subject-character-grid__items');
            let items = fisher_yates_shuffle(Array.from(grid.children));
            for (let item of items) grid.append(item);
        }
    }

    //========================================================================
    function mode_changed(e) {
        let value;
        if (e !== undefined) {
            value = e.target.value;
            settings.mode = value;
            save_settings();
        } else {
            value = settings.mode;
        }
        for (let section of sections) {
            section.querySelector('select.mode').value = value;
            section.classList.remove('ss_hidechar', 'ss_hideread', 'ss_hidemean');
            switch (value) {
                case 'jp2en':
                    section.classList.add('ss_hidemean', 'ss_hideread');
                    break;

                case 'en2jp':
                    section.classList.add('ss_hidechar', 'ss_hideread');
                    break;
            }
        }
    }

    //========================================================================
    function lockburn_changed(e) {
        let value;
        if (e !== undefined) {
            value = e.target.value;
            settings.lockburn = value;
            save_settings();
        } else {
            value = settings.lockburn;
        }
        for (let section of sections) {
            section.querySelector('select.lockburn').value = value;
            section.classList.remove('ss_hidelocked', 'ss_hideburned', 'ss_hideunlocked', 'ss_hideunburned');
            switch (value) {
                case 'all':
                    break;

                case 'hidelocked':
                    section.classList.add('ss_hidelocked');
                    break;

                case 'hideburned':
                    section.classList.add('ss_hideburned');
                    break;

                case 'hidelockedburned':
                    section.classList.add('ss_hidelocked', 'ss_hideburned');
                    break;

                case 'hideunlocked':
                    section.classList.add('ss_hideunlocked');
                    break;

                case 'hideunburned':
                    section.classList.add('ss_hideunburned');
                    break;
            }
        }
    }

    //========================================================================
    function open_quiz(e) {
        var btn = e.currentTarget;
        var sec = btn.closest('section.character-grid');
        var level, item_type;
        var path = window.location.pathname.split('/');
        var header = btn.closest('section').querySelector('header').textContent.trim();
        if (path[1] === 'level') {
            level = path[2];
            item_type = header.toLowerCase();
        } else {
            level = header.split(' ')[1];
            item_type = path[1];
        }
        var item_type_text = item_type;
        item_type_text[0] = item_type_text[0].toUpperCase();
        var title = 'Level '+level+' '+item_type_text;
        item_type = item_type.replace(/s$/g, '');

        var custom_options = {
            ipreset: {name: title, content: {
                wk_items: {enabled: true, filters: {
                    level: {enabled: true, value: level},
                    item_type: {enabled: true, value: item_type},
                }},
            }},
        };
        switch (settings.lockburn) {
            case 'all':
                break;

            case 'hidelocked':
                custom_options.ipreset.content.wk_items.filters.srs = {enabled:true,value:'1,2,3,4,5,6,7,8,9'};
                break;

            case 'hideburned':
                custom_options.ipreset.content.wk_items.filters.srs = {enabled:true,value:'-1,0,1,2,3,4,5,6,7,8'};
                break;

            case 'hidelockedburned':
                custom_options.ipreset.content.wk_items.filters.srs = {enabled:true,value:'1,2,3,4,5,6,7,8'};
                break;

            case 'hideunlocked':
                custom_options.ipreset.content.wk_items.filters.srs = {enabled:true,value:'-1,0'};
                break;

            case 'hideunburned':
                custom_options.ipreset.content.wk_items.filters.srs = {enabled:true,value:'9'};
                break;
        }

        if (settings.mode === 'en2jp' && item_type === 'vocabulary') {
            custom_options.qpreset = {
                name: 'English to Japanese',
                content: {
                    mean2read:true,
                }
            };
        } else {
            custom_options.qpreset = {
                name: 'Japanese to English',
                content: {
                    char2read:true,
                    char2mean:true,
                }
            };
        }
        ss_quiz.open(custom_options);
    }

})(window.ss_hideinfo);