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

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

// ==UserScript==
// @name         (v3.4 Simple+Border+TopNav+Glow+Gradient+Font) Стиль для BR Forum
// @namespace    http://tampermonkey.net/
// @version      3.4
// @description  Простая настройка фона, цвета, окантовки, верхней навигации, свечения текста, анимированного градиента и шрифта для forum.blackrussia.online.
// @author       M. 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+Gradient+Font v3.4] Инициализация...');

    const STYLE_ID = 'blackrussia-custom-style-v34';
    const PANEL_ID = 'blackrussia-settings-panel-v34';
    const TOP_NAV_ID = 'blackrussia-top-nav-bar-v34';
    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,
        // Новые настройки градиента
        enableGradient: false,
        gradientColors: ['#ff0000', '#00ff00', '#0000ff'],
        gradientSpeed: 20,
        // Новая настройка шрифта
        fontFamily: '' // По умолчанию шрифт не изменен
    };

    // --- Вспомогательные Функции ---
    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 if (key === 'gradientColors') {
                    currentSettings[key] = typeof savedValue === 'string' ?
                        savedValue.split(',') :
                        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);
        }

        try {
            const bgRgb = hexToRgb(settings.bgColor);
            const elementBgColor = bgRgb ?
                `rgba(${bgRgb.r}, ${bgRgb.g}, ${bgRgb.b}, ${settings.opacityValue})` :
                defaultSettings.bgColor;

            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 gradientCss = settings.enableGradient ? `
                body {
                    background: linear-gradient(-45deg, ${settings.gradientColors.join(',')});
                    background-size: 400% 400%;
                    animation: gradient ${settings.gradientSpeed}s ease infinite;
                }
                @keyframes gradient {
                    0% { background-position: 0% 50%; }
                    50% { background-position: 100% 50%; }
                    100% { background-position: 0% 50%; }
                }
            ` : '';

            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 forumCss = `
                ${gradientCss}

                body:not([style*="background-image"]) {
                    background-color: ${fallbackBgColor} !important;
                }

                ${!settings.enableGradient && settings.bgImageUrl ? `
                body {
                    background-image: url('${settings.bgImageUrl}') !important;
                    background-size: cover !important;
                    background-attachment: fixed !important;
                }
                ` : ''}

                .p-pageWrapper {
                    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;' : ''}
                }

                .p-body-inner, .message, .message-cell, .block-body, .bbCodeBlock,
                .widget-container, .notice, .overlay-container .overlay {
                    background: none !important;
                    border: none !important;
                    box-shadow: none !important;
                }

                /* Применяем выбранный шрифт ко всему body */
                body {
                    font-family: ${settings.fontFamily || 'inherit'} !important;
                }
            `;

            styleElement.textContent = forumCss;
        } catch (e) {
            console.error('[BR Style] Ошибка применения стилей!', e);
        }
    }

    // --- 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;
            settingsPanel.innerHTML = `
                <h3>🎨 Настройки Стиля (v3.4)</h3>

                <div class="setting-group">
                    <label for="s_fontFamily">Шрифт:</label>
                    <input type="text" id="s_fontFamily" name="fontFamily" placeholder="Например: Arial, sans-serif">
                    <small>Введите название шрифта (как в CSS).</small>
                </div>

                <div class="setting-group">
                    <input type="checkbox" id="s_enableGradient" name="enableGradient">
                    <label for="s_enableGradient" class="inline-label">Анимированный градиент</label>
                    <div class="sub-settings">
                        <div>
                            <label for="s_gradientColors">Цвета градиента (через запятую):</label>
                            <input type="text" id="s_gradientColors" name="gradientColors"
                                placeholder="Например: #ff0000, #00ff00, #0000ff">
                        </div>
                        <div style="margin-top: 8px;">
                            <label for="s_gradientSpeed">Скорость анимации (секунды):</label>
                            <input type="number" id="s_gradientSpeed" name="gradientSpeed"
                                min="5" max="60" step="1">
                        </div>
                    </div>
                </div>

                <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 if (key === 'gradientSpeed') {
                                 newSettings[key] = parseInt(input.value, 10) || defaultSettings[key];
                             }
                        } else if (key === 'gradientColors') {
                            newSettings[key] = input.value.split(',').map(s => s.trim());
                        } else if (key === 'fontFamily') {
                            newSettings[key] = input.value.trim();
                        } 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 if (key === 'gradientColors') {
                         input.value = Array.isArray(currentSettings[key]) ? currentSettings[key].join(', ') : '';
                     } 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;
                }

                /* === Стили для Панели Настроек (desktop) === */
                 #${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; }

                /* === Стили для Панели Настроек (mobile) === */
                @media (max-width: 768px) {
                    #${PANEL_ID} {
                        position: fixed;
                        z-index: 9999;
                        bottom: 0; /* Располагаем внизу */
                        left: 0;
                        width: 100%; /* Занимает всю ширину экрана */
                        margin: 0; /* Убираем отступы */
                        border-radius: 0; /* Убираем скругление */
                        box-shadow: 0 -3px 10px rgba(0,0,0,0.5); /* Тень сверху */
                        padding: 10px;
                        font-size: 14px; /* Увеличиваем шрифт */
                        max-height: 80vh; /* Ограничиваем высоту, чтобы не занимать весь экран */
                        overflow-y: auto;
                    }
                    #${PANEL_ID} h3 {
                        font-size: 18px;
                        margin-bottom: 10px;
                    }
                    #${PANEL_ID} div.setting-group {
                        margin-bottom: 15px;
                    }
                    #${PANEL_ID} label {
                        font-size: 15px;
                        margin-bottom: 6px;
                    }
                    #${PANEL_ID} input[type="text"],
                    #${PANEL_ID} input[type="number"],
                    #${PANEL_ID} input[type="color"] {
                        padding: 8px; /* Увеличиваем отступы */
                        font-size: 16px; /* Увеличиваем шрифт */
                    }
                    #${PANEL_ID} input[type="color"] {
                        height: 30px;
                        width: 40px;
                    }
                    #${PANEL_ID} button {
                        padding: 10px 16px; /* Увеличиваем отступы */
                        font-size: 16px; /* Увеличиваем шрифт */
                        margin-left: 10px;
                    }
                    #${PANEL_ID} .sub-settings {
                        margin-left: 15px;
                        padding-left: 10px;
                        border-left: 2px solid #555;
                        margin-top: 10px;
                    }
                    #${PANEL_ID} hr {
                        margin: 10px 0;
                    }
                    #${PANEL_ID} .button-group {
                        text-align: center; /* Кнопки по центру */
                        padding-top: 15px;
                    }
                    #${PANEL_ID} .button-group button {
                        margin: 5px; /* Увеличиваем отступы между кнопками */
                    }
                }
            `;
            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+Gradient+Font v3.4] Инициализация завершена.');
        } catch (e) {
            console.error('[BR Style] Ошибка инициализации!', e);
            alert('[BR Style] Ошибка инициализации скрипта!');
        }
    }

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

})();