Открыть меню
Переключить меню настроек
Открыть персональное меню
Вы не представились системе
Ваш IP-адрес будет виден всем, если вы внесёте какие-либо изменения.

MediaWiki:Common.js: различия между версиями

Страница интерфейса MediaWiki
Нет описания правки
Метка: отменено
Нет описания правки
Метка: отменено
Строка 1: Строка 1:
/**
* Если CDN недоступен: залей на вики popper.min.js, tippy-bundle.umd.min.js, tippy.css (через Загрузить файл),
* затем в консоли или в начале скрипта задай:
*  MediaWikiTooltips.baseUrl = "https://fire.station.wiki.shizainc.com/wiki/Special:FilePath/";
* (подставь свой домен вики)
*/
(function () {
(function () {
     "use strict";
     "use strict";
Строка 10: Строка 4:
     window.MediaWikiTooltips = {
     window.MediaWikiTooltips = {
         loaded: false,
         loaded: false,
        loading: false,
         callbacks: [],
         callbacks: [],
        baseUrl: null,


         load: function (callback) {
         load: function (callback) {
Строка 26: Строка 18:


             var self = this;
             var self = this;
             var cdns = [
             var popperScript = document.createElement("script");
                 {
            popperScript.src =
                    base: "https://cdnjs.cloudflare.com/ajax/libs",
                 "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js";
                    popper: "/popper.js/2.11.8/umd/popper.min.js",
             popperScript.onload = function () {
                    tippy: "/tippy.js/6.3.7/tippy-bundle.umd.min.js",
                    css: "/tippy.js/6.3.7/tippy.css",
                },
            ];
             if (self.baseUrl) {
                var b = self.baseUrl.replace(/\/?$/, "/");
                cdns = [
                    {
                        base: b,
                        popper: "popper.min.js",
                        tippy: "tippy-bundle.umd.min.js",
                        css: "tippy.css",
                    },
                ];
            }
            var cdnIndex = 0;
 
            var tryFallback = function (which) {
                self.loading = false;
                self.callbacks = [];
            };
 
            var runCallbacks = function () {
                self.loaded = true;
                self.loading = false;
                self.callbacks.forEach(function (cb) {
                    try { cb(); } catch (e) {}
                });
                self.callbacks = [];
            };
 
            function urlJoin(base, path) {
                return base.replace(/\/$/, "") + "/" + path.replace(/^\//, "");
            }
 
            function loadTippyAndCss() {
                var cdn = cdns[cdnIndex];
                 var tippyScript = document.createElement("script");
                 var tippyScript = document.createElement("script");
                 tippyScript.src = urlJoin(cdn.base, cdn.tippy);
                 tippyScript.src =
                tippyScript.onerror = function () {
                    "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.7/tippy-bundle.umd.min.js";
                    if (cdnIndex + 1 < cdns.length) {
                        cdnIndex++;
                        loadPopper();
                    } else {
                        tryFallback("Tippy.js");
                    }
                };
                 tippyScript.onload = function () {
                 tippyScript.onload = function () {
                     if (!window.tippy) {
                     self.loaded = true;
                        if (cdnIndex + 1 < cdns.length) {
                     self.loading = false;
                            cdnIndex++;
                     self.callbacks.forEach(function (cb) {
                            loadPopper();
                        cb();
                        } else {
                     });
                            tryFallback("Tippy.js (не в глобальной области)");
                     self.callbacks = [];
                        }
                        return;
                    }
                    var tippyCSS = document.createElement("link");
                     tippyCSS.rel = "stylesheet";
                     tippyCSS.href = urlJoin(cdn.base, cdn.css);
                    tippyCSS.onerror = function () {};
                     document.head.appendChild(tippyCSS);
                     runCallbacks();
                 };
                 };
                 document.head.appendChild(tippyScript);
                 document.head.appendChild(tippyScript);
             }
             };
 
             document.head.appendChild(popperScript);
             function loadPopper() {
                var cdn = cdns[cdnIndex];
                var popperScript = document.createElement("script");
                popperScript.src = urlJoin(cdn.base, cdn.popper);
                popperScript.onerror = function () {
                    if (cdnIndex + 1 < cdns.length) {
                        cdnIndex++;
                        loadPopper();
                    } else {
                        tryFallback("Popper.js");
                    }
                };
                popperScript.onload = loadTippyAndCss;
                document.head.appendChild(popperScript);
            }


             loadPopper();
             var tippyCSS = document.createElement("link");
            tippyCSS.rel = "stylesheet";
            tippyCSS.href =
                "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.7/tippy.css";
            document.head.appendChild(tippyCSS);
         },
         },
     };
     };
Строка 194: Строка 122:


     var LawTooltipsModule = {
     var LawTooltipsModule = {
         init: function() {
         init: function () {
             var tableCells = document.querySelectorAll('.law-tooltips td, .law-tooltips th');
            console.log("[LawTooltips] init()");
             if (tableCells.length === 0) return;
             var tableCells = document.querySelectorAll(
              
                ".law-tooltips td, .law-tooltips th",
             MediaWikiTooltips.load(function() {
            );
            console.log(
                "[LawTooltips] .law-tooltips ячеек:",
                tableCells.length,
            );
             if (tableCells.length === 0) {
                console.warn("[LawTooltips] Нет ячеек .law-tooltips — выход");
                return;
             }
 
             function run() {
                console.log("[LawTooltips] run() — извлечение данных...");
                 var lawData = LawTooltipsModule.extractLawData();
                 var lawData = LawTooltipsModule.extractLawData();
                 LawTooltipsModule.initCellTooltips(tableCells, lawData);
                 var count = Object.keys(lawData).length;
                console.log(
                    "[LawTooltips] Загружено записей законов:",
                    count,
                    count
                        ? Object.keys(lawData).slice(0, 10).join(", ") +
                              (count > 10 ? "..." : "")
                        : "",
                );
                if (count > 0) {
                    LawTooltipsModule.initCellTooltips(tableCells, lawData);
                    return true;
                }
                return false;
            }
 
            if (typeof MediaWikiTooltips === "undefined") {
                console.error(
                    "[LawTooltips] MediaWikiTooltips не найден — тултипы не загрузятся",
                );
                run();
                return;
            }
            MediaWikiTooltips.load(function () {
                console.log("[LawTooltips] MediaWikiTooltips.load вызван");
                if (run()) {
                    console.log("[LawTooltips] Инициализация с первой попытки");
                    return;
                }
                setTimeout(function () {
                    if (run()) {
                        console.log("[LawTooltips] Инициализация после 300ms");
                        return;
                    }
                    setTimeout(function () {
                        if (run())
                            console.log(
                                "[LawTooltips] Инициализация после 800ms",
                            );
                        else
                            console.warn(
                                "[LawTooltips] Данные законов не найдены после всех попыток",
                            );
                    }, 500);
                }, 300);
            });
        },
 
        extractLawData: function () {
            var lawData = {};
            var containers = document.querySelectorAll(".mw-collapsible");
            console.log(
                "[LawTooltips] extractLawData: коллапсибл-блоков:",
                containers.length,
            );
            containers.forEach(function (block) {
                var content = block.querySelector(".mw-collapsible-content");
                if (content && block.classList.contains("mw-collapsed")) {
                    block.classList.remove("mw-collapsed");
                    var toggle = content.querySelector(
                        ".mw-collapsible-toggle",
                    );
                    if (toggle) toggle.setAttribute("aria-expanded", "true");
                }
            });
 
            var detailTables = document.querySelectorAll(
                ".mw-collapsible-content table",
            );
            console.log(
                "[LawTooltips] extractLawData: таблиц в .mw-collapsible-content:",
                detailTables.length,
            );
            detailTables.forEach(function (table, tableIndex) {
                if (table.classList.contains("law-tooltips")) {
                    console.log(
                        "[LawTooltips] extractLawData: таблица",
                        tableIndex,
                        "— пропуск (law-tooltips)",
                    );
                    return;
                }
                var rows = table.querySelectorAll("tr");
 
                rows.forEach(function (row) {
                    var cells = row.querySelectorAll("td");
                    if (cells.length < 3) return;
 
                    var crimeCell = cells[0];
                    var descriptionCell = cells[1];
                    var exampleCell = cells[2];
 
                    var anchor = crimeCell.querySelector("[id]");
                    if (!anchor) return;
 
                    var lawCode = anchor.id;
                    var titleElement = crimeCell.querySelector("b");
                    if (!titleElement || !lawCode.match(/^\d{3}$/)) return;
 
                    var titleText = titleElement.textContent.trim();
                    var lawTitle = titleText
                        .replace(/^\d{3}\s*/, "")
                        .replace(/^\d{3}/, "")
                        .trim();
                    var description = descriptionCell.innerHTML.trim();
 
                    var examples = [];
                    var exampleItems = exampleCell.querySelectorAll("li");
                    exampleItems.forEach(function (item) {
                        var html = item.innerHTML
                            .trim()
                            .replace(/<br\s*\/?>/gi, "");
                        if (html) examples.push(html);
                    });
 
                    lawData[lawCode] = {
                        title: lawTitle,
                        description: description,
                        examples: examples,
                    };
                });
             });
             });
            console.log(
                "[LawTooltips] extractLawData: итого кодов:",
                Object.keys(lawData).length,
            );
            return lawData;
         },
         },
       
 
         extractLawData: function() {
         createTooltipContent: function (lawCode, lawData) {
    var lawData = {};
            var data = lawData[lawCode];
    var detailTables = document.querySelectorAll('.mw-collapsible-content table');
            if (!data) return null;
   
 
    detailTables.forEach(function(table) {
            var html = '<div class="law-tooltip">';
        var rows = table.querySelectorAll('tr');
            html +=
       
                '<h4 class="law-tooltip-title">' +
        rows.forEach(function(row) {
                lawCode +
            var cells = row.querySelectorAll('td');
                " - " +
            if (cells.length < 3) return;
                data.title +
           
                "</h4>";
            var crimeCell = cells[0];
            html +=
            var descriptionCell = cells[1];
                '<p class="law-tooltip-description">' +
            var exampleCell = cells[2];
                data.description +
           
                "</p>";
            var anchor = crimeCell.querySelector('[id]');
 
            if (!anchor) return;
            if (data.examples && data.examples.length > 0) {
           
                html += '<div class="law-tooltip-examples">';
            var lawCode = anchor.id;
                html += "<strong>Примеры:</strong><ul>";
            var titleElement = crimeCell.querySelector('b');
                data.examples.slice(0, 5).forEach(function (example) {
           
                    html += "<li>" + example + "</li>";
            if (titleElement && lawCode.match(/^\d{3}$/)) {
                });
                var titleText = titleElement.textContent.trim();
                html += "</ul></div>";
                var lawTitle = titleText.replace(/^\d{3}\s*/, '').replace(/^\d{3}/, '').trim();
            }
                var description = descriptionCell.innerHTML.trim();
            html += "</div>";
               
 
                var examples = [];
            return html;
                var exampleItems = exampleCell.querySelectorAll('li');
        },
exampleItems.forEach(function(item) {
 
    var html = item.innerHTML.trim();
         initCellTooltips: function (tableCells, lawData) {
    // Удаляем все <br> теги
            var withLink = 0,
    html = html.replace(/<br\s*\/?>/gi, '');
                withMatch = 0,
    if (html) examples.push(html);
                withContent = 0,
});
                tooltipsCreated = 0;
               
             tableCells.forEach(function (cell) {
                lawData[lawCode] = {
                    title: lawTitle,
                    description: description,
                    examples: examples
                };
            }
        });
    });
   
    return lawData;
},
       
    createTooltipContent: function(lawCode, lawData) {
    var data = lawData[lawCode];
    if (!data) return null;
   
    var html = '<div class="law-tooltip">';
    html += '<h4 class="law-tooltip-title">' + lawCode + ' - ' + data.title + '</h4>';
    html += '<p class="law-tooltip-description">' + data.description + '</p>';
   
    if (data.examples && data.examples.length > 0) {
        html += '<div class="law-tooltip-examples">';
        html += '<strong>Примеры:</strong><ul>';
        data.examples.slice(0, 5).forEach(function(example) {
            html += '<li>' + example + '</li>'; // Теперь example содержит HTML
        });
        html += '</ul></div>';
    }
    html += '</div>';
   
    return html;
},
       
         initCellTooltips: function(tableCells, lawData) {
             tableCells.forEach(function(cell) {
                 var link = cell.querySelector('a[href*="#"]');
                 var link = cell.querySelector('a[href*="#"]');
                 if (!link) return;
                 if (!link) return;
                  
                 withLink++;
                 var href = link.getAttribute('href');
 
                 var href = link.getAttribute("href");
                 var match = href.match(/#(\d{3})/);
                 var match = href.match(/#(\d{3})/);
                 if (!match) return;
                 if (!match) return;
                  
                 withMatch++;
 
                 var lawCode = match[1];
                 var lawCode = match[1];
                 var tooltipContent = LawTooltipsModule.createTooltipContent(lawCode, lawData);
                 var tooltipContent = LawTooltipsModule.createTooltipContent(
                    lawCode,
                    lawData,
                );
                 if (!tooltipContent) return;
                 if (!tooltipContent) return;
                  
                 withContent++;
                 cell.classList.add('law-cell-with-tooltip');
 
                  
                 cell.classList.add("law-cell-with-tooltip");
                 cell.addEventListener('click', function(e) {
                 tooltipsCreated++;
 
                 cell.addEventListener("click", function (e) {
                     e.preventDefault();
                     e.preventDefault();
                     window.location.hash = lawCode;
                     window.location.hash = lawCode;
                 });
                 });
                  
 
                 if (typeof tippy === "undefined") {
                    console.error(
                        "[LawTooltips] tippy не найден — подключи библиотеку Tippy.js",
                    );
                    return;
                }
                 tippy(cell, {
                 tippy(cell, {
                     content: tooltipContent,
                     content: tooltipContent,
                     allowHTML: true,
                     allowHTML: true,
                     interactive: true,
                     interactive: true,
                     placement: 'top',
                     placement: "top",
                     maxWidth: 400,
                     maxWidth: 400,
                     theme: 'law-theme',
                     theme: "law-theme",
                     arrow: true,
                     arrow: true,
                     duration: [200, 150],
                     duration: [200, 150],
                     delay: [0, 50],
                     delay: [0, 50],
                     appendTo: document.body,
                     appendTo: document.body,
                     boundary: 'viewport',
                     boundary: "viewport",
                     popperOptions: {
                     popperOptions: {
                         strategy: 'fixed',
                         strategy: "fixed",
                         modifiers: [
                         modifiers: [
                             {
                             {
                                 name: 'preventOverflow',
                                 name: "preventOverflow",
                                 options: {
                                 options: {
                                     boundary: 'viewport',
                                     boundary: "viewport",
                                     padding: 20,
                                     padding: 20,
                                     altAxis: true,
                                     altAxis: true,
                                     altBoundary: true
                                     altBoundary: true,
                                 }
                                 },
                             },
                             },
                             {
                             {
                                 name: 'flip',
                                 name: "flip",
                                 options: {
                                 options: {
                                     fallbackPlacements: ['bottom', 'left', 'right']
                                     fallbackPlacements: [
                                 }
                                        "bottom",
                                        "left",
                                        "right",
                                    ],
                                 },
                             },
                             },
                             {
                             {
                                 name: 'computeStyles',
                                 name: "computeStyles",
                                 options: { adaptive: false }
                                 options: { adaptive: false },
                             }
                             },
                         ]
                        ],
                    },
                    onShow: function (instance) {
                         document
                            .querySelectorAll("[data-tippy-root]")
                            .forEach(function (tooltip) {
                                if (
                                    tooltip._tippy &&
                                    tooltip._tippy !== instance
                                ) {
                                    tooltip._tippy.hide();
                                }
                            });
                     },
                     },
                    onShow: function(instance) {
                        document.querySelectorAll('[data-tippy-root]').forEach(function(tooltip) {
                            if (tooltip._tippy && tooltip._tippy !== instance) {
                                tooltip._tippy.hide();
                            }
                        });
                    }
                 });
                 });
             });
             });
         }
            console.log(
                "[LawTooltips] initCellTooltips: ячеек с ссылкой",
                withLink,
                "| с #XXX",
                withMatch,
                "| с данными",
                withContent,
                "| тултипов создано",
                tooltipsCreated,
            );
         },
     };
     };


Строка 1015: Строка 1077:
         stopTimeUpdates: function () {
         stopTimeUpdates: function () {
             DocumentAutoFillModule.stopTimeUpdateTimer();
             DocumentAutoFillModule.stopTimeUpdateTimer();
        },
    };
    var LawCalculatorModule = {
        isCalculatorMode: false,
        selectedViolations: [],
        lawData: {},
        init: function () {
            this.extractLawData();
            this.createModeToggle();
            this.observeTables();
        },
        extractLawData: function () {
            // Используем данные из существующего LawTooltipsModule
            if (
                window.LawTooltipsModule &&
                window.LawTooltipsModule.extractLawData
            ) {
                this.lawData = window.LawTooltipsModule.extractLawData();
            } else {
                // Fallback - извлекаем данные самостоятельно
                this.lawData = this.extractLawDataFallback();
            }
            // Добавляем информацию о категориях и сроках
            this.addCategoryInfo();
        },
        extractLawDataFallback: function () {
            var lawData = {};
            var detailTables = document.querySelectorAll(
                ".mw-collapsible-content table",
            );
            detailTables.forEach(function (table) {
                var rows = table.querySelectorAll("tr");
                rows.forEach(function (row) {
                    var cells = row.querySelectorAll("td");
                    if (cells.length < 3) return;
                    var crimeCell = cells[0];
                    var anchor = crimeCell.querySelector("[id]");
                    if (!anchor) return;
                    var lawCode = anchor.id;
                    var titleElement = crimeCell.querySelector("b");
                    if (titleElement && lawCode.match(/^\d{3}$/)) {
                        var titleText = titleElement.textContent.trim();
                        var lawTitle = titleText
                            .replace(/^\d{3}\s*/, "")
                            .replace(/^\d{3}/, "")
                            .trim();
                        var description = cells[1].innerHTML.trim();
                        var examples = [];
                        var exampleItems = cells[2].querySelectorAll("li");
                        exampleItems.forEach(function (item) {
                            var html = item.innerHTML.trim();
                            html = html.replace(/<br\s*\/?>/gi, "");
                            if (html) examples.push(html);
                        });
                        lawData[lawCode] = {
                            title: lawTitle,
                            description: description,
                            examples: examples,
                        };
                    }
                });
            });
            return lawData;
        },
        addCategoryInfo: function () {
            // Определяем тип страницы и извлекаем информацию о наказаниях
            this.pageType = this.detectPageType();
            this.extractPunishmentInfo();
        },
        detectPageType: function () {
            // Проверяем заголовки страницы для определения типа
            var pageTitle = document.title.toLowerCase();
            var headings = document.querySelectorAll("h1, h2, h3");
            for (var i = 0; i < headings.length; i++) {
                var headingText = headings[i].textContent.toLowerCase();
                if (
                    headingText.includes("д-класс") ||
                    headingText.includes("d-class")
                ) {
                    return "d-class";
                }
                if (
                    headingText.includes("персонал") ||
                    headingText.includes("сотрудник")
                ) {
                    return "personnel";
                }
            }
            // Проверяем по URL или другим признакам
            if (
                window.location.href.toLowerCase().includes("d-class") ||
                window.location.href.toLowerCase().includes("д-класс")
            ) {
                return "d-class";
            }
            return "personnel"; // По умолчанию
        },
        extractPunishmentInfo: function () {
            // Извлекаем информацию о наказаниях из текста на странице
            var punishmentSections = document.querySelectorAll(
                ".mw-collapsible-content p",
            );
            var categories = {};
            punishmentSections.forEach(
                function (section) {
                    var text = section.textContent;
                    var punishmentMatch = text.match(
                        /Наказание[:\s]*(.+?)(?:\n|$)/i,
                    );
                    if (punishmentMatch) {
                        var punishmentText = punishmentMatch[1].trim();
                        var categoryInfo =
                            this.parsePunishmentText(punishmentText);
                        if (categoryInfo) {
                            // Находим соответствующую категорию по цвету или другим признакам
                            var categoryKey =
                                this.findCategoryByContext(section);
                            if (categoryKey) {
                                categories[categoryKey] = categoryInfo;
                            }
                        }
                    }
                }.bind(this),
            );
            // Применяем найденную информацию к законам
            this.applyCategoryInfo(categories);
        },
        parsePunishmentText: function (text) {
            // Парсим текст наказания
            var originalText = text;
            text = text.toLowerCase();
            // Проверяем на критические нарушения
            if (
                text.includes("д-класс") ||
                text.includes("казнь") ||
                text.includes("перевод")
            ) {
                return {
                    isCritical: true,
                    punishment: originalText.trim(),
                };
            }
            // Для Д-класса ищем штрафы
            if (this.pageType === "d-class") {
                return this.parseDClassPunishment(originalText);
            }
            // Для персонала ищем временные рамки
            return this.parsePersonnelPunishment(originalText);
        },
        parseDClassPunishment: function (text) {
            // Парсим наказания для Д-класса (штрафы, дополнительные наказания)
            var lowerText = text.toLowerCase();
            // Ищем штрафы в кредитах
            var creditMatch = text.match(/(\d+[\s,.]?\d*)\s*кредит/);
            if (creditMatch) {
                var amount = creditMatch[1].replace(/[\s,]/g, "");
                return {
                    isCritical: false,
                    punishment: "Штраф " + amount + " кредитов",
                    fine: parseInt(amount),
                };
            }
            // Ищем временные наказания для Д-класса
            var timeMatch = text.match(/(\d+)\s*до\s*(\d+)\s*минут/);
            if (timeMatch) {
                return {
                    minTime: parseInt(timeMatch[1]),
                    maxTime: parseInt(timeMatch[2]),
                    isCritical: false,
                    punishment: timeMatch[1] + "-" + timeMatch[2] + " минут",
                };
            }
            var rangeMatch = text.match(/от\s*(\d+)\s*до\s*(\d+)\s*минут/);
            if (rangeMatch) {
                return {
                    minTime: parseInt(rangeMatch[1]),
                    maxTime: parseInt(rangeMatch[2]),
                    isCritical: false,
                    punishment: rangeMatch[1] + "-" + rangeMatch[2] + " минут",
                };
            }
            var fixedMatch = text.match(/(\d+)\s*минут/);
            if (fixedMatch) {
                var time = parseInt(fixedMatch[1]);
                return {
                    minTime: time,
                    maxTime: time,
                    isCritical: false,
                    punishment: time + " минут",
                };
            }
            // Если не нашли конкретные наказания, возвращаем текст как есть
            return {
                isCritical: false,
                punishment: text.trim(),
            };
        },
        parsePersonnelPunishment: function (text) {
            // Парсим наказания для персонала
            var lowerText = text.toLowerCase();
            // Ищем временные рамки
            var timeMatch = text.match(/(\d+)\s*до\s*(\d+)\s*минут/);
            if (timeMatch) {
                return {
                    minTime: parseInt(timeMatch[1]),
                    maxTime: parseInt(timeMatch[2]),
                    isCritical: false,
                };
            }
            // Ищем диапазон времени
            var rangeMatch = text.match(/от\s*(\d+)\s*до\s*(\d+)\s*минут/);
            if (rangeMatch) {
                return {
                    minTime: parseInt(rangeMatch[1]),
                    maxTime: parseInt(rangeMatch[2]),
                    isCritical: false,
                };
            }
            // Ищем фиксированное время
            var fixedMatch = text.match(/(\d+)\s*минут/);
            if (fixedMatch) {
                var time = parseInt(fixedMatch[1]);
                return {
                    minTime: time,
                    maxTime: time,
                    isCritical: false,
                };
            }
            return null;
        },
        findCategoryByContext: function (section) {
            // Находим категорию по контексту (заголовок секции)
            var collapsible = section.closest(".mw-collapsible");
            if (!collapsible) return null;
            var toggle = collapsible.querySelector(".mw-collapsible-toggle");
            if (!toggle) return null;
            var titleElement = toggle.querySelector(".рамка-название");
            if (!titleElement) return null;
            var title = titleElement.textContent.trim();
            // Определяем диапазон статей по заголовку
            if (title.includes("Лёгкие")) return "100-109";
            if (title.includes("Средние")) return "200-211";
            if (title.includes("Тяжкие") && !title.includes("Особо"))
                return "300-311";
            if (title.includes("Особо тяжкие")) return "400-411";
            if (title.includes("Критические")) return "500-511";
            return null;
        },
        applyCategoryInfo: function (categories) {
            // Применяем информацию о категориях к законам
            Object.keys(this.lawData).forEach(
                function (lawCode) {
                    var code = parseInt(lawCode);
                    var categoryKey = null;
                    // Определяем категорию по номеру статьи
                    if (code >= 100 && code <= 109) categoryKey = "100-109";
                    else if (code >= 200 && code <= 211)
                        categoryKey = "200-211";
                    else if (code >= 300 && code <= 311)
                        categoryKey = "300-311";
                    else if (code >= 400 && code <= 411)
                        categoryKey = "400-411";
                    else if (code >= 500 && code <= 511)
                        categoryKey = "500-511";
                    if (categoryKey && categories[categoryKey]) {
                        this.lawData[lawCode].category =
                            categories[categoryKey];
                    }
                }.bind(this),
            );
        },
        createModeToggle: function () {
            var tables = document.querySelectorAll(
                ".citizen-table-wrapper table",
            );
            tables.forEach(
                function (table) {
                    var wrapper = table.closest(".citizen-table-wrapper");
                    if (!wrapper || wrapper.querySelector(".law-mode-toggle"))
                        return;
                    var toggleContainer = document.createElement("div");
                    toggleContainer.className = "law-mode-toggle";
                    toggleContainer.innerHTML =
                        '<div class="mode-toggle-buttons">' +
                        '<button class="mode-btn active" data-mode="navigation">Навигация</button>' +
                        '<button class="mode-btn" data-mode="calculator">Калькулятор</button>' +
                        "</div>";
                    wrapper.appendChild(toggleContainer);
                    // Добавляем стили
                    this.addToggleStyles();
                    // Обработчики событий
                    var buttons = toggleContainer.querySelectorAll(".mode-btn");
                    buttons.forEach(function (btn) {
                        btn.addEventListener("click", function () {
                            buttons.forEach(function (b) {
                                b.classList.remove("active");
                            });
                            this.classList.add("active");
                            var mode = this.dataset.mode;
                            LawCalculatorModule.setMode(mode, table);
                        });
                    });
                }.bind(this),
            );
        },
        addToggleStyles: function () {
            if (document.getElementById("law-calculator-styles")) return;
            var style = document.createElement("style");
            style.id = "law-calculator-styles";
            style.textContent =
                ".law-mode-toggle {" +
                "margin: 10px 0;" +
                "text-align: center;" +
                "}" +
                ".mode-toggle-buttons {" +
                "display: inline-flex;" +
                "background: #f0f0f0;" +
                "border-radius: 5px;" +
                "padding: 2px;" +
                "border: 1px solid #ccc;" +
                "}" +
                ".mode-btn {" +
                "padding: 8px 16px;" +
                "border: none;" +
                "background: transparent;" +
                "cursor: pointer;" +
                "border-radius: 3px;" +
                "font-size: 14px;" +
                "transition: all 0.2s;" +
                "}" +
                ".mode-btn.active {" +
                "background: #007cba;" +
                "color: white;" +
                "}" +
                ".mode-btn:hover:not(.active) {" +
                "background: #e0e0e0;" +
                "}" +
                ".calculator-interface {" +
                "margin-top: 15px;" +
                "padding: 15px;" +
                "background: #f9f9f9;" +
                "border: 1px solid #ddd;" +
                "border-radius: 5px;" +
                "display: none;" +
                "}" +
                ".calculator-interface.active {" +
                "display: block;" +
                "}" +
                ".selected-violations {" +
                "margin: 10px 0;" +
                "}" +
                ".violation-item {" +
                "display: inline-block;" +
                "margin: 5px;" +
                "padding: 5px 10px;" +
                "background: #e3f2fd;" +
                "border: 1px solid #2196f3;" +
                "border-radius: 15px;" +
                "font-size: 12px;" +
                "}" +
                ".violation-item .remove-btn {" +
                "margin-left: 5px;" +
                "cursor: pointer;" +
                "color: #f44336;" +
                "font-weight: bold;" +
                "}" +
                ".calculation-result {" +
                "margin-top: 15px;" +
                "padding: 10px;" +
                "background: #fff3cd;" +
                "border: 1px solid #ffeaa7;" +
                "border-radius: 5px;" +
                "font-weight: bold;" +
                "white-space: pre-line;" +
                "}" +
                ".calculation-result.critical {" +
                "background: #f8d7da;" +
                "border-color: #f5c6cb;" +
                "color: #721c24;" +
                "}" +
                ".law-cell-calculator {" +
                "cursor: pointer;" +
                "transition: background-color 0.2s;" +
                "}" +
                ".law-cell-calculator:hover {" +
                "background-color: rgba(0, 124, 186, 0.1) !important;" +
                "}" +
                ".law-cell-calculator.selected {" +
                "background-color: rgba(0, 124, 186, 0.3) !important;" +
                "}" +
                ".calculator-actions {" +
                "margin-top: 10px;" +
                "}" +
                ".calculator-actions button {" +
                "padding: 8px 16px;" +
                "border: 1px solid #ccc;" +
                "background: white;" +
                "cursor: pointer;" +
                "border-radius: 3px;" +
                "font-size: 14px;" +
                "}" +
                ".calculator-actions button:hover {" +
                "background: #f0f0f0;" +
                "}" +
                ".calculator-actions .calculate-btn {" +
                "background: #007cba;" +
                "color: white;" +
                "border-color: #007cba;" +
                "}" +
                ".calculator-actions .calculate-btn:hover {" +
                "background: #005a87;" +
                "}";
            document.head.appendChild(style);
        },
        setMode: function (mode, table) {
            this.isCalculatorMode = mode === "calculator";
            var wrapper = table.closest(".citizen-table-wrapper");
            var calculatorInterface = wrapper.querySelector(
                ".calculator-interface",
            );
            if (this.isCalculatorMode) {
                if (!calculatorInterface) {
                    calculatorInterface = this.createCalculatorInterface();
                    wrapper.appendChild(calculatorInterface);
                }
                calculatorInterface.classList.add("active");
                this.makeTableInteractive(table);
            } else {
                if (calculatorInterface) {
                    calculatorInterface.classList.remove("active");
                }
                this.makeTableNormal(table);
            }
        },
        createCalculatorInterface: function () {
            var calculatorInterface = document.createElement("div");
            calculatorInterface.className = "calculator-interface";
            calculatorInterface.innerHTML =
                "<h4>Калькулятор сроков заключения</h4>" +
                "<p>Выберите нарушения, кликая по статьям в таблице выше:</p>" +
                '<div class="selected-violations">' +
                '<div class="violations-list"></div>' +
                "</div>" +
                '<div class="calculation-result" style="display: none;"></div>' +
                '<div class="calculator-actions">' +
                '<button class="clear-btn" style="margin-right: 10px;">Очистить</button>' +
                '<button class="calculate-btn">Рассчитать</button>' +
                "</div>";
            // Обработчики событий
            calculatorInterface
                .querySelector(".clear-btn")
                .addEventListener("click", function () {
                    LawCalculatorModule.clearSelection();
                });
            calculatorInterface
                .querySelector(".calculate-btn")
                .addEventListener("click", function () {
                    LawCalculatorModule.calculateSentence();
                });
            return calculatorInterface;
        },
        makeTableInteractive: function (table) {
            var cells = table.querySelectorAll("td");
            cells.forEach(
                function (cell) {
                    var link = cell.querySelector('a[href*="#"]');
                    if (!link) return;
                    var href = link.getAttribute("href");
                    var match = href.match(/#(\d{3})/);
                    if (!match) return;
                    var lawCode = match[1];
                    if (!this.lawData[lawCode]) return;
                    cell.classList.add("law-cell-calculator");
                    cell.dataset.lawCode = lawCode;
                    // Сохраняем оригинальный обработчик клика
                    var originalClickHandler = cell.onclick;
                    cell.addEventListener("click", function (e) {
                        e.preventDefault();
                        e.stopPropagation();
                        LawCalculatorModule.toggleViolation(lawCode, cell);
                    });
                }.bind(this),
            );
        },
        makeTableNormal: function (table) {
            var cells = table.querySelectorAll(".law-cell-calculator");
            cells.forEach(function (cell) {
                cell.classList.remove("law-cell-calculator", "selected");
                // Восстанавливаем оригинальную функциональность навигации
                var lawCode = cell.dataset.lawCode;
                if (lawCode) {
                    cell.addEventListener("click", function (e) {
                        e.preventDefault();
                        window.location.hash = lawCode;
                    });
                }
            });
        },
        toggleViolation: function (lawCode, cell) {
            var index = this.selectedViolations.indexOf(lawCode);
            if (index > -1) {
                // Убираем нарушение
                this.selectedViolations.splice(index, 1);
                cell.classList.remove("selected");
            } else {
                // Добавляем нарушение
                this.selectedViolations.push(lawCode);
                cell.classList.add("selected");
            }
            this.updateViolationsList();
        },
        updateViolationsList: function () {
            var violationsList = document.querySelector(".violations-list");
            if (!violationsList) return;
            violationsList.innerHTML = "";
            this.selectedViolations.forEach(
                function (lawCode) {
                    var law = this.lawData[lawCode];
                    if (!law) return;
                    var item = document.createElement("div");
                    item.className = "violation-item";
                    item.innerHTML =
                        lawCode +
                        " - " +
                        law.title +
                        '<span class="remove-btn" data-law-code="' +
                        lawCode +
                        '">×</span>';
                    item.querySelector(".remove-btn").addEventListener(
                        "click",
                        function () {
                            LawCalculatorModule.removeViolation(lawCode);
                        },
                    );
                    violationsList.appendChild(item);
                }.bind(this),
            );
        },
        removeViolation: function (lawCode) {
            var index = this.selectedViolations.indexOf(lawCode);
            if (index > -1) {
                this.selectedViolations.splice(index, 1);
                // Убираем выделение с ячейки
                var cell = document.querySelector(
                    '[data-law-code="' + lawCode + '"]',
                );
                if (cell) {
                    cell.classList.remove("selected");
                }
                this.updateViolationsList();
            }
        },
        clearSelection: function () {
            this.selectedViolations = [];
            // Убираем выделение со всех ячеек
            var selectedCells = document.querySelectorAll(
                ".law-cell-calculator.selected",
            );
            selectedCells.forEach(function (cell) {
                cell.classList.remove("selected");
            });
            this.updateViolationsList();
            this.hideResult();
        },
        calculateSentence: function () {
            if (this.selectedViolations.length === 0) {
                alert("Выберите хотя бы одно нарушение");
                return;
            }
            var result = this.calculateSentenceLogic(this.selectedViolations);
            this.showResult(result);
        },
        calculateSentenceLogic: function (violations) {
            // Группируем нарушения по категориям
            var categories = {};
            violations.forEach(
                function (lawCode) {
                    var law = this.lawData[lawCode];
                    if (!law || !law.category) return;
                    // Используем номер статьи как ключ категории
                    var categoryKey = this.getCategoryKeyByLawCode(lawCode);
                    if (!categories[categoryKey]) {
                        categories[categoryKey] = [];
                    }
                    categories[categoryKey].push(law);
                }.bind(this),
            );
            var totalMinTime = 0;
            var totalMaxTime = 0;
            var totalFine = 0;
            var hasCritical = false;
            var resultText = "";
            // Обрабатываем каждую категорию
            Object.keys(categories).forEach(
                function (categoryKey) {
                    var laws = categories[categoryKey];
                    var category = laws[0].category;
                    var categoryName = this.getCategoryName(categoryKey);
                    if (category.isCritical) {
                        hasCritical = true;
                        resultText +=
                            "• " +
                            categoryName +
                            ": " +
                            (category.punishment || "Перевод в Д-класс/казнь") +
                            "\n";
                    } else {
                        // Для одной категории берем самую тяжкую статью
                        var maxLaw = laws.reduce(function (max, current) {
                            var currentCode = parseInt(lawCode);
                            var maxCode = parseInt(max.title.match(/\d{3}/)[0]);
                            return currentCode > maxCode ? current : max;
                        });
                        totalMinTime += category.minTime || 0;
                        totalMaxTime += category.maxTime || 0;
                        totalFine += category.fine || 0;
                        var punishmentText = "";
                        if (category.punishment) {
                            punishmentText = category.punishment;
                        } else if (category.minTime !== undefined) {
                            punishmentText =
                                category.minTime === category.maxTime
                                    ? category.minTime + " минут"
                                    : category.minTime +
                                      "-" +
                                      category.maxTime +
                                      " минут";
                        }
                        resultText +=
                            "• " +
                            categoryName +
                            ": " +
                            punishmentText +
                            " (статья " +
                            lawCode +
                            ")\n";
                    }
                }.bind(this),
            );
            if (hasCritical) {
                return {
                    isCritical: true,
                    text:
                        "КРИТИЧЕСКИЕ НАРУШЕНИЯ\n" +
                        resultText +
                        "\nПриговор: Перевод в Д-класс или казнь",
                };
            } else if (this.pageType === "d-class") {
                // Для Д-класса показываем штрафы и время отдельно
                var summaryText = "";
                if (totalFine > 0) {
                    summaryText += "Общий штраф: " + totalFine + " кредитов\n";
                }
                if (totalMinTime > 0 || totalMaxTime > 0) {
                    summaryText +=
                        "Общее время: " +
                        this.formatTime(totalMinTime, totalMaxTime) +
                        "\n";
                }
                return {
                    isCritical: false,
                    text:
                        "РАСЧЕТ НАКАЗАНИЙ Д-КЛАССА\n" +
                        resultText +
                        "\n" +
                        summaryText,
                };
            } else if (totalMinTime > 0 || totalMaxTime > 0) {
                var timeText = this.formatTime(totalMinTime, totalMaxTime);
                return {
                    isCritical: false,
                    text:
                        "РАСЧЕТ СРОКА ЗАКЛЮЧЕНИЯ\n" +
                        resultText +
                        "\nОбщий срок: " +
                        timeText,
                };
            } else {
                return {
                    isCritical: false,
                    text:
                        "РАСЧЕТ НАКАЗАНИЙ\n" +
                        resultText +
                        "\nНе удалось определить наказания",
                };
            }
        },
        getCategoryKeyByLawCode: function (lawCode) {
            var code = parseInt(lawCode);
            if (code >= 100 && code <= 109) return "100-109";
            else if (code >= 200 && code <= 211) return "200-211";
            else if (code >= 300 && code <= 311) return "300-311";
            else if (code >= 400 && code <= 411) return "400-411";
            else if (code >= 500 && code <= 511) return "500-511";
            return "unknown";
        },
        getCategoryName: function (categoryKey) {
            var names = {
                "100-109": "Лёгкие нарушения",
                "200-211": "Средние нарушения",
                "300-311": "Тяжкие нарушения",
                "400-411": "Особо тяжкие нарушения",
                "500-511": "Критические нарушения",
            };
            return names[categoryKey] || "Неизвестная категория";
        },
        formatTime: function (minTime, maxTime) {
            if (minTime === maxTime) {
                return this.formatMinutes(minTime);
            } else {
                return (
                    this.formatMinutes(minTime) +
                    " - " +
                    this.formatMinutes(maxTime)
                );
            }
        },
        formatMinutes: function (minutes) {
            if (minutes < 60) {
                return minutes + " минут";
            } else {
                var hours = Math.floor(minutes / 60);
                var remainingMinutes = minutes % 60;
                var result = hours + " час";
                if (hours > 1 && hours < 5) result += "а";
                if (hours >= 5) result += "ов";
                if (remainingMinutes > 0) {
                    result += " " + remainingMinutes + " минут";
                }
                return result;
            }
        },
        showResult: function (result) {
            var resultDiv = document.querySelector(".calculation-result");
            if (!resultDiv) return;
            resultDiv.textContent = result.text;
            resultDiv.className =
                "calculation-result" + (result.isCritical ? " critical" : "");
            resultDiv.style.display = "block";
        },
        hideResult: function () {
            var resultDiv = document.querySelector(".calculation-result");
            if (resultDiv) {
                resultDiv.style.display = "none";
            }
        },
        observeTables: function () {
            // Наблюдаем за динамически добавляемыми таблицами
            var observer = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    mutation.addedNodes.forEach(function (node) {
                        if (node.nodeType === 1) {
                            if (
                                node.classList &&
                                node.classList.contains("citizen-table-wrapper")
                            ) {
                                setTimeout(function () {
                                    LawCalculatorModule.createModeToggle();
                                }, 100);
                            } else if (node.querySelectorAll) {
                                var tables = node.querySelectorAll(
                                    ".citizen-table-wrapper",
                                );
                                if (tables.length > 0) {
                                    setTimeout(function () {
                                        LawCalculatorModule.createModeToggle();
                                    }, 100);
                                }
                            }
                        }
                    });
                });
            });
            observer.observe(document.body, {
                childList: true,
                subtree: true,
            });
         },
         },
     };
     };
Строка 2059: Строка 1235:


     window.SnowflakesModule = SnowflakesModule;
     window.SnowflakesModule = SnowflakesModule;
    var FireworksModule = {
        fireworks: null,
        container: null,
        checkInterval: null,
        isActive: false,
        loaded: false,
        loading: false,
        callbacks: [],
        init: function () {
            this.startTimeCheck();
        },
        loadFireworks: function (callback) {
            // Проверяем разные варианты экспорта библиотеки
            if (
                this.loaded &&
                (window.Fireworks ||
                    (window.Fireworks && window.Fireworks.default))
            ) {
                if (callback) callback();
                return;
            }
            if (callback) {
                this.callbacks.push(callback);
            }
            if (this.loading) return;
            this.loading = true;
            var self = this;
            // Загружаем библиотеку fireworks.js через UMD версию
            var script = document.createElement("script");
            script.src = "https://unpkg.com/fireworks-js@2/dist/index.umd.js";
            script.onload = function () {
                self.loaded = true;
                self.loading = false;
                // Вызываем все колбэки
                self.callbacks.forEach(function (cb) {
                    cb();
                });
                self.callbacks = [];
            };
            script.onerror = function () {
                console.error("Ошибка загрузки fireworks.js");
                self.loading = false;
                self.callbacks = [];
            };
            document.head.appendChild(script);
        },
        getMoscowTime: function () {
            // МСК = UTC+3
            var now = new Date();
            var utc = now.getTime() + now.getTimezoneOffset() * 60000;
            var moscowTime = new Date(utc + 3 * 3600000); // UTC+3
            return moscowTime;
        },
        isFireworksTime: function () {
            var moscowTime = this.getMoscowTime();
            var hours = moscowTime.getHours();
            // С 00:00 до 01:00 по МСК
            return hours === 0;
        },
        startTimeCheck: function () {
            var self = this;
            // Проверяем сразу при загрузке
            this.checkTime();
            // Проверяем каждую минуту
            this.checkInterval = setInterval(function () {
                self.checkTime();
            }, 60000);
        },
        checkTime: function () {
            if (this.isFireworksTime()) {
                if (!this.isActive) {
                    this.startFireworks();
                }
            } else {
                if (this.isActive) {
                    this.stopFireworks();
                }
            }
        },
        getFireworksConstructor: function () {
            // Пробуем разные варианты доступа к конструктору
            if (window.Fireworks && typeof window.Fireworks === "function") {
                return window.Fireworks;
            }
            if (
                window.Fireworks &&
                window.Fireworks.default &&
                typeof window.Fireworks.default === "function"
            ) {
                return window.Fireworks.default;
            }
            if (window.fireworks && typeof window.fireworks === "function") {
                return window.fireworks;
            }
            // Если библиотека экспортируется по-другому
            if (
                typeof Fireworks !== "undefined" &&
                typeof Fireworks === "function"
            ) {
                return Fireworks;
            }
            return null;
        },
        startFireworks: function () {
            var self = this;
            this.loadFireworks(function () {
                if (!self.container) {
                    self.container = document.createElement("div");
                    self.container.id = "fireworks-container";
                    self.container.style.cssText =
                        "position: fixed;" +
                        "top: 0;" +
                        "left: 0;" +
                        "width: 100%;" +
                        "height: 100%;" +
                        "pointer-events: none;" +
                        "z-index: 99998;";
                    document.body.appendChild(self.container);
                }
                var FireworksConstructor = self.getFireworksConstructor();
                if (!FireworksConstructor) {
                    return;
                }
                if (!self.fireworks) {
                    try {
                        self.fireworks = new FireworksConstructor(
                            self.container,
                            {
                                autoresize: true,
                                opacity: 0.5,
                                // Замедляем фейерверки
                                acceleration: 1.02, // Уменьшено с 1.05 для более медленного движения
                                friction: 0.98, // Увеличено с 0.97 для большего сопротивления
                                gravity: 1.2, // Уменьшено с 1.5 для более медленного падения
                                particles: 50,
                                traceLength: 5, // Увеличено с 3 для более длинного следа
                                traceSpeed: 5, // Уменьшено с 10 для более медленного следа
                                explosion: 5,
                                intensity: 30,
                                flickering: 50,
                                lineStyle: "round",
                                hue: { min: 0, max: 360 },
                                // Увеличиваем задержку между запусками для более медленного темпа
                                delay: { min: 30, max: 60 }, // Увеличено с 15-30 до 30-60
                                rocketsPoint: { min: 50, max: 50 },
                                lineWidth: {
                                    explosion: { min: 1, max: 3 },
                                    trace: { min: 1, max: 2 },
                                },
                                brightness: { min: 50, max: 80 },
                                decay: { min: 0.01, max: 0.02 }, // Уменьшено для более медленного затухания
                                mouse: { click: false, move: false, max: 1 },
                                // Включаем звук
                                sound: {
                                    enable: true,
                                    files: [
                                        "https://fireworks.js.org/sounds/explosion0.mp3",
                                        "https://fireworks.js.org/sounds/explosion1.mp3",
                                        "https://fireworks.js.org/sounds/explosion2.mp3",
                                    ],
                                    volume: { min: 0.3, max: 0.6 }, // Громкость от 30% до 60%
                                },
                            },
                        );
                    } catch (e) {
                        console.error(
                            "Ошибка создания экземпляра Fireworks:",
                            e,
                        );
                        return;
                    }
                }
                if (self.fireworks) {
                    self.fireworks.start();
                    self.isActive = true;
                }
            });
        },
        stopFireworks: function () {
            if (this.fireworks) {
                this.fireworks.stop();
                this.isActive = false;
            }
        },
        toggleFireworks: function () {
            if (this.isActive) {
                this.stopFireworks();
            } else {
                this.startFireworks();
            }
        },
    };
    window.FireworksModule = FireworksModule;


     function initAllModules() {
     function initAllModules() {
Строка 2286: Строка 1244:
         DocumentAutoFillModule.init();
         DocumentAutoFillModule.init();
         // SnowflakesModule.init()
         // SnowflakesModule.init()
        // FireworksModule.init();
        // LawCalculatorModule.init();
     }
     }



Версия от 18:28, 6 февраля 2026

(function () {
    "use strict";

    window.MediaWikiTooltips = {
        loaded: false,
        callbacks: [],

        load: function (callback) {
            if (this.loaded && window.tippy) {
                callback();
                return;
            }

            this.callbacks.push(callback);

            if (this.loading) return;
            this.loading = true;

            var self = this;
            var popperScript = document.createElement("script");
            popperScript.src =
                "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js";
            popperScript.onload = function () {
                var tippyScript = document.createElement("script");
                tippyScript.src =
                    "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.7/tippy-bundle.umd.min.js";
                tippyScript.onload = function () {
                    self.loaded = true;
                    self.loading = false;
                    self.callbacks.forEach(function (cb) {
                        cb();
                    });
                    self.callbacks = [];
                };
                document.head.appendChild(tippyScript);
            };
            document.head.appendChild(popperScript);

            var tippyCSS = document.createElement("link");
            tippyCSS.rel = "stylesheet";
            tippyCSS.href =
                "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.7/tippy.css";
            document.head.appendChild(tippyCSS);
        },
    };

    var SidebarModule = {
        init: function () {
            var buttons = document.querySelectorAll(".боковая-панель-кнопка");
            var sections = document.querySelectorAll(".боковая-панель-раздел");

            if (buttons.length === 0) return;

            buttons.forEach(function (button) {
                button.addEventListener("click", function () {
                    var targetId = this.dataset.target;

                    buttons.forEach(function (btn) {
                        btn.classList.remove("active");
                    });
                    this.classList.add("active");

                    sections.forEach(function (section) {
                        section.classList.remove("default");
                    });
                    var targetSection = document.getElementById(targetId);
                    if (targetSection) {
                        targetSection.classList.add("default");
                    }
                });
            });

            buttons[0].click();
        },
    };

    var AccessTooltipsModule = {
        init: function () {
            var containers = document.querySelectorAll(".допуск-контейнер");
            if (containers.length === 0) return;

            MediaWikiTooltips.load(function () {
                containers.forEach(function (container) {
                    var contentElement =
                        container.querySelector(".допуск-подсказка");
                    if (!contentElement) return;

                    var content = contentElement.innerHTML;
                    tippy(container, {
                        content: content,
                        allowHTML: true,
                        interactive: true,
                        placement: "auto",
                        maxWidth: 500,
                        theme: "dark",
                        arrow: true,
                        duration: [200, 200],
                        popperOptions: {
                            modifiers: [
                                {
                                    name: "preventOverflow",
                                    options: { padding: 8 },
                                },
                                {
                                    name: "flip",
                                    options: {
                                        fallbackPlacements: [
                                            "top",
                                            "bottom",
                                            "right",
                                            "left",
                                        ],
                                    },
                                },
                            ],
                        },
                    });
                });
            });
        },
    };

    var LawTooltipsModule = {
        init: function () {
            console.log("[LawTooltips] init()");
            var tableCells = document.querySelectorAll(
                ".law-tooltips td, .law-tooltips th",
            );
            console.log(
                "[LawTooltips] .law-tooltips ячеек:",
                tableCells.length,
            );
            if (tableCells.length === 0) {
                console.warn("[LawTooltips] Нет ячеек .law-tooltips — выход");
                return;
            }

            function run() {
                console.log("[LawTooltips] run() — извлечение данных...");
                var lawData = LawTooltipsModule.extractLawData();
                var count = Object.keys(lawData).length;
                console.log(
                    "[LawTooltips] Загружено записей законов:",
                    count,
                    count
                        ? Object.keys(lawData).slice(0, 10).join(", ") +
                              (count > 10 ? "..." : "")
                        : "",
                );
                if (count > 0) {
                    LawTooltipsModule.initCellTooltips(tableCells, lawData);
                    return true;
                }
                return false;
            }

            if (typeof MediaWikiTooltips === "undefined") {
                console.error(
                    "[LawTooltips] MediaWikiTooltips не найден — тултипы не загрузятся",
                );
                run();
                return;
            }
            MediaWikiTooltips.load(function () {
                console.log("[LawTooltips] MediaWikiTooltips.load вызван");
                if (run()) {
                    console.log("[LawTooltips] Инициализация с первой попытки");
                    return;
                }
                setTimeout(function () {
                    if (run()) {
                        console.log("[LawTooltips] Инициализация после 300ms");
                        return;
                    }
                    setTimeout(function () {
                        if (run())
                            console.log(
                                "[LawTooltips] Инициализация после 800ms",
                            );
                        else
                            console.warn(
                                "[LawTooltips] Данные законов не найдены после всех попыток",
                            );
                    }, 500);
                }, 300);
            });
        },

        extractLawData: function () {
            var lawData = {};
            var containers = document.querySelectorAll(".mw-collapsible");
            console.log(
                "[LawTooltips] extractLawData: коллапсибл-блоков:",
                containers.length,
            );
            containers.forEach(function (block) {
                var content = block.querySelector(".mw-collapsible-content");
                if (content && block.classList.contains("mw-collapsed")) {
                    block.classList.remove("mw-collapsed");
                    var toggle = content.querySelector(
                        ".mw-collapsible-toggle",
                    );
                    if (toggle) toggle.setAttribute("aria-expanded", "true");
                }
            });

            var detailTables = document.querySelectorAll(
                ".mw-collapsible-content table",
            );
            console.log(
                "[LawTooltips] extractLawData: таблиц в .mw-collapsible-content:",
                detailTables.length,
            );
            detailTables.forEach(function (table, tableIndex) {
                if (table.classList.contains("law-tooltips")) {
                    console.log(
                        "[LawTooltips] extractLawData: таблица",
                        tableIndex,
                        "— пропуск (law-tooltips)",
                    );
                    return;
                }
                var rows = table.querySelectorAll("tr");

                rows.forEach(function (row) {
                    var cells = row.querySelectorAll("td");
                    if (cells.length < 3) return;

                    var crimeCell = cells[0];
                    var descriptionCell = cells[1];
                    var exampleCell = cells[2];

                    var anchor = crimeCell.querySelector("[id]");
                    if (!anchor) return;

                    var lawCode = anchor.id;
                    var titleElement = crimeCell.querySelector("b");
                    if (!titleElement || !lawCode.match(/^\d{3}$/)) return;

                    var titleText = titleElement.textContent.trim();
                    var lawTitle = titleText
                        .replace(/^\d{3}\s*/, "")
                        .replace(/^\d{3}/, "")
                        .trim();
                    var description = descriptionCell.innerHTML.trim();

                    var examples = [];
                    var exampleItems = exampleCell.querySelectorAll("li");
                    exampleItems.forEach(function (item) {
                        var html = item.innerHTML
                            .trim()
                            .replace(/<br\s*\/?>/gi, "");
                        if (html) examples.push(html);
                    });

                    lawData[lawCode] = {
                        title: lawTitle,
                        description: description,
                        examples: examples,
                    };
                });
            });

            console.log(
                "[LawTooltips] extractLawData: итого кодов:",
                Object.keys(lawData).length,
            );
            return lawData;
        },

        createTooltipContent: function (lawCode, lawData) {
            var data = lawData[lawCode];
            if (!data) return null;

            var html = '<div class="law-tooltip">';
            html +=
                '<h4 class="law-tooltip-title">' +
                lawCode +
                " - " +
                data.title +
                "</h4>";
            html +=
                '<p class="law-tooltip-description">' +
                data.description +
                "</p>";

            if (data.examples && data.examples.length > 0) {
                html += '<div class="law-tooltip-examples">';
                html += "<strong>Примеры:</strong><ul>";
                data.examples.slice(0, 5).forEach(function (example) {
                    html += "<li>" + example + "</li>";
                });
                html += "</ul></div>";
            }
            html += "</div>";

            return html;
        },

        initCellTooltips: function (tableCells, lawData) {
            var withLink = 0,
                withMatch = 0,
                withContent = 0,
                tooltipsCreated = 0;
            tableCells.forEach(function (cell) {
                var link = cell.querySelector('a[href*="#"]');
                if (!link) return;
                withLink++;

                var href = link.getAttribute("href");
                var match = href.match(/#(\d{3})/);
                if (!match) return;
                withMatch++;

                var lawCode = match[1];
                var tooltipContent = LawTooltipsModule.createTooltipContent(
                    lawCode,
                    lawData,
                );
                if (!tooltipContent) return;
                withContent++;

                cell.classList.add("law-cell-with-tooltip");
                tooltipsCreated++;

                cell.addEventListener("click", function (e) {
                    e.preventDefault();
                    window.location.hash = lawCode;
                });

                if (typeof tippy === "undefined") {
                    console.error(
                        "[LawTooltips] tippy не найден — подключи библиотеку Tippy.js",
                    );
                    return;
                }
                tippy(cell, {
                    content: tooltipContent,
                    allowHTML: true,
                    interactive: true,
                    placement: "top",
                    maxWidth: 400,
                    theme: "law-theme",
                    arrow: true,
                    duration: [200, 150],
                    delay: [0, 50],
                    appendTo: document.body,
                    boundary: "viewport",
                    popperOptions: {
                        strategy: "fixed",
                        modifiers: [
                            {
                                name: "preventOverflow",
                                options: {
                                    boundary: "viewport",
                                    padding: 20,
                                    altAxis: true,
                                    altBoundary: true,
                                },
                            },
                            {
                                name: "flip",
                                options: {
                                    fallbackPlacements: [
                                        "bottom",
                                        "left",
                                        "right",
                                    ],
                                },
                            },
                            {
                                name: "computeStyles",
                                options: { adaptive: false },
                            },
                        ],
                    },
                    onShow: function (instance) {
                        document
                            .querySelectorAll("[data-tippy-root]")
                            .forEach(function (tooltip) {
                                if (
                                    tooltip._tippy &&
                                    tooltip._tippy !== instance
                                ) {
                                    tooltip._tippy.hide();
                                }
                            });
                    },
                });
            });
            console.log(
                "[LawTooltips] initCellTooltips: ячеек с ссылкой",
                withLink,
                "| с #XXX",
                withMatch,
                "| с данными",
                withContent,
                "| тултипов создано",
                tooltipsCreated,
            );
        },
    };

    var DataTooltipsModule = {
        init: function () {
            var elements = document.querySelectorAll(
                ".data-tooltip[data-text]",
            );
            if (elements.length === 0) return;

            MediaWikiTooltips.load(function () {
                elements.forEach(function (element) {
                    var tooltipText = element.getAttribute("data-text");
                    if (!tooltipText || !tooltipText.trim()) return;

                    tippy(element, {
                        content: tooltipText,
                        allowHTML: true,
                        interactive: false,
                        placement: "top",
                        maxWidth: 300,
                        theme: "data-tooltip-theme",
                        arrow: true,
                        duration: [200, 150],
                        delay: [0, 50],
                    });
                });
            });

            var observer = new MutationObserver(function (mutations) {
                var shouldReinit = false;
                mutations.forEach(function (mutation) {
                    mutation.addedNodes.forEach(function (node) {
                        if (node.nodeType === 1) {
                            if (
                                node.classList &&
                                node.classList.contains("data-tooltip") &&
                                node.hasAttribute("data-text")
                            ) {
                                shouldReinit = true;
                            } else if (node.querySelectorAll) {
                                var newElements = node.querySelectorAll(
                                    ".data-tooltip[data-text]",
                                );
                                if (newElements.length > 0) {
                                    shouldReinit = true;
                                }
                            }
                        }
                    });
                });

                if (shouldReinit) {
                    setTimeout(function () {
                        DataTooltipsModule.init();
                    }, 100);
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true,
            });
        },
    };

    var CopyTextModule = {
        init: function () {
            CopyTextModule.initDirectElements();
            CopyTextModule.initAutoContainers();
        },

        initDirectElements: function () {
            var elements = document.querySelectorAll(".copyable-text");
            if (elements.length === 0) return;

            elements.forEach(function (element) {
                CopyTextModule.cleanPreContent(element);
                CopyTextModule.setupElement(element);
            });
        },

        initAutoContainers: function () {
            var containers = document.querySelectorAll(
                ".copyable-pre-container",
            );
            if (containers.length === 0) return;

            containers.forEach(function (container) {
                CopyTextModule.processContainerPre(container);
            });

            // Наблюдатель за динамически появляющимися элементами
            var observer = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    mutation.addedNodes.forEach(function (node) {
                        if (node.nodeType === 1) {
                            // Проверяем сам элемент
                            if (node.tagName === "PRE") {
                                var container = node.closest(
                                    ".copyable-pre-container",
                                );
                                if (
                                    container &&
                                    !node.classList.contains("copyable-text")
                                ) {
                                    node.classList.add("copyable-text");
                                    CopyTextModule.cleanPreContent(node);
                                    CopyTextModule.setupElement(node);
                                }
                            }
                            // Проверяем дочерние элементы
                            else if (node.querySelectorAll) {
                                var containers = node.querySelectorAll(
                                    ".copyable-pre-container",
                                );
                                containers.forEach(function (container) {
                                    CopyTextModule.processContainerPre(
                                        container,
                                    );
                                });

                                // Также проверяем pre внутри уже существующих контейнеров
                                var preElements = node.querySelectorAll(
                                    ".copyable-pre-container pre",
                                );
                                preElements.forEach(function (pre) {
                                    if (
                                        !pre.classList.contains("copyable-text")
                                    ) {
                                        pre.classList.add("copyable-text");
                                        CopyTextModule.cleanPreContent(pre);
                                        CopyTextModule.setupElement(pre);
                                    }
                                });
                            }
                        }
                    });

                    // Обрабатываем изменения атрибутов (например, style="display: none" -> "")
                    if (
                        mutation.type === "attributes" &&
                        mutation.attributeName === "style"
                    ) {
                        var target = mutation.target;
                        if (target.style.display !== "none") {
                            var containers = target.querySelectorAll(
                                ".copyable-pre-container",
                            );
                            containers.forEach(function (container) {
                                CopyTextModule.processContainerPre(container);
                            });
                        }
                    }
                });
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ["style"],
            });
        },

        processContainerPre: function (container) {
            var preElements = container.querySelectorAll("pre");
            preElements.forEach(function (pre) {
                if (!pre.classList.contains("copyable-text")) {
                    pre.classList.add("copyable-text");
                    CopyTextModule.cleanPreContent(pre);
                    CopyTextModule.setupElement(pre);
                }
            });
        },

        cleanPreContent: function (preElement) {
            var text = preElement.textContent || preElement.innerText;

            var cleanedText = text
                .split("\n")
                .map(function (line) {
                    return line.replace(/^\s*\|\s*/, "");
                })
                .join("\n");

            if (cleanedText !== text) {
                preElement.textContent = cleanedText;
            }
        },

        setupElement: function (element) {
            element.style.cursor = "pointer";
            element.addEventListener("click", CopyTextModule.handleClick);

            var isIOS =
                /iPad|iPhone|iPod/.test(navigator.userAgent) &&
                !window.MSStream;
            if (isIOS) {
                element.style.webkitUserSelect = "text";
                element.style.userSelect = "text";
            }
        },

        handleClick: function (e) {
            e.preventDefault();

            var element = this;
            var textToCopy = element.textContent;

            if (navigator.clipboard && window.isSecureContext) {
                navigator.clipboard
                    .writeText(textToCopy)
                    .then(function () {
                        CopyTextModule.showNotification(element);
                    })
                    .catch(function (err) {
                        console.error("Ошибка при копировании: ", err);
                        CopyTextModule.fallbackCopy(textToCopy, element);
                    });
            } else {
                CopyTextModule.fallbackCopy(textToCopy, element);
            }

            element.classList.add("copying");
            setTimeout(function () {
                element.classList.remove("copying");
            }, 200);
        },

        fallbackCopy: function (text, element) {
            var textArea = document.createElement("textarea");
            textArea.value = text;
            textArea.style.position = "fixed";
            textArea.style.top = "-9999px";
            textArea.style.left = "-9999px";
            textArea.style.width = "2em";
            textArea.style.height = "2em";
            textArea.style.padding = "0";
            textArea.style.border = "none";
            textArea.style.outline = "none";
            textArea.style.boxShadow = "none";
            textArea.style.background = "transparent";

            document.body.appendChild(textArea);
            textArea.focus();
            textArea.select();

            try {
                var successful = document.execCommand("copy");
                if (successful) {
                    CopyTextModule.showNotification(element);
                } else {
                    console.error("Не удалось скопировать текст");
                }
            } catch (err) {
                console.error("Ошибка при копировании: ", err);
            }

            document.body.removeChild(textArea);
        },

        showNotification: function (element) {
            var existingNotifications =
                document.querySelectorAll(".copy-notification");
            existingNotifications.forEach(function (notification) {
                notification.remove();
            });

            var notification = document.createElement("div");
            notification.className = "copy-notification";
            notification.textContent = "Текст скопирован!";
            document.body.appendChild(notification);

            var rect = element.getBoundingClientRect();
            var isMobile = window.innerWidth <= 768;

            if (isMobile) {
                notification.style.position = "fixed";
                notification.style.top = "20px";
                notification.style.left = "50%";
                notification.style.transform =
                    "translateX(-50%) translateY(-10px)";
                notification.style.zIndex = "10000";
            } else {
                notification.style.position = "absolute";
                notification.style.top = rect.top + window.scrollY - 40 + "px";
                notification.style.left = rect.left + window.scrollX + "px";
                notification.style.zIndex = "9999";
            }

            setTimeout(function () {
                notification.classList.add("show");
            }, 10);

            setTimeout(function () {
                notification.classList.remove("show");
                setTimeout(function () {
                    if (notification.parentNode) {
                        notification.remove();
                    }
                }, 300);
            }, 2000);
        },
    };

    var DocumentAutoFillModule = {
        init: function () {
            this.processExistingContainers();
            this.observeNewContainers();
            this.startTimeUpdateTimer();
        },

        processExistingContainers: function () {
            var containers = document.querySelectorAll(
                ".copyable-pre-container.auto-fill-enabled",
            );

            containers.forEach(
                function (container) {
                    this.setupAutoFillContainer(container);
                }.bind(this),
            );
        },

        setupAutoFillContainer: function (container) {
            if (container.querySelector(".document-fields-container")) {
                return;
            }

            var fieldsContainer = document.createElement("div");
            fieldsContainer.className = "document-fields-container";

            var gridContainer = document.createElement("div");
            gridContainer.className = "fields-grid";

            var siteNumberField = this.createField(
                "site-number",
                "Номер участка:",
                "Введите номер участка...",
                false,
            );
            var authorField = this.createField(
                "author",
                "Автор документа:",
                "Введите ваше имя...",
                true,
            );
            var positionField = this.createField(
                "position",
                "Должность:",
                "Введите вашу должность...",
                true,
            );
            var signatureField = this.createField(
                "signature",
                "Подпись:",
                "Введите подпись...",
                true,
            );

            gridContainer.appendChild(siteNumberField.container);
            gridContainer.appendChild(authorField.container);
            gridContainer.appendChild(positionField.container);
            gridContainer.appendChild(signatureField.container);

            fieldsContainer.appendChild(gridContainer);

            if (container.firstChild) {
                container.insertBefore(fieldsContainer, container.firstChild);
            } else {
                container.appendChild(fieldsContainer);
            }

            setTimeout(
                function () {
                    this.applyAllFieldsToContainer(container);
                }.bind(this),
                100,
            );
        },

        createField: function (fieldType, labelText, placeholder, shouldCache) {
            var fieldContainer = document.createElement("div");
            fieldContainer.className = "field-container";

            var label = document.createElement("label");
            label.textContent = labelText;
            label.htmlFor = fieldType + "-input";

            var input = document.createElement("input");
            input.type = "text";
            input.id = fieldType + "-input";
            input.className = "field-input " + fieldType + "-input";
            input.placeholder = placeholder;

            if (shouldCache) {
                var savedValue = localStorage.getItem(
                    "document" + this.capitalizeFirstLetter(fieldType),
                );
                if (savedValue) {
                    input.value = savedValue;
                }
            }

            input.addEventListener(
                "input",
                function () {
                    var value = input.value.trim();
                    if (shouldCache) {
                        localStorage.setItem(
                            "document" + this.capitalizeFirstLetter(fieldType),
                            value,
                        );
                    }
                }.bind(this),
            );

            fieldContainer.appendChild(label);
            fieldContainer.appendChild(input);

            return {
                container: fieldContainer,
                input: input,
                type: fieldType,
                shouldCache: shouldCache,
            };
        },

        capitalizeFirstLetter: function (string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        },

        applyAllFieldsToContainer: function (container) {
            var fieldsContainer = container.querySelector(
                ".document-fields-container",
            );
            if (!fieldsContainer) return;

            var siteNumber = fieldsContainer
                .querySelector(".site-number-input")
                .value.trim();
            var author = fieldsContainer
                .querySelector(".author-input")
                .value.trim();
            var position = fieldsContainer
                .querySelector(".position-input")
                .value.trim();
            var signature = fieldsContainer
                .querySelector(".signature-input")
                .value.trim();

            this.autoFillAllDocumentsInContainer(container, {
                siteNumber: siteNumber,
                author: author,
                position: position,
                signature: signature,
            });
        },

        autoFillAllDocumentsInContainer: function (container, fields) {
            var preElements = container.querySelectorAll("pre");

            preElements.forEach(
                function (preElement) {
                    this.autoFillDocument(preElement, fields);
                }.bind(this),
            );
        },

        autoFillDocument: function (preElement, fields) {
            if (!preElement) return;

            var originalContent =
                preElement.textContent || preElement.innerText;

            if (!this.isDocumentTemplate(originalContent)) {
                return;
            }

            var updatedContent = this.fillDocumentContent(
                originalContent,
                fields,
            );

            if (updatedContent !== originalContent) {
                preElement.textContent = updatedContent;
            }
        },

        isDocumentTemplate: function (text) {
            return (
                text.includes("SCP") &&
                text.includes("Обезопасить. Удержать. Сохранить.") &&
                text.includes("Автор документа:") &&
                text.includes("Дата и время:")
            );
        },

        fillDocumentContent: function (content, fields) {
            var currentDateTime = this.getCurrentDateTime();

            content = content.replace(
                /(\[bold\]Дата и время:\[\/bold\])([^\n]*)/g,
                "$1 " + currentDateTime,
            );

            if (fields.siteNumber) {
                content = content.replace(
                    /Участок-([^|\n\[\]]*)/g,
                    "Участок-" + fields.siteNumber + " ",
                );
            }

            if (fields.author) {
                content = content.replace(
                    /(\[bold\]Автор документа:\[\/bold\])([^\n]*)/g,
                    "$1 " + fields.author,
                );
            } else {
                content = content.replace(
                    /(\[bold\]Автор документа:\[\/bold\])([^\n]*)/g,
                    "$1",
                );
            }

            if (fields.position) {
                content = content.replace(
                    /(\[bold\]Должность:\[\/bold\])([^\n]*)/g,
                    "$1 " + fields.position,
                );
            } else {
                content = content.replace(
                    /(\[bold\]Должность:\[\/bold\])([^\n]*)/g,
                    "$1",
                );
            }

            if (fields.signature) {
                content = content.replace(
                    /(\[bold\]Подпись:\[\/bold\])[^\n]*/g,
                    "$1 " + fields.signature,
                );
            } else {
                content = content.replace(
                    /(\[bold\]Подпись:\[\/bold\])([^\n]*)/g,
                    "$1_____",
                );
            }

            return content;
        },

        getCurrentDateTime: function () {
            var now = new Date();

            var day = String(now.getDate()).padStart(2, "0");
            var month = String(now.getMonth() + 1).padStart(2, "0");
            var year = now.getFullYear();

            var hours = String(now.getHours()).padStart(2, "0");
            var minutes = String(now.getMinutes()).padStart(2, "0");

            return (
                hours + ":" + minutes + ", " + day + "." + month + "." + year
            );
        },

        startTimeUpdateTimer: function () {
            var self = this;

            function updateTimeIfNeeded() {
                var containers = document.querySelectorAll(
                    ".copyable-pre-container.auto-fill-enabled",
                );

                if (containers.length > 0) {
                    containers.forEach(function (container) {
                        var fieldsContainer = container.querySelector(
                            ".document-fields-container",
                        );
                        if (fieldsContainer) {
                            self.applyAllFieldsToContainer(container);
                        }
                    });
                }
            }

            this.timeUpdateInterval = setInterval(updateTimeIfNeeded, 5000);

            document.addEventListener("visibilitychange", function () {
                if (!document.hidden) {
                    updateTimeIfNeeded();
                }
            });

            window.addEventListener("focus", updateTimeIfNeeded);
        },

        stopTimeUpdateTimer: function () {
            if (this.timeUpdateInterval) {
                clearInterval(this.timeUpdateInterval);
                this.timeUpdateInterval = null;
            }
        },

        observeNewContainers: function () {
            var observer = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    mutation.addedNodes.forEach(function (node) {
                        if (node.nodeType === 1) {
                            if (
                                node.classList &&
                                node.classList.contains(
                                    "copyable-pre-container",
                                ) &&
                                node.classList.contains("auto-fill-enabled")
                            ) {
                                setTimeout(function () {
                                    DocumentAutoFillModule.setupAutoFillContainer(
                                        node,
                                    );
                                }, 100);
                            } else if (node.querySelectorAll) {
                                var containers = node.querySelectorAll(
                                    ".copyable-pre-container.auto-fill-enabled",
                                );
                                containers.forEach(function (container) {
                                    setTimeout(function () {
                                        DocumentAutoFillModule.setupAutoFillContainer(
                                            container,
                                        );
                                    }, 100);
                                });
                            }
                        }
                    });
                });
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true,
            });
        },
    };

    window.DocumentAutoFill = {
        refreshAll: function () {
            var containers = document.querySelectorAll(
                ".copyable-pre-container.auto-fill-enabled",
            );
            containers.forEach(function (container) {
                DocumentAutoFillModule.applyAllFieldsToContainer(container);
            });
        },

        clearAllNames: function () {
            localStorage.removeItem("documentAuthor");
            localStorage.removeItem("documentPosition");
            localStorage.removeItem("documentSignature");

            var inputs = document.querySelectorAll(
                ".author-input, .position-input, .signature-input",
            );
            inputs.forEach(function (input) {
                input.value = "";
            });

            window.DocumentAutoFill.refreshAll();
        },

        startTimeUpdates: function () {
            DocumentAutoFillModule.startTimeUpdateTimer();
        },

        stopTimeUpdates: function () {
            DocumentAutoFillModule.stopTimeUpdateTimer();
        },
    };

    var SnowflakesModule = {
        snowflakes: [],
        createInterval: null,
        isActive: false,
        snowflakeCount: 30,

        random: function (min, max) {
            return Math.random() * (max - min) + min;
        },

        init: function () {
            if (this.isActive) return;

            this.addStyles();
            this.startContinuousCreation();
            this.isActive = true;
        },

        addStyles: function () {
            if (document.getElementById("snowflakes-styles")) return;

            var style = document.createElement("style");
            style.id = "snowflakes-styles";
            style.textContent =
                ".snowflake {" +
                "position: fixed;" +
                "top: -10px;" +
                "color: #fff;" +
                "font-family: Arial, sans-serif;" +
                "user-select: none;" +
                "pointer-events: none;" +
                "z-index: 1;" +
                "animation-name: snowflake-fall;" +
                "animation-timing-function: linear;" +
                "animation-iteration-count: infinite;" +
                "will-change: transform;" +
                "}" +
                "@keyframes snowflake-fall {" +
                "0% {" +
                "transform: translateY(0) translateX(0) rotate(0deg);" +
                "}" +
                "25% {" +
                "transform: translateY(25vh) translateX(10px) rotate(90deg);" +
                "}" +
                "50% {" +
                "transform: translateY(50vh) translateX(-10px) rotate(180deg);" +
                "}" +
                "75% {" +
                "transform: translateY(75vh) translateX(10px) rotate(270deg);" +
                "}" +
                "100% {" +
                "transform: translateY(calc(100vh + 100px)) translateX(0) rotate(360deg);" +
                "}" +
                "}" +
                ".snowflake.flare {" +
                "text-shadow: 0 0 5px rgba(255, 255, 255, 0.4), 0 0 10px rgba(255, 255, 255, 0.2);" +
                "}" +
                ".snowflake.blurred {" +
                "filter: blur(2px);" +
                "}";

            document.head.appendChild(style);
        },

        createSnowflake: function () {
            var snowflake = document.createElement("div");
            snowflake.className = "snowflake";

            var symbol = this.getSnowflakeSymbol();
            snowflake.textContent = symbol;

            var fontSize = this.random(0.5, 1.2);
            snowflake.style.fontSize = fontSize + "em";
            snowflake.style.left = this.random(0, 100) + "vw";
            snowflake.style.opacity = this.random(0.25, 0.5);

            var fallDuration = this.random(10, 30);
            snowflake.style.animationDuration = fallDuration + "s";
            snowflake.style.animationDelay = this.random(0, 2) + "s";

            if (Math.random() > 0.95) {
                snowflake.classList.add("flare");
            }

            if (Math.random() > 0.4) {
                snowflake.classList.add("blurred");
            }

            document.body.appendChild(snowflake);
            this.snowflakes.push(snowflake);

            var self = this;
            var checkRemove = setInterval(function () {
                var rect = snowflake.getBoundingClientRect();
                if (rect.top > window.innerHeight + 50) {
                    if (snowflake.parentNode) {
                        snowflake.remove();
                    }
                    var index = self.snowflakes.indexOf(snowflake);
                    if (index > -1) {
                        self.snowflakes.splice(index, 1);
                    }
                    clearInterval(checkRemove);
                }
            }, 100);
        },

        getSnowflakeSymbol: function () {
            var symbols = ["❄", "❅", "❆"];
            return symbols[Math.floor(Math.random() * symbols.length)];
        },

        startContinuousCreation: function () {
            var self = this;
            this.createInterval = setInterval(function () {
                if (
                    self.isActive &&
                    self.snowflakes.length < self.snowflakeCount * 1.5
                ) {
                    self.createSnowflake();
                }
            }, 500);
        },

        stop: function () {
            this.isActive = false;

            if (this.createInterval) {
                clearInterval(this.createInterval);
                this.createInterval = null;
            }

            this.snowflakes.forEach(function (snowflake) {
                if (snowflake && snowflake.parentNode) {
                    snowflake.remove();
                }
            });
            this.snowflakes = [];

            var styles = document.getElementById("snowflakes-styles");
            if (styles) {
                styles.remove();
            }
        },

        toggle: function () {
            if (this.isActive) {
                this.stop();
            } else {
                this.init();
            }
        },
    };

    window.SnowflakesModule = SnowflakesModule;

    function initAllModules() {
        SidebarModule.init();
        AccessTooltipsModule.init();
        LawTooltipsModule.init();
        DataTooltipsModule.init();
        CopyTextModule.init();
        DocumentAutoFillModule.init();
        // SnowflakesModule.init()
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", initAllModules);
    } else {
        initAllModules();
    }
})();