HeroesWM Logs Loader

Automatically load multiple pages of battle log, transfers, clan log, and warehouse log

// ==UserScript==
// @name         HeroesWM Logs Loader
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Automatically load multiple pages of battle log, transfers, clan log, and warehouse log
// @author       Any Key Fake Leader
// @license MIT
// @match        https://www.heroeswm.ru/pl_warlog.php*
// @match        https://www.heroeswm.ru/pl_transfers.php*
// @match        https://www.heroeswm.ru/clan_log.php*
// @match        https://www.heroeswm.ru/sklad_log.php*
// @match        https://mirror.heroeswm.ru/pl_warlog.php*
// @match        https://mirror.heroeswm.ru/pl_transfers.php*
// @match        https://mirror.heroeswm.ru/clan_log.php*
// @match        https://mirror.heroeswm.ru/sklad_log.php*
// @match        https://my.lordswm.com/pl_warlog.php*
// @match        https://my.lordswm.com/pl_transfers.php*
// @match        https://my.lordswm.com/clan_log.php*
// @match        https://my.lordswm.com/sklad_log.php*
// @match        https://www.lordswm.com/pl_warlog.php*
// @match        https://www.lordswm.com/pl_transfers.php*
// @match        https://www.lordswm.com/clan_log.php*
// @match        https://www.lordswm.com/sklad_log.php*
// @grant        GM_setClipboard
// ==/UserScript==


