Greasy Fork is available in English.

Jisho Mahou

12/28/2024, 12:50:00 PM

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name        Jisho Mahou
// @namespace   Violentmonkey Scripts
// @match       *://jisho.org/*
// @grant       GM_setClipboard
// @version     1.3
// @author      harrisonmg
// @description 12/28/2024, 12:50:00 PM
// @license     MIT
// ==/UserScript==

/// make entire search result list entries clickable ///

const selectedText = () => {
    var text = "";
    if (window.getSelection) {
        text = window.getSelection().toString();
    } else if (document.selection && document.selection.type != "Control") {
        text = document.selection.createRange().text;
    }
    return text;
}

for (const entry of document.querySelectorAll('.kanji_light, .concept_light, .sentence')) {
    const details = entry.querySelector('.light-details_link');
    if (details !== null) {
        entry.style.cursor = "pointer";

        entry.addEventListener("click", (event) => {
            // don't clobber clicks on existing links
            const anchor = event.target.tagName === "A";

            // don't trigger if the user is selecting text
            const selection = selectedText() !== "";

            if (!anchor && !selection) {
                entry.dataset.singleClicked = "true";

                // use a timeout to avoid triggering on double clicks
                // which might be for selecting text
                setTimeout(() => {
                    if (entry.dataset.singleClicked === "true") {
                        entry.dataset.singleClicked = "false";
                        details.click();
                    }
                }, 150);
            }
        });

        // use mousedown events to help detect double clicks
        entry.addEventListener("mousedown", () => {
            entry.dataset.singleClicked = "false";
        });
    }
}

/// click any kanji to go to that kanji's page ///

const isKanji = (char) => {
    const kanjiRegex = /[\u4E00-\u9FAF]/;
    return kanjiRegex.test(char);
}

let lastHighlighted = null;
let lastHighlightedBefore = null;
let lastHighlightedAfter = null;
let oldNode = null;

document.addEventListener('mousemove', (event) => {
    // remove highlighting from the last character
    if (lastHighlighted) {
        const parent = lastHighlighted.parentNode;

        parent.removeChild(lastHighlightedBefore);
        parent.removeChild(lastHighlightedAfter);
        parent.replaceChild(oldNode, lastHighlighted);

        lastHighlighted = null;
        lastHighlightedBefore = null;
        lastHighlightedAfter = null;
        oldNode = null;
    }

    // try to avoid activating while selecting text
    if (event.buttons > 0) return;

    // get char under cursor
    const caretPosition = document.caretPositionFromPoint(event.clientX, event.clientY);
    if (caretPosition && caretPosition.offsetNode.nodeType === Node.TEXT_NODE) {
        const textNode = caretPosition.offsetNode;

        // don't break radical search
        if (textNode.parentNode.classList.contains("radical")) return;

        const textContent = textNode.textContent;

        // don't clobber existing links
        const parent = textNode.parentNode;
        if (parent.tagName === 'A') return;

        let charIndex = caretPosition.offset;

        if (charIndex >= textContent.length) {
            charIndex = textContent.length - 1;
        }

        let char = textContent[charIndex];

        // not perfect the best for getting under cursor
        if (charIndex > 0 && (!isKanji(char) || char === "undefined")) {
            --charIndex;
        }

        char = textContent[charIndex];

        // only apply to kanji
        if (!isKanji(char)) return;

        oldNode = textNode.cloneNode(true);

        // make span for highlighted char
        const span = document.createElement('a');
        span.style.color = '#909dc0';
        span.href = `https://jisho.org/search/${encodeURIComponent(char)} %23kanji`;
        span.style.textDecoration = 'none';
        span.textContent = char;

        // split the text node around the char
        const beforeText = textContent.slice(0, charIndex);
        const afterText = textContent.slice(charIndex + 1);

        const beforeNode = document.createTextNode(beforeText)
        const afterNode = document.createTextNode(afterText)

        // replace with highlighted char
        parent.insertBefore(beforeNode, textNode);
        parent.insertBefore(span, textNode);
        parent.insertBefore(afterNode, textNode);
        parent.removeChild(textNode);

        lastHighlightedBefore = beforeNode;
        lastHighlighted = span;
        lastHighlightedAfter = afterNode;
    }
});

// ctrl+c copies kanji under cursor
document.addEventListener('keydown', (event) => {
    if (event.ctrlKey && event.key === 'c' && lastHighlighted) {
        GM_setClipboard(lastHighlighted.textContent);
    }
});