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

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

Страница интерфейса MediaWiki
Нет описания правки
Метка: отменено
Нет описания правки
 
(не показано 30 промежуточных версий 2 участников)
Строка 1: Строка 1:
(function () {
(function() {
     "use strict";
     'use strict';
 
   
     window.MediaWikiTooltips = {
     window.MediaWikiTooltips = {
         loaded: false,
         loaded: false,
        loading: false,
         callbacks: [],
         callbacks: [],
         baseUrl: null,
          
 
         load: function(callback) {
         load: function (callback) {
             if (this.loaded && window.tippy) {
             if (this.loaded && window.tippy) {
                 callback();
                 callback();
                 return;
                 return;
             }
             }
 
           
             this.callbacks.push(callback);
             this.callbacks.push(callback);
 
           
             if (this.loading) return;
             if (this.loading) return;
             this.loading = true;
             this.loading = true;
 
           
             var self = this;
             var self = this;
             var cdns = [
             var popperScript = document.createElement('script');
                {
            popperScript.src = 'https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js';
                    base: "https://cdnjs.cloudflare.com/ajax/libs",
             popperScript.onload = function() {
                    popper: "/popper.js/2.11.8/umd/popper.min.js",
                 var tippyScript = document.createElement('script');
                    tippy: "/tippy.js/6.3.7/tippy-bundle.umd.min.js",
                 tippyScript.src = 'https://unpkg.com/tippy.js@6/dist/tippy-bundle.umd.min.js';
                    css: "/tippy.js/6.3.7/tippy.css",
                 tippyScript.onload = function() {
                },
                    self.loaded = true;
                {
                    self.loading = false;
                    base: "https://unpkg.com",
                    self.callbacks.forEach(function(cb) { cb(); });
                    popper: "/@popperjs/core@2/dist/umd/popper.min.js",
                     self.callbacks = [];
                    tippy: "/tippy.js@6/dist/tippy-bundle.umd.min.js",
                    css: "/tippy.js@6/dist/tippy.css",
                },
                {
                    base: "https://cdn.jsdelivr.net/npm",
                    popper: "/@popperjs/core@2/dist/umd/popper.min.js",
                    tippy: "/tippy.js@6/dist/tippy-bundle.umd.min.js",
                    css: "/tippy.js@6/dist/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) {
                        console.error("[MediaWikiTooltips] callback error:", e);
                     }
                });
                self.callbacks = [];
            };
 
            function urlJoin(base, path) {
                return base.replace(/\/$/, "") + "/" + path.replace(/^\//, "");
            }
 
            function loadTippyAndCss() {
                var cdn = cdns[cdnIndex];
                var tippyScript = document.createElement("script");
                tippyScript.src = urlJoin(cdn.base, cdn.tippy);
                tippyScript.onerror = function () {
                    if (cdnIndex + 1 < cdns.length) {
                        cdnIndex++;
                        console.warn(
                            "[MediaWikiTooltips] Повтор с CDN:",
                            cdns[cdnIndex].base,
                        );
                        loadPopper();
                    } else {
                        tryFallback("Tippy.js");
                    }
                };
                tippyScript.onload = function () {
                    if (!window.tippy) {
                        if (cdnIndex + 1 < cdns.length) {
                            cdnIndex++;
                            loadPopper();
                        } else {
                            tryFallback("Tippy.js (не в глобальной области)");
                        }
                        return;
                    }
                    var tippyCSS = document.createElement("link");
                    tippyCSS.rel = "stylesheet";
                    tippyCSS.href = urlJoin(cdn.base, cdn.css);
                    tippyCSS.onerror = function () {
                        console.warn(
                            "[MediaWikiTooltips] Стили Tippy не загрузились.",
                        );
                    };
                    document.head.appendChild(tippyCSS);
                    console.log(
                        "[MediaWikiTooltips] Tippy.js загружен с",
                        cdn.base,
                    );
                    runCallbacks();
                 };
                 };
                 document.head.appendChild(tippyScript);
                 document.head.appendChild(tippyScript);
             }
             };
 
             document.head.appendChild(popperScript);
             function loadPopper() {
           
                var cdn = cdns[cdnIndex];
            var tippyCSS = document.createElement('link');
                var popperScript = document.createElement("script");
            tippyCSS.rel = 'stylesheet';
                popperScript.src = urlJoin(cdn.base, cdn.popper);
            tippyCSS.href = 'https://unpkg.com/tippy.js@6/dist/tippy.css';
                popperScript.onerror = function () {
            document.head.appendChild(tippyCSS);
                    if (cdnIndex + 1 < cdns.length) {
         }
                        cdnIndex++;
                        console.warn(
                            "[MediaWikiTooltips] Повтор с CDN:",
                            cdns[cdnIndex].base,
                        );
                        loadPopper();
                    } else {
                        tryFallback("Popper.js");
                    }
                };
                popperScript.onload = loadTippyAndCss;
                document.head.appendChild(popperScript);
            }
 
            loadPopper();
         },
     };
     };
 
   
     var SidebarModule = {
     var SidebarModule = {
         init: function () {
         init: function() {
             var buttons = document.querySelectorAll(".боковая-панель-кнопка");
             var buttons = document.querySelectorAll('.боковая-панель-кнопка');
             var sections = document.querySelectorAll(".боковая-панель-раздел");
             var sections = document.querySelectorAll('.боковая-панель-раздел');
 
           
             if (buttons.length === 0) return;
             if (buttons.length === 0) return;
 
           
             buttons.forEach(function (button) {
             buttons.forEach(function(button) {
                 button.addEventListener("click", function () {
                 button.addEventListener('click', function() {
                     var targetId = this.dataset.target;
                     var targetId = this.dataset.target;
 
                   
                     buttons.forEach(function (btn) {
                     buttons.forEach(function(btn) { btn.classList.remove('active'); });
                        btn.classList.remove("active");
                     this.classList.add('active');
                    });
                   
                     this.classList.add("active");
                     sections.forEach(function(section) { section.classList.remove('default'); });
 
                     sections.forEach(function (section) {
                        section.classList.remove("default");
                    });
                     var targetSection = document.getElementById(targetId);
                     var targetSection = document.getElementById(targetId);
                     if (targetSection) {
                     if (targetSection) {
                         targetSection.classList.add("default");
                         targetSection.classList.add('default');
                     }
                     }
                 });
                 });
             });
             });
 
           
             buttons[0].click();
             buttons[0].click();
         },
         }
     };
     };
 
   
     var AccessTooltipsModule = {
     var AccessTooltipsModule = {
         init: function () {
         init: function() {
             var containers = document.querySelectorAll(".допуск-контейнер");
             var containers = document.querySelectorAll('.допуск-контейнер');
             if (containers.length === 0) return;
             if (containers.length === 0) return;
 
           
             MediaWikiTooltips.load(function () {
             MediaWikiTooltips.load(function() {
                 containers.forEach(function (container) {
                 containers.forEach(function(container) {
                     var contentElement =
                     var contentElement = container.querySelector('.допуск-подсказка');
                        container.querySelector(".допуск-подсказка");
                     if (!contentElement) return;
                     if (!contentElement) return;
 
                   
                     var content = contentElement.innerHTML;
                     var content = contentElement.innerHTML;
                     tippy(container, {
                     tippy(container, {
Строка 189: Строка 81:
                         allowHTML: true,
                         allowHTML: true,
                         interactive: true,
                         interactive: true,
                         placement: "auto",
                         placement: 'auto',
                         maxWidth: 500,
                         maxWidth: 500,
                         theme: "dark",
                         theme: 'dark',
                         arrow: true,
                         arrow: true,
                         duration: [200, 200],
                         duration: [200, 200],
Строка 197: Строка 89:
                             modifiers: [
                             modifiers: [
                                 {
                                 {
                                     name: "preventOverflow",
                                     name: 'preventOverflow',
                                     options: { padding: 8 },
                                     options: { padding: 8 }
                                 },
                                 },
                                 {
                                 {
                                     name: "flip",
                                     name: 'flip',
                                     options: {
                                     options: {
                                         fallbackPlacements: [
                                         fallbackPlacements: ['top', 'bottom', 'right', 'left']
                                            "top",
                                     }
                                            "bottom",
                                 }
                                            "right",
                             ]
                                            "left",
                         }
                                        ],
                                     },
                                 },
                             ],
                         },
                     });
                     });
                 });
                 });
             });
             });
         },
         }
     };
     };
   
var LawTooltipsModule = {
    init: function () {
        var tableCells = document.querySelectorAll('.law-tooltips td, .law-tooltips th');
        if (tableCells.length === 0) return;


    var LawTooltipsModule = {
         MediaWikiTooltips.load(function () {
         init: function () {
             var linkCodes = LawTooltipsModule.collectLinkCodes(tableCells);
            console.log("[LawTooltips] init()");
             var lawData = LawTooltipsModule.extractLawData(linkCodes);
             var tableCells = document.querySelectorAll(
            LawTooltipsModule.initCellTooltips(tableCells, lawData);
                ".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];
    collectLinkCodes: function (tableCells) {
                    var descriptionCell = cells[1];
        var codes = new Set();
                    var exampleCell = cells[2];


                    var anchor = crimeCell.querySelector("[id]");
        tableCells.forEach(function (cell) {
                    if (!anchor) return;
            var link = cell.querySelector('a[href^="#"]');
            if (!link) return;


                    var lawCode = anchor.id;
            var href = link.getAttribute('href') || '';
                    var titleElement = crimeCell.querySelector("b");
            var rawCode = LawTooltipsModule.extractCodeFromHref(href);
                    if (!titleElement || !lawCode.match(/^\d{3}$/)) return;
            if (!rawCode) return;


                    var titleText = titleElement.textContent.trim();
            codes.add(rawCode);
                    var lawTitle = titleText
            codes.add(LawTooltipsModule.normalizeLawCode(rawCode));
                        .replace(/^\d{3}\s*/, "")
            codes.add(LawTooltipsModule.toSectionCode(rawCode));
                        .replace(/^\d{3}/, "")
            codes.add(LawTooltipsModule.toLegacyCode(rawCode));
                        .trim();
        });
                    var description = descriptionCell.innerHTML.trim();


                    var examples = [];
        return codes;
                    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] = {
    extractLawData: function (expectedCodes) {
                        title: lawTitle,
        var lawData = {};
                        description: description,
        var crimeIdSpans = document.querySelectorAll('span[id]');
                        examples: examples,
                    };
                });
            });


            console.log(
        crimeIdSpans.forEach(function (span) {
                "[LawTooltips] extractLawData: итого кодов:",
            var rawId = (span.id || '').trim();
                Object.keys(lawData).length,
             if (!rawId) return;
            );
             return lawData;
        },


        createTooltipContent: function (lawCode, lawData) {
             var normalizedId = LawTooltipsModule.normalizeLawCode(rawId);
             var data = lawData[lawCode];
            if (!data) return null;


             var html = '<div class="law-tooltip">';
             if (expectedCodes && expectedCodes.size > 0) {
            html +=
                 var isExpected =
                 '<h4 class="law-tooltip-title">' +
                    expectedCodes.has(rawId) ||
                lawCode +
                    expectedCodes.has(normalizedId) ||
                " - " +
                    expectedCodes.has(LawTooltipsModule.toSectionCode(rawId)) ||
                data.title +
                    expectedCodes.has(LawTooltipsModule.toLegacyCode(rawId));
                "</h4>";
            html +=
                '<p class="law-tooltip-description">' +
                data.description +
                "</p>";


            if (data.examples && data.examples.length > 0) {
                if (!isExpected) return;
                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;
             var tr = span.closest('tr');
        },
            if (!tr) return;


        initCellTooltips: function (tableCells, lawData) {
             var cells = tr.querySelectorAll('td');
            var withLink = 0,
            if (cells.length < 3) return;
                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 crimeCell = cells[0];
                var match = href.match(/#(\d{3})/);
            var descriptionCell = cells[1];
                if (!match) return;
            var exampleCell = cells[2];
                withMatch++;


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


                cell.classList.add("law-cell-with-tooltip");
            var description = (descriptionCell.innerHTML || '').trim();
                tooltipsCreated++;
            var examples = LawTooltipsModule.extractExamplesFromExampleCell(exampleCell);


                cell.addEventListener("click", function (e) {
            var payload = {
                    e.preventDefault();
                 title: title,
                    window.location.hash = lawCode;
                 description: description,
                });
                 examples: examples,
 
                 sourceCode: rawId
                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) {
            LawTooltipsModule.addLawDataAliases(lawData, rawId, payload);
                    setTimeout(function () {
        });
                        DataTooltipsModule.init();
                    }, 100);
                }
            });


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


     var CopyTextModule = {
     addLawDataAliases: function (lawData, rawCode, payload) {
        init: function () {
        var normalized = LawTooltipsModule.normalizeLawCode(rawCode);
            CopyTextModule.initDirectElements();
        var sectionCode = LawTooltipsModule.toSectionCode(rawCode);
            CopyTextModule.initAutoContainers();
         var legacyCode = LawTooltipsModule.toLegacyCode(rawCode);
         },


         initDirectElements: function () {
         var keys = [rawCode, normalized, sectionCode, legacyCode];
            var elements = document.querySelectorAll(".copyable-text");
            if (elements.length === 0) return;


            elements.forEach(function (element) {
        keys.forEach(function (key) {
                CopyTextModule.cleanPreContent(element);
            if (!key) return;
                CopyTextModule.setupElement(element);
            lawData[key] = payload;
            });
        });
        },
    },


        initAutoContainers: function () {
extractTitleFromCrimeCell: function (crimeCell, rawCode) {
            var containers = document.querySelectorAll(
    var b = crimeCell.querySelector('b');
                ".copyable-pre-container",
    var text = (b ? b.textContent : crimeCell.textContent) || '';
            );
    text = text.replace(/\s+/g, ' ').trim();
            if (containers.length === 0) return;


            containers.forEach(function (container) {
    // Новый формат: "§ 1.1 Название"
                CopyTextModule.processContainerPre(container);
    text = text.replace(/^§\s*[\d]+(?:\.\s*[\d]+)*\s*/u, '').trim();
            });


            // Наблюдатель за динамически появляющимися элементами
    // Старый формат: "200Название" / "200 Название" / "200. Название"
            var observer = new MutationObserver(function (mutations) {
    var normalizedCode = LawTooltipsModule.normalizeLawCode(rawCode);
                mutations.forEach(function (mutation) {
    if (normalizedCode) {
                    mutation.addedNodes.forEach(function (node) {
        var escapedCode = normalizedCode.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
                        if (node.nodeType === 1) {
        var legacyPrefix = new RegExp(
                            // Проверяем сам элемент
            '^(?:§\\s*_?\\s*)?' + escapedCode + '[\\s\\u00A0\\-–—:.,)]*',
                            if (node.tagName === "PRE") {
            'u'
                                var container = node.closest(
        );
                                    ".copyable-pre-container",
        text = text.replace(legacyPrefix, '').trim();
                                );
    }
                                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 внутри уже существующих контейнеров
    return text;
                                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" -> "")
    extractExamplesFromExampleCell: function (exampleCell) {
                    if (
        var examples = [];
                        mutation.type === "attributes" &&
        var liItems = exampleCell.querySelectorAll('li');
                        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, {
        if (liItems.length > 0) {
                 childList: true,
             liItems.forEach(function (item) {
                 subtree: true,
                 var html = (item.innerHTML || '').trim();
                 attributes: true,
                 html = html.replace(/<br\s*\/?>/gi, '');
                 attributeFilter: ["style"],
                 html = html.trim();
                 if (html) examples.push(html);
             });
             });
         },
            return examples;
         }


         processContainerPre: function (container) {
         var fallback = (exampleCell.innerHTML || '').trim();
            var preElements = container.querySelectorAll("pre");
        fallback = fallback.replace(/<br\s*\/?>/gi, '').trim();
            preElements.forEach(function (pre) {
        if (fallback) examples.push(fallback);
                if (!pre.classList.contains("copyable-text")) {
                    pre.classList.add("copyable-text");
                    CopyTextModule.cleanPreContent(pre);
                    CopyTextModule.setupElement(pre);
                }
            });
        },


         cleanPreContent: function (preElement) {
         return examples;
            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) {
    extractCodeFromHref: function (href) {
            element.style.cursor = "pointer";
        var match = (href || '').match(/^#(.+)$/);
            element.addEventListener("click", CopyTextModule.handleClick);
        if (!match) return '';
        return (match[1] || '').trim();
    },


            var isIOS =
    normalizeLawCode: function (lawCode) {
                /iPad|iPhone|iPod/.test(navigator.userAgent) &&
        var code = (lawCode || '').trim();
                !window.MSStream;
        code = code.replace(/^#/, '');
            if (isIOS) {
        code = code.replace(/^§\s*_?\s*/u, '');
                element.style.webkitUserSelect = "text";
        code = code.replace(/\s+/g, '');
                element.style.userSelect = "text";
        return code;
            }
    },
        },


        handleClick: function (e) {
    toSectionCode: function (lawCode) {
            e.preventDefault();
        var normalized = LawTooltipsModule.normalizeLawCode(lawCode);
        if (!normalized) return '';
        return '§_' + normalized;
    },


            var element = this;
    toLegacyCode: function (lawCode) {
            var textToCopy = element.textContent;
        return LawTooltipsModule.normalizeLawCode(lawCode);
    },


            if (navigator.clipboard && window.isSecureContext) {
    formatLawCode: function (lawCode, data) {
                navigator.clipboard
        var raw = (data && data.sourceCode ? data.sourceCode : lawCode) || '';
                    .writeText(textToCopy)
        var normalized = LawTooltipsModule.normalizeLawCode(raw);
                    .then(function () {
                        CopyTextModule.showNotification(element);
                    })
                    .catch(function (err) {
                        console.error("Ошибка при копировании: ", err);
                        CopyTextModule.fallbackCopy(textToCopy, element);
                    });
            } else {
                CopyTextModule.fallbackCopy(textToCopy, element);
            }


            element.classList.add("copying");
        if (/^§\s*_?/u.test(raw) || raw.indexOf('§_') === 0 || normalized.indexOf('.') !== -1) {
            setTimeout(function () {
             return '§ ' + normalized;
                element.classList.remove("copying");
         }
             }, 200);
         },


         fallbackCopy: function (text, element) {
         return normalized || raw;
            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);
    createTooltipContent: function (lawCode, lawData) {
            textArea.focus();
        var data = lawData[lawCode];
            textArea.select();
        if (!data) return null;


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


            document.body.removeChild(textArea);
        var html = '<div class="law-tooltip">';
        },
        html += '<h4 class="law-tooltip-title">' + displayCode + ' - ' + data.title + '</h4>';
        html += '<p class="law-tooltip-description">' + (data.description || '') + '</p>';


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


            var notification = document.createElement("div");
        html += '</div>';
            notification.className = "copy-notification";
        return html;
            notification.textContent = "Текст скопирован!";
    },
            document.body.appendChild(notification);


            var rect = element.getBoundingClientRect();
    initCellTooltips: function (tableCells, lawData) {
             var isMobile = window.innerWidth <= 768;
        tableCells.forEach(function (cell) {
             if (cell.dataset.lawTooltipAttached === '1') return;


             if (isMobile) {
             var link = cell.querySelector('a[href^="#"]');
                notification.style.position = "fixed";
            if (!link) return;
                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 () {
             var href = link.getAttribute('href') || '';
                notification.classList.add("show");
            var rawCode = LawTooltipsModule.extractCodeFromHref(href);
             }, 10);
             if (!rawCode) return;


            setTimeout(function () {
             var candidateKeys = [
                notification.classList.remove("show");
                 rawCode,
                setTimeout(function () {
                 LawTooltipsModule.normalizeLawCode(rawCode),
                    if (notification.parentNode) {
                 LawTooltipsModule.toSectionCode(rawCode),
                        notification.remove();
                 LawTooltipsModule.toLegacyCode(rawCode)
                    }
             ];
                }, 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(
             var tooltipContent = null;
                function () {
             var matchedKey = null;
                    this.applyAllFieldsToContainer(container);
                }.bind(this),
                100,
             );
        },


        createField: function (fieldType, labelText, placeholder, shouldCache) {
            for (var i = 0; i < candidateKeys.length; i++) {
            var fieldContainer = document.createElement("div");
                var key = candidateKeys[i];
            fieldContainer.className = "field-container";
                if (!key) continue;


            var label = document.createElement("label");
                 tooltipContent = LawTooltipsModule.createTooltipContent(key, lawData);
            label.textContent = labelText;
                 if (tooltipContent) {
            label.htmlFor = fieldType + "-input";
                     matchedKey = key;
 
                    break;
            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(
             if (!tooltipContent) return;
                "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
             cell.dataset.lawTooltipAttached = '1';
                .querySelector(".site-number-input")
             cell.classList.add('law-cell-with-tooltip');
                .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, {
             cell.addEventListener('click', function (e) {
                 siteNumber: siteNumber,
                 e.preventDefault();
                 author: author,
                 window.location.hash = rawCode;
                position: position,
                signature: signature,
             });
             });
        },


        autoFillAllDocumentsInContainer: function (container, fields) {
            tippy(cell, {
            var preElements = container.querySelectorAll("pre");
                 content: tooltipContent,
 
                 allowHTML: true,
            preElements.forEach(
                 interactive: true,
                 function (preElement) {
                 placement: 'top',
                    this.autoFillDocument(preElement, fields);
                 maxWidth: 400,
                 }.bind(this),
                 theme: 'law-theme',
            );
                 arrow: true,
        },
                 duration: [200, 150],
 
                 delay: [0, 50],
        autoFillDocument: function (preElement, fields) {
                appendTo: document.body,
            if (!preElement) return;
                 boundary: 'viewport',
 
                 popperOptions: {
            var originalContent =
                     strategy: 'fixed',
                 preElement.textContent || preElement.innerText;
                     modifiers: [
 
                        {
            if (!this.isDocumentTemplate(originalContent)) {
                            name: 'preventOverflow',
                return;
                            options: {
            }
                                boundary: 'viewport',
 
                                padding: 20,
            var updatedContent = this.fillDocumentContent(
                                altAxis: true,
                originalContent,
                                altBoundary: true
                 fields,
                            }
            );
                        },
 
                        {
            if (updatedContent !== originalContent) {
                            name: 'flip',
                 preElement.textContent = updatedContent;
                            options: {
            }
                                fallbackPlacements: ['bottom', 'left', 'right']
        },
                            }
 
                        },
        isDocumentTemplate: function (text) {
                        {
            return (
                            name: 'computeStyles',
                 text.includes("SCP") &&
                            options: { adaptive: false }
                text.includes("Обезопасить. Удержать. Сохранить.") &&
                        }
                text.includes("Автор документа:") &&
                    ]
                 text.includes("Дата и время:")
                 },
            );
                onShow: function (instance) {
        },
                    document.querySelectorAll('[data-tippy-root]').forEach(function (tooltip) {
 
                         if (tooltip._tippy && tooltip._tippy !== instance) {
        fillDocumentContent: function (content, fields) {
                             tooltip._tippy.hide();
            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 () {
    var DataTooltipsModule = {
             if (this.timeUpdateInterval) {
         init: function() {
                 clearInterval(this.timeUpdateInterval);
            var elements = document.querySelectorAll('.data-tooltip[data-text]');
                this.timeUpdateInterval = null;
             if (elements.length === 0) return;
            }
           
        },
            MediaWikiTooltips.load(function() {
 
                 elements.forEach(function(element) {
        observeNewContainers: function () {
                    var tooltipText = element.getAttribute('data-text');
             var observer = new MutationObserver(function (mutations) {
                    if (!tooltipText || !tooltipText.trim()) return;
                 mutations.forEach(function (mutation) {
                   
                     mutation.addedNodes.forEach(function (node) {
                    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.nodeType === 1) {
                             if (
                             if (node.classList && node.classList.contains('data-tooltip') && node.hasAttribute('data-text')) {
                                node.classList &&
                                 shouldReinit = true;
                                node.classList.contains(
                                    "copyable-pre-container",
                                ) &&
                                node.classList.contains("auto-fill-enabled")
                            ) {
                                 setTimeout(function () {
                                    DocumentAutoFillModule.setupAutoFillContainer(
                                        node,
                                    );
                                }, 100);
                             } else if (node.querySelectorAll) {
                             } else if (node.querySelectorAll) {
                                 var containers = node.querySelectorAll(
                                 var newElements = node.querySelectorAll('.data-tooltip[data-text]');
                                    ".copyable-pre-container.auto-fill-enabled",
                                 if (newElements.length > 0) {
                                );
                                     shouldReinit = true;
                                 containers.forEach(function (container) {
                                 }
                                     setTimeout(function () {
                                        DocumentAutoFillModule.setupAutoFillContainer(
                                            container,
                                        );
                                    }, 100);
                                 });
                             }
                             }
                         }
                         }
                     });
                     });
                 });
                 });
               
                if (shouldReinit) {
                    setTimeout(function() { DataTooltipsModule.init(); }, 100);
                }
             });
             });
 
           
             observer.observe(document.body, {
             observer.observe(document.body, {
                 childList: true,
                 childList: true,
                 subtree: 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');


    window.DocumentAutoFill = {
    if (cleanedText !== text) {
        refreshAll: function () {
        preElement.textContent = cleanedText;
            var containers = document.querySelectorAll(
    }
                ".copyable-pre-container.auto-fill-enabled",
},
            );
   
            containers.forEach(function (container) {
    setupElement: function(element) {
                DocumentAutoFillModule.applyAllFieldsToContainer(container);
        element.style.cursor = 'pointer';
            });
        element.addEventListener('click', CopyTextModule.handleClick);
        },
       
 
        var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
        clearAllNames: function () {
        if (isIOS) {
            localStorage.removeItem("documentAuthor");
            element.style.webkitUserSelect = 'text';
            localStorage.removeItem("documentPosition");
            element.style.userSelect = 'text';
            localStorage.removeItem("documentSignature");
        }
 
    },
            var inputs = document.querySelectorAll(
   
                ".author-input, .position-input, .signature-input",
    handleClick: function(e) {
            );
        e.preventDefault();
            inputs.forEach(function (input) {
       
                input.value = "";
        var element = this;
            });
        var textToCopy = element.textContent;
 
       
            window.DocumentAutoFill.refreshAll();
        if (navigator.clipboard && window.isSecureContext) {
        },
            navigator.clipboard.writeText(textToCopy).then(function() {
 
                CopyTextModule.showNotification(element);
        startTimeUpdates: function () {
            }).catch(function(err) {
            DocumentAutoFillModule.startTimeUpdateTimer();
                console.error('Ошибка при копировании: ', err);
        },
                CopyTextModule.fallbackCopy(textToCopy, element);
 
            });
        stopTimeUpdates: function () {
        } else {
            DocumentAutoFillModule.stopTimeUpdateTimer();
            CopyTextModule.fallbackCopy(textToCopy, element);
        },
        }
    };
       
 
        element.classList.add('copying');
    var LawCalculatorModule = {
        setTimeout(function() {
        isCalculatorMode: false,
            element.classList.remove('copying');
        selectedViolations: [],
        }, 200);
        lawData: {},
    },
 
   
        init: function () {
    fallbackCopy: function(text, element) {
            this.extractLawData();
        var textArea = document.createElement('textarea');
            this.createModeToggle();
        textArea.value = text;
            this.observeTables();
        textArea.style.position = 'fixed';
        },
        textArea.style.top = '-9999px';
 
        textArea.style.left = '-9999px';
        extractLawData: function () {
        textArea.style.width = '2em';
            // Используем данные из существующего LawTooltipsModule
        textArea.style.height = '2em';
            if (
        textArea.style.padding = '0';
                window.LawTooltipsModule &&
        textArea.style.border = 'none';
                window.LawTooltipsModule.extractLawData
        textArea.style.outline = 'none';
            ) {
        textArea.style.boxShadow = 'none';
                this.lawData = window.LawTooltipsModule.extractLawData();
        textArea.style.background = 'transparent';
            } else {
       
                // Fallback - извлекаем данные самостоятельно
        document.body.appendChild(textArea);
                this.lawData = this.extractLawDataFallback();
        textArea.focus();
            }
        textArea.select();
 
       
            // Добавляем информацию о категориях и сроках
        try {
            this.addCategoryInfo();
            var successful = document.execCommand('copy');
        },
            if (successful) {
 
                CopyTextModule.showNotification(element);
        extractLawDataFallback: function () {
            } else {
            var lawData = {};
                console.error('Не удалось скопировать текст');
            var detailTables = document.querySelectorAll(
            }
                ".mw-collapsible-content table",
        } catch (err) {
            );
            console.error('Ошибка при копировании: ', err);
 
        }
            detailTables.forEach(function (table) {
       
                var rows = table.querySelectorAll("tr");
        document.body.removeChild(textArea);
 
    },
                rows.forEach(function (row) {
   
                    var cells = row.querySelectorAll("td");
    showNotification: function(element) {
                    if (cells.length < 3) return;
        var existingNotifications = document.querySelectorAll('.copy-notification');
 
        existingNotifications.forEach(function(notification) {
                    var crimeCell = cells[0];
            notification.remove();
                    var anchor = crimeCell.querySelector("[id]");
        });
                    if (!anchor) return;
       
 
        var notification = document.createElement('div');
                    var lawCode = anchor.id;
        notification.className = 'copy-notification';
                    var titleElement = crimeCell.querySelector("b");
        notification.textContent = 'Текст скопирован!';
 
        document.body.appendChild(notification);
                    if (titleElement && lawCode.match(/^\d{3}$/)) {
       
                        var titleText = titleElement.textContent.trim();
        var rect = element.getBoundingClientRect();
                        var lawTitle = titleText
        var isMobile = window.innerWidth <= 768;
                            .replace(/^\d{3}\s*/, "")
       
                            .replace(/^\d{3}/, "")
        if (isMobile) {
                            .trim();
            notification.style.position = 'fixed';
                        var description = cells[1].innerHTML.trim();
            notification.style.top = '20px';
 
            notification.style.left = '50%';
                        var examples = [];
            notification.style.transform = 'translateX(-50%) translateY(-10px)';
                        var exampleItems = cells[2].querySelectorAll("li");
            notification.style.zIndex = '10000';
                        exampleItems.forEach(function (item) {
        } else {
                            var html = item.innerHTML.trim();
            notification.style.position = 'absolute';
                            html = html.replace(/<br\s*\/?>/gi, "");
            notification.style.top = (rect.top + window.scrollY - 40) + 'px';
                            if (html) examples.push(html);
            notification.style.left = (rect.left + window.scrollX) + 'px';
                        });
            notification.style.zIndex = '9999';
 
        }
                        lawData[lawCode] = {
       
                            title: lawTitle,
        setTimeout(function() {
                            description: description,
            notification.classList.add('show');
                            examples: examples,
        }, 10);
                        };
       
                    }
        setTimeout(function() {
                });
            notification.classList.remove('show');
            });
            setTimeout(function() {
 
                if (notification.parentNode) {
            return lawData;
                    notification.remove();
        },
                }
 
            }, 300);
        addCategoryInfo: function () {
        }, 2000);
            // Определяем тип страницы и извлекаем информацию о наказаниях
    }
            this.pageType = this.detectPageType();
};
            this.extractPunishmentInfo();
   
        },
var DocumentAutoFillModule = {
 
    init: function() {
        detectPageType: function () {
        this.processExistingContainers();
            // Проверяем заголовки страницы для определения типа
        this.observeNewContainers();
            var pageTitle = document.title.toLowerCase();
        this.startTimeUpdateTimer();
            var headings = document.querySelectorAll("h1, h2, h3");
    },
 
   
            for (var i = 0; i < headings.length; i++) {
    processExistingContainers: function() {
                var headingText = headings[i].textContent.toLowerCase();
        var containers = document.querySelectorAll('.copyable-pre-container.auto-fill-enabled');
                if (
       
                    headingText.includes(-класс") ||
        containers.forEach(function(container) {
                    headingText.includes("d-class")
            this.setupAutoFillContainer(container);
                ) {
        }.bind(this));
                    return "d-class";
    },
                }
   
                if (
    setupAutoFillContainer: function(container) {
                    headingText.includes("персонал") ||
        if (container.querySelector('.document-fields-container')) {
                    headingText.includes("сотрудник")
            return;
                ) {
        }
                    return "personnel";
       
                }
        var fieldsContainer = document.createElement('div');
            }
        fieldsContainer.className = 'document-fields-container';
 
       
            // Проверяем по URL или другим признакам
        var gridContainer = document.createElement('div');
            if (
        gridContainer.className = 'fields-grid';
                window.location.href.toLowerCase().includes("d-class") ||
       
                window.location.href.toLowerCase().includes("д-класс")
        var siteNumberField = this.createField('site-number', 'Номер участка:', 'Введите номер участка...', false);
            ) {
        var authorField = this.createField('author', 'Автор документа:', 'Введите ваше имя...', true);
                return "d-class";
        var positionField = this.createField('position', 'Должность:', 'Введите вашу должность...', true);
            }
        var signatureField = this.createField('signature', 'Подпись:', 'Введите подпись...', true);
 
       
            return "personnel"; // По умолчанию
        gridContainer.appendChild(siteNumberField.container);
        },
        gridContainer.appendChild(authorField.container);
 
        gridContainer.appendChild(positionField.container);
        extractPunishmentInfo: function () {
        gridContainer.appendChild(signatureField.container);
            // Извлекаем информацию о наказаниях из текста на странице
       
            var punishmentSections = document.querySelectorAll(
        fieldsContainer.appendChild(gridContainer);
                ".mw-collapsible-content p",
       
            );
        if (container.firstChild) {
            var categories = {};
            container.insertBefore(fieldsContainer, container.firstChild);
 
        } else {
            console.log(
            container.appendChild(fieldsContainer);
                "LawCalculator: Найдено секций с наказаниями:",
        }
                punishmentSections.length,
       
            );
        setTimeout(function() {
            console.log("LawCalculator: Тип страницы:", this.pageType);
            this.applyAllFieldsToContainer(container);
 
        }.bind(this), 100);
            punishmentSections.forEach(
    },
                function (section) {
   
                    var text = section.textContent;
    createField: function(fieldType, labelText, placeholder, shouldCache) {
                    var punishmentMatch = text.match(
        var fieldContainer = document.createElement('div');
                        /Наказание[:\s]*(.+?)(?:\n|$)/i,
        fieldContainer.className = 'field-container';
                    );
       
 
        var label = document.createElement('label');
                    if (punishmentMatch) {
        label.textContent = labelText;
                        var punishmentText = punishmentMatch[1].trim();
        label.htmlFor = fieldType + '-input';
                        console.log(
       
                            "LawCalculator: Найдено наказание:",
        var input = document.createElement('input');
                            punishmentText,
        input.type = 'text';
                        );
        input.id = fieldType + '-input';
 
        input.className = 'field-input ' + fieldType + '-input';
                        var categoryInfo =
        input.placeholder = placeholder;
                            this.parsePunishmentText(punishmentText);
       
 
        if (shouldCache) {
                        if (categoryInfo) {
            var savedValue = localStorage.getItem('document' + this.capitalizeFirstLetter(fieldType));
                            // Находим соответствующую категорию по цвету или другим признакам
            if (savedValue) {
                            var categoryKey =
                input.value = savedValue;
                                this.findCategoryByContext(section);
            }
                            if (categoryKey) {
        }
                                categories[categoryKey] = categoryInfo;
       
                                console.log(
        input.addEventListener('input', function() {
                                    "LawCalculator: Применено к категории:",
            var value = input.value.trim();
                                    categoryKey,
            if (shouldCache) {
                                    categoryInfo,
                localStorage.setItem('document' + this.capitalizeFirstLetter(fieldType), value);
                                );
            }
                            }
        }.bind(this));
                        }
       
                    }
        fieldContainer.appendChild(label);
                }.bind(this),
        fieldContainer.appendChild(input);
            );
       
 
        return {
            console.log("LawCalculator: Итоговые категории:", categories);
            container: fieldContainer,
 
            input: input,
            // Применяем найденную информацию к законам
            type: fieldType,
            this.applyCategoryInfo(categories);
            shouldCache: shouldCache
        },
        };
 
    },
        parsePunishmentText: function (text) {
   
            // Парсим текст наказания
    capitalizeFirstLetter: function(string) {
            var originalText = text;
        return string.charAt(0).toUpperCase() + string.slice(1);
            text = text.toLowerCase();
    },
 
   
            // Проверяем на критические нарушения
    applyAllFieldsToContainer: function(container) {
            if (
        var fieldsContainer = container.querySelector('.document-fields-container');
                text.includes(-класс") ||
        if (!fieldsContainer) return;
                text.includes("казнь") ||
       
                text.includes("перевод")
        var siteNumber = fieldsContainer.querySelector('.site-number-input').value.trim();
            ) {
        var author = fieldsContainer.querySelector('.author-input').value.trim();
                return {
        var position = fieldsContainer.querySelector('.position-input').value.trim();
                    isCritical: true,
        var signature = fieldsContainer.querySelector('.signature-input').value.trim();
                    punishment: originalText.trim(),
       
                };
        this.autoFillAllDocumentsInContainer(container, {
            }
            siteNumber: siteNumber,
 
            author: author,
            // Для Д-класса ищем штрафы
            position: position,
            if (this.pageType === "d-class") {
            signature: signature
                return this.parseDClassPunishment(originalText);
        });
            }
    },
 
   
            // Для персонала ищем временные рамки
    autoFillAllDocumentsInContainer: function(container, fields) {
            return this.parsePersonnelPunishment(originalText);
        var preElements = container.querySelectorAll('pre');
        },
       
 
        preElements.forEach(function(preElement) {
        parseDClassPunishment: function (text) {
            this.autoFillDocument(preElement, fields);
            // Парсим наказания для Д-класса (штрафы, дополнительные наказания)
        }.bind(this));
            var lowerText = text.toLowerCase();
    },
 
   
            // Ищем штрафы в кредитах
    autoFillDocument: function(preElement, fields) {
            var creditMatch = text.match(/(\d+[\s,.]?\d*)\s*кредит/);
        if (!preElement) return;
            if (creditMatch) {
       
                var amount = creditMatch[1].replace(/[\s,]/g, "");
        var originalContent = preElement.textContent || preElement.innerText;
                return {
       
                    isCritical: false,
        if (!this.isDocumentTemplate(originalContent)) {
                    punishment: "Штраф " + amount + " кредитов",
            return;
                    fine: parseInt(amount),
        }
                };
       
            }
        var updatedContent = this.fillDocumentContent(originalContent, fields);
 
       
            // Ищем временные наказания для Д-класса
        if (updatedContent !== originalContent) {
            var timeMatch = text.match(/(\d+)\s*до\s*(\d+)\s*минут/);
            preElement.textContent = updatedContent;
            if (timeMatch) {
        }
                return {
    },
                    minTime: parseInt(timeMatch[1]),
   
                    maxTime: parseInt(timeMatch[2]),
    isDocumentTemplate: function(text) {
                    isCritical: false,
        return text.includes('SCP') &&
                    punishment: timeMatch[1] + "-" + timeMatch[2] + " минут",
              text.includes('Обезопасить. Удержать. Сохранить.') &&
                };
              text.includes('Автор документа:') &&
            }
              text.includes('Дата и время:');
 
    },
            var rangeMatch = text.match(/от\s*(\d+)\s*до\s*(\d+)\s*минут/);
   
            if (rangeMatch) {
fillDocumentContent: function(content, fields) {
                return {
    var currentDateTime = this.getCurrentDateTime();
                    minTime: parseInt(rangeMatch[1]),
   
                    maxTime: parseInt(rangeMatch[2]),
    content = content.replace(
                    isCritical: false,
        /(\[bold\]Дата и время:\[\/bold\])([^\n]*)/g,
                    punishment: rangeMatch[1] + "-" + rangeMatch[2] + " минут",
        '$1 ' + currentDateTime
                };
    );
            }
   
 
    if (fields.siteNumber) {
            var fixedMatch = text.match(/(\d+)\s*минут/);
        content = content.replace(
            if (fixedMatch) {
            /Участок-([^|\n\[\]]*)/g,  
                var time = parseInt(fixedMatch[1]);
            'Участок-' + fields.siteNumber + ' '
                return {
        );
                    minTime: time,
    }
                    maxTime: time,
   
                    isCritical: false,
    if (fields.author) {
                    punishment: time + " минут",
        content = content.replace(
                };
            /(\[bold\]Автор документа:\[\/bold\])([^\n]*)/g,
            }
            '$1 ' + fields.author
 
        );
            // Если не нашли конкретные наказания, возвращаем текст как есть
    } else {
            return {
        content = content.replace(
                isCritical: false,
            /(\[bold\]Автор документа:\[\/bold\])([^\n]*)/g,  
                punishment: text.trim(),
            '$1'
            };
        );
        },
    }
 
   
        parsePersonnelPunishment: function (text) {
    if (fields.position) {
            // Парсим наказания для персонала
        content = content.replace(
            var lowerText = text.toLowerCase();
            /(\[bold\]Должность:\[\/bold\])([^\n]*)/g,
 
            '$1 ' + fields.position
            // Ищем временные рамки
        );
            var timeMatch = text.match(/(\d+)\s*до\s*(\d+)\s*минут/);
    } else {
            if (timeMatch) {
        content = content.replace(
                return {
            /(\[bold\]Должность:\[\/bold\])([^\n]*)/g,
                    minTime: parseInt(timeMatch[1]),
            '$1'
                    maxTime: parseInt(timeMatch[2]),
        );
                    isCritical: false,
    }
                };
   
            }
    if (fields.signature) {
 
        content = content.replace(
            // Ищем диапазон времени
            /(\[bold\]Подпись:\[\/bold\])[^\n]*/g,  
            var rangeMatch = text.match(/от\s*(\d+)\s*до\s*(\d+)\s*минут/);
            '$1 ' + fields.signature
            if (rangeMatch) {
        );
                return {
    } else {
                    minTime: parseInt(rangeMatch[1]),
        content = content.replace(
                    maxTime: parseInt(rangeMatch[2]),
            /(\[bold\]Подпись:\[\/bold\])([^\n]*)/g,
                    isCritical: false,
            '$1_____'
                };
        );
            }
    }
 
   
            // Ищем фиксированное время
    return content;
            var fixedMatch = text.match(/(\d+)\s*минут/);
},
            if (fixedMatch) {
   
                var time = parseInt(fixedMatch[1]);
    getCurrentDateTime: function() {
                return {
        var now = new Date();
                    minTime: time,
       
                    maxTime: time,
        var day = String(now.getDate()).padStart(2, '0');
                    isCritical: false,
        var month = String(now.getMonth() + 1).padStart(2, '0');
                };
        var year = now.getFullYear();
            }
       
 
        var hours = String(now.getHours()).padStart(2, '0');
            return null;
        var minutes = String(now.getMinutes()).padStart(2, '0');
        },
       
 
        return hours + ':' + minutes + ', ' + day + '.' + month + '.' + year;
        findCategoryByContext: function (section) {
    },
            // Находим категорию по контексту (заголовок секции)
   
            var collapsible = section.closest(".mw-collapsible");
    startTimeUpdateTimer: function() {
            if (!collapsible) return null;
        var self = this;
 
       
            var toggle = collapsible.querySelector(".mw-collapsible-toggle");
        function updateTimeIfNeeded() {
            if (!toggle) return null;
            var containers = document.querySelectorAll('.copyable-pre-container.auto-fill-enabled');
 
           
            var titleElement = toggle.querySelector(".рамка-название");
            if (containers.length > 0) {
            if (!titleElement) return null;
                containers.forEach(function(container) {
 
                    var fieldsContainer = container.querySelector('.document-fields-container');
            var title = titleElement.textContent.trim();
                    if (fieldsContainer) {
 
                        self.applyAllFieldsToContainer(container);
            // Определяем диапазон статей по заголовку
                    }
            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";
        this.timeUpdateInterval = setInterval(updateTimeIfNeeded, 5000);
            if (title.includes("Критические")) return "500-511";
       
 
        document.addEventListener('visibilitychange', function() {
            return null;
            if (!document.hidden) {
        },
                updateTimeIfNeeded();
 
            }
        applyCategoryInfo: function (categories) {
        });
            // Применяем информацию о категориях к законам
       
            Object.keys(this.lawData).forEach(
        window.addEventListener('focus', updateTimeIfNeeded);
                function (lawCode) {
    },
                    var code = parseInt(lawCode);
   
                    var categoryKey = null;
    stopTimeUpdateTimer: function() {
 
        if (this.timeUpdateInterval) {
                    // Определяем категорию по номеру статьи
            clearInterval(this.timeUpdateInterval);
                    if (code >= 100 && code <= 109) categoryKey = "100-109";
            this.timeUpdateInterval = null;
                    else if (code >= 200 && code <= 211)
        }
                        categoryKey = "200-211";
    },
                    else if (code >= 300 && code <= 311)
   
                        categoryKey = "300-311";
    observeNewContainers: function() {
                    else if (code >= 400 && code <= 411)
        var observer = new MutationObserver(function(mutations) {
                        categoryKey = "400-411";
            mutations.forEach(function(mutation) {
                    else if (code >= 500 && code <= 511)
                mutation.addedNodes.forEach(function(node) {
                        categoryKey = "500-511";
                    if (node.nodeType === 1) {
 
                        if (node.classList &&  
                    if (categoryKey && categories[categoryKey]) {
                            node.classList.contains('copyable-pre-container') &&  
                        this.lawData[lawCode].category =
                            node.classList.contains('auto-fill-enabled')) {
                            categories[categoryKey];
                            setTimeout(function() {
                    }
                                DocumentAutoFillModule.setupAutoFillContainer(node);
                }.bind(this),
                            }, 100);
            );
                        }
        },
                        else if (node.querySelectorAll) {
 
                            var containers = node.querySelectorAll('.copyable-pre-container.auto-fill-enabled');
        createModeToggle: function () {
                            containers.forEach(function(container) {
            var tables = document.querySelectorAll(
                                setTimeout(function() {
                ".citizen-table-wrapper table",
                                    DocumentAutoFillModule.setupAutoFillContainer(container);
            );
                                }, 100);
 
                            });
            tables.forEach(
                        }
                function (table) {
                    }
                    var wrapper = table.closest(".citizen-table-wrapper");
                });
                    if (!wrapper || wrapper.querySelector(".law-mode-toggle"))
            });
                        return;
        });
 
       
                    var toggleContainer = document.createElement("div");
        observer.observe(document.body, {
                    toggleContainer.className = "law-mode-toggle";
            childList: true,
                    toggleContainer.innerHTML =
            subtree: true
                        '<div class="mode-toggle-buttons">' +
        });
                        '<button class="mode-btn active" data-mode="navigation">Навигация</button>' +
    }
                        '<button class="mode-btn" data-mode="calculator">Калькулятор</button>' +
};
                        "</div>";
 
window.DocumentAutoFill = {
                    wrapper.appendChild(toggleContainer);
    refreshAll: function() {
 
        var containers = document.querySelectorAll('.copyable-pre-container.auto-fill-enabled');
                    // Добавляем стили
        containers.forEach(function(container) {
                    this.addToggleStyles();
            DocumentAutoFillModule.applyAllFieldsToContainer(container);
 
        });
                    // Обработчики событий
    },
                    var buttons = toggleContainer.querySelectorAll(".mode-btn");
   
                    buttons.forEach(function (btn) {
    clearAllNames: function() {
                        btn.addEventListener("click", function () {
        localStorage.removeItem('documentAuthor');
                            buttons.forEach(function (b) {
        localStorage.removeItem('documentPosition');
                                b.classList.remove("active");
        localStorage.removeItem('documentSignature');
                            });
       
                            this.classList.add("active");
        var inputs = document.querySelectorAll('.author-input, .position-input, .signature-input');
 
        inputs.forEach(function(input) {
                            var mode = this.dataset.mode;
            input.value = '';
                            LawCalculatorModule.setMode(mode, table);
        });
                        });
       
                    });
        window.DocumentAutoFill.refreshAll();
                }.bind(this),
    },
            );
   
        },
    startTimeUpdates: function() {
 
        DocumentAutoFillModule.startTimeUpdateTimer();
        addToggleStyles: function () {
    },
            if (document.getElementById("law-calculator-styles")) return;
   
 
    stopTimeUpdates: function() {
            var style = document.createElement("style");
        DocumentAutoFillModule.stopTimeUpdateTimer();
            style.id = "law-calculator-styles";
    }
            style.textContent =
};
                ".law-mode-toggle {" +
                "margin: 10px 0;" +
var LawCalculatorModule = {
                "text-align: center;" +
    isCalculatorMode: false,
                "}" +
    selectedViolations: [],
                ".mode-toggle-buttons {" +
    lawData: {},
                "display: inline-flex;" +
   
                "background: #f0f0f0;" +
    init: function() {
                "border-radius: 5px;" +
        this.extractLawData();
                "padding: 2px;" +
        this.createModeToggle();
                "border: 1px solid #ccc;" +
        this.observeTables();
                "}" +
    },
                ".mode-btn {" +
   
                "padding: 8px 16px;" +
    extractLawData: function() {
                "border: none;" +
        // Используем данные из существующего LawTooltipsModule
                "background: transparent;" +
        if (window.LawTooltipsModule && window.LawTooltipsModule.extractLawData) {
                "cursor: pointer;" +
            this.lawData = window.LawTooltipsModule.extractLawData();
                "border-radius: 3px;" +
        } else {
                "font-size: 14px;" +
            // Fallback - извлекаем данные самостоятельно
                "transition: all 0.2s;" +
            this.lawData = this.extractLawDataFallback();
                "}" +
        }
                ".mode-btn.active {" +
       
                "background: #007cba;" +
        // Добавляем информацию о категориях и сроках
                "color: white;" +
        this.addCategoryInfo();
                "}" +
    },
                ".mode-btn:hover:not(.active) {" +
   
                "background: #e0e0e0;" +
    extractLawDataFallback: function() {
                "}" +
        var lawData = {};
                ".calculator-interface {" +
        var detailTables = document.querySelectorAll('.mw-collapsible-content table');
                "margin-top: 15px;" +
       
                "padding: 15px;" +
        detailTables.forEach(function(table) {
                "background: #f9f9f9;" +
            var rows = table.querySelectorAll('tr');
                "border: 1px solid #ddd;" +
           
                "border-radius: 5px;" +
            rows.forEach(function(row) {
                "display: none;" +
                var cells = row.querySelectorAll('td');
                "}" +
                if (cells.length < 3) return;
                ".calculator-interface.active {" +
               
                "display: block;" +
                var crimeCell = cells[0];
                "}" +
                var anchor = crimeCell.querySelector('[id]');
                ".selected-violations {" +
                if (!anchor) return;
                "margin: 10px 0;" +
               
                "}" +
                var lawCode = anchor.id;
                ".violation-item {" +
                var titleElement = crimeCell.querySelector('b');
                "display: inline-block;" +
               
                "margin: 5px;" +
                if (titleElement && lawCode.match(/^\d{3}$/)) {
                "padding: 5px 10px;" +
                    var titleText = titleElement.textContent.trim();
                "background: #e3f2fd;" +
                    var lawTitle = titleText.replace(/^\d{3}\s*/, '').replace(/^\d{3}/, '').trim();
                "border: 1px solid #2196f3;" +
                    var description = cells[1].innerHTML.trim();
                "border-radius: 15px;" +
                   
                "font-size: 12px;" +
                    var examples = [];
                "}" +
                    var exampleItems = cells[2].querySelectorAll('li');
                ".violation-item .remove-btn {" +
                    exampleItems.forEach(function(item) {
                "margin-left: 5px;" +
                        var html = item.innerHTML.trim();
                "cursor: pointer;" +
                        html = html.replace(/<br\s*\/?>/gi, '');
                "color: #f44336;" +
                        if (html) examples.push(html);
                "font-weight: bold;" +
                    });
                "}" +
                   
                ".calculation-result {" +
                    lawData[lawCode] = {
                "margin-top: 15px;" +
                        title: lawTitle,
                "padding: 10px;" +
                        description: description,
                "background: #fff3cd;" +
                        examples: examples
                "border: 1px solid #ffeaa7;" +
                    };
                "border-radius: 5px;" +
                }
                "font-weight: bold;" +
            });
                "white-space: pre-line;" +
        });
                "}" +
       
                ".calculation-result.critical {" +
        return lawData;
                "background: #f8d7da;" +
    },
                "border-color: #f5c6cb;" +
   
                "color: #721c24;" +
    addCategoryInfo: function() {
                "}" +
        // Определяем тип страницы и извлекаем информацию о наказаниях
                ".law-cell-calculator {" +
        this.pageType = this.detectPageType();
                "cursor: pointer;" +
        this.extractPunishmentInfo();
                "transition: background-color 0.2s;" +
    },
                "}" +
   
                ".law-cell-calculator:hover {" +
    detectPageType: function() {
                "background-color: rgba(0, 124, 186, 0.1) !important;" +
        // Проверяем заголовки страницы для определения типа
                "}" +
        var pageTitle = document.title.toLowerCase();
                ".law-cell-calculator.selected {" +
        var headings = document.querySelectorAll('h1, h2, h3');
                "background-color: rgba(0, 124, 186, 0.3) !important;" +
       
                "}" +
        for (var i = 0; i < headings.length; i++) {
                ".calculator-actions {" +
            var headingText = headings[i].textContent.toLowerCase();
                "margin-top: 10px;" +
            if (headingText.includes('д-класс') || headingText.includes('d-class')) {
                "}" +
                return 'd-class';
                ".calculator-actions button {" +
            }
                "padding: 8px 16px;" +
            if (headingText.includes('персонал') || headingText.includes('сотрудник')) {
                "border: 1px solid #ccc;" +
                return 'personnel';
                "background: white;" +
            }
                "cursor: pointer;" +
        }
                "border-radius: 3px;" +
       
                "font-size: 14px;" +
        // Проверяем по URL или другим признакам
                "}" +
        if (window.location.href.toLowerCase().includes('d-class') ||
                ".calculator-actions button:hover {" +
            window.location.href.toLowerCase().includes('д-класс')) {
                "background: #f0f0f0;" +
            return 'd-class';
                "}" +
        }
                ".calculator-actions .calculate-btn {" +
       
                "background: #007cba;" +
        return 'personnel'; // По умолчанию
                "color: white;" +
    },
                "border-color: #007cba;" +
   
                "}" +
    extractPunishmentInfo: function() {
                ".calculator-actions .calculate-btn:hover {" +
        // Извлекаем информацию о наказаниях из текста на странице
                "background: #005a87;" +
        var punishmentSections = document.querySelectorAll('.mw-collapsible-content p');
                "}";
        var categories = {};
 
       
            document.head.appendChild(style);
        console.log('LawCalculator: Найдено секций с наказаниями:', punishmentSections.length);
        },
        console.log('LawCalculator: Тип страницы:', this.pageType);
 
       
        setMode: function (mode, table) {
        punishmentSections.forEach(function(section) {
            this.isCalculatorMode = mode === "calculator";
            var text = section.textContent;
 
            var punishmentMatch = text.match(/Наказание[:\s]*(.+?)(?:\n|$)/i);
            var wrapper = table.closest(".citizen-table-wrapper");
           
            var calculatorInterface = wrapper.querySelector(
            if (punishmentMatch) {
                ".calculator-interface",
                var punishmentText = punishmentMatch[1].trim();
            );
                console.log('LawCalculator: Найдено наказание:', punishmentText);
 
               
            if (this.isCalculatorMode) {
                var categoryInfo = this.parsePunishmentText(punishmentText);
                if (!calculatorInterface) {
               
                    calculatorInterface = this.createCalculatorInterface();
                if (categoryInfo) {
                    wrapper.appendChild(calculatorInterface);
                    // Находим соответствующую категорию по цвету или другим признакам
                }
                    var categoryKey = this.findCategoryByContext(section);
                calculatorInterface.classList.add("active");
                    if (categoryKey) {
                this.makeTableInteractive(table);
                        categories[categoryKey] = categoryInfo;
            } else {
                        console.log('LawCalculator: Применено к категории:', categoryKey, categoryInfo);
                if (calculatorInterface) {
                    }
                    calculatorInterface.classList.remove("active");
                }
                }
            }
                this.makeTableNormal(table);
        }.bind(this));
            }
       
        },
        console.log('LawCalculator: Итоговые категории:', categories);
 
       
        createCalculatorInterface: function () {
        // Применяем найденную информацию к законам
            var calculatorInterface = document.createElement("div");
        this.applyCategoryInfo(categories);
            calculatorInterface.className = "calculator-interface";
    },
            calculatorInterface.innerHTML =
   
                "<h4>Калькулятор сроков заключения</h4>" +
    parsePunishmentText: function(text) {
                "<p>Выберите нарушения, кликая по статьям в таблице выше:</p>" +
        // Парсим текст наказания
                '<div class="selected-violations">' +
        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 class="violations-list"></div>' +
                "</div>" +
            '</div>' +
                '<div class="calculation-result" style="display: none;"></div>' +
            '<div class="calculation-result" style="display: none;"></div>' +
                '<div class="calculator-actions">' +
            '<div class="calculator-actions">' +
                 '<button class="clear-btn" style="margin-right: 10px;">Очистить</button>' +
                 '<button class="clear-btn" style="margin-right: 10px;">Очистить</button>' +
                 '<button class="calculate-btn">Рассчитать</button>' +
                 '<button class="calculate-btn">Рассчитать</button>' +
                "</div>";
            '</div>';
 
       
            // Обработчики событий
        // Обработчики событий
            calculatorInterface
        calculatorInterface.querySelector('.clear-btn').addEventListener('click', function() {
                .querySelector(".clear-btn")
            LawCalculatorModule.clearSelection();
                .addEventListener("click", function () {
        });
                    LawCalculatorModule.clearSelection();
       
                });
        calculatorInterface.querySelector('.calculate-btn').addEventListener('click', function() {
 
            LawCalculatorModule.calculateSentence();
            calculatorInterface
        });
                .querySelector(".calculate-btn")
       
                .addEventListener("click", function () {
        return calculatorInterface;
                    LawCalculatorModule.calculateSentence();
    },
                });
   
 
    makeTableInteractive: function(table) {
            return calculatorInterface;
        var cells = table.querySelectorAll('td');
        },
        cells.forEach(function(cell) {
 
            var link = cell.querySelector('a[href*="#"]');
        makeTableInteractive: function (table) {
            if (!link) return;
            var cells = table.querySelectorAll("td");
           
            cells.forEach(
            var href = link.getAttribute('href');
                function (cell) {
            var match = href.match(/#(\d{3})/);
                    var link = cell.querySelector('a[href*="#"]');
            if (!match) return;
                    if (!link) return;
           
 
            var lawCode = match[1];
                    var href = link.getAttribute("href");
            if (!this.lawData[lawCode]) return;
                    var match = href.match(/#(\d{3})/);
           
                    if (!match) return;
            cell.classList.add('law-cell-calculator');
 
            cell.dataset.lawCode = lawCode;
                    var lawCode = match[1];
           
                    if (!this.lawData[lawCode]) return;
            // Сохраняем оригинальный обработчик клика
 
            var originalClickHandler = cell.onclick;
                    cell.classList.add("law-cell-calculator");
           
                    cell.dataset.lawCode = lawCode;
            cell.addEventListener('click', function(e) {
 
                e.preventDefault();
                    // Сохраняем оригинальный обработчик клика
                e.stopPropagation();
                    var originalClickHandler = cell.onclick;
                LawCalculatorModule.toggleViolation(lawCode, cell);
 
            });
                    cell.addEventListener("click", function (e) {
        }.bind(this));
                        e.preventDefault();
    },
                        e.stopPropagation();
   
                        LawCalculatorModule.toggleViolation(lawCode, cell);
    makeTableNormal: function(table) {
                    });
        var cells = table.querySelectorAll('.law-cell-calculator');
                }.bind(this),
        cells.forEach(function(cell) {
            );
            cell.classList.remove('law-cell-calculator', 'selected');
        },
            // Восстанавливаем оригинальную функциональность навигации
 
            var lawCode = cell.dataset.lawCode;
        makeTableNormal: function (table) {
            if (lawCode) {
            var cells = table.querySelectorAll(".law-cell-calculator");
                cell.addEventListener('click', function(e) {
            cells.forEach(function (cell) {
                    e.preventDefault();
                cell.classList.remove("law-cell-calculator", "selected");
                    window.location.hash = lawCode;
                // Восстанавливаем оригинальную функциональность навигации
                });
                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);
        toggleViolation: function (lawCode, cell) {
            cell.classList.remove('selected');
            var index = this.selectedViolations.indexOf(lawCode);
        } else {
 
            // Добавляем нарушение
            if (index > -1) {
            this.selectedViolations.push(lawCode);
                // Убираем нарушение
            cell.classList.add('selected');
                this.selectedViolations.splice(index, 1);
        }
                cell.classList.remove("selected");
       
            } else {
        this.updateViolationsList();
                // Добавляем нарушение
    },
                this.selectedViolations.push(lawCode);
   
                cell.classList.add("selected");
    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();
            this.updateViolationsList();
        },
        }
 
    },
        updateViolationsList: function () {
   
            var violationsList = document.querySelector(".violations-list");
    clearSelection: function() {
            if (!violationsList) return;
        this.selectedViolations = [];
 
       
            violationsList.innerHTML = "";
        // Убираем выделение со всех ячеек
 
        var selectedCells = document.querySelectorAll('.law-cell-calculator.selected');
            this.selectedViolations.forEach(
        selectedCells.forEach(function(cell) {
                function (lawCode) {
            cell.classList.remove('selected');
                    var law = this.lawData[lawCode];
        });
                    if (!law) return;
       
 
        this.updateViolationsList();
                    var item = document.createElement("div");
        this.hideResult();
                    item.className = "violation-item";
    },
                    item.innerHTML =
   
                        lawCode +
    calculateSentence: function() {
                        " - " +
        if (this.selectedViolations.length === 0) {
                        law.title +
            alert('Выберите хотя бы одно нарушение');
                        '<span class="remove-btn" data-law-code="' +
            return;
                        lawCode +
        }
                        '">×</span>';
       
 
            var result = this.calculateSentenceLogic(this.selectedViolations);
                    item.querySelector(".remove-btn").addEventListener(
            this.showResult(result);
                        "click",
        },
                        function () {
       
                            LawCalculatorModule.removeViolation(lawCode);
        calculateSentenceLogic: function(violations) {
                        },
            // Группируем нарушения по категориям
                    );
            var categories = {};
 
           
                    violationsList.appendChild(item);
            violations.forEach(function(lawCode) {
                }.bind(this),
                var law = this.lawData[lawCode];
            );
                if (!law || !law.category) return;
        },
               
 
                // Используем номер статьи как ключ категории
        removeViolation: function (lawCode) {
                var categoryKey = this.getCategoryKeyByLawCode(lawCode);
            var index = this.selectedViolations.indexOf(lawCode);
                if (!categories[categoryKey]) {
            if (index > -1) {
                    categories[categoryKey] = [];
                this.selectedViolations.splice(index, 1);
                }
 
                categories[categoryKey].push(law);
                // Убираем выделение с ячейки
            }.bind(this));
                var cell = document.querySelector(
           
                    '[data-law-code="' + lawCode + '"]',
            var totalMinTime = 0;
                );
            var totalMaxTime = 0;
                if (cell) {
            var totalFine = 0;
                    cell.classList.remove("selected");
            var hasCritical = false;
                }
            var resultText = '';
 
           
                this.updateViolationsList();
            // Обрабатываем каждую категорию
            }
            Object.keys(categories).forEach(function(categoryKey) {
        },
                var laws = categories[categoryKey];
 
                var category = laws[0].category;
        clearSelection: function () {
                var categoryName = this.getCategoryName(categoryKey);
            this.selectedViolations = [];
               
 
                if (category.isCritical) {
            // Убираем выделение со всех ячеек
                    hasCritical = true;
            var selectedCells = document.querySelectorAll(
                    resultText += '' + categoryName + ': ' + (category.punishment || 'Перевод в Д-класс/казнь') + '\n';
                ".law-cell-calculator.selected",
                } else {
            );
                    // Для одной категории берем самую тяжкую статью
            selectedCells.forEach(function (cell) {
                    var maxLaw = laws.reduce(function(max, current) {
                cell.classList.remove("selected");
                        var currentCode = parseInt(lawCode);
            });
                        var maxCode = parseInt(max.title.match(/\d{3}/)[0]);
 
                        return currentCode > maxCode ? current : max;
            this.updateViolationsList();
                    });
            this.hideResult();
                   
        },
                    totalMinTime += category.minTime || 0;
 
                    totalMaxTime += category.maxTime || 0;
        calculateSentence: function () {
                    totalFine += category.fine || 0;
            if (this.selectedViolations.length === 0) {
                   
                alert("Выберите хотя бы одно нарушение");
                    var punishmentText = '';
                return;
                    if (category.punishment) {
            }
                        punishmentText = category.punishment;
 
                    } else if (category.minTime !== undefined) {
            var result = this.calculateSentenceLogic(this.selectedViolations);
                        punishmentText = category.minTime === category.maxTime ?
            this.showResult(result);
                            category.minTime + ' минут' :
        },
                            category.minTime + '-' + category.maxTime + ' минут';
 
                    }
        calculateSentenceLogic: function (violations) {
                   
            // Группируем нарушения по категориям
                    resultText += '' + categoryName + ': ' + punishmentText + ' (статья ' + lawCode + ')\n';
            var categories = {};
                }
 
            }.bind(this));
            violations.forEach(
           
                function (lawCode) {
            if (hasCritical) {
                    var law = this.lawData[lawCode];
                return {
                    if (!law || !law.category) return;
                    isCritical: true,
 
                    text: 'КРИТИЧЕСКИЕ НАРУШЕНИЯ\n' + resultText + '\nПриговор: Перевод в Д-класс или казнь'
                    // Используем номер статьи как ключ категории
                };
                    var categoryKey = this.getCategoryKeyByLawCode(lawCode);
            } else if (this.pageType === 'd-class') {
                    if (!categories[categoryKey]) {
                // Для Д-класса показываем штрафы и время отдельно
                        categories[categoryKey] = [];
                var summaryText = '';
                    }
                    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) {
                 if (totalFine > 0) {
                     summaryText += "Общий штраф: " + totalFine + " кредитов\n";
                     summaryText += 'Общий штраф: ' + totalFine + ' кредитов\n';
                 }
                 }
                 if (totalMinTime > 0 || totalMaxTime > 0) {
                 if (totalMinTime > 0 || totalMaxTime > 0) {
                     summaryText +=
                     summaryText += 'Общее время: ' + this.formatTime(totalMinTime, totalMaxTime) + '\n';
                        "Общее время: " +
                        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
            });
        }
    };
   
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;


                return {
var FireworksModule = {
                    isCritical: false,
    fireworks: null,
                    text:
    container: null,
                        "РАСЧЕТ НАКАЗАНИЙ Д-КЛАССА\n" +
    checkInterval: null,
                        resultText +
    isActive: false,
                        "\n" +
    loaded: false,
                        summaryText,
    loading: false,
                };
    callbacks: [],
            } else if (totalMinTime > 0 || totalMaxTime > 0) {
   
                var timeText = this.formatTime(totalMinTime, totalMaxTime);
    init: function() {
                return {
        this.startTimeCheck();
                    isCritical: false,
    },
                    text:
   
                        "РАСЧЕТ СРОКА ЗАКЛЮЧЕНИЯ\n" +
    loadFireworks: function(callback) {
                        resultText +
        // Проверяем разные варианты экспорта библиотеки
                        "\nОбщий срок: " +
        if (this.loaded && (window.Fireworks || (window.Fireworks && window.Fireworks.default))) {
                        timeText,
            if (callback) callback();
                };
            return;
            } else {
        }
                return {
       
                    isCritical: false,
        if (callback) {
                    text:
            this.callbacks.push(callback);
                        "РАСЧЕТ НАКАЗАНИЙ\n" +
        }
                        resultText +
       
                        "\nНе удалось определить наказания",
        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) {
                console.error('Не удалось найти конструктор Fireworks. Проверьте загрузку библиотеки.');
                console.log('Доступные объекты:', {
                    'window.Fireworks': window.Fireworks,
                    'window.Fireworks.default': window.Fireworks && window.Fireworks.default,
                    'window.fireworks': window.fireworks
                });
                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;
var EquipmentFilterModule = {
    init: function() {
        // Проверяем маркер страницы
        if ($('#enable-group-filter').length === 0) return;


         getCategoryKeyByLawCode: function (lawCode) {
         // Список групп
            var code = parseInt(lawCode);
        var groups = [
             if (code >= 100 && code <= 109) return "100-109";
             { className: 'equip-epsilon11', label: 'Эпсилон-11' },
             else if (code >= 200 && code <= 211) return "200-211";
             { className: 'equip-nyu7',      label: 'Ню-7' },
             else if (code >= 300 && code <= 311) return "300-311";
             { className: 'equip-beta7',    label: 'Бета-7' },
             else if (code >= 400 && code <= 411) return "400-411";
             { className: 'equip-omega1',    label: 'Омега-1' },
             else if (code >= 500 && code <= 511) return "500-511";
             { className: 'equip-alfa1',    label: 'Альфа-1' }
            return "unknown";
        ];
        },


         getCategoryName: function (categoryKey) {
         // Класс таблиц, которые нужно фильтровать
            var names = {
        var tableClass = 'mogtable'; // если у вас класс moptable – поменяйте
                "100-109": "Лёгкие нарушения",
                "200-211": "Средние нарушения",
                "300-311": "Тяжкие нарушения",
                "400-411": "Особо тяжкие нарушения",
                "500-511": "Критические нарушения",
            };
            return names[categoryKey] || "Неизвестная категория";
        },


         formatTime: function (minTime, maxTime) {
         // Находим все таблицы с нужным классом
            if (minTime === maxTime) {
        var $tables = $('.wikitable.noresize.' + tableClass);
                return this.formatMinutes(minTime);
        if ($tables.length === 0) return;
            } else {
                return (
                    this.formatMinutes(minTime) +
                    " - " +
                    this.formatMinutes(maxTime)
                );
            }
        },


         formatMinutes: function (minutes) {
         // Контейнер для чекбоксов
            if (minutes < 60) {
        var $container = $('#filter-placeholder');
                return minutes + " минут";
        if ($container.length === 0) {
             } else {
            // Если контейнера нет – создаём и вставляем перед первой таблицей
                var hours = Math.floor(minutes / 60);
             $container = $('<div id="filter-placeholder"></div>');
                var remainingMinutes = minutes % 60;
            $tables.first().before($container);
                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");
        $container.html('');
             if (!resultDiv) return;
        groups.forEach(function(group) {
             var $label = $('<label style="display: block; margin-bottom: 5px;">' +
                '<input type="checkbox" class="group-filter" data-class="' + group.className + '"> ' +
                group.label + '</label>');
             $container.append($label);
        });


             resultDiv.textContent = result.text;
        // Функция фильтрации
             resultDiv.className =
        function updateVisibility() {
                "calculation-result" + (result.isCritical ? " critical" : "");
             // Скрываем все строки, у которых есть класс, начинающийся с equip-
            resultDiv.style.display = "block";
             $tables.find('tr[class*="equip-"]').hide();
        },


        hideResult: function () {
            // Выбранные классы
             var resultDiv = document.querySelector(".calculation-result");
             var selectedClasses = [];
            if (resultDiv) {
            $('.group-filter:checked').each(function() {
                 resultDiv.style.display = "none";
                 selectedClasses.push($(this).data('class'));
             }
             });
        },


        observeTables: function () {
             if (selectedClasses.length === 0) return;
            // Наблюдаем за динамически добавляемыми таблицами
             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,
            selectedClasses.forEach(function(cls) {
                 subtree: true,
                $tables.find('tr').filter(function() {
                    return $(this).hasClass(cls);
                 }).show();
             });
             });
         },
         }
    };


    var SnowflakesModule = {
         // Обработчики
         snowflakes: [],
         $(document).on('change', '.group-filter', updateVisibility);
         createInterval: null,
        isActive: false,
        snowflakeCount: 30,


         random: function (min, max) {
         // Начальное скрытие всех групповых строк
            return Math.random() * (max - min) + min;
        updateVisibility();
        },
    }
};


        init: function () {
var AprilPrankModule = {
            if (this.isActive) return;
    FORCE_PRANK_FOR_ALL: true,
    ONLY_MAIN_PAGE: false,     
    ANIMATION_MS: 5000,
    SHOWN_SESSION_KEY: 'april-prank-shown-v2',


            this.addStyles();
    overlay: null,
            this.startContinuousCreation();
    audio: null,
            this.isActive = true;
    isClosing: false,
        },


        addStyles: function () {
    init: function () {
            if (document.getElementById("snowflakes-styles")) return;
        if (this.ONLY_MAIN_PAGE && window.mw && mw.config && !mw.config.get('wgIsMainPage')) {
            return;
        }


            var style = document.createElement("style");
        if (!this.shouldRun()) {
            style.id = "snowflakes-styles";
            return;
            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);
        // Чтобы не показывалось повторно в одной вкладке
         },
        if (sessionStorage.getItem(this.SHOWN_SESSION_KEY) === '1') {
            return;
         }
        sessionStorage.setItem(this.SHOWN_SESSION_KEY, '1');


         createSnowflake: function () {
         this.injectStyles();
            var snowflake = document.createElement("div");
        this.buildOverlay();
            snowflake.className = "snowflake";
        this.bindEvents();
    },


            var symbol = this.getSnowflakeSymbol();
    shouldRun: function () {
             snowflake.textContent = symbol;
        if (this.FORCE_PRANK_FOR_ALL) {
             return true;
        }


            var fontSize = this.random(0.5, 1.2);
        var params = new URLSearchParams(window.location.search);
            snowflake.style.fontSize = fontSize + "em";
        if (!params.has('prank')) {
            snowflake.style.left = this.random(0, 100) + "vw";
             return false;
             snowflake.style.opacity = this.random(0.25, 0.5);
        }


            var fallDuration = this.random(10, 30);
        var value = (params.get('prank') || '').trim().toLowerCase();
            snowflake.style.animationDuration = fallDuration + "s";
            snowflake.style.animationDelay = this.random(0, 2) + "s";


            if (Math.random() > 0.95) {
        // Работает для ?prank, ?prank=1, ?prank=true, ?prank=on, ?prank=yes
                snowflake.classList.add("flare");
        if (value === '' || value === '1' || value === 'true' || value === 'on' || value === 'yes') {
            }
            return true;
        }


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


            document.body.appendChild(snowflake);
injectStyles: function () {
            this.snowflakes.push(snowflake);
    if (document.getElementById('april-prank-styles')) {
        return;
    }
    var style = document.createElement('style');
    style.id = 'april-prank-styles';
    style.textContent =
        '#april-prank-overlay {' +
            'position: fixed;' +
            'inset: 0;' +
            'z-index: 2147483647;' +
            'background: #000;' +
            'overflow: auto;' +
            'cursor: pointer;' +
            'transform: translateY(0);' +
            'will-change: transform;' +
        '}' +
        '#april-prank-overlay.closing {' +
            'transition: transform 5000ms linear;' +
            'transform: translateY(-100%);' +
        '}' +
        '#april-prank-overlay img {' +
            'display: block;' +
            'width: 100%;' +
            'height: auto;' +
            'min-height: 100vh;' +
            'object-fit: cover;' +
            'user-select: none;' +
            '-webkit-user-drag: none;' +
            'pointer-events: none;' +
        '}' +
        '#april-prank-overlay.mobile {' +
            'display: flex;' +
            'align-items: center;' +
            'justify-content: center;' +
            'padding: 0;' +
            'overflow: hidden;' +
            'background: #0b0b0b;' +
        '}' +
        '#april-prank-overlay.mobile img {' +
            'width: 100%;' +
            'height: 100svh;' +
            'min-height: 0;' +
            'object-fit: contain;' +
        '}';
    document.head.appendChild(style);
},


            var self = this;
buildOverlay: function () {
            var checkRemove = setInterval(function () {
    var overlay = document.createElement('div');
                var rect = snowflake.getBoundingClientRect();
    overlay.id = 'april-prank-overlay';
                if (rect.top > window.innerHeight + 50) {
                    if (snowflake.parentNode) {
    var img = document.createElement('img');
                        snowflake.remove();
                    }
    var isMobile =
                    var index = self.snowflakes.indexOf(snowflake);
        window.matchMedia('(max-width: 768px)').matches ||
                    if (index > -1) {
        /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
                        self.snowflakes.splice(index, 1);
                    }
    if (isMobile) {
                    clearInterval(checkRemove);
        overlay.classList.add('mobile');
                }
    }
            }, 100);
        },
    img.src = isMobile
        ? 'https://i.redd.it/yjmiajn4uq5a1.jpg'
        : 'https://upload.wikimedia.org/wikipedia/commons/d/d4/2019_Screenshot_of_English_Wikipedia_homepage.png';
    img.alt = 'Wikipedia homepage screenshot';
    overlay.appendChild(img);
    document.body.appendChild(overlay);
    this.overlay = overlay;
    this.audio = new Audio('https://zvukogram.com/mp3/40/stone-plate.mp3');
    this.audio.preload = 'auto';
},


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


         startContinuousCreation: function () {
         this.overlay.addEventListener('click', function () {
            var self = this;
            self.startClose();
            this.createInterval = setInterval(function () {
        }, { passive: true });
                if (
    },
                    self.isActive &&
                    self.snowflakes.length < self.snowflakeCount * 1.5
                ) {
                    self.createSnowflake();
                }
            }, 500);
        },


        stop: function () {
    startClose: function () {
            this.isActive = false;
        if (this.isClosing || !this.overlay) {
            return;
        }


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


             this.snowflakes.forEach(function (snowflake) {
        if (this.audio) {
                if (snowflake && snowflake.parentNode) {
             this.audio.currentTime = 0;
                    snowflake.remove();
            this.audio.play().catch(function () {
                 }
                 // Браузер может заблокировать autoplay — клик уже есть, обычно играет
             });
             });
            this.snowflakes = [];
        }
 
        this.overlay.classList.add('closing');


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


         toggle: function () {
         setTimeout(function () {
             if (this.isActive) {
             if (self.audio) {
                 this.stop();
                 self.audio.pause();
            } else {
                 self.audio.currentTime = 0;
                 this.init();
             }
             }
         },
         }, this.ANIMATION_MS);
    };
 
    window.SnowflakesModule = SnowflakesModule;


    var FireworksModule = {
         this.overlay.addEventListener('transitionend', function onEnd(e) {
        fireworks: null,
             if (e.propertyName !== 'transform') {
        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;
                 return;
             }
             }


             if (callback) {
             self.overlay.removeEventListener('transitionend', onEnd);
                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 () {
             if (self.overlay && self.overlay.parentNode) {
             // МСК = UTC+3
                 self.overlay.parentNode.removeChild(self.overlay);
            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 () {
             self.overlay = null;
             // Пробуем разные варианты доступа к конструктору
         });
            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) {
                    console.error(
                        "Не удалось найти конструктор Fireworks. Проверьте загрузку библиотеки.",
                    );
                    console.log("Доступные объекты:", {
                        "window.Fireworks": window.Fireworks,
                        "window.Fireworks.default":
                            window.Fireworks && window.Fireworks.default,
                        "window.fireworks": window.fireworks,
                    });
                    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() {
Строка 2474: Строка 2356:
         // FireworksModule.init();
         // FireworksModule.init();
         // LawCalculatorModule.init();
         // LawCalculatorModule.init();
        EquipmentFilterModule.init();
        // AprilPrankModule.init();
     }
     }
 
   
     if (document.readyState === "loading") {
     if (document.readyState === 'loading') {
         document.addEventListener("DOMContentLoaded", initAllModules);
         document.addEventListener('DOMContentLoaded', initAllModules);
     } else {
     } else {
         initAllModules();
         initAllModules();
     }
     }
   
})();
})();

Текущая версия от 10:02, 25 апреля 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://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js';
            popperScript.onload = function() {
                var tippyScript = document.createElement('script');
                tippyScript.src = 'https://unpkg.com/tippy.js@6/dist/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://unpkg.com/tippy.js@6/dist/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 () {
        var tableCells = document.querySelectorAll('.law-tooltips td, .law-tooltips th');
        if (tableCells.length === 0) return;

        MediaWikiTooltips.load(function () {
            var linkCodes = LawTooltipsModule.collectLinkCodes(tableCells);
            var lawData = LawTooltipsModule.extractLawData(linkCodes);
            LawTooltipsModule.initCellTooltips(tableCells, lawData);
        });
    },

    collectLinkCodes: function (tableCells) {
        var codes = new Set();

        tableCells.forEach(function (cell) {
            var link = cell.querySelector('a[href^="#"]');
            if (!link) return;

            var href = link.getAttribute('href') || '';
            var rawCode = LawTooltipsModule.extractCodeFromHref(href);
            if (!rawCode) return;

            codes.add(rawCode);
            codes.add(LawTooltipsModule.normalizeLawCode(rawCode));
            codes.add(LawTooltipsModule.toSectionCode(rawCode));
            codes.add(LawTooltipsModule.toLegacyCode(rawCode));
        });

        return codes;
    },

    extractLawData: function (expectedCodes) {
        var lawData = {};
        var crimeIdSpans = document.querySelectorAll('span[id]');

        crimeIdSpans.forEach(function (span) {
            var rawId = (span.id || '').trim();
            if (!rawId) return;

            var normalizedId = LawTooltipsModule.normalizeLawCode(rawId);

            if (expectedCodes && expectedCodes.size > 0) {
                var isExpected =
                    expectedCodes.has(rawId) ||
                    expectedCodes.has(normalizedId) ||
                    expectedCodes.has(LawTooltipsModule.toSectionCode(rawId)) ||
                    expectedCodes.has(LawTooltipsModule.toLegacyCode(rawId));

                if (!isExpected) return;
            }

            var tr = span.closest('tr');
            if (!tr) return;

            var cells = tr.querySelectorAll('td');
            if (cells.length < 3) return;

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

            var title = LawTooltipsModule.extractTitleFromCrimeCell(crimeCell, rawId);
            if (!title) return;

            var description = (descriptionCell.innerHTML || '').trim();
            var examples = LawTooltipsModule.extractExamplesFromExampleCell(exampleCell);

            var payload = {
                title: title,
                description: description,
                examples: examples,
                sourceCode: rawId
            };

            LawTooltipsModule.addLawDataAliases(lawData, rawId, payload);
        });

        return lawData;
    },

    addLawDataAliases: function (lawData, rawCode, payload) {
        var normalized = LawTooltipsModule.normalizeLawCode(rawCode);
        var sectionCode = LawTooltipsModule.toSectionCode(rawCode);
        var legacyCode = LawTooltipsModule.toLegacyCode(rawCode);

        var keys = [rawCode, normalized, sectionCode, legacyCode];

        keys.forEach(function (key) {
            if (!key) return;
            lawData[key] = payload;
        });
    },

extractTitleFromCrimeCell: function (crimeCell, rawCode) {
    var b = crimeCell.querySelector('b');
    var text = (b ? b.textContent : crimeCell.textContent) || '';
    text = text.replace(/\s+/g, ' ').trim();

    // Новый формат: "§ 1.1 Название"
    text = text.replace(/^§\s*[\d]+(?:\.\s*[\d]+)*\s*/u, '').trim();

    // Старый формат: "200Название" / "200 Название" / "200. Название"
    var normalizedCode = LawTooltipsModule.normalizeLawCode(rawCode);
    if (normalizedCode) {
        var escapedCode = normalizedCode.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        var legacyPrefix = new RegExp(
            '^(?:§\\s*_?\\s*)?' + escapedCode + '[\\s\\u00A0\\-–—:.,)]*',
            'u'
        );
        text = text.replace(legacyPrefix, '').trim();
    }

    return text;
},

    extractExamplesFromExampleCell: function (exampleCell) {
        var examples = [];
        var liItems = exampleCell.querySelectorAll('li');

        if (liItems.length > 0) {
            liItems.forEach(function (item) {
                var html = (item.innerHTML || '').trim();
                html = html.replace(/<br\s*\/?>/gi, '');
                html = html.trim();
                if (html) examples.push(html);
            });
            return examples;
        }

        var fallback = (exampleCell.innerHTML || '').trim();
        fallback = fallback.replace(/<br\s*\/?>/gi, '').trim();
        if (fallback) examples.push(fallback);

        return examples;
    },

    extractCodeFromHref: function (href) {
        var match = (href || '').match(/^#(.+)$/);
        if (!match) return '';
        return (match[1] || '').trim();
    },

    normalizeLawCode: function (lawCode) {
        var code = (lawCode || '').trim();
        code = code.replace(/^#/, '');
        code = code.replace(/^§\s*_?\s*/u, '');
        code = code.replace(/\s+/g, '');
        return code;
    },

    toSectionCode: function (lawCode) {
        var normalized = LawTooltipsModule.normalizeLawCode(lawCode);
        if (!normalized) return '';
        return '§_' + normalized;
    },

    toLegacyCode: function (lawCode) {
        return LawTooltipsModule.normalizeLawCode(lawCode);
    },

    formatLawCode: function (lawCode, data) {
        var raw = (data && data.sourceCode ? data.sourceCode : lawCode) || '';
        var normalized = LawTooltipsModule.normalizeLawCode(raw);

        if (/^§\s*_?/u.test(raw) || raw.indexOf('§_') === 0 || normalized.indexOf('.') !== -1) {
            return '§ ' + normalized;
        }

        return normalized || raw;
    },

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

        var displayCode = LawTooltipsModule.formatLawCode(lawCode, data);

        var html = '<div class="law-tooltip">';
        html += '<h4 class="law-tooltip-title">' + displayCode + ' - ' + 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) {
        tableCells.forEach(function (cell) {
            if (cell.dataset.lawTooltipAttached === '1') return;

            var link = cell.querySelector('a[href^="#"]');
            if (!link) return;

            var href = link.getAttribute('href') || '';
            var rawCode = LawTooltipsModule.extractCodeFromHref(href);
            if (!rawCode) return;

            var candidateKeys = [
                rawCode,
                LawTooltipsModule.normalizeLawCode(rawCode),
                LawTooltipsModule.toSectionCode(rawCode),
                LawTooltipsModule.toLegacyCode(rawCode)
            ];

            var tooltipContent = null;
            var matchedKey = null;

            for (var i = 0; i < candidateKeys.length; i++) {
                var key = candidateKeys[i];
                if (!key) continue;

                tooltipContent = LawTooltipsModule.createTooltipContent(key, lawData);
                if (tooltipContent) {
                    matchedKey = key;
                    break;
                }
            }

            if (!tooltipContent) return;

            cell.dataset.lawTooltipAttached = '1';
            cell.classList.add('law-cell-with-tooltip');

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

            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();
                        }
                    });
                }
            });
        });
    }
};
    
    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 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 = {};
	        
	        console.log('LawCalculator: Найдено секций с наказаниями:', punishmentSections.length);
	        console.log('LawCalculator: Тип страницы:', this.pageType);
	        
	        punishmentSections.forEach(function(section) {
	            var text = section.textContent;
	            var punishmentMatch = text.match(/Наказание[:\s]*(.+?)(?:\n|$)/i);
	            
	            if (punishmentMatch) {
	                var punishmentText = punishmentMatch[1].trim();
	                console.log('LawCalculator: Найдено наказание:', punishmentText);
	                
	                var categoryInfo = this.parsePunishmentText(punishmentText);
	                
	                if (categoryInfo) {
	                    // Находим соответствующую категорию по цвету или другим признакам
	                    var categoryKey = this.findCategoryByContext(section);
	                    if (categoryKey) {
	                        categories[categoryKey] = categoryInfo;
	                        console.log('LawCalculator: Применено к категории:', categoryKey, categoryInfo);
	                    }
	                }
	            }
	        }.bind(this));
	        
	        console.log('LawCalculator: Итоговые категории:', categories);
	        
	        // Применяем найденную информацию к законам
	        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
	            });
	        }
	    };
	    
	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;

	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) {
	                console.error('Не удалось найти конструктор Fireworks. Проверьте загрузку библиотеки.');
	                console.log('Доступные объекты:', {
	                    'window.Fireworks': window.Fireworks,
	                    'window.Fireworks.default': window.Fireworks && window.Fireworks.default,
	                    'window.fireworks': window.fireworks
	                });
	                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;
	
	var EquipmentFilterModule = {
    init: function() {
        // Проверяем маркер страницы
        if ($('#enable-group-filter').length === 0) return;

        // Список групп
        var groups = [
            { className: 'equip-epsilon11', label: 'Эпсилон-11' },
            { className: 'equip-nyu7',      label: 'Ню-7' },
            { className: 'equip-beta7',     label: 'Бета-7' },
            { className: 'equip-omega1',    label: 'Омега-1' },
            { className: 'equip-alfa1',     label: 'Альфа-1' }
        ];

        // Класс таблиц, которые нужно фильтровать
        var tableClass = 'mogtable'; // если у вас класс moptable – поменяйте

        // Находим все таблицы с нужным классом
        var $tables = $('.wikitable.noresize.' + tableClass);
        if ($tables.length === 0) return;

        // Контейнер для чекбоксов
        var $container = $('#filter-placeholder');
        if ($container.length === 0) {
            // Если контейнера нет – создаём и вставляем перед первой таблицей
            $container = $('<div id="filter-placeholder"></div>');
            $tables.first().before($container);
        }

        // Очищаем и создаём чекбоксы
        $container.html('');
        groups.forEach(function(group) {
            var $label = $('<label style="display: block; margin-bottom: 5px;">' +
                '<input type="checkbox" class="group-filter" data-class="' + group.className + '"> ' +
                group.label + '</label>');
            $container.append($label);
        });

        // Функция фильтрации
        function updateVisibility() {
            // Скрываем все строки, у которых есть класс, начинающийся с equip-
            $tables.find('tr[class*="equip-"]').hide();

            // Выбранные классы
            var selectedClasses = [];
            $('.group-filter:checked').each(function() {
                selectedClasses.push($(this).data('class'));
            });

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

            // Показываем строки, содержащие любой из выбранных классов
            selectedClasses.forEach(function(cls) {
                $tables.find('tr').filter(function() {
                    return $(this).hasClass(cls);
                }).show();
            });
        }

        // Обработчики
        $(document).on('change', '.group-filter', updateVisibility);

        // Начальное скрытие всех групповых строк
        updateVisibility();
    }
};

var AprilPrankModule = {
    FORCE_PRANK_FOR_ALL: true, 
    ONLY_MAIN_PAGE: false,       
    ANIMATION_MS: 5000,
    SHOWN_SESSION_KEY: 'april-prank-shown-v2',

    overlay: null,
    audio: null,
    isClosing: false,

    init: function () {
        if (this.ONLY_MAIN_PAGE && window.mw && mw.config && !mw.config.get('wgIsMainPage')) {
            return;
        }

        if (!this.shouldRun()) {
            return;
        }

        // Чтобы не показывалось повторно в одной вкладке
        if (sessionStorage.getItem(this.SHOWN_SESSION_KEY) === '1') {
            return;
        }
        sessionStorage.setItem(this.SHOWN_SESSION_KEY, '1');

        this.injectStyles();
        this.buildOverlay();
        this.bindEvents();
    },

    shouldRun: function () {
        if (this.FORCE_PRANK_FOR_ALL) {
            return true;
        }

        var params = new URLSearchParams(window.location.search);
        if (!params.has('prank')) {
            return false;
        }

        var value = (params.get('prank') || '').trim().toLowerCase();

        // Работает для ?prank, ?prank=1, ?prank=true, ?prank=on, ?prank=yes
        if (value === '' || value === '1' || value === 'true' || value === 'on' || value === 'yes') {
            return true;
        }

        return false;
    },

	injectStyles: function () {
	    if (document.getElementById('april-prank-styles')) {
	        return;
	    }
	
	    var style = document.createElement('style');
	    style.id = 'april-prank-styles';
	    style.textContent =
	        '#april-prank-overlay {' +
	            'position: fixed;' +
	            'inset: 0;' +
	            'z-index: 2147483647;' +
	            'background: #000;' +
	            'overflow: auto;' +
	            'cursor: pointer;' +
	            'transform: translateY(0);' +
	            'will-change: transform;' +
	        '}' +
	        '#april-prank-overlay.closing {' +
	            'transition: transform 5000ms linear;' +
	            'transform: translateY(-100%);' +
	        '}' +
	        '#april-prank-overlay img {' +
	            'display: block;' +
	            'width: 100%;' +
	            'height: auto;' +
	            'min-height: 100vh;' +
	            'object-fit: cover;' +
	            'user-select: none;' +
	            '-webkit-user-drag: none;' +
	            'pointer-events: none;' +
	        '}' +
	        '#april-prank-overlay.mobile {' +
	            'display: flex;' +
	            'align-items: center;' +
	            'justify-content: center;' +
	            'padding: 0;' +
	            'overflow: hidden;' +
	            'background: #0b0b0b;' +
	        '}' +
	        '#april-prank-overlay.mobile img {' +
	            'width: 100%;' +
	            'height: 100svh;' +
	            'min-height: 0;' +
	            'object-fit: contain;' +
	        '}';
	    document.head.appendChild(style);
	},

	buildOverlay: function () {
	    var overlay = document.createElement('div');
	    overlay.id = 'april-prank-overlay';
	
	    var img = document.createElement('img');
	
	    var isMobile =
	        window.matchMedia('(max-width: 768px)').matches ||
	        /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
	
	    if (isMobile) {
	        overlay.classList.add('mobile');
	    }
	
	    img.src = isMobile
	        ? 'https://i.redd.it/yjmiajn4uq5a1.jpg'
	        : 'https://upload.wikimedia.org/wikipedia/commons/d/d4/2019_Screenshot_of_English_Wikipedia_homepage.png';
	
	    img.alt = 'Wikipedia homepage screenshot';
	
	    overlay.appendChild(img);
	    document.body.appendChild(overlay);
	
	    this.overlay = overlay;
	    this.audio = new Audio('https://zvukogram.com/mp3/40/stone-plate.mp3');
	    this.audio.preload = 'auto';
	},

    bindEvents: function () {
        var self = this;
        if (!this.overlay) {
            return;
        }

        this.overlay.addEventListener('click', function () {
            self.startClose();
        }, { passive: true });
    },

    startClose: function () {
        if (this.isClosing || !this.overlay) {
            return;
        }

        this.isClosing = true;

        if (this.audio) {
            this.audio.currentTime = 0;
            this.audio.play().catch(function () {
                // Браузер может заблокировать autoplay — клик уже есть, обычно играет
            });
        }

        this.overlay.classList.add('closing');

        var self = this;

        setTimeout(function () {
            if (self.audio) {
                self.audio.pause();
                self.audio.currentTime = 0;
            }
        }, this.ANIMATION_MS);

        this.overlay.addEventListener('transitionend', function onEnd(e) {
            if (e.propertyName !== 'transform') {
                return;
            }

            self.overlay.removeEventListener('transitionend', onEnd);

            if (self.overlay && self.overlay.parentNode) {
                self.overlay.parentNode.removeChild(self.overlay);
            }

            self.overlay = null;
        });
    }
};

    function initAllModules() {
        SidebarModule.init();
        AccessTooltipsModule.init();
        LawTooltipsModule.init();
        DataTooltipsModule.init();
        CopyTextModule.init();
        DocumentAutoFillModule.init();
        // SnowflakesModule.init()
        // FireworksModule.init();
        // LawCalculatorModule.init();
        EquipmentFilterModule.init();
        // AprilPrankModule.init();
    }
    
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initAllModules);
    } else {
        initAllModules();
    }
    
})();