在2DFan搜索 / Add 2DFan Search Button

Adds a 'Jump to 2DFan' button next to the <h1> tag on moyu.moe

// ==UserScript==
// @name         在2DFan搜索 / Add 2DFan Search Button
// @namespace    http://tampermonkey.net/
// @version      1.0.3
// @description  Adds a 'Jump to 2DFan' button next to the <h1> tag on moyu.moe
// @author       owninnn
// @match        https://www.moyu.moe/*
// @match        http://www.moyu.moe/*
// @match        https://www.ai2.moe/files/file/*
// @grant        none
// @license GUN-V3
// ==/UserScript==

(function() {
    'use strict';

        // Add style for the buttons
    const style = document.createElement('style');
    style.textContent = `
        .search-buttons-container {
            display: inline-block;
            margin-left: 5px;
        }
        .search-buttons-container button {
            background: none;
            border: none;
            text-decoration: underline;
            margin-left: 5px;
        }
        .search-buttons-container button:hover {
            text-decoration: none;
        }
    `;
    document.head.appendChild(style);

    // Define your search engines here
    const EnigneDefines = {
        "2dfan": {
            url: "https://2dfan.com/subjects/search?keyword=",
            name: "2dfan"
        },
        "ggbase": {
            url: "https://ggbases.dlgal.com/search.so?p=0&title=",
            name: "ggbase"
        },
        "moyu": {
            url: "https://www.moyu.moe/search?q=",
            name: "moyu"
        },
        "ai2moe": {
            url: "https://www.ai2.moe/search/?q=",
            name: "ai2moe"
        }
        // Add more search engines here following the same format
    };

    function getEnigneDefines(keys) {
        // Use the `filter` and `map` methods to extract matching items
        return keys
            .filter(key => EnigneDefines.hasOwnProperty(key)) // Ensure the key exists in the dictionary
            .map(key => EnigneDefines[key]); // Map the key to its corresponding object
    }

    function extractAndList(input) {
        let cleanedString = input.replace(/\t|\n|\r/g, '');
        let cleanedString2 = cleanedString.replace(/\[.*?\]|\{.*?\}|\(.*?\)/g, '@=@=@');
        let result = cleanedString2.split(/@=@=@/).filter(Boolean);
        return result;
    }

    function findLongestCJKString(strings) {
        const cjkRegex = /[\u3040-\u309F\u30A0-\u30FF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFF00-\uFFEF]/;

        if (strings.length === 1) return strings[0];
        const cjkStrings = strings.filter(str => cjkRegex.test(str));

        return (cjkStrings.length > 0 
            ? cjkStrings.reduce((longest, current) => current.length > longest.length ? current : longest, '')
            : strings.reduce((longest, current) => current.length > longest.length ? current : longest, '')
        );
    }

    function addSearchButtons(keys, query_tag) {
        const h1Tags = document.querySelectorAll(query_tag);
        h1Tags.forEach(h1 => {
            // Check if buttons container already exists
            if (h1.nextSibling && h1.nextSibling.classList && h1.nextSibling.classList.contains('search-buttons-container')) {
                return;
            }

            // Create container for buttons
            const container = document.createElement('div');
            container.className = 'search-buttons-container';

            const searchEngines = getEnigneDefines(keys);

            // Create buttons for each search engine
            searchEngines.forEach(engine => {
                const button = document.createElement('button');
                button.textContent = ` Search in ${engine.name} `;
                button.className = `jump-to-${engine.name}`;

                const post_keyword = findLongestCJKString(extractAndList(h1.textContent.trim()));
                const url_keyword = encodeURIComponent(post_keyword);

                button.addEventListener('click', () => {
                    window.open(`${engine.url}${url_keyword}`, '_blank');
                });

                container.appendChild(button);
            });

            // Insert the container after the h1
            h1.parentNode.insertBefore(container, h1.nextSibling);
        });
    }




    if (window.location.host.includes('moyu.moe')) {
        const observeDOMChanges = () => {
            const observer = new MutationObserver((mutations) => {
                mutations.forEach(() => {
                    if (window.location.pathname.startsWith('/patch')) {
                        addSearchButtons(["2dfan", "ggbase","ai2moe"], 'h1');
                    }
                });
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }
        observeDOMChanges();
        addSearchButtons(["2dfan", "ggbase", "ai2moe"], 'h1');
    } else /* if (window.location.host.includes('ai2.moe')) */ {
        addSearchButtons(["2dfan", "ggbase", "moyu"], 'h1');
    };
       // else if (window.location.host.includes('2dfan')) {
       //  addSearchButtons(["2dfan", "ggbase", "moyu"], 'h3');
       // }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址