[E/Ex]Switch

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

2026-09-12 기준 버전입니다. 최신 버전을 확인하세요.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

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