(function() {
    'use strict';

// Словарь категорий боёв по подкатегориям
const BATTLE_CATEGORIES = {
    'Гильдии': {
        '0': 'Охотников',
        '40': 'Тактиков',
        '66': 'Воров (мобы)',
        '26': 'Воров (игроки)',
        '61': 'Рейнджеров',
        '95': 'Стражей',
        '127': 'Лидеров',
        '135': 'Опасные бандиты',
        '145': 'КСЗС',
        '110': 'Искателей'
    },
    'Наёмники': {
        '8': 'Набеги',
        '5': 'Захватчики',
        '12': 'Армии',
        '7': 'Монстры',
        '28': 'Заговорщики',
        '29': 'Разбойники',
        '10': 'Отряды'
    },
    'Ивенты': {
        '94': 'Портал (парный и старый одиночный)',
        '96': 'Пиратская блокада',
        '99': 'Поиск сокровищ',
        '115': 'Охота на пиратов (парная)',
        '119': 'Защита деревень',
        '120': 'Подземные пещеры',
        '123': 'Пиратские рейды',
        '133': 'Рисковая авантюра',
        '138': 'Экспедиция',
        '139': 'Гильдия Лидеров',
        '140': 'Сезон охоты',
        '142': 'Новый исход орды',
        '143': 'Контрабандисты (старое поле)',
        '144': 'Единство',
        '146': 'Распутье тайн',
        '147': 'Новогоднее дело',
        '148': 'Одиночный портал',
        '117': 'Поиск существ (портал)',
        '150': 'Контрабандисты (новое поле)',
        '151': 'Счёт наёмника',
        '152': 'Гробница'
    },
    'Сурвилурги': {
        '80': 'Защиты',
        '81': 'Захваты',
        '88': 'Перехваты'
    },
    'Турниры': {
        '14': 'Малый турнир',
        '134': 'Парный турнир',
        '113': 'Великое состязание',
        '137': 'Гильдии Лидеров',
        '141': 'На выживание',
        '68': 'Я не знаю что это за бои'
    },
    'Групповые бои': {
        '1': 'КСЗС (на 4)',
        '24': 'КСЗС (на 6)',
        '37': 'Командные',
        '21': 'Я не знаю что это за бои'
    },
    'Другое': {
        '104': 'Налоги',
        '89': 'КБО ПвП',
        '67': 'Нарушители границы',
        '126': 'Группа разбойников',
        '111': 'Армия холода',
        '44': 'Ночные кошмары'
    },
    'Неизвестное': {
        'unknown': 'Неизвестные типы боёв'
    }
};

// Хранилище для всех загруженных боёв
let allBattlesData = [];

// Определение типа страницы
function getPageType() {
    const path = window.location.pathname;
    if (path.includes('pl_warlog.php')) return 'warlog';
    if (path.includes('pl_transfers.php')) return 'transfers';
    if (path.includes('clan_log.php')) return 'clan_log';
    if (path.includes('sklad_log.php')) return 'sklad_log';
    return 'unknown';
}

// Получение ID из URL (универсальная функция)
function getEntityId() {
    const url = new URL(window.location.href);
    return url.searchParams.get('id');
}

// Получение номера текущей страницы
function getCurrentPage() {
    const url = new URL(window.location.href);
    const pageParam = url.searchParams.get('page');
    return pageParam ? parseInt(pageParam) : 0;
}

// Декодирование windows-1251 контента
function decodeWindows1251(buffer) {
    const decoder = new TextDecoder('windows-1251');
    return decoder.decode(buffer);
}

// Извлечение записей протокола с учетом HTML комментариев
function extractLogLines(html) {
    const lines = [];
    // Обновленный regex учитывает опциональные HTML комментарии перед   
    const regex = /(<!--\d+-->)?&nbsp;&nbsp;.*?<BR>/gi;
    const matches = html.match(regex);
    if (matches) {
        lines.push(...matches);
    }
    return lines;
}

// Построение URL для загрузки страницы с сохранением всех параметров
function buildPageUrl(pageType, entityId, pageNum) {
    // Получаем текущий URL
    const currentUrl = new URL(window.location.href);

    // Создаем новый URL на основе текущего
    const newUrl = new URL(currentUrl);

    // Обновляем только параметр page
    newUrl.searchParams.set('page', pageNum.toString());

    // Убеждаемся что id установлен правильно (на случай если его нет в текущем URL)
    if (entityId) {
        newUrl.searchParams.set('id', entityId);
    }

    console.log(`Построен URL для страницы ${pageNum}:`, newUrl.toString());

    return newUrl.toString();
}

// Получение названия типа протокола для отображения
function getLogTypeName(pageType) {
    switch (pageType) {
        case 'warlog': return 'протокола боёв';
        case 'transfers': return 'протокола передач';
        case 'clan_log': return 'протокола клана';
        case 'sklad_log': return 'протокола склада';
        default: return 'протокола';
    }
}

// Обновленная функция создания интерфейса
function createStatusDisplay() {
    const container = document.querySelector('.global_container_block_header');
    if (!container) return;

    const pageCount = localStorage.getItem('hwm_page_count') || '25';
    const pageType = getPageType();
    const logTypeName = getLogTypeName(pageType);

    const statusContainer = document.createElement('div');
    statusContainer.style.cssText = 'margin: 10px 0; text-align: center; background: #f0f0f0; padding: 8px; border-radius: 4px; display: flex; align-items: center; justify-content: center; gap: 10px;';

    const textContainer = document.createElement('div');
    textContainer.style.cssText = 'display: flex; align-items: center;';

    const beforeText = document.createElement('span');
    beforeText.textContent = 'Скрипт отображает ';
    beforeText.style.cssText = 'font-weight: bold; color: #333; margin-right: 5px;';

    const input = document.createElement('input');
    input.type = 'number';
    input.id = 'pageCount';
    input.min = '1';
    input.max = '100';
    input.value = pageCount;
    input.style.cssText = 'width: 60px; padding: 2px; text-align: center; margin-left: 5px; margin-right: 5px;';
    input.onchange = function() {
        localStorage.setItem('hwm_page_count', this.value);
    };

    const afterText = document.createElement('span');
    afterText.textContent = ' страниц';
    afterText.style.cssText = 'font-weight: bold; color: #333; margin-left: 5px;';

    const copyButton = document.createElement('button');
    copyButton.textContent = `Скопировать HTML код ${logTypeName}`;
    copyButton.style.cssText = 'padding: 4px 12px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 12px;';
    copyButton.onclick = function() {
        copyLogHTML(copyButton, logTypeName);
    };

    // Кнопка "Запуск" для перезагрузки страниц
    const reloadButton = document.createElement('button');
    reloadButton.textContent = 'Запуск';
    reloadButton.style.cssText = 'padding: 4px 12px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; margin-left: 10px;';
    reloadButton.onclick = function() {
        // Очищаем данные и запускаем загрузку заново
        allBattlesData = [];
        // Очищаем контейнер перед загрузкой (решает проблему №3)
        const logContainer = getLogContainer();
        if (logContainer) {
            logContainer.innerHTML = '';
        }
        loadPagesAutomatically(true);
    };

    textContainer.appendChild(beforeText);
    textContainer.appendChild(input);
    textContainer.appendChild(afterText);
    textContainer.appendChild(reloadButton);

    statusContainer.appendChild(textContainer);
    statusContainer.appendChild(copyButton);

    container.parentNode.insertBefore(statusContainer, container.nextSibling);
}

// Функция для сворачивания/разворачивания всех категорий
function toggleAllCategories(expand) {
    const toggles = document.querySelectorAll('.category-toggle');
    const contents = document.querySelectorAll('.subcategory-content');
    const newCollapseStates = {};
    
    // Устанавливаем состояние для всех категорий
    toggles.forEach(toggle => {
        if (expand) {
            toggle.classList.add('expanded');
        } else {
            toggle.classList.remove('expanded');
        }
    });
    
    contents.forEach(content => {
        const subcategoryBlock = content.closest('[data-category]');
        const subcategory = subcategoryBlock ? subcategoryBlock.dataset.category : null;
        
        if (expand) {
            content.classList.add('expanded');
            if (subcategory) {
                newCollapseStates[subcategory] = true;
            }
        } else {
            content.classList.remove('expanded');
            if (subcategory) {
                newCollapseStates[subcategory] = false;
            }
        }
    });
    
    // Сохраняем состояния
    localStorage.setItem('hwm_battle_filter_collapse', JSON.stringify(newCollapseStates));
}

// Функция создания интерфейса фильтрации боёв с сворачиваемыми подкатегориями
function createBattleFilterUI() {
    if (getPageType() !== 'warlog') return;
    
    // Получаем сохранённые настройки
    const savedPreferences = JSON.parse(localStorage.getItem('hwm_battle_filter') || '{}');
    
    // Получаем сохранённые состояния свёрнутости подкатегорий
    const savedCollapseStates = JSON.parse(localStorage.getItem('hwm_battle_filter_collapse') || '{}');
    
    // Создаём контейнер
    const filterContainer = document.createElement('div');
    filterContainer.id = 'battle-filter-container';
    filterContainer.style.cssText = 'margin: 10px 0; background: #f0f0f0; padding: 12px; border-radius: 4px;';
    
    // Добавляем CSS для треугольников свёрнутости
    const style = document.createElement('style');
    style.textContent = `
        .category-toggle {
            display: inline-block;
            width: 0;
            height: 0;
            margin-right: 8px;
            border-left: 6px solid #666;
            border-top: 6px solid transparent;
            border-bottom: 6px solid transparent;
            transition: transform 0.3s;
            cursor: pointer;
        }
        .category-toggle.expanded {
            transform: rotate(90deg);
        }
        .subcategory-header {
            display: flex;
            align-items: center;
            cursor: pointer;
            user-select: none;
        }
        .subcategory-content {
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.3s ease-out;
        }
        .subcategory-content.expanded {
            max-height: 1000px; /* Достаточно большое значение для содержимого */
        }
    `;
    document.head.appendChild(style);
    
    // Заголовок и кнопки управления
    const headerContainer = document.createElement('div');
    headerContainer.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;';
    
    const header = document.createElement('div');
    header.textContent = 'Фильтр боёв:';
    header.style.cssText = 'font-weight: bold; font-size: 14px;';
    
    const buttonsContainer = document.createElement('div');
    
    // Кнопки управления фильтром
    const expandAllBtn = document.createElement('button');
    expandAllBtn.textContent = 'Развернуть все';
    expandAllBtn.style.cssText = 'margin-right: 8px; padding: 4px 8px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer;';
    expandAllBtn.onclick = () => toggleAllCategories(true);
    
    const collapseAllBtn = document.createElement('button');
    collapseAllBtn.textContent = 'Свернуть все';
    collapseAllBtn.style.cssText = 'margin-right: 8px; padding: 4px 8px; background: #607D8B; color: white; border: none; border-radius: 3px; cursor: pointer;';
    collapseAllBtn.onclick = () => toggleAllCategories(false);
    
    const selectAllBtn = document.createElement('button');
    selectAllBtn.textContent = 'Выбрать все';
    selectAllBtn.style.cssText = 'margin-right: 8px; padding: 4px 8px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;';
    selectAllBtn.onclick = selectAllCategories;
    
    const deselectAllBtn = document.createElement('button');
    deselectAllBtn.textContent = 'Отменить все';
    deselectAllBtn.style.cssText = 'padding: 4px 8px; background: #F44336; color: white; border: none; border-radius: 3px; cursor: pointer;';
    deselectAllBtn.onclick = deselectAllCategories;
    
    buttonsContainer.appendChild(expandAllBtn);
    buttonsContainer.appendChild(collapseAllBtn);
    buttonsContainer.appendChild(selectAllBtn);
    buttonsContainer.appendChild(deselectAllBtn);
    
    headerContainer.appendChild(header);
    headerContainer.appendChild(buttonsContainer);
    filterContainer.appendChild(headerContainer);
    
    // Контейнер для подкатегорий
    const categoriesContainer = document.createElement('div');
    categoriesContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 15px;';
    filterContainer.appendChild(categoriesContainer);
    
    // Создаём блоки для каждой подкатегории
    for (const [subcategory, categories] of Object.entries(BATTLE_CATEGORIES)) {
        const subcategoryBlock = document.createElement('div');
        subcategoryBlock.style.cssText = 'background: white; padding: 10px; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); flex: 1; min-width: 200px;';
        subcategoryBlock.dataset.category = subcategory;
        
        // Заголовок подкатегории с треугольником
        const subcategoryHeader = document.createElement('div');
        subcategoryHeader.className = 'subcategory-header';

        // Добавляем треугольник для индикации свёрнутости
        const toggle = document.createElement('span');
        toggle.className = 'category-toggle';
        if (savedCollapseStates[subcategory]) {
            toggle.classList.add('expanded');
        }

        // Создаем чекбокс для выбора/снятия всех подкатегорий
        const toggleAllCheckbox = document.createElement('input');
        toggleAllCheckbox.type = 'checkbox';
        toggleAllCheckbox.className = 'toggle-all-subcategories';
        toggleAllCheckbox.dataset.category = subcategory;
        toggleAllCheckbox.style.marginRight = '5px';

        // Загружаем сохраненное состояние
        const savedToggleAllStates = JSON.parse(localStorage.getItem('hwm_battle_filter_toggle_all') || '{}');
        toggleAllCheckbox.checked = savedToggleAllStates[subcategory] !== false;

        // Добавляем обработчик события
        toggleAllCheckbox.addEventListener('change', function() {
            const isChecked = this.checked;
            const subcategory = this.dataset.category;
            const preferences = JSON.parse(localStorage.getItem('hwm_battle_filter') || '{}');
            
            // Получаем все чекбоксы этой категории
            const checkboxes = document.querySelectorAll(`div[data-category="${subcategory}"] .subcategory-content input[type="checkbox"]`);
            
            // Обновляем все чекбоксы
            checkboxes.forEach(checkbox => {
                checkbox.checked = isChecked;
                preferences[checkbox.dataset.category] = isChecked;
            });
            
            // Сохраняем состояние выбора всех
            const toggleAllStates = JSON.parse(localStorage.getItem('hwm_battle_filter_toggle_all') || '{}');
            toggleAllStates[subcategory] = isChecked;
            localStorage.setItem('hwm_battle_filter_toggle_all', JSON.stringify(toggleAllStates));
            
            // Сохраняем состояния фильтров
            localStorage.setItem('hwm_battle_filter', JSON.stringify(preferences));
            
            // Применяем фильтры
            filterBattles();
        });

        const titleText = document.createElement('span');
        titleText.textContent = subcategory;
        titleText.style.cssText = 'font-weight: bold; color: #444;';

        subcategoryHeader.appendChild(toggle);
        subcategoryHeader.appendChild(toggleAllCheckbox);
        subcategoryHeader.appendChild(titleText);
        subcategoryHeader.style.cssText = 'margin-bottom: 8px; border-bottom: 1px solid #eee; padding-bottom: 5px;';
        
        // Контейнер для содержимого подкатегории
        const contentContainer = document.createElement('div');
        contentContainer.className = 'subcategory-content';
        if (savedCollapseStates[subcategory]) {
            contentContainer.classList.add('expanded');
        }
        
        // Обработчик клика для сворачивания/разворачивания
        subcategoryHeader.addEventListener('click', function() {
            toggle.classList.toggle('expanded');
            contentContainer.classList.toggle('expanded');
            
            // Сохраняем состояние свёрнутости
            const newCollapseStates = JSON.parse(localStorage.getItem('hwm_battle_filter_collapse') || '{}');
            newCollapseStates[subcategory] = contentContainer.classList.contains('expanded');
            localStorage.setItem('hwm_battle_filter_collapse', JSON.stringify(newCollapseStates));
        });
        
        subcategoryBlock.appendChild(subcategoryHeader);
        subcategoryBlock.appendChild(contentContainer);
        
        // Чекбоксы для категорий
        for (const [categoryId, categoryName] of Object.entries(categories)) {
            const checkboxWrapper = document.createElement('label');
            checkboxWrapper.style.cssText = 'display: flex; align-items: center; margin: 5px 0;';
            
            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.value = categoryId;
            checkbox.dataset.category = categoryId;
            // Для категории "Неизвестное" проверяем значение по ключу 'unknown'
            if (subcategory === 'Неизвестное') {
                checkbox.checked = savedPreferences['unknown'] !== false;
            } else {
                checkbox.checked = savedPreferences[categoryId] !== false;
            }
            checkbox.addEventListener('change', updateCategoryPreference);
            
            const label = document.createElement('span');
            label.textContent = ' ' + categoryName;
            label.style.cssText = 'margin-left: 5px;';
            
            checkboxWrapper.appendChild(checkbox);
            checkboxWrapper.appendChild(label);
            contentContainer.appendChild(checkboxWrapper);
        }
        
        categoriesContainer.appendChild(subcategoryBlock);
    }
    
    // Счетчик отображаемых боёв
    const counterElem = document.createElement('div');
    counterElem.id = 'battleFilterCounter';
    counterElem.style.cssText = 'margin-top: 12px; font-style: italic; text-align: center;';
    counterElem.textContent = 'Загрузка...';
    filterContainer.appendChild(counterElem);
    
    // Добавляем фильтр на страницу
    const container = document.querySelector('.global_container_block_header');
    if (container) {
        container.parentNode.insertBefore(filterContainer, container.nextSibling);
    }
}

// Выбор всех категорий
function selectAllCategories() {
    const preferences = {};
    const checkboxes = document.querySelectorAll('#battle-filter-container input[type="checkbox"]');
    
    checkboxes.forEach(cb => {
        cb.checked = true;
        preferences[cb.dataset.category] = true;
    });
    
    localStorage.setItem('hwm_battle_filter', JSON.stringify(preferences));
    filterBattles();
}

// Отмена выбора всех категорий
function deselectAllCategories() {
    const preferences = {};
    const checkboxes = document.querySelectorAll('#battle-filter-container input[type="checkbox"]');
    
    checkboxes.forEach(cb => {
        cb.checked = false;
        preferences[cb.dataset.category] = false;
    });
    
    localStorage.setItem('hwm_battle_filter', JSON.stringify(preferences));
    filterBattles();
}

// Обновление предпочтения для категории
function updateCategoryPreference() {
    const preferences = JSON.parse(localStorage.getItem('hwm_battle_filter') || '{}');
    preferences[this.dataset.category] = this.checked;
    localStorage.setItem('hwm_battle_filter', JSON.stringify(preferences));
    filterBattles();
}

// Кешированная ссылка на контейнер лога
let cachedLogContainer = null;

// Улучшенная функция получения контейнера с протоколом
function getLogContainer() {
    // Если у нас есть кешированный контейнер и он всё ещё в DOM
    if (cachedLogContainer && document.contains(cachedLogContainer)) {
        return cachedLogContainer;
    }
    
    // Ищем контейнер заново
    const allContainers = document.querySelectorAll('.global_a_hover');
    
    // Перебираем контейнеры, ища тот, который содержит лог или был ранее использован
    for (const container of allContainers) {
        // Если контейнер содержит лог или просто имеет подходящего родителя
        if (container.innerHTML.includes('&nbsp;&nbsp;') || 
            container.innerHTML === "") {
            cachedLogContainer = container;
            return container;
        }
    }
    
    // Если ничего не нашли, берём первый контейнер
    if (allContainers.length > 0) {
        cachedLogContainer = allContainers[0];
        return cachedLogContainer;
    }
    
    return null;
}

// Обновленная функция автоматической загрузки
async function loadPagesAutomatically(includeCurrentPage = false) {
    const pageCount = parseInt(localStorage.getItem('hwm_page_count') || '25');
    const pageType = getPageType();
    const currentPage = getCurrentPage();
    const entityId = getEntityId();

    if (!entityId) {
        console.error('Не удается определить ID из URL');
        return;
    }

    console.log(`Загружаем ${pageType}, ID: ${entityId}, текущая страница: ${currentPage}`);

    const promises = [];

    // Начинаем с текущей страницы, если указан параметр includeCurrentPage
    const startIndex = includeCurrentPage ? 0 : 1;

    // Создаем промисы для загрузки каждой страницы
    for (let i = startIndex; i < pageCount; i++) {
        const pageNum = currentPage + i;
        const url = buildPageUrl(pageType, entityId, pageNum);

        const promise = fetch(url)
            .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}`);
                }
                return response.arrayBuffer();
            })
            .then(buffer => decodeWindows1251(buffer))
            .then(html => extractLogLines(html))
            .catch(error => {
                console.error(`Ошибка загрузки страницы ${pageNum}:`, error);
                return [];
            });

        promises.push(promise);
    }

    try {
        // Ждем загрузки всех страниц
        const results = await Promise.all(promises);

        // Находим правильный контейнер с записями
        const allContainers = document.querySelectorAll('.global_a_hover');
        const logContainer = getLogContainer();
        if (!logContainer) {
            console.error('Не удается найти контейнер с записями');
            return;
        }

        // Получаем существующие записи для проверки дубликатов
        const existingEntries = extractLogLines(logContainer.innerHTML);

        // Объединяем все новые записи
        const allNewEntries = results.flat();

        if (allNewEntries.length > 0) {
            // Фильтруем дубликаты
            const uniqueNewEntries = filterNewEntries(existingEntries, allNewEntries);

            if (uniqueNewEntries.length > 0) {
                // Если это протокол боёв, сохраняем все записи для фильтрации
                if (pageType === 'warlog') {
                    // Объединяем существующие и новые записи
                    allBattlesData = [...existingEntries, ...uniqueNewEntries];
                    
                    // Применяем фильтр к полному набору данных
                    filterBattles();
                    
                    console.log(`Добавлено ${uniqueNewEntries.length} новых уникальных записей`);
                    console.log(`Всего записей загружено: ${allBattlesData.length}`);
                } else {
                    // Для других типов протоколов, просто добавляем новые записи
                    uniqueNewEntries.forEach(entry => {
                        logContainer.insertAdjacentHTML('beforeend', entry + '\n');
                    });
                    
                    console.log(`Добавлено ${uniqueNewEntries.length} новых уникальных записей`);
                    console.log(`Всего записей загружено: ${allNewEntries.length}`);
                    console.log(`Отфильтровано дубликатов: ${allNewEntries.length - uniqueNewEntries.length}`);
                }
            } else {
                console.log('Новых уникальных записей не найдено');
            }
        } else {
            console.log('Не загружено записей с дополнительных страниц');
        }
    } catch (error) {
        console.error('Ошибка при загрузке страниц:', error);
    }
}

// Обновленная функция копирования (переименована для универсальности)
function copyLogHTML(button, logTypeName) {
    // Находим контейнер с протоколом
    const allContainers = document.querySelectorAll('.global_a_hover');
    const logContainer = getLogContainer();
    if (!logContainer) {
        alert(`Не удается найти контейнер с ${logTypeName}`);
        return;
    }

    // Получаем HTML и декодируем сущности
    const rawHtmlContent = logContainer.innerHTML;
    const htmlContent = replaceAmpEntities(rawHtmlContent);

    console.log(`Размер HTML контента ${logTypeName}:`, htmlContent.length);
    console.log('Первые 200 символов HTML:', htmlContent.substring(0, 200));
    console.log('Замен &amp; на &:', (rawHtmlContent.match(/&amp;/g) || []).length);

    // Пробуем GM_setClipboard
    let copySuccess = false;
    let method = '';

    try {
        GM_setClipboard(htmlContent);
        method = 'text';
        copySuccess = true;
        console.log('GM_setClipboard (text) выполнен');
    } catch (error) {
        console.error('Ошибка GM_setClipboard (text):', error);
    }

    if (!copySuccess) {
        try {
            GM_setClipboard(htmlContent, 'text');
            method = 'text explicit';
            copySuccess = true;
            console.log('GM_setClipboard (text explicit) выполнен');
        } catch (error) {
            console.error('Ошибка GM_setClipboard (text explicit):', error);
        }
    }

    // Проверяем результат через небольшую задержку
    setTimeout(() => {
        checkClipboardContent(copySuccess, method, htmlContent, button, logTypeName);
    }, 100);
}

function checkClipboardContent(copySuccess, method, originalContent, button, logTypeName) {
    if (navigator.clipboard && navigator.clipboard.readText) {
        navigator.clipboard.readText().then(clipboardText => {
            console.log('Содержимое буфера обмена:', clipboardText.length, 'символов');

            if (clipboardText.length > 0) {
                if (clipboardText === originalContent || clipboardText.length > 1000) {
                    console.log('✅ HTML код корректно скопирован');
                    button.style.background = '#4CAF50';
                    button.textContent = 'HTML скопирован!';
                } else {
                    console.log('⚠️ В буфере другой контент');
                    showHTMLModal(originalContent, button, logTypeName);
                    return;
                }
            } else {
                console.log('❌ Буфер обмена пустой');
                showHTMLModal(originalContent, button, logTypeName);
                return;
            }

            setTimeout(() => {
                button.style.background = '#2196F3';
                button.textContent = `Скопировать HTML код ${logTypeName}`;
            }, 3000);

        }).catch(error => {
            console.log('Не удалось прочитать буфер обмена:', error);
            if (copySuccess) {
                button.style.background = '#4CAF50';
                button.textContent = 'HTML скопирован!';
                setTimeout(() => {
                    button.style.background = '#2196F3';
                    button.textContent = `Скопировать HTML код ${logTypeName}`;
                }, 3000);
            } else {
                showHTMLModal(originalContent, button, logTypeName);
            }
        });
    } else {
        if (copySuccess) {
            console.log('Clipboard API недоступен, но GM_setClipboard выполнен');
            button.style.background = '#4CAF50';
            button.textContent = 'HTML скопирован!';
            setTimeout(() => {
                button.style.background = '#2196F3';
                button.textContent = `Скопировать HTML код ${logTypeName}`;
            }, 3000);
        } else {
            showHTMLModal(originalContent, button, logTypeName);
        }
    }
}

// Простая функция замены &amp; на &
function replaceAmpEntities(text) {
    return text.replace(/&amp;/g, '&');
}

// Проверка идентичности записей (точное совпадение)
function areEntriesIdentical(entry1, entry2) {
    return entry1.trim() === entry2.trim();
}

// Фильтрация новых записей, исключение дубликатов
function filterNewEntries(existingEntries, newEntries) {
    const uniqueNewEntries = [];

    for (const newEntry of newEntries) {
        let isDuplicate = false;

        for (const existingEntry of existingEntries) {
            if (areEntriesIdentical(newEntry, existingEntry)) {
                isDuplicate = true;
                break;
            }
        }

        if (!isDuplicate) {
            // Также проверяем против уже добавленных новых записей
            let isAlreadyAdded = false;
            for (const addedEntry of uniqueNewEntries) {
                if (areEntriesIdentical(newEntry, addedEntry)) {
                    isAlreadyAdded = true;
                    break;
                }
            }

            if (!isAlreadyAdded) {
                uniqueNewEntries.push(newEntry);
            }
        }
    }

    return uniqueNewEntries;
}

// Полная функция модального окна
function showHTMLModal(htmlContent, button, logTypeName) {
    const modal = document.createElement('div');
    modal.style.cssText = `
        position: fixed; top: 0; left: 0; width: 100%; height: 100%;
        background: rgba(0,0,0,0.8); z-index: 10000; display: flex;
        align-items: center; justify-content: center;
    `;

    const content = document.createElement('div');
    content.style.cssText = `
        background: white; padding: 20px; border-radius: 8px;
        max-width: 90%; max-height: 90%; overflow: auto;
    `;

    const title = document.createElement('h3');
    title.textContent = `HTML код ${logTypeName}`;
    title.style.cssText = 'margin-top: 0; color: #333;';

    const instruction = document.createElement('p');
    instruction.textContent = 'Выделите весь текст (Ctrl+A) и скопируйте (Ctrl+C):';
    instruction.style.cssText = 'color: #666; margin-bottom: 10px;';

    const textarea = document.createElement('textarea');
    textarea.value = htmlContent;
    textarea.style.cssText = `
        width: 700px; height: 400px; font-family: monospace; font-size: 12px;
        border: 1px solid #ccc; padding: 10px; resize: both;
    `;

    const buttonContainer = document.createElement('div');
    buttonContainer.style.cssText = 'text-align: center; margin-top: 15px;';

    const selectAllBtn = document.createElement('button');
    selectAllBtn.textContent = 'Выделить всё';
    selectAllBtn.style.cssText = 'margin-right: 10px; padding: 8px 16px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;';
    selectAllBtn.onclick = () => {
        textarea.select();
        textarea.setSelectionRange(0, textarea.value.length);
    };

    const closeBtn = document.createElement('button');
    closeBtn.textContent = 'Закрыть';
    closeBtn.style.cssText = 'padding: 8px 16px; background: #666; color: white; border: none; border-radius: 4px; cursor: pointer;';
    closeBtn.onclick = () => document.body.removeChild(modal);

    // Собираем модальное окно
    buttonContainer.appendChild(selectAllBtn);
    buttonContainer.appendChild(closeBtn);

    content.appendChild(title);
    content.appendChild(instruction);
    content.appendChild(textarea);
    content.appendChild(buttonContainer);

    modal.appendChild(content);
    document.body.appendChild(modal);

    // Автоматически выделяем весь текст
    setTimeout(() => {
        textarea.focus();
        textarea.select();
    }, 100);

    // Обновляем кнопку
    button.style.background = '#FFA500';
    button.textContent = 'Окно открыто';
    setTimeout(() => {
        button.style.background = '#2196F3';
        button.textContent = `Скопировать HTML код ${logTypeName}`;
    }, 3000);
}

// Функция фильтрации боёв
function filterBattles() {
    if (getPageType() !== 'warlog' || !allBattlesData.length) return;
    
    // Получаем предпочтения и проверяем, есть ли хотя бы одна активная категория
    const preferences = JSON.parse(localStorage.getItem('hwm_battle_filter') || '{}');
    let hasActiveCategories = false;
    
    // Проверяем все категории во всех подкатегориях
    for (const subcategory of Object.values(BATTLE_CATEGORIES)) {
        for (const categoryId of Object.keys(subcategory)) {
            if (preferences[categoryId] !== false) {
                hasActiveCategories = true;
                break;
            }
        }
        if (hasActiveCategories) break;
    }
    
    // Результирующий HTML для отображения
    let filteredHTML = '';
    let totalBattles = 0;
    let shownBattles = 0;
    
    // Если все фильтры отключены, показываем сообщение
    if (!hasActiveCategories) {
        const logContainer = getLogContainer();
        if (logContainer) {
            logContainer.innerHTML = '<div style="padding: 20px; text-align: center; color: #666;">Все фильтры отключены. Выберите хотя бы одну категорию боёв.</div>';
            
            // Обновляем счетчик
            const counterElem = document.getElementById('battleFilterCounter');
            if (counterElem) {
                counterElem.textContent = `Показано 0 из ${allBattlesData.length} боёв`;
            }
        }
        return;
    }
    
    // Проходим по всем записям боёв
    for (const battleEntry of allBattlesData) {
        totalBattles++;
        
        // Извлекаем категорию боя
        const categoryMatch = battleEntry.match(/<!--(\d+)-->/);
        const category = categoryMatch ? categoryMatch[1] : 'unknown';
        
        // Находим, к какой категории относится этот бой
        let foundInCategory = false;
        for (const [subcategoryName, subcategories] of Object.entries(BATTLE_CATEGORIES)) {
            if (subcategoryName !== 'Неизвестное' && subcategories[category]) {
                foundInCategory = true;
                break;
            }
        }
        
        // Определяем, нужно ли показывать бой
        let shouldShowBattle;
        if (foundInCategory) {
            shouldShowBattle = preferences[category] !== false;
        } else {
            shouldShowBattle = preferences['unknown'] !== false;
        }
        
        // Добавляем бой, если он проходит фильтр
        if (shouldShowBattle) {
            filteredHTML += battleEntry + '\n';
            shownBattles++;
        }
    }
    
    // Обновляем отображение
    const logContainer = getLogContainer();
    if (logContainer) {
        logContainer.innerHTML = filteredHTML || '<div style="padding: 20px; text-align: center; color: #666;">Нет боёв, соответствующих выбранным фильтрам.</div>';
        
        // Обновляем счетчик
        const counterElem = document.getElementById('battleFilterCounter');
        if (counterElem) {
            counterElem.textContent = `Показано ${shownBattles} из ${totalBattles} боёв`;
        }
    }
}

// Обновление отображения протокола боёв
function updateBattleLogDisplay(battles) {
    // Находим контейнер с записями
    const allContainers = document.querySelectorAll('.global_a_hover');
    const logContainer = getLogContainer();
    if (!logContainer) return;
    
    // Обновляем HTML контейнера
    logContainer.innerHTML = battles.join('\n');
}

// Initialize when page loads
function init() {
    // Wait for page to fully load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            createStatusDisplay();
            createBattleFilterUI(); // Добавляем создание фильтра
            loadPagesAutomatically();
        });
    } else {
        createStatusDisplay();
        createBattleFilterUI(); // Добавляем создание фильтра
        loadPagesAutomatically();
    }
}


init();

})();