[E/Ex]Switch

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

Versão de: 12/09/2026. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

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

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

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