Greasy Fork is available in English.

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);