JPDB add context menu in reviews

Adds a new word's context menu from its top deck in JPDB reviews

29.07.2023 itibariyledir. En son verisyonu görün.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         JPDB add context menu in reviews
// @namespace    jpdb.io
// @version      0.1
// @description  Adds a new word's context menu from its top deck in JPDB reviews
// @author       daruko
// @match        https://jpdb.io/review*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=jpdb.io
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const debug = false;

    let menuDetails = init();
    const observer = new MutationObserver(handleMutation);
    observer.observe(document.body, { childList: true, subtree: true });

    function handleMutation(mutations) {
        if (!menuDetails || !document.body.contains(menuDetails)) {
            menuDetails = init();
        }
    }

    function init() {
        const wordUri = document.querySelector('.review-reveal .answer-box a[href^="/vocabulary"]')?.href;
        const deckUri = document.querySelector('.review-reveal a[href^="/deck"]')?.href;
        if (!wordUri || !deckUri) {
            debug && console.debug('URI not found');
            return;
        }
        const [,, wordId, word] = new URL(wordUri).pathname.split('/', 4);
        if (!word) {
            debug && console.debug('Word URI not recognized');
            return;
        }
        return createDropdownButton(wordId, word, deckUri);
    }

    function createDropdownButton(wordId, word, deckUri) {
        const sideButton = document.querySelector('.review-button-group .side-button');
        if (!sideButton) {
            debug && console.debug('Side button not found');
            return;
        }
        const buttonWrapper = document.createElement("div");
        buttonWrapper.style = "display: flex; flex-direction: column;";
        sideButton.style = "flex-grow: 1;" + sideButton.style;
        sideButton.parentNode.insertBefore(buttonWrapper, sideButton);
        buttonWrapper.appendChild(sideButton);

        const dropdownButtonLabel = document.createElement("label");
        dropdownButtonLabel.className = "side-button";
        dropdownButtonLabel.style = "margin-top: 0;";
        const dropdownButtonDiv = document.createElement("div");
        dropdownButtonDiv.className = "dropdown right-aligned";
        dropdownButtonLabel.appendChild(dropdownButtonDiv);
        const dropdownButtonDetails = document.createElement("details");
        dropdownButtonDiv.appendChild(dropdownButtonDetails);
        const dropdownButtonSummary = document.createElement("summary");
        dropdownButtonSummary.style = "padding: 0; border: none;";
        dropdownButtonSummary.appendChild(document.createTextNode("⋮"));
        dropdownButtonDetails.appendChild(dropdownButtonSummary);
        buttonWrapper.appendChild(dropdownButtonLabel);

        const onToggle = () => {
            loadDropdown(dropdownButtonDetails, wordId, word, deckUri)
                .then(() => dropdownButtonDetails.removeEventListener("toggle", onToggle));
        };
        dropdownButtonDetails.addEventListener("toggle", onToggle);

        return dropdownButtonDetails;
    }

    function loadDropdown(details, wordId, word, deckUri) {
        const filteredDeckUri = `${deckUri}&q=${word}&show_only=new,locked`;
        return fetch(filteredDeckUri)
            .then((response) => response.text())
            .then((text) => {
                const html = new DOMParser().parseFromString(text, "text/html");
                const dropdownContent = html.querySelector(`.entry .vocabulary-spelling a[href^="/vocabulary/${wordId}"]`)
                    ?.closest('.entry')
                    ?.querySelector('.dropdown details .dropdown-content');
                if (!dropdownContent) {
                    debug && console.debug('Dropdown not found in the deck page');
                    return;
                }
                dropdownContent.style = "bottom: 0;" + dropdownContent.style;
                details.appendChild(dropdownContent);
            })
            .catch((err) => {
                console.error('An error has occurred.', err);
            });
    }
})();