Wanikani Wrap-up Button Enhancement (Jerky Edition)

Beefed-up Wrap-up button (Jerky Edition)

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

// ==UserScript==
// @name         Wanikani Wrap-up Button Enhancement (Jerky Edition)
// @namespace    https://www.wanikani.com
// @version      4.0.3
// @description  Beefed-up Wrap-up button (Jerky Edition)
// @author       Inserio (Orig. Mempo)
// @match        https://www.wanikani.com/*
// @grant        none
// @license      MIT
// ==/UserScript==
/* global Stimulus */
/* jshint esversion: 11 */

(function() {
    'use strict';

    // ========================================================================
    // Globals
    const scriptId = 'wrap-up-button-enhancement';
    const buttonElementId = 'wrap-up-amount-button';
    const inputElementId = 'wrap-up-amount-input';
    const defaultAmount = 10; // by default
    const state = {
        quizQueueController: null,
        queueCount: defaultAmount
    };

    // ========================================================================
    // Startup

    installCSS();
    document.documentElement.addEventListener('turbo:load', () => {
        setTimeout(() => {
            if (!initUi()) return;
            resize_buttons();
            registerOnWrapUpListener();
        }, 0);
    });

    // ========================================================================
    // Functions

    /**
     * Install stylesheet.
     */
    function installCSS() {
        const css = `#additional-content ul li#${buttonElementId} span:hover::before {content: none;}`+
              `input#${inputElementId} {`+
              'position: relative;'+
              'margin-right: 10px;'+
              'width: 100%;'+
              'min-width: 10px;'+
              'outline: none;' +
              'text-decoration: none;'+
              'text-align: center;'+
              'border: none;'+
              '};';
        const head = document.getElementsByTagName('head')[0];
        if (head) {
            const style = document.createElement('style');
            style.setAttribute('id', scriptId);
            style.setAttribute('type', 'text/css');
            style.textContent = css;
            head.insertAdjacentElement('beforeend', style);
        }
    }

    /**
     * Initialize the user interface.
     */
    function initUi() {
        const wrapUpBox = document.querySelector('#additional-content li:has(.additional-content__item--wrap-up)');
        if (!wrapUpBox) return false;
        state.quizQueueController = null;
        //const wrapUpButton = wrapUpBox.querySelector('.additional-content__item.additional-content__item--wrap-up');
        //const buttonText = wrapUpButton.querySelector('.additional-content__item-text');
        const editBox = document.createElement('li');
        editBox.setAttribute('id', buttonElementId);
        editBox.classList.add('additional-content__menu-item', 'additional-content__menu-item--5');
        const input = document.createElement('input');
        input.setAttribute('id', inputElementId);
        input.classList.add('additional-content__item','additional-content__item--last-items');
        input.setAttribute('type', 'number');
        input.setAttribute('min', '1');
        input.setAttribute('value', state.queueCount);
        input.addEventListener('input', function (event) {
            onWrapUpStartedOrValueChanged(event.target);
        });
        editBox.append(input);

        wrapUpBox.insertAdjacentElement('afterend', editBox);

        updateQueueCount(state.queueCount);
        return true;
    }

    // ------------------------------------------------------------------------
    // Resize the buttons according to how many are visible.
    // ------------------------------------------------------------------------
    function resize_buttons() {
        let buttons = Array.from(document.querySelectorAll('#additional-content .additional-content__menu-item'));
        let visible_buttons = buttons.filter((elem)=>!elem.matches('.hidden,[hidden]'));
        let btn_count = visible_buttons.length;
        for (let btn of visible_buttons) {
            let percent = Math.floor(10000/btn_count)/100 + '%';
            btn.style.width = `calc(${percent} - 10px)`;
            btn.style.flex = `0 0 calc(${percent} - 10px)`;
            btn.style.marginRight = '10px';
        }
        visible_buttons.slice(-1)[0].style.marginRight = '0px';
    }

    function registerOnWrapUpListener() {
        let onRegistration = ({toggleWrap, deregisterObserver}) => {};
        let onUpdateCount = ({currentCount}) => {};
        let onWrapUp = ({isWrappingUp, currentCount}) => {
            if (isWrappingUp)
                onWrapUpStartedOrValueChanged(document.getElementById(inputElementId));
        };
        let registerWrapUpObserver = {
            onRegistration: onRegistration,
            onUpdateCount: onUpdateCount,
            onWrapUp: onWrapUp
        };
        window.dispatchEvent(new CustomEvent('registerWrapUpObserver', {detail: {observer: registerWrapUpObserver}}));
    }

    function getController(name) {
        return Stimulus.getControllerForElementAndIdentifier(document.querySelector(`[data-controller~="${name}"]`),name);
    }

    function onWrapUpStartedOrValueChanged(element) {
        const newQueueSize = getCustomWrapUpAmount(element);
        if (newQueueSize === null) return;
        updateQueueCount(newQueueSize);
    }

    function getCustomWrapUpAmount(element) {
        if (!element) return null;
        let amount = parseInt(element.value);
        if (amount && !Number.isNaN(Number(amount)))
            return state.queueCount = amount;
        return state.queueCount;
    }

    function updateQueueCount(newSize) {
        if (!newSize || Number.isNaN(Number(newSize))) return;
        const quizQueue = (state.quizQueueController = state.quizQueueController ?? getController('quiz-queue'))?.quizQueue;
        if (!quizQueue) return;
        const queueDifference = newSize - quizQueue.maxActiveQueueSize;
        // update the queue similar to how it is done in `onWrapUp({isWrappingUp})`
        // empty slots must be calculated because a user could have previously been in wrap up mode
        const emptySlots = quizQueue.maxActiveQueueSize - quizQueue.activeQueue.length;
        quizQueue.maxActiveQueueSize = newSize;
        if (queueDifference >= 0) {
            quizQueue.activeQueue = quizQueue.activeQueue.concat(quizQueue.backlogQueue.slice(0, emptySlots + queueDifference));
            quizQueue.backlogQueue = quizQueue.backlogQueue.slice(emptySlots + queueDifference);
        } else if (queueDifference < 0) {
            quizQueue.activeQueue = quizQueue.activeQueue.slice(0, quizQueue.maxActiveQueueSize - emptySlots);
            quizQueue.backlogQueue = quizQueue.backlogQueue.slice(emptySlots);
        }
        quizQueue.wrapUpManager.updateQueueSize(quizQueue.activeQueue.length);
        quizQueue.fetchMoreItems();
    }

})();