GGN Theme Switcher

Modify page body class to specified class and provide theme switching functionality

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         GGN Theme Switcher
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Modify page body class to specified class and provide theme switching functionality
// @author       Robin27
// @license      MIT
// @match        *://gazellegames.net/*
// @match        *://*.gazellegames.net/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // ===== Configuration Area =====
    // Get configuration from storage, use default values if not exists
    let TARGET_BODY_CLASS = GM_getValue('TARGET_BODY_CLASS', '');
    // ==============================

    // Register configuration menu commands
    GM_registerMenuCommand('Configure Target Theme Class', function() {
        const newClassName = prompt('Enter target theme class name (e.g.: master_3944):', TARGET_BODY_CLASS);
        if (newClassName !== null && newClassName.trim() !== '') {
            TARGET_BODY_CLASS = newClassName.trim();
            GM_setValue('TARGET_BODY_CLASS', TARGET_BODY_CLASS);
            alert('Configuration saved! Refresh the page to take effect.\nCurrent setting: ' + TARGET_BODY_CLASS);
            console.log('✅ Theme class name updated to:', TARGET_BODY_CLASS);
        }
    });

    // Theme ID list - all theme IDs extracted from comments
    const THEME_IDS = [
        // FRANCHISE type
        'franchise_3', 'franchise_16', 'franchise_19', 'franchise_50', 'franchise_53', 'franchise_55', 'franchise_57', 'franchise_58', 'franchise_65', 'franchise_75', 'franchise_91', 'franchise_126', 'franchise_135', 'franchise_179', 'franchise_198', 'franchise_211', 'franchise_344', 'franchise_360', 'franchise_491', 'franchise_587', 'franchise_595', 'franchise_646', 'franchise_833', 'franchise_836', 'franchise_896', 'franchise_986', 'franchise_1519', 'franchise_2521', 'franchise_2773', 'franchise_3155', 'franchise_5276', 'franchise_6816', 'franchise_9300', 'franchise_12545', 'franchise_12565',
        // MASTER type
        'master_117', 'master_465', 'master_680', 'master_691', 'master_904', 'master_2639', 'master_2940', 'master_3073', 'master_3787', 'master_3805', 'master_3944', 'master_4715', 'master_4742', 'master_4752', 'master_5127', 'master_5154', 'master_6427', 'master_6928', 'master_7718', 'master_7884', 'master_15933', 'master_20225', 'master_23228', 'master_29116', 'master_30510', 'master_39874', 'master_43997', 'master_50731', 'master_56065', 'master_65286', 'master_99091',
        // SERIES type
        'series_11', 'series_12', 'series_22', 'series_26', 'series_27', 'series_31', 'series_35', 'series_66', 'series_70', 'series_169', 'series_177', 'series_178', 'series_183', 'series_184', 'series_185', 'series_188', 'series_191', 'series_196', 'series_204', 'series_205', 'series_208', 'series_228', 'series_234', 'series_237', 'series_260', 'series_268', 'series_279', 'series_291', 'series_298', 'series_302', 'series_327', 'series_418', 'series_483', 'series_548', 'series_555', 'series_565', 'series_575', 'series_581', 'series_630', 'series_827', 'series_857', 'series_877', 'series_882', 'series_885', 'series_987', 'series_1109', 'series_1316', 'series_1434', 'series_1503', 'series_2786', 'series_2918', 'series_3308', 'series_3327', 'series_3369', 'series_4624', 'series_4949', 'series_5588', 'series_5635', 'series_6614', 'series_7385', 'series_12344',
        // TGROUP type
        'tgroup_107', 'tgroup_172', 'tgroup_3609', 'tgroup_4231', 'tgroup_6454', 'tgroup_6526', 'tgroup_9559', 'tgroup_11495', 'tgroup_14972', 'tgroup_18505', 'tgroup_18702', 'tgroup_22694', 'tgroup_23617', 'tgroup_30661', 'tgroup_33733', 'tgroup_65589', 'tgroup_71747', 'tgroup_108726'
    ];

    // Current theme index, -1 means using default theme
    let currentThemeIndex = -1;

    console.log('🚀 GGN Page Enhancer started, target class:', GM_getValue('TARGET_BODY_CLASS', 'master_3944'));

    // High-frequency checking - using requestAnimationFrame
    function checkAndModify() {
        if (document.body) {
            const currentClass = document.body.className;
            const targetClass = GM_getValue('TARGET_BODY_CLASS', 'master_3944');
            
            // Use default theme on index.php page
            if (window.location.pathname.includes('index.php')) {
                if (currentClass === '' || currentClass === 'warned' || currentClass.includes('master_112041')) {
                    document.body.className = targetClass;
                    currentThemeIndex = -1; // Keep default state
                    console.log('✅ Homepage theme set successfully - using default theme:', targetClass);
                }
            } else {
                // Other pages use original logic
                if (currentClass === '') {
                    document.body.className = targetClass;
                    console.log('✅ Added successfully - body has no class, added "' + targetClass + '"');
                } else if (currentClass === 'warned') {
                    document.body.className = targetClass;
                    console.log('✅ Replaced successfully - from "warned" to "' + targetClass + '"');
                } else if (currentClass.includes('master_112041')) {
                    // If class contains master_112041, replace it with custom class
                    const newClass = currentClass.replace(/master_112041/g, targetClass);
                    document.body.className = newClass;
                    console.log('✅ Replaced successfully - from class containing "master_112041" to "' + targetClass + '"');
                    console.log('Original class:', currentClass, '→ New class:', newClass);
                }
            }
        }
        // If page is not fully loaded, continue checking
        if (document.readyState !== 'complete') {
            requestAnimationFrame(checkAndModify);
        } else {
            console.log('🏁 Page loading completed, script finished');
        }
    }
    // Start high-frequency checking
    checkAndModify();

    // ------------------------------------------------------------------------------------------
    // Theme switching functionality

    // Wait for specific elements to load
    function waitForElement(selector, callback) {
        const element = document.querySelector(selector);
        if (element) {
            callback(element);
        } else {
            setTimeout(() => waitForElement(selector, callback), 100);
        }
    }

    // Create theme switcher button
    function createThemeSwitcherButton() {
        // Check if on index.php page
        if (!window.location.pathname.includes('index.php')) {
            return;
        }

        // Create button element
        const themeButton = document.createElement('button');
        themeButton.id = 'ggn-theme-switcher';
        themeButton.textContent = 'Switch Theme';
        themeButton.title = 'Left click: Switch theme | Right click: Copy current theme ID';
        
        // Set button styles
        themeButton.style.cssText = `
            position: fixed;
            top: 300px;
            right: 20px;
            z-index: 9999;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 8px;
            padding: 16px 24px;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
            transition: all 0.3s ease;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            width: 140px;
            min-height: 50px;
            display: flex;
            align-items: center;
            justify-content: center;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        `;

        // Add hover effects
        themeButton.addEventListener('mouseenter', function() {
            this.style.transform = 'translateY(-2px)';
            this.style.boxShadow = '0 6px 20px rgba(0, 0, 0, 0.3)';
        });

        themeButton.addEventListener('mouseleave', function() {
            this.style.transform = 'translateY(0)';
            this.style.boxShadow = '0 4px 15px rgba(0, 0, 0, 0.2)';
        });

        // Add left click event
        themeButton.addEventListener('click', function() {
            switchToNextTheme();
        });

        // Add right click event - copy current theme ID
        themeButton.addEventListener('contextmenu', function(e) {
            e.preventDefault(); // Prevent default context menu
            
            let currentThemeId;
            if (currentThemeIndex === -1) {
                // If still using default theme
                currentThemeId = GM_getValue('TARGET_BODY_CLASS', 'master_3944');
            } else {
                // If already switched to theme in the list
                currentThemeId = THEME_IDS[currentThemeIndex];
            }
            
            // Copy to clipboard
            navigator.clipboard.writeText(currentThemeId).then(function() {
                // Create temporary notification
                const originalText = themeButton.textContent;
                themeButton.textContent = 'Copied!';
                themeButton.style.background = 'linear-gradient(135deg, #4CAF50 0%, #45a049 100%)';
                
                // Restore after 1 second
                setTimeout(function() {
                    themeButton.textContent = originalText;
                    themeButton.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
                }, 1000);
                
                console.log(`📋 Theme ID copied: ${currentThemeId}`);
            }).catch(function(err) {
                console.error('Copy failed:', err);
                // If clipboard API fails, use traditional method
                const textArea = document.createElement('textarea');
                textArea.value = currentThemeId;
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
                
                // Show notification
                const originalText = themeButton.textContent;
                themeButton.textContent = 'Copied!';
                themeButton.style.background = 'linear-gradient(135deg, #4CAF50 0%, #45a049 100%)';
                
                setTimeout(function() {
                    themeButton.textContent = originalText;
                    themeButton.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
                }, 1000);
                
                console.log(`📋 Theme ID copied: ${currentThemeId}`);
            });
        });

        // Add button to page
        document.body.appendChild(themeButton);
        console.log('✅ Theme switcher button created');
    }

    // Switch to next theme
    function switchToNextTheme() {
        // If first click (starting from default theme), start from index 0
        if (currentThemeIndex === -1) {
            currentThemeIndex = 0;
        } else {
            // Move to next theme index
            currentThemeIndex = (currentThemeIndex + 1) % THEME_IDS.length;
        }
        
        const newTheme = THEME_IDS[currentThemeIndex];
        
        // Update body class
        document.body.className = newTheme;
        
        // Update button text to show current theme info
        const themeButton = document.getElementById('ggn-theme-switcher');
        if (themeButton) {
            const themeType = newTheme.split('_')[0];
            const themeNumber = newTheme.split('_')[1];
            // Use more concise display
            themeButton.textContent = `${themeType}_${themeNumber}`;
            themeButton.title = `Current theme: ${newTheme} (${currentThemeIndex + 1}/${THEME_IDS.length})`;
            
            // Button width is fixed at 140px, no need for dynamic adjustment
        }
        
        console.log(`🎨 Theme switched to: ${newTheme} (${currentThemeIndex + 1}/${THEME_IDS.length})`);
    }

    // Wait for page to load completely before creating theme switcher button
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createThemeSwitcherButton);
    } else {
        createThemeSwitcherButton();
    }
})();