Jisho Mahou

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

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

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

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.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

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

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

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

// ==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);
    }
});