BlackRussia Forum Quick Replies UNIVERSAL

Добавляет настраиваемые кнопки быстрых ответов на форум BlackRussia с улучшенной мобильной поддержкой, возможностью добавления новых ответов, префиксами к темам, случайной цветной окантовкой (опционально), автоматическим приветствием с панорамными цветами и встроенным редактором стиля текста с визуальным выбором цвета, а также кнопкой помощи с инструкцией.

// ==UserScript==
// @name         BlackRussia Forum Quick Replies UNIVERSAL
// @namespace    http://tampermonkey.net/
// @version      3.1
// @description  Добавляет настраиваемые кнопки быстрых ответов на форум BlackRussia с улучшенной мобильной поддержкой, возможностью добавления новых ответов, префиксами к темам, случайной цветной окантовкой (опционально), автоматическим приветствием с панорамными цветами и встроенным редактором стиля текста с визуальным выбором цвета, а также кнопкой помощи с инструкцией.
// @author       Maras Ageev Тех. [06]
// @match        https://forum.blackrussia.online/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const storageKey = 'br_forum_quick_replies';
    const greetingStorageKey = 'br_forum_greeting';
    let quickRepliesConfig = GM_getValue(storageKey, [
        { label: 'Ответить', text: 'Благодарю за обращение!', prefix: '', color: '', size: '', font: '' }
    ]);
    let customGreeting = GM_getValue(greetingStorageKey, 'Добро пожаловать на форум BlackRussia!');

    GM_addStyle(`
        /* Обновленные стили */
        .quick-replies-container {
            margin-top: 10px;
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
        }

        .quick-replies-container button {
            background-color: #2C2F33;
            color: #F0F0F0;
            border-radius: 5px;
            padding: 6px 10px;
            cursor: pointer;
            font-size: 12px;
            box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
            border: none;
            display: flex; /* Для центрирования изображения и текста */
            align-items: center;
            gap: 5px;
        }

        .quick-replies-container button:hover {
            background-color: #3A3D42;
        }

        .quick-replies-container button img {
            max-height: 20px; /* Ограничьте высоту изображения */
        }

        .config-button-inline, .help-button-inline {
            background-color: #7289da; /* Цветные иконки */
            color: #FFFFFF;
            border: 1px solid #7289da;
            border-radius: 5px;
            padding: 8px 12px; /* Увеличим отступы */
            margin-left: 5px;
            cursor: pointer;
            font-size: 14px; /* Увеличим размер шрифта */
            box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
            display: inline-flex; /* Для центрирования иконки */
            align-items: center;
            justify-content: center;
            line-height: 1; /* Убираем лишнее пространство вокруг иконки */
        }

        .config-button-inline:hover, .help-button-inline:hover {
            background-color: #5865F2;
            border-color: #5865F2;
        }

        /* Увеличим размер иконки */
        .config-button-inline::before {
            content: '⚙️';
            font-size: 1.2em;
            margin-right: 3px; /* Небольшой отступ справа от иконки */
        }

        .help-button-inline::before {
            content: '❓';
            font-size: 1.2em;
            margin-right: 3px; /* Небольшой отступ справа от иконки */
        }

        #quickReplyConfigModal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: #23272a;
            color: #dcddde;
            padding: 15px;
            border-radius: 5px;
            z-index: 1000;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
            max-width: 95%; /* Уменьшим немного для десктопа */
            max-height: 90%; /* Увеличим высоту для мобильных */
            overflow-y: auto;
        }

        #quickReplyConfigModal h2 {
            margin-bottom: 10px;
            font-size: 1.2em;
            text-align: center; /* Центрируем заголовок */
        }

        #quickReplyConfigModal .config-item {
            margin-bottom: 15px;
            padding: 10px;
            border: 1px solid #5865F2; /* Добавим границу блоку */
            border-radius: 5px;
        }

        #quickReplyConfigModal .config-item > div {
            margin-bottom: 8px; /* Отступ между строками */
        }

        #quickReplyConfigModal .config-item > div:last-child {
            margin-bottom: 0;
        }

        #quickReplyConfigModal label {
            font-size: 0.9em;
            margin-right: 5px;
            display: block; /* Занимает всю ширину на мобильных */
            margin-bottom: 3px;
        }

        #quickReplyConfigModal input[type="text"],
        #quickReplyConfigModal select,
        #quickReplyConfigModal input[type="color"] { /* Добавим стиль для color input */
            background-color: #36393f;
            color: #dcddde;
            border: 1px solid #7289da;
            border-radius: 3px;
            padding: 8px; /* Увеличим отступы для касания */
            font-size: 14px; /* Увеличим размер шрифта */
            width: calc(100% - 16px); /* Учтем отступы */
            box-sizing: border-box;
            margin-bottom: 8px; /* Увеличим отступ снизу */
        }

        #quickReplyConfigModal .style-controls {
            display: flex;
            flex-direction: column; /* Разместим элементы вертикально на мобильных */
            gap: 10px;
            align-items: stretch; /* Растянем элементы на всю ширину */
        }

        #quickReplyConfigModal .style-controls > div {
            display: flex;
            flex-direction: column; /* Внутри тоже вертикально */
            align-items: stretch;
        }

        #quickReplyConfigModal button {
            background-color: #7289da;
            color: #fff;
            border: none;
            border-radius: 5px; /* Увеличим радиус */
            padding: 10px 15px; /* Увеличим отступы */
            cursor: pointer;
            font-size: 14px; /* Увеличим размер шрифта */
            margin-top: 5px;
        }

        #quickReplyConfigModal button.delete-button {
            background-color: #f04747;
        }

        #quickReplyConfigModal button.add-button {
            background-color: #43b581;
            margin-bottom: 10px;
        }

        #quickReplyConfigModal .config-list {
            margin-bottom: 10px;
        }

        #quickReplyConfigModal .modal-buttons {
            display: flex;
            gap: 10px;
            justify-content: space-around; /* Распределим кнопки по ширине */
            padding-top: 15px;
        }

        #quickReplyConfigModal .modal-buttons button {
            flex-grow: 1; /* Кнопки занимают равную ширину */
        }

        /* Стиль для приветствия */
        .forum-greeting {
            width: 100%;
            padding: 15px 0;
            text-align: center;
            font-size: 1.1em;
            color: #fff;
            background-image: linear-gradient(to right, #30475E, #F06A6A, #8E44AD, #27AE60, #F39C12);
            background-size: 500% 100%;
            animation: panoramic-gradient 10s linear infinite alternate;
            margin-bottom: 10px;
        }

        @keyframes panoramic-gradient {
            0% { background-position: 0% 50%; }
            100% { background-position: 100% 50%; }
        }

        /* Стиль для поля ввода приветствия */
        #greetingInput {
            background-color: #36393f;
            color: #dcddde;
            border: 1px solid #7289da;
            border-radius: 5px; /* Увеличим радиус */
            padding: 10px; /* Увеличим отступы */
            width: calc(100% - 20px); /* Учтем отступы */
            margin-bottom: 10px;
            font-size: 14px; /* Увеличим размер шрифта */
            box-sizing: border-box;
        }

        /* Медиа запрос для мобильных устройств (ширина экрана до 600px) */
        @media (max-width: 600px) {
            #quickReplyConfigModal {
                max-width: 98%; /* Занимает почти всю ширину */
                top: 0;
                left: 0;
                transform: none;
                width: 100%;
                height: 100%;
                border-radius: 0;
                padding: 10px;
            }

            #quickReplyConfigModal .modal-buttons {
                flex-direction: column; /* Кнопки в столбик на маленьких экранах */
            }

            #quickReplyConfigModal .modal-buttons button {
                margin-bottom: 10px;
            }

            #quickReplyConfigModal .style-controls {
                flex-direction: column; /* Размещаем элементы вертикально */
            }

            #quickReplyHelpModal {
                max-width: 95%;
            }
        }

        /* Стиль для модального окна помощи */
        #quickReplyHelpModal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: #23272a;
            color: #dcddde;
            padding: 20px;
            border-radius: 5px;
            z-index: 1001;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
            max-width: 90%;
            max-height: 80%;
            overflow-y: auto;
            text-align: left;
        }

        #quickReplyHelpModal h3 {
            margin-top: 0;
            margin-bottom: 10px;
            font-size: 1.1em;
        }

        #quickReplyHelpModal p {
            margin-bottom: 10px;
        }

        #quickReplyHelpModal button {
            background-color: #7289da;
            color: #fff;
            border: none;
            border-radius: 5px;
            padding: 10px 15px;
            cursor: pointer;
            font-size: 14px;
            margin-top: 15px;
            display: block;
            width: 100%;
            box-sizing: border-box;
        }
    `);

    function saveConfig() {
        GM_setValue(storageKey, quickRepliesConfig);
        GM_setValue(greetingStorageKey, customGreeting);
    }

    function createQuickReplyButton(config, targetElement) {
        const button = document.createElement('button');
        button.textContent = config.label;

        button.addEventListener('click', function() {
            const editorElement = targetElement.querySelector('.fr-wrapper .fr-element');
            const textarea = targetElement.querySelector('textarea');
            const bbWrapper = targetElement.querySelector('.bbWrapper');

            let styledText = config.text;
            let styles = [];
            if (config.color) {
                styles.push(`color: ${config.color}`);
            }
            if (config.size) {
                styles.push(`font-size: ${config.size}`);
            }
            if (config.font) {
                styles.push(`font-family: ${config.font}`);
            }

            if (styles.length > 0) {
                styledText = `<span style="${styles.join(';')}">${styledText}</span>`;
            }

            if (editorElement) {
                editorElement.innerHTML = styledText;
            } else if (textarea) {
                textarea.value = styledText;
            } else if (bbWrapper) {
                const editor = XenForo.getEditor(bbWrapper);
                if (editor) {
                    editor.insertText(config.text);
                } else {
                    textarea.value += config.text;
                }
            } else {
                console.error('Не найдено поле ввода текста.');
            }

            // Применение префикса, если это новая тема
            if (window.location.href.includes('/post-thread')) {
                const titleInput = document.querySelector('input[name="title"]');
                if (titleInput && config.prefix) {
                    titleInput.value = config.prefix + ' ' + titleInput.value;
                }
            }
        });

        return button;
    }

    let buttonsAdded = false;
    let modalVisible = false;
    let greetingShown = false;
    let helpModalVisible = false;

    function getRandomBorderColor() {
        const colors = ['#ffba52', '#ffdd57', '#50a3ff', '#81ff52', '#5bff73', '#ff5252', '#ff5299', '#52ffbd', '#ff8952'];
        return colors[Math.floor(Math.random() * colors.length)];
    }

    function showHelpModal() {
        if (helpModalVisible) return;
        helpModalVisible = true;

        const helpModal = document.createElement('div');
        helpModal.id = 'quickReplyHelpModal';

        const title = document.createElement('h3');
        title.textContent = 'Инструкция по использованию быстрых ответов BlackRussia Forum';
        helpModal.appendChild(title);

        const authorInfo = document.createElement('p');
        authorInfo.textContent = 'Автор: Maras Ageev Тех. [06]';
        helpModal.appendChild(authorInfo);

        const description = document.createElement('p');
        description.textContent = 'Этот скрипт добавляет настраиваемые кнопки быстрых ответов под формой ввода текста на форуме BlackRussia. Вы можете настроить текст, префикс (который будет добавляться к заголовку новой темы), цвет текста, размер шрифта и шрифт для каждой кнопки.';
        helpModal.appendChild(description);

        const configSectionTitle = document.createElement('h3');
        configSectionTitle.textContent = 'Настройка кнопок:';
        helpModal.appendChild(configSectionTitle);

        const configSteps = document.createElement('ol');
        const step1 = document.createElement('li');
        step1.textContent = 'Нажмите кнопку с иконкой "⚙️", чтобы открыть панель конфигурации.';
        configSteps.appendChild(step1);
        const step2 = document.createElement('li');
        step2.textContent = 'В панели конфигурации вы можете добавлять, редактировать и удалять быстрые ответы.';
        configSteps.appendChild(step2);
        const step3 = document.createElement('li');
        step3.textContent = 'Для каждого ответа вы можете указать: название кнопки, текст ответа, префикс, цвет текста, размер шрифта и шрифт.';
        configSteps.appendChild(step3);
        const step4 = document.createElement('li');
        step4.textContent = 'Нажмите "Сохранить", чтобы применить изменения.';
        configSteps.appendChild(step4);
        helpModal.appendChild(configSteps);

        const usingButtonsTitle = document.createElement('h3');
        usingButtonsTitle.textContent = 'Использование кнопок:';
        helpModal.appendChild(usingButtonsTitle);

        const usingButtonsSteps = document.createElement('ol');
        const step5 = document.createElement('li');
        step5.textContent = 'Просто нажмите на нужную кнопку быстрого ответа, и текст будет автоматически вставлен в поле ввода.';
        usingButtonsSteps.appendChild(step5);
        helpModal.appendChild(usingButtonsSteps);

        const closeButton = document.createElement('button');
        closeButton.textContent = 'Закрыть';
        closeButton.addEventListener('click', () => {
            helpModal.remove();
            helpModalVisible = false;
        });
        helpModal.appendChild(closeButton);

        document.body.appendChild(helpModal);
    }

    function addQuickReplyButtons(formElement) {
        if (!formElement || buttonsAdded) {
            return;
        }
        buttonsAdded = true;

        const quickRepliesContainer = document.createElement('div');
        quickRepliesContainer.style.marginTop = '10px';
        quickRepliesContainer.classList.add('quick-replies-container');

        quickRepliesConfig.forEach(config => {
            const button = createQuickReplyButton(config, formElement);
            if (button.textContent !== 'Настроить') {
                button.style.border = '2px solid ' + getRandomBorderColor();
            }
            quickRepliesContainer.appendChild(button);
        });

        const configButtonInline = document.createElement('button');
        configButtonInline.classList.add('config-button-inline');
        configButtonInline.addEventListener('click', openConfigModal);
        quickRepliesContainer.appendChild(configButtonInline);

        const helpButtonInline = document.createElement('button');
        helpButtonInline.title = 'Инструкция';
        helpButtonInline.classList.add('help-button-inline');
        helpButtonInline.addEventListener('click', showHelpModal);
        quickRepliesContainer.appendChild(helpButtonInline);

        const editorContainer = formElement.querySelector('.fr-wrapper'); // Froala Editor
        if (editorContainer && editorContainer.parentNode) {
            editorContainer.parentNode.insertBefore(quickRepliesContainer, editorContainer.nextSibling);
            return;
        }

        const textarea = formElement.querySelector('textarea'); // Обычный textarea
        if (textarea && textarea.parentNode) {
            textarea.parentNode.insertBefore(quickRepliesContainer, textarea.nextSibling);
            return;
        }

        const bbWrapper = formElement.querySelector('.bbWrapper'); // XenForo BBCode Editor
        if (bbWrapper && bbWrapper.parentNode) {
            bbWrapper.parentNode.insertBefore(quickRepliesContainer, bbWrapper.nextSibling);
            return;
        }

        // Попробуем найти div с классом "message-editor" или "editor-container"
        const editorDiv = formElement.querySelector('.message-editor') || formElement.querySelector('.editor-container');
        if (editorDiv && editorDiv.parentNode) {
            editorDiv.parentNode.insertBefore(quickRepliesContainer, editorDiv.nextSibling);
            return;
        }

        // Если ничего не найдено, попробуем добавить кнопки в конец формы
        formElement.appendChild(quickRepliesContainer);
    }

    function findAndAddButtons() {
        if (buttonsAdded) return; // Чтобы не добавлять кнопки многократно

        const replyForm = document.querySelector('form[action*="/post-reply"]'); // Поиск формы по action
        if (replyForm) {
            console.log("Найдена форма ответа по action.");
            addQuickReplyButtons(replyForm);
            return;
        }

        const quickReplyForm = document.querySelector('.block.message.reply form'); // Форма быстрого ответа
        if (quickReplyForm) {
            console.log("Найдена форма быстрого ответа по классу.");
            addQuickReplyButtons(quickReplyForm);
            return;
        }

        const formWithTextarea = document.querySelector('form:has(textarea)'); // Любая форма с textarea
        if (formWithTextarea) {
            console.log("Найдена форма с textarea.");
            addQuickReplyButtons(formWithTextarea);
            return;
        }

        const blockWithEditor = document.querySelector('.block:has(.fr-wrapper)') || document.querySelector('.block:has(.bbWrapper)'); // Блок с редактором
        if (blockWithEditor && blockWithEditor.querySelector('form')) {
            console.log("Найден блок с редактором и формой.");
            addQuickReplyButtons(blockWithEditor.querySelector('form'));
            return;
        }

        console.log("Не удалось найти форму ответа на странице.");
    }

    function addGreeting() {
        if (greetingShown) return;
        greetingShown = true;
        const greetingDiv = document.createElement('div');
        greetingDiv.classList.add('forum-greeting');
        greetingDiv.textContent = customGreeting;
        document.body.insertBefore(greetingDiv, document.body.firstChild);
    }

    function openConfigModal() {
        if (modalVisible) return;
        modalVisible = true;

        const modal = document.createElement('div');
        modal.id = 'quickReplyConfigModal';
        const title = document.createElement('h2');
        title.textContent = 'Настройка быстрых ответов';
        modal.appendChild(title);

        const greetingLabel = document.createElement('label');
        greetingLabel.textContent = 'Приветствие:';
        greetingLabel.style.display = 'block';
        greetingLabel.style.marginBottom = '10px';
        modal.appendChild(greetingLabel);

        const greetingInput = document.createElement('input');
        greetingInput.type = 'text';
        greetingInput.id = 'greetingInput';
        greetingInput.value = customGreeting;
        greetingInput.addEventListener('change', (event) => {
            customGreeting = event.target.value;
        });
        modal.appendChild(greetingInput);

        const addButton = document.createElement('button');
        addButton.textContent = 'Добавить ответ';
        addButton.classList.add('add-button');
        addButton.addEventListener('click', () => {
            quickRepliesConfig.push({ label: '', text: '', prefix: '', color: '', size: '', font: '' });
            renderConfigList(modal);
        });
        modal.appendChild(addButton);

        const configList = document.createElement('div');
        configList.classList.add('config-list');
        modal.appendChild(configList);

        function renderConfigList(modalElement) {
            configList.innerHTML = '';
            quickRepliesConfig.forEach((config, index) => {
                const configItem = document.createElement('div');
                configItem.classList.add('config-item');

                // Первая строка: Название
                const row1 = document.createElement('div');
                const labelLabel = document.createElement('label');
                labelLabel.textContent = 'Название:';
                const labelInput = document.createElement('input');
                labelInput.type = 'text';
                labelInput.value = config.label;
                labelInput.placeholder = 'Название';
                labelInput.addEventListener('change', (event) => {
                    quickRepliesConfig[index].label = event.target.value;
                });
                row1.appendChild(labelLabel);
                row1.appendChild(labelInput);
                configItem.appendChild(row1);

                // Вторая строка: Содержимое
                const row2 = document.createElement('div');
                const textLabel = document.createElement('label');
                textLabel.textContent = 'Содержимое:';
                const textInput = document.createElement('input');
                textInput.type = 'text';
                textInput.value = config.text;
                textInput.placeholder = 'Содержимое';
                textInput.addEventListener('change', (event) => {
                    quickRepliesConfig[index].text = event.target.value;
                });
                row2.appendChild(textLabel);
                row2.appendChild(textInput);
                configItem.appendChild(row2);

                // Третья строка: Цвет, Шрифт, Размер
                const row3 = document.createElement('div');
                row3.classList.add('style-controls');

                // Цвет (выбор цвета)
                const colorDiv = document.createElement('div');
                const colorLabel = document.createElement('label');
                colorLabel.textContent = 'Цвет:';
                const colorInput = document.createElement('input');
                colorInput.type = 'color';
                colorInput.value = config.color || '#ffffff'; // Default to white
                colorInput.addEventListener('change', (event) => {
                    quickRepliesConfig[index].color = event.target.value;
                });
                colorDiv.appendChild(colorLabel);
                colorDiv.appendChild(colorInput);
                row3.appendChild(colorDiv);

                // Шрифт
                const fontDiv = document.createElement('div');
                const fontLabel = document.createElement('label');
                fontLabel.textContent = 'Шрифт:';
                const fontSelect = document.createElement('select');
                const fonts = ['', 'Arial', 'Verdana', 'Times New Roman', 'Georgia'];
                fonts.forEach(font => {
                    const option = document.createElement('option');
                    option.value = font;
                    option.textContent = font ? font : 'По умолчанию';
                    option.selected = config.font === font;
                    fontSelect.appendChild(option);
                });
                fontSelect.addEventListener('change', (event) => {
                    quickRepliesConfig[index].font = event.target.value;
                });
                fontDiv.appendChild(fontLabel);
                fontDiv.appendChild(fontSelect);
                row3.appendChild(fontDiv);

                // Размер
                const sizeDiv = document.createElement('div');
                const sizeLabel = document.createElement('label');
                sizeLabel.textContent = 'Размер:';
                const sizeSelect = document.createElement('select');
                const sizes = ['', '12px', '14px', '16px', '18px', '20px'];
                sizes.forEach(size => {
                    const option = document.createElement('option');
                    option.value = size;
                    option.textContent = size ? size : 'По умолчанию';
                    option.selected = config.size === size;
                    sizeSelect.appendChild(option);
                });
                sizeSelect.addEventListener('change', (event) => {
                    quickRepliesConfig[index].size = event.target.value;
                });
                sizeDiv.appendChild(sizeLabel);
                sizeDiv.appendChild(sizeSelect);
                row3.appendChild(sizeDiv);

                configItem.appendChild(row3);

                // Четвертая строка: Удалить
                const row4 = document.createElement('div');
                row4.style.display = 'flex';
                row4.style.alignItems = 'center';
                row4.style.gap = '10px';

                const deleteButton = document.createElement('button');
                deleteButton.textContent = 'Удалить';
                deleteButton.classList.add('delete-button');
                deleteButton.addEventListener('click', () => {
                    quickRepliesConfig.splice(index, 1);
                    renderConfigList(modalElement);
                });
                row4.appendChild(deleteButton);

                configItem.appendChild(row4);

                configList.appendChild(configItem);
            });
        }

        renderConfigList(modal);

        const modalButtons = document.createElement('div');
        modalButtons.classList.add('modal-buttons');

        const saveButton = document.createElement('button');
        saveButton.textContent = 'Сохранить';
        saveButton.addEventListener('click', () => {
            saveConfig();
            const existingContainer = document.querySelector('.quick-replies-container');
            if (existingContainer) {
                existingContainer.remove();
            }
            buttonsAdded = false; // Сбрасываем флаг, чтобы кнопки добавились заново
            findAndAddButtons();
            addGreeting();
            modal.remove();
            modalVisible = false;
        });
        modalButtons.appendChild(saveButton);

        const cancelButton = document.createElement('button');
        cancelButton.textContent = 'Отмена';
        cancelButton.addEventListener('click', () => {
            modal.remove();
            modalVisible = false;
            quickRepliesConfig = GM_getValue(storageKey, [
                { label: 'Ответить', text: 'Благодарю за обращение!', prefix: '', color: '', size: '', font: '' }
            ]);
            customGreeting = GM_getValue(greetingStorageKey, 'Добро пожаловать на форум BlackRussia!');
            const existingContainer = document.querySelector('.quick-replies-container');
            if (existingContainer) {
                existingContainer.remove();
            }
            buttonsAdded = false; // Сбрасываем флаг при отмене
            findAndAddButtons();
            addGreeting();
        });
        modalButtons.appendChild(cancelButton);

        modal.appendChild(modalButtons);
        document.body.appendChild(modal);
    }

    GM_registerMenuCommand("Настроить быстрые ответы", openConfigModal);

    window.addEventListener('load', () => {
        addGreeting();
        findAndAddButtons();
    });

    const observer = new MutationObserver(mutationsList => {
        for (let mutation of mutationsList) {
            if (mutation.type === 'childList') {
                findAndAddButtons();
            }
        }
    });

    const targetNode = document.body;
    const observerConfig = { childList: true, subtree: true };
    observer.observe(targetNode, observerConfig);

})();