[E/Ex]Switch

E/Ex-Hentai switch + HentaiVerse quick launch + e-hentai login-reward peek

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name        [E/Ex]Switch
// @name:JP     [E/Ex]切替
// @name:zh-TW  E網 表/裏站切換
// @name:zh-CN  E网 表/里站切换
// @namespace   https://github.com/awdrrawd
// @version     1.1
// @description E/Ex-Hentai switch + HentaiVerse quick launch + e-hentai login-reward peek
// @description:zh-tw E網表/裏站切換,並在 exhentai 加上 HentaiVerse 按鈕與登入獎勵提示
// @description:zh-cn E网表/里站切换,并在 exhentai 加上 HentaiVerse 按钮与登入奖励提示
// @match       https://e-hentai.org/*
// @match       https://exhentai.org/*
// @icon        https://e-hentai.org/favicon.ico
// @grant       GM_xmlhttpRequest
// @grant       GM_getValue
// @grant       GM_setValue
// @connect     e-hentai.org
// @license     MIT
// ==/UserScript==
(function () {
    'use strict';

    /* =====================================================================
     * 0. 共用小工具
     * ===================================================================*/
    const $el = (tag, attrs) => {
        const element = document.createElement(tag);
        for (const key in attrs) {
            if (key === 'textContent') {
                element.textContent = attrs[key];
            } else {
                element.setAttribute(key, attrs[key]);
            }
        }
        return element;
    };

    // 語言偵測(沿用原本邏輯)
    const getButtonText = () => {
        const lang = navigator.language || navigator.userLanguage;
        const isTraditionalChinese = /^(zh-TW|zh-HK|zh-Hant)/.test(lang);
        const isSimplifiedChinese = /^(zh-CN|zh-Hans)/.test(lang);
        const isJapanese = /^ja/.test(lang);
        if (isTraditionalChinese) return '[E/Ex]切換';
        if (isSimplifiedChinese) return '[E/Ex]切换';
        if (isJapanese) return '[E/Ex]切替';
        return '[E/Ex]Switch';
    };

    /* =====================================================================
     * 1. 不顯示 [E/Ex]切換 按鈕的頁面清單
     * ===================================================================*/
    const SWITCH_EXCLUDED_PAGES = [
        { path: '/home.php' },
        { path: '/stats.php' },
        { path: '/hentaiathome.php' },
        { path: '/bitcoin.php' },
        { path: '/hathperks.php' },
        { path: '/exchange.php', params: { t: 'hath' } },
        { path: '/exchange.php', params: { t: 'gp' } },
        { path: '/logs.php', params: { t: 'credits' } },
        { path: '/logs.php', params: { t: 'karma' } },
        { path: '/toplist.php' },
        // /bounty.php 不論有沒有 ?act=tops / topt / tope 或根本沒有 query,一律排除
        { path: '/bounty.php' },
        { path: '/news.php' },
        // repo.e-hentai.org/bounty_post.php 不在 @match 範圍內,這裡列出僅供備查
    ];

    function matchesRule(rule) {
        const { pathname, search } = window.location;
        if (rule.path !== pathname) return false;
        if (!rule.params) return true;
        const params = new URLSearchParams(search);
        return Object.entries(rule.params).every(([k, v]) => params.get(k) === v);
    }

    function isSwitchExcludedPage() {
        return SWITCH_EXCLUDED_PAGES.some(matchesRule);
    }

    /* =====================================================================
     * 2. 建立導覽列按鈕的共用函式(改良版)
     * ===================================================================*/
    function addNavButton({ id, text, href = '#', onClick, title }) {
        if (document.getElementById(id)) return null; // 已存在就不重複插入
        const nb = document.getElementById('nb');
        if (!nb) {
            console.error('[E/Ex]Switch: 導覽列 #nb 未找到,無法添加按鈕');
            return null;
        }
        const a = $el('a', { id, href, textContent: text });
        if (title) a.title = title;
        if (onClick) {
            a.addEventListener('click', (e) => {
                e.preventDefault();
                onClick(e);
            });
        }
        const wrapper = $el('div');
        wrapper.appendChild(a);
        nb.appendChild(wrapper);
        return a;
    }

    /* =====================================================================
     * 3. [E/Ex]切換按鈕
     * ===================================================================*/
    function initSwitchButton() {
        if (isSwitchExcludedPage()) return;
        addNavButton({
            id: 'switch-btn',
            text: getButtonText(),
            onClick: () => {
                const currentUrl = window.location.href;
                const newUrl = currentUrl.startsWith('https://exhentai.org')
                    ? currentUrl.replace('https://exhentai.org', 'https://e-hentai.org')
                    : currentUrl.replace('https://e-hentai.org', 'https://exhentai.org');
                window.location.href = newUrl;
            },
        });
    }

    /* =====================================================================
     * 4. HentaiVerse 按鈕(只在 exhentai.org 顯示)
     * ===================================================================*/
    function initHentaiVerseButton() {
        if (location.hostname !== 'exhentai.org') return;
        if (isSwitchExcludedPage()) return; // 工具頁一律不加,跟 [E/Ex]切換 保持一致
        addNavButton({
            id: 'hv-btn',
            text: 'HentaiVerse',
            href: 'https://hentaiverse.org/',
            onClick: () => {
                if (typeof window.popUp === 'function') {
                    window.popUp('https://hentaiverse.org/', 1250, 720);
                } else {
                    const w = 1250, h = 720;
                    window.open(
                        'https://hentaiverse.org/',
                        '_hentaiverse',
                        `toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=${w},height=${h},left=${(screen.width - w) / 2},top=${(screen.height - h) / 2}`
                    );
                }
            },
        });
    }

    /* =====================================================================
     * 5.(選用)在 exhentai.org 也提示 e-hentai.org 的 HentaiVerse 登入獎勵
     * ===================================================================*/
    const EVENTPANE_CACHE_KEY = 'eex_eventpane_cache';
    const EVENTPANE_TTL_MS = 10 * 60 * 1000; // 10 分鐘節流

    function fetchHomeEventPaneHtml() {
        return new Promise((resolve, reject) => {
            if (typeof GM_xmlhttpRequest !== 'function') {
                reject(new Error('GM_xmlhttpRequest 未授權,無法跨網域讀取 e-hentai.org/home.php'));
                return;
            }
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://e-hentai.org/home.php',
                onload: (res) => {
                    try {
                        const doc = new DOMParser().parseFromString(res.responseText, 'text/html');
                        const pane = doc.getElementById('eventpane');
                        resolve(pane ? pane.outerHTML : null);
                    } catch (e) {
                        reject(e);
                    }
                },
                onerror: reject,
            });
        });
    }

    /* =====================================================================
     * 執行
     * ===================================================================*/
    initHentaiVerseButton();
    initSwitchButton();
})();