Greasy Fork

Greasy Fork is available in English.

Better Lesson Picker

Lesson picker improvements

当前为 2024-03-12 提交的版本,查看 最新版本

// ==UserScript==
// @name         Better Lesson Picker
// @namespace    better-lesson-picker
// @version      1.0.3
// @description  Lesson picker improvements
// @author       Mystery
// @license      MIT
// @match        https://www.wanikani.com/subject-lessons/picker
// @run-at       document-end
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Insert CSS
    document.head.insertAdjacentHTML('beforeend',`
        <style name="better_lesson_picker" type="text/css">
            .better-lesson-picker__input::-webkit-outer-spin-button,
            .better-lesson-picker__input::-webkit-inner-spin-button {
                -webkit-appearance: none;
            }
            .better-lesson-picker__input {
                -moz-appearance: textfield;
                margin-left: 10px;
                width: 3em;
                height: 48px;
                font: inherit;
                font-size: 20px;
                text-align: center;
                border-radius: var(--border-radius-tight);
            }
            .better-lesson-picker__input[disabled] {
                background-color: var(--color-button-lesson-picker-disabled-background);
                color: var(--color-button-lesson-picker-disabled-text);
                user-select: none;
            }
            .better-lesson-picker__input--radical {
                border: var(--color-radical) 2px solid;
            }
            .better-lesson-picker__input--kanji {
                border: var(--color-kanji) 2px solid;
            }
            .better-lesson-picker__input--vocabulary {
                border: var(--color-vocabulary) 2px solid;
            }

            .lesson-picker__footer-content {
                display: flex;
            }

            .lesson-picker__button {
                max-width: 200px;
            }
        </style>
        `);

    // variables
    let currentEl;
    let firstEl;
    let dragged;

    const items = {
        radical: [],
        kanji: [],
        vocabulary: []
    };

    const inputs = {
        radical: undefined,
        kanji: undefined,
        vocabulary: undefined
    };

    const inputFocusValue = {};

    // main functions
    populateItems();
    addInputs();
    addEventListeners();


    function populateItems() {
        document.querySelectorAll('div.lesson-picker__subjects').forEach(el => {
            const type = el.querySelector('span').classList[1].split('--')[1];
            items[type].push(...el.children);
        });
    }

    function addInputs() {
        const form = document.querySelector('form.lesson-picker__footer-content');
        for (const key in inputs) {
            inputs[key] = createNumberInput(form, key);
        }
    }

    function addEventListeners() {
        document.addEventListener('mousedown', handleMousedown);

        // Update inputs when selecting all
        document.querySelector('button.lesson-picker__section-toggle-all').addEventListener('click', () => setTimeout(updateInputs, 10));

        document.querySelectorAll('button.lesson-picker__section-toggle').forEach(el => {
            el.addEventListener('click', () => setTimeout(updateInputs, 10));
        });
    }

    function handleMousedown(e) {
        if (isItem(e)) {
            dragged = false;
            currentEl = undefined;
            firstEl = e.target;
            document.addEventListener('mousemove', handleDrag);
            document.addEventListener('mouseup', handleMouseup);
        }

    }

    function handleDrag(e) {
        dragged = true;
        if (currentEl !== e.target) {
            currentEl = e.target;
            if (isItem(e)) {
                e.target.click();
                updateInputs();
            }
        }
    }

    function handleMouseup(e) {
        if (firstEl === e.target && dragged) {
            // if you drag and release on the same item triggers the click event negating the drag click
            e.target.click();
        }
        if (!dragged) {
            setTimeout(updateInputs, 10);
        }
        document.removeEventListener('mousemove', handleDrag);
        document.removeEventListener('mouseup', handleMouseup);
    }

    function handleInput(type) {
        return function(e) {
            const val = Number(e.target.value);
            const list = items[type];
            if (val >= 0) {
                list.forEach((el, i) => {
                    if (i <= val - 1) {
                        if (el.ariaSelected === 'false') {
                            el.click();
                        }
                    } else {
                        if (el.ariaSelected === 'true') {
                            el.click();
                        }
                    }
                });
            }
            if (val > list.length) {
                e.target.value = list.length;
            }
        }
    }

    function isItem(e) {
        return e.target.tagName === 'SPAN' && e.target.classList.contains('subject-character__characters');
    }

    function createNumberInput(form, type) {
        const input = document.createElement('input');
        input.type = 'number';
        input.classList.add('better-lesson-picker__input');
        input.classList.add(`better-lesson-picker__input--${type}`);
        input.addEventListener('input', handleInput(type));
        input.addEventListener('focus', e => {
            inputFocusValue[type] = e.target.value;
            e.target.value = '';
        });
        input.addEventListener('blur', e => {
            if (e.target.value === '') {
                e.target.value = inputFocusValue[type];
            }
        });
        input.value = 0;
        if (items[type].length === 0) {
            input.disabled = true;
        }
        form.appendChild(input);
        return input;
    }

    function updateInputs() {
        for (const key in inputs) {
            let count = 0;
            items[key].forEach(el => {
                if (el.ariaSelected === 'true') {
                    count++;
                }
            });
            inputs[key].value = count;
        }
    }
})();