(v3.3 Simple+Border+TopNav+Glow) Стиль для BR Forum

Простая настройка фона, цвета, окантовки, добавление верхней навигации и свечения текста для forum.blackrussia.online.

当前为 2025-04-10 提交的版本,查看 最新版本

// ==UserScript==
// @name         (v3.3 Simple+Border+TopNav+Glow) Стиль для BR Forum
// @namespace    http://tampermonkey.net/
// @version      3.3 // <-- Увеличил версию
// @description  Простая настройка фона, цвета, окантовки, добавление верхней навигации и свечения текста для forum.blackrussia.online.
// @author       Maras Ageev
// @match        https://forum.blackrussia.online/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    console.log('[BR Style+Border+TopNav+Glow v3.3] Инициализация...'); // Обновил лог

    const STYLE_ID = 'blackrussia-custom-style-v33'; // Обновил ID для версии
    const PANEL_ID = 'blackrussia-settings-panel-v33'; // Обновил ID для версии
    const TOP_NAV_ID = 'blackrussia-top-nav-bar-v33'; // Обновил ID для версии
    const TOP_NAV_HEIGHT = '45px';

    let settingsPanel = null;
    let currentSettings = {};

    // --- Стандартные Настройки (с окантовкой) ---
    const defaultSettings = {
        bgImageUrl: '',
        opacityValue: 0.9,
        borderRadius: '8px',
        bgColor: '#2E2E2E',
        enableRounding: true,
        // Настройки окантовки
        enableEdge: true,
        edgeColor: '#FFEB3B',
        edgeWidth: '1px',
        edgeOpacity: 0.7
        // Настройку для свечения можно добавить сюда, если нужно будет ее включать/выключать
    };

    // --- Вспомогательные Функции ---
    function hexToRgb(hex) {
        if (!hex || typeof hex !== 'string') return null;
        let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : null;
    }

    // --- Управление Настройками ---
    async function loadSettings() {
        console.log('[BR Style] Загрузка настроек...');
        currentSettings = {};
        try {
            for (const key in defaultSettings) {
                const savedValue = await GM_getValue(key, defaultSettings[key]);
                if (typeof defaultSettings[key] === 'boolean') {
                    currentSettings[key] = (savedValue === true || savedValue === 'true');
                } else if (key === 'opacityValue' || key === 'edgeOpacity') {
                    currentSettings[key] = parseFloat(savedValue) || defaultSettings[key];
                 } else if (typeof defaultSettings[key] === 'number') {
                     currentSettings[key] = parseInt(savedValue, 10) || defaultSettings[key];
                } else {
                    currentSettings[key] = savedValue;
                }
            }
             console.log('[BR Style] Настройки загружены:', currentSettings);
        } catch (e) {
             console.error('[BR Style] Ошибка загрузки настроек!', e);
             currentSettings = { ...defaultSettings };
             alert('[BR Style] Ошибка загрузки настроек! Применены стандартные значения.');
        }
    }

    async function saveSettings(settingsToSave) {
       console.log('[BR Style] Сохранение настроек...');
       try {
           for (const key in settingsToSave) {
                if (defaultSettings.hasOwnProperty(key)) {
                     await GM_setValue(key, settingsToSave[key]);
                }
           }
           currentSettings = { ...settingsToSave };
           console.log('[BR Style] Настройки сохранены.');
           return true;
       } catch (e) {
           console.error('[BR Style] Ошибка сохранения настроек!', e);
           alert('[BR Style] Ошибка сохранения настроек!');
           return false;
       }
    }

    // --- Применение Динамических Стилей Форума (на основе настроек) ---
    function applyForumStyles(settings) {
         console.log('[BR Style] Применение динамических стилей...');
         let styleElement = document.getElementById(STYLE_ID);
         if (!styleElement) {
             styleElement = document.createElement('style');
             styleElement.id = STYLE_ID;
             styleElement.type = 'text/css';
             (document.head || document.documentElement).appendChild(styleElement);
             console.log('[BR Style] Создан элемент style для динамических стилей.');
         }

        try {
            // Основной фон элементов
            const bgRgb = hexToRgb(settings.bgColor);
            const elementBgColor = bgRgb
                ? `rgba(${bgRgb.r}, ${bgRgb.g}, ${bgRgb.b}, ${settings.opacityValue})`
                : defaultSettings.bgColor; // Fallback

            // Окантовка
            const edgeRgb = hexToRgb(settings.edgeColor);
            const edgeColorWithOpacity = edgeRgb
                ? `rgba(${edgeRgb.r}, ${edgeRgb.g}, ${edgeRgb.b}, ${settings.edgeOpacity})`
                : 'transparent';
            const finalEdgeBoxShadow = settings.enableEdge
                ? `0 0 0 ${settings.edgeWidth} ${edgeColorWithOpacity}`
                : 'none';

            // Скругление
            const finalBorderRadius = settings.enableRounding ? settings.borderRadius : '0px';
            const fallbackBgColor = settings.bgColor || '#1e1e1e';

            // Селекторы (основные элементы, к которым применяем фон, радиус и окантовку)
             const mainElementsSelector = `
                .block-container, .block-filterBar, .message-inner,
                .widget-container .widget, .bbCodeBlock-content, .formPopup .menu-content,
                .tooltip-content, .structItem, .notice-content, .overlay-container .overlay-content,
                .p-header, .p-nav, .p-navSticky.is-sticky .p-nav, .p-footer
            `;
            // Селекторы элементов, которые должны оставаться прозрачными
             const transparentElementsSelector = `
                .p-body-inner, .message, .message-cell, .block-body, .bbCodeBlock,
                .widget-container, .notice, .overlay-container .overlay
            `;
            // Специальный селектор для главного контейнера страницы (для фона body и отступов)
            const pageWrapperSelector = '.p-pageWrapper';

            const forumCss = `
                /* Фон для body (если картинка задана) */
                body {
                    ${settings.bgImageUrl ? `
                    background-image: url('${settings.bgImageUrl}') !important;
                    background-size: cover !important;
                    background-attachment: fixed !important;
                    background-position: center center !important;
                    background-repeat: no-repeat !important;
                    ` : ''}
                    background-color: ${fallbackBgColor} !important; /* Всегда задаем фоновый цвет */
                }

                /* Стиль для основного контейнера страницы */
                ${pageWrapperSelector} {
                     background-color: ${elementBgColor} !important;
                     border-radius: ${finalBorderRadius} !important;
                     box-shadow: ${finalEdgeBoxShadow} !important; /* Окантовка через тень */
                     ${settings.enableRounding ? 'overflow: hidden;' : ''}
                     /* Отступ сверху из-за фиксированной панели навигации - УЖЕ В СТАТИЧНЫХ СТИЛЯХ */
                 }

                /* Стили для других основных блоков */
                ${mainElementsSelector} {
                    background-color: ${elementBgColor} !important;
                    border-radius: ${finalBorderRadius} !important;
                    box-shadow: ${finalEdgeBoxShadow} !important; /* Окантовка через тень */
                    ${settings.enableRounding ? 'overflow: hidden;' : ''}
                }

                 /* Сброс фона и границ для вложенных элементов */
                 ${transparentElementsSelector} {
                     background: none !important;
                     border: none !important;
                     /* Сбрасываем тень у вложенных, если она была */
                     box-shadow: none !important;
                 }
            `;
            styleElement.textContent = forumCss;
             console.log('[BR Style] Динамические стили применены.');
        } catch (e) {
             console.error('[BR Style] Ошибка применения динамических стилей!', e);
             alert(`[BR Style] Ошибка применения динамических стилей! ${e.message}`);
             if (styleElement) styleElement.textContent = '/* Ошибка применения динамических стилей */';
        }
    }


    // --- UI Панель Настроек ---
    function createPanel() {
         console.log('[BR Style] Создание панели настроек...');
         if (document.getElementById(PANEL_ID)) return document.getElementById(PANEL_ID);

        try {
            settingsPanel = document.createElement('div');
            settingsPanel.id = PANEL_ID;
            // HTML панели остается прежним
            settingsPanel.innerHTML = `
                <h3>🎨 Настройки Стиля (v3.3)</h3>

                <div class="setting-group">
                    <label for="s_bgImageUrl_simple">URL Фона:</label>
                    <input type="text" id="s_bgImageUrl_simple" name="bgImageUrl" placeholder="Ссылка на картинку...">
                </div>

                <div class="setting-group">
                    <label for="s_bgColor_simple">Цвет Фона Элементов:</label>
                    <input type="color" id="s_bgColor_simple" name="bgColor">
                </div>

                 <div class="setting-group">
                    <label for="s_opacityValue_simple">Прозрачность Фона Элементов (0-1):</label>
                    <input type="number" id="s_opacityValue_simple" name="opacityValue" min="0" max="1" step="0.05">
                </div>

                <hr>

                <div class="setting-group">
                    <input type="checkbox" id="s_enableRounding_simple" name="enableRounding">
                    <label for="s_enableRounding_simple" class="inline-label">Включить скругление</label>
                    <div class="sub-settings">
                        <label for="s_borderRadius_simple">Радиус Скругления:</label>
                        <input type="text" id="s_borderRadius_simple" name="borderRadius" placeholder="Например: 8px, 10px">
                    </div>
                </div>

                <hr>

                <div class="setting-group">
                    <input type="checkbox" id="s_enableEdge_simple" name="enableEdge">
                    <label for="s_enableEdge_simple" class="inline-label">Цветная Окантовка</label>
                    <div class="sub-settings">
                        <div>
                            <label for="s_edgeColor_simple">Цвет Окантовки:</label>
                            <input type="color" id="s_edgeColor_simple" name="edgeColor">
                        </div>
                        <div style="margin-top: 8px;">
                            <label for="s_edgeWidth_simple">Толщина Окантовки:</label>
                            <input type="text" id="s_edgeWidth_simple" name="edgeWidth" placeholder="Например: 1px, 2px">
                        </div>
                        <div style="margin-top: 8px;">
                            <label for="s_edgeOpacity_simple">Прозрачность Окантовки (0-1):</label>
                            <input type="number" id="s_edgeOpacity_simple" name="edgeOpacity" min="0" max="1" step="0.05">
                        </div>
                    </div>
                </div>

                <div class="button-group">
                    <button id="save-btn-simple">Сохранить</button>
                    <button id="close-btn-simple">Закрыть</button>
                </div>
            `;

            document.body.appendChild(settingsPanel);
            console.log('[BR Style] Панель настроек создана.');

            // Логика кнопок
            settingsPanel.querySelector('#save-btn-simple').addEventListener('click', async () => {
                console.log('[BR Style] Нажата кнопка Сохранить.');
                const newSettings = {};
                const inputs = settingsPanel.querySelectorAll('input[name]');
                inputs.forEach(input => {
                    const key = input.name;
                    if (defaultSettings.hasOwnProperty(key)) {
                        if (input.type === 'checkbox') {
                            newSettings[key] = input.checked;
                        } else if (input.type === 'number') {
                             newSettings[key] = parseFloat(input.value) || defaultSettings[key];
                             if (key === 'opacityValue' || key === 'edgeOpacity') {
                                 newSettings[key] = Math.max(0, Math.min(1, newSettings[key]));
                             }
                        } else {
                             newSettings[key] = input.value;
                        }
                    }
                });

                const success = await saveSettings(newSettings);
                if (success) {
                     applyForumStyles(currentSettings); // Применяем динамические стили
                     closePanel();
                }
            });

            settingsPanel.querySelector('#close-btn-simple').addEventListener('click', () => {
                console.log('[BR Style] Нажата кнопка Закрыть.');
                closePanel();
            });

            return settingsPanel;

        } catch (e) {
            console.error('[BR Style] Ошибка создания панели настроек!', e);
            alert('[BR Style] Не удалось создать панель настроек!');
            return null;
        }
    }

    function openPanel() {
         console.log('[BR Style] Открытие панели настроек...');
        try {
            if (!settingsPanel) {
                settingsPanel = createPanel();
                if (!settingsPanel) return;
            }
            const inputs = settingsPanel.querySelectorAll('input[name]');
            inputs.forEach(input => {
                const key = input.name;
                if (currentSettings.hasOwnProperty(key)) {
                     if (input.type === 'checkbox') {
                         input.checked = currentSettings[key];
                     } else {
                         input.value = currentSettings[key] ?? '';
                     }
                }
            });

            settingsPanel.style.display = 'block';
            console.log('[BR Style] Панель настроек открыта.');
        } catch (e) {
             console.error('[BR Style] Ошибка открытия панели настроек!', e);
             alert('[BR Style] Не удалось открыть панель настроек!');
        }
    }

    function closePanel() {
        if (settingsPanel) {
            settingsPanel.style.display = 'none';
             console.log('[BR Style] Панель настроек закрыта.');
        }
    }

    // --- Добавление HTML для верхней навигационной панели ---
    function addTopNavBarHTML() {
        console.log('[BR TopNav] Добавление HTML верхней панели...');
        if (document.getElementById(TOP_NAV_ID)) return;

        try {
            const topNav = document.createElement('nav');
            topNav.id = TOP_NAV_ID;
            topNav.className = 'br-top-nav-bar';

            // --- ЗАМЕНИТЕ ЭТИ ССЫЛКИ И НАЗВАНИЯ ---
            const link1_href = "https://forum.blackrussia.online/";
            const link1_text = "Главная Форума";
            const link2_href = "https://forum.blackrussia.online/index.php?forums/%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%BE%D0%B2.10/";
            const link2_text = "Правила";
            const link3_href = "https://forum.blackrussia.online/index.php?forums/%D0%96%D0%B0%D0%BB%D0%BE%D0%B1%D1%8B.14/";
            const link3_text = "Жалобы";
            // ---------------------------------------

            topNav.innerHTML = `
                <a href="${link1_href}">${link1_text}</a>
                <a href="${link2_href}">${link2_text}</a>
                <a href="${link3_href}">${link3_text}</a>
            `;

            document.body.insertBefore(topNav, document.body.firstChild);
            console.log('[BR TopNav] HTML верхней панели добавлен.');

        } catch (e) {
            console.error('[BR TopNav] Ошибка добавления HTML верхней панели!', e);
            alert('[BR TopNav] Не удалось добавить верхнюю панель навигации!');
        }
    }

    // --- Внедрение Статичных CSS (Панель настроек, Верхняя панель, СВЕЧЕНИЕ ТЕКСТА) ---
    function injectStaticStyles() {
        console.log('[BR Style] Внедрение статичных CSS...');
        try {
            const staticCss = `
                /* ============ ДОБАВЛЕНО: Легкое свечение для ВСЕГО текста ============ */
                * {
                    /* Белое свечение: смещение 0 0, размытие 5px, цвет белый с 60% непрозрачностью */
                    /* Вы можете настроить размытие (5px) и прозрачность (0.6) */
                    text-shadow: 0 0 5px rgba(255, 255, 255, 0.6);
                    /* Альтернатива: светло-серое свечение (если белый слишком яркий) */
                    /* text-shadow: 0 0 5px rgba(200, 200, 200, 0.5); */
                }
                /* =================================================================== */

                /* === Стили для Верхней Навигационной Панели === */
                #${TOP_NAV_ID} {
                    background-color: #222;
                    box-shadow: 0 2px 5px rgba(0,0,0,0.3);
                    height: ${TOP_NAV_HEIGHT};
                    width: 100%;
                    position: fixed;
                    top: 0;
                    left: 0;
                    z-index: 9998;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    padding: 0 15px;
                    box-sizing: border-box;
                }
                #${TOP_NAV_ID} a {
                    color: #eee;
                    text-decoration: none;
                    padding: 0 15px;
                    font-size: 16px;
                    font-weight: bold;
                    line-height: ${TOP_NAV_HEIGHT};
                    transition: color 0.2s ease;
                    /* Если свечение на ссылках панели мешает, раскомментируйте строку ниже */
                    /* text-shadow: none !important; */
                }
                #${TOP_NAV_ID} a:hover {
                    color: #FFEB3B;
                }

                /* === Отступ для контента из-за фиксированной верхней панели === */
                .p-pageWrapper {
                     margin-top: ${TOP_NAV_HEIGHT} !important;
                }

                /* === Стили для Панели Настроек === */
                 #${PANEL_ID} {
                     position: fixed; z-index: 9999; bottom: 10px; left: 10px; width: 300px;
                     background: #333; color: #eee; padding: 15px; border-radius: 5px;
                     box-shadow: 0 3px 10px rgba(0,0,0,0.5); display: none; border: 1px solid #555;
                     font-family: sans-serif; font-size: 13px; max-height: calc(100vh - 30px); overflow-y: auto;
                     /* Отключаем глобальное свечение для текста панели настроек для лучшей читаемости */
                     text-shadow: none !important;
                 }
                 #${PANEL_ID} * {
                     /* Убедимся, что и вложенные элементы панели не наследуют свечение */
                     text-shadow: none !important;
                 }
                 #${PANEL_ID} h3 { margin: 0 0 15px; text-align: center; font-size: 16px; border-bottom: 1px solid #555; padding-bottom: 8px;}
                 #${PANEL_ID} div.setting-group { margin-bottom: 12px; }
                 #${PANEL_ID} label { display: block; margin-bottom: 4px; font-weight: bold; color: #ccc; }
                 #${PANEL_ID} input[type="text"], #${PANEL_ID} input[type="number"] { width: calc(100% - 12px); padding: 5px; background: #444; border: 1px solid #666; color: #eee; border-radius: 3px; box-sizing: border-box; }
                 #${PANEL_ID} input[type="color"] { padding: 0; border: 1px solid #666; height: 25px; width: 35px; vertical-align: middle; margin-left: 5px; border-radius: 3px; cursor: pointer;}
                 #${PANEL_ID} input[type="checkbox"] { vertical-align: middle; margin-right: 5px; }
                 #${PANEL_ID} label.inline-label { display: inline; font-weight: normal; vertical-align: middle; }
                 #${PANEL_ID} .button-group { margin-top: 15px; text-align: right; border-top: 1px solid #555; padding-top: 10px; }
                 #${PANEL_ID} button { padding: 6px 12px; margin-left: 8px; border: none; border-radius: 3px; cursor: pointer; font-weight: bold;}
                 #${PANEL_ID} #save-btn-simple { background-color: #4CAF50; color: white; }
                 #${PANEL_ID} #close-btn-simple { background-color: #f44336; color: white; }
                 #${PANEL_ID} hr { border: none; border-top: 1px solid #555; margin: 15px 0; }
                 #${PANEL_ID} .sub-settings { margin-left: 20px; padding-left: 10px; border-left: 2px solid #555; margin-top: 8px; }
            `;
            GM_addStyle(staticCss);
            console.log('[BR Style] Статичные CSS внедрены (включая свечение текста).'); // Обновил лог
        } catch (e) {
            console.error('[BR Style] Ошибка внедрения статичных CSS!', e);
            alert('[BR Style] Ошибка внедрения статичных CSS!');
        }
    }

    // --- Инициализация Скрипта ---
    async function initialize() {
        try {
            injectStaticStyles(); // Внедряем статичные CSS (панель, верхняя панель, ОТСТУП, СВЕЧЕНИЕ ТЕКСТА)
            addTopNavBarHTML();
            await loadSettings();
            applyForumStyles(currentSettings); // Применяем динамические стили (фон, окантовка, скругление)
            GM_registerMenuCommand('🎨 Настроить стиль (+Окантовка)', openPanel, 'b');
            console.log('[BR Style+Border+TopNav+Glow v3.3] Инициализация завершена.'); // Обновил лог
        } catch (e) {
            console.error('[BR Style] Ошибка инициализации!', e);
            alert('[BR Style] Ошибка инициализации скрипта!');
        }
    }

    // --- Запуск ---
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }

})();