Mediawiki:common.js

Вернуться

Строка 1: Строка 1:
(function() {
(function () {
     'use strict';
     "use strict";
   
 
     window.MediaWikiTooltips = {
     window.MediaWikiTooltips = {
         loaded: false,
         loaded: false,
        loading: false,
         callbacks: [],
         callbacks: [],
       
 
         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 popperScript = document.createElement('script');
             var cdns = [
            popperScript.src = 'https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js';
                { base: "https://unpkg.com", 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" },
            popperScript.onload = function() {
                 { 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" }
                 var tippyScript = document.createElement('script');
            ];
                tippyScript.src = 'https://unpkg.com/tippy.js@6/dist/tippy-bundle.umd.min.js';
            var cdnIndex = 0;
                 tippyScript.onload = function() {
 
                    self.loaded = true;
            var tryFallback = function (which) {
                    self.loading = false;
                self.loading = false;
                    self.callbacks.forEach(function(cb) { cb(); });
                 console.error(
                    self.callbacks = [];
                    "[MediaWikiTooltips] Не удалось загрузить " + which + ". " +
                    "Возможные причины: CSP вики блокирует внешние скрипты, нет сети, или CDN недоступен."
                );
                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 loadTippyAndCss() {
                var cdn = cdns[cdnIndex];
                var tippyScript = document.createElement("script");
                tippyScript.src = cdn.base + cdn.tippy;
                tippyScript.onerror = function () { tryFallback("Tippy.js"); };
                tippyScript.onload = function () {
                    if (!window.tippy) {
                        tryFallback("Tippy.js (не в глобальной области)");
                        return;
                    }
                    var tippyCSS = document.createElement("link");
                    tippyCSS.rel = "stylesheet";
                    tippyCSS.href = 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 tippyCSS = document.createElement('link');
                var cdn = cdns[cdnIndex];
            tippyCSS.rel = 'stylesheet';
                var popperScript = document.createElement("script");
            tippyCSS.href = 'https://unpkg.com/tippy.js@6/dist/tippy.css';
                popperScript.src = cdn.base + cdn.popper;
            document.head.appendChild(tippyCSS);
                popperScript.onerror = function () {
         }
                    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) { btn.classList.remove('active'); });
                     buttons.forEach(function (btn) {
                     this.classList.add('active');
                        btn.classList.remove("active");
                   
                    });
                     sections.forEach(function(section) { section.classList.remove('default'); });
                     this.classList.add("active");
 
                     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 = container.querySelector('.допуск-подсказка');
                     var contentElement =
                        container.querySelector(".допуск-подсказка");
                     if (!contentElement) return;
                     if (!contentElement) return;
                   
 
                     var content = contentElement.innerHTML;
                     var content = contentElement.innerHTML;
                     tippy(container, {
                     tippy(container, {
Строка 81: Строка 131:
                         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],
Строка 89: Строка 139:
                             modifiers: [
                             modifiers: [
                                 {
                                 {
                                     name: 'preventOverflow',
                                     name: "preventOverflow",
                                     options: { padding: 8 }
                                     options: { padding: 8 },
                                 },
                                 },
                                 {
                                 {
                                     name: 'flip',
                                     name: "flip",
                                     options: {
                                     options: {
                                         fallbackPlacements: ['top', 'bottom', 'right', 'left']
                                         fallbackPlacements: [
                                     }
                                            "top",
                                 }
                                            "bottom",
                             ]
                                            "right",
                         }
                                            "left",
                                        ],
                                     },
                                 },
                             ],
                         },
                     });
                     });
                 });
                 });
             });
             });
         }
         },
     };
     };
   
var LawTooltipsModule = {
    init: function() {
        console.log('[LawTooltips] init()');
        var tableCells = document.querySelectorAll('.law-tooltips td, .law-tooltips th');
        console.log('[LawTooltips] .law-tooltips ячеек:', tableCells.length);
        if (tableCells.length === 0) {
            console.warn('[LawTooltips] Нет ячеек .law-tooltips — выход');
            return;
        }


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


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


    extractLawData: function() {
            var detailTables = document.querySelectorAll(
        var lawData = {};
                ".mw-collapsible-content table",
        var containers = document.querySelectorAll('.mw-collapsible');
            );
        console.log('[LawTooltips] extractLawData: коллапсибл-блоков:', containers.length);
            console.log(
        containers.forEach(function(block) {
                "[LawTooltips] extractLawData: таблиц в .mw-collapsible-content:",
            var content = block.querySelector('.mw-collapsible-content');
                detailTables.length,
            if (content && block.classList.contains('mw-collapsed')) {
            );
                block.classList.remove('mw-collapsed');
            detailTables.forEach(function (table, tableIndex) {
                var toggle = content.querySelector('.mw-collapsible-toggle');
                if (table.classList.contains("law-tooltips")) {
                if (toggle) toggle.setAttribute('aria-expanded', 'true');
                    console.log(
            }
                        "[LawTooltips] extractLawData: таблица",
        });
                        tableIndex,
                        "— пропуск (law-tooltips)",
                    );
                    return;
                }
                var rows = table.querySelectorAll("tr");


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


            rows.forEach(function(row) {
                    var crimeCell = cells[0];
                var cells = row.querySelectorAll('td');
                    var descriptionCell = cells[1];
                if (cells.length < 3) return;
                    var exampleCell = cells[2];


                var crimeCell = cells[0];
                    var anchor = crimeCell.querySelector("[id]");
                var descriptionCell = cells[1];
                    if (!anchor) return;
                var exampleCell = cells[2];


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


                var lawCode = anchor.id;
                    var titleText = titleElement.textContent.trim();
                var titleElement = crimeCell.querySelector('b');
                    var lawTitle = titleText
                if (!titleElement || !lawCode.match(/^\d{3}$/)) return;
                        .replace(/^\d{3}\s*/, "")
                        .replace(/^\d{3}/, "")
                        .trim();
                    var description = descriptionCell.innerHTML.trim();


                var titleText = titleElement.textContent.trim();
                    var examples = [];
                var lawTitle = titleText.replace(/^\d{3}\s*/, '').replace(/^\d{3}/, '').trim();
                    var exampleItems = exampleCell.querySelectorAll("li");
                var description = descriptionCell.innerHTML.trim();
                    exampleItems.forEach(function (item) {
                        var html = item.innerHTML
                            .trim()
                            .replace(/<br\s*\/?>/gi, "");
                        if (html) examples.push(html);
                    });


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


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


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


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


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


        return html;
            return html;
    },
        },


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


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


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


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


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


            if (typeof tippy === 'undefined') {
                if (typeof tippy === "undefined") {
                console.error('[LawTooltips] tippy не найден — подключи библиотеку Tippy.js');
                    console.error(
                return;
                        "[LawTooltips] tippy не найден — подключи библиотеку Tippy.js",
            }
                     );
            tippy(cell, {
                     return;
                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();
                        }
                     });
                 }
                 }
                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(
        console.log('[LawTooltips] initCellTooltips: ячеек с ссылкой', withLink, '| с #XXX', withMatch, '| с данными', withContent, '| тултипов создано', tooltipsCreated);
                "[LawTooltips] initCellTooltips: ячеек с ссылкой",
    }
                withLink,
};
                "| с #XXX",
 
                withMatch,
                "| с данными",
                withContent,
                "| тултипов создано",
                tooltipsCreated,
            );
        },
    };


   
     var DataTooltipsModule = {
     var DataTooltipsModule = {
         init: function() {
         init: function () {
             var elements = document.querySelectorAll('.data-tooltip[data-text]');
             var elements = document.querySelectorAll(
                ".data-tooltip[data-text]",
            );
             if (elements.length === 0) return;
             if (elements.length === 0) return;
           
 
             MediaWikiTooltips.load(function() {
             MediaWikiTooltips.load(function () {
                 elements.forEach(function(element) {
                 elements.forEach(function (element) {
                     var tooltipText = element.getAttribute('data-text');
                     var tooltipText = element.getAttribute("data-text");
                     if (!tooltipText || !tooltipText.trim()) return;
                     if (!tooltipText || !tooltipText.trim()) return;
                   
 
                     tippy(element, {
                     tippy(element, {
                         content: tooltipText,
                         content: tooltipText,
                         allowHTML: true,
                         allowHTML: true,
                         interactive: false,
                         interactive: false,
                         placement: 'top',
                         placement: "top",
                         maxWidth: 300,
                         maxWidth: 300,
                         theme: 'data-tooltip-theme',
                         theme: "data-tooltip-theme",
                         arrow: true,
                         arrow: true,
                         duration: [200, 150],
                         duration: [200, 150],
                         delay: [0, 50]
                         delay: [0, 50],
                     });
                     });
                 });
                 });
             });
             });
           
 
             var observer = new MutationObserver(function(mutations) {
             var observer = new MutationObserver(function (mutations) {
                 var shouldReinit = false;
                 var shouldReinit = false;
                 mutations.forEach(function(mutation) {
                 mutations.forEach(function (mutation) {
                     mutation.addedNodes.forEach(function(node) {
                     mutation.addedNodes.forEach(function (node) {
                         if (node.nodeType === 1) {
                         if (node.nodeType === 1) {
                             if (node.classList && node.classList.contains('data-tooltip') && node.hasAttribute('data-text')) {
                             if (
                                node.classList &&
                                node.classList.contains("data-tooltip") &&
                                node.hasAttribute("data-text")
                            ) {
                                 shouldReinit = true;
                                 shouldReinit = true;
                             } else if (node.querySelectorAll) {
                             } else if (node.querySelectorAll) {
                                 var newElements = node.querySelectorAll('.data-tooltip[data-text]');
                                 var newElements = node.querySelectorAll(
                                    ".data-tooltip[data-text]",
                                );
                                 if (newElements.length > 0) {
                                 if (newElements.length > 0) {
                                     shouldReinit = true;
                                     shouldReinit = true;
Строка 353: Строка 490:
                     });
                     });
                 });
                 });
               
 
                 if (shouldReinit) {
                 if (shouldReinit) {
                     setTimeout(function() { DataTooltipsModule.init(); }, 100);
                     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');


    if (cleanedText !== text) {
    var CopyTextModule = {
        preElement.textContent = cleanedText;
        init: function () {
    }
            CopyTextModule.initDirectElements();
},
            CopyTextModule.initAutoContainers();
   
        },
    setupElement: function(element) {
 
        element.style.cursor = 'pointer';
        initDirectElements: function () {
        element.addEventListener('click', CopyTextModule.handleClick);
            var elements = document.querySelectorAll(".copyable-text");
       
            if (elements.length === 0) return;
        var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
 
        if (isIOS) {
            elements.forEach(function (element) {
            element.style.webkitUserSelect = 'text';
                CopyTextModule.cleanPreContent(element);
            element.style.userSelect = 'text';
                CopyTextModule.setupElement(element);
        }
            });
    },
        },
   
 
    handleClick: function(e) {
        initAutoContainers: function () {
        e.preventDefault();
            var containers = document.querySelectorAll(
       
                ".copyable-pre-container",
        var element = this;
            );
        var textToCopy = element.textContent;
            if (containers.length === 0) return;
       
 
        if (navigator.clipboard && window.isSecureContext) {
            containers.forEach(function (container) {
            navigator.clipboard.writeText(textToCopy).then(function() {
                CopyTextModule.processContainerPre(container);
                CopyTextModule.showNotification(element);
            });
            }).catch(function(err) {
 
                console.error('Ошибка при копировании: ', err);
            // Наблюдатель за динамически появляющимися элементами
                CopyTextModule.fallbackCopy(textToCopy, element);
            var observer = new MutationObserver(function (mutations) {
            });
                mutations.forEach(function (mutation) {
        } else {
                    mutation.addedNodes.forEach(function (node) {
            CopyTextModule.fallbackCopy(textToCopy, element);
                        if (node.nodeType === 1) {
        }
                            // Проверяем сам элемент
       
                            if (node.tagName === "PRE") {
        element.classList.add('copying');
                                var container = node.closest(
        setTimeout(function() {
                                    ".copyable-pre-container",
            element.classList.remove('copying');
                                );
        }, 200);
                                if (
    },
                                    container &&
   
                                    !node.classList.contains("copyable-text")
    fallbackCopy: function(text, element) {
                                ) {
        var textArea = document.createElement('textarea');
                                    node.classList.add("copyable-text");
        textArea.value = text;
                                    CopyTextModule.cleanPreContent(node);
        textArea.style.position = 'fixed';
                                    CopyTextModule.setupElement(node);
        textArea.style.top = '-9999px';
                                }
        textArea.style.left = '-9999px';
                            }
        textArea.style.width = '2em';
                            // Проверяем дочерние элементы
        textArea.style.height = '2em';
                            else if (node.querySelectorAll) {
        textArea.style.padding = '0';
                                var containers = node.querySelectorAll(
        textArea.style.border = 'none';
                                    ".copyable-pre-container",
        textArea.style.outline = 'none';
                                );
        textArea.style.boxShadow = 'none';
                                containers.forEach(function (container) {
        textArea.style.background = 'transparent';
                                    CopyTextModule.processContainerPre(
       
                                        container,
        document.body.appendChild(textArea);
                                    );
        textArea.focus();
                                });
        textArea.select();
 
       
                                // Также проверяем pre внутри уже существующих контейнеров
        try {
                                var preElements = node.querySelectorAll(
            var successful = document.execCommand('copy');
                                    ".copyable-pre-container pre",
            if (successful) {
                                );
                CopyTextModule.showNotification(element);
                                preElements.forEach(function (pre) {
            } else {
                                    if (
                console.error('Не удалось скопировать текст');
                                        !pre.classList.contains("copyable-text")
            }
                                    ) {
        } catch (err) {
                                        pre.classList.add("copyable-text");
            console.error('Ошибка при копировании: ', err);
                                        CopyTextModule.cleanPreContent(pre);
        }
                                        CopyTextModule.setupElement(pre);
       
                                    }
        document.body.removeChild(textArea);
                                });
    },
                            }
   
                        }
    showNotification: function(element) {
                    });
        var existingNotifications = document.querySelectorAll('.copy-notification');
 
        existingNotifications.forEach(function(notification) {
                    // Обрабатываем изменения атрибутов (например, style="display: none" -> "")
            notification.remove();
                    if (
        });
                        mutation.type === "attributes" &&
       
                        mutation.attributeName === "style"
        var notification = document.createElement('div');
                    ) {
        notification.className = 'copy-notification';
                        var target = mutation.target;
        notification.textContent = 'Текст скопирован!';
                        if (target.style.display !== "none") {
        document.body.appendChild(notification);
                            var containers = target.querySelectorAll(
       
                                ".copyable-pre-container",
        var rect = element.getBoundingClientRect();
                            );
        var isMobile = window.innerWidth <= 768;
                            containers.forEach(function (container) {
       
                                CopyTextModule.processContainerPre(container);
        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 {
            observer.observe(document.body, {
            notification.style.position = 'absolute';
                childList: true,
            notification.style.top = (rect.top + window.scrollY - 40) + 'px';
                subtree: true,
            notification.style.left = (rect.left + window.scrollX) + 'px';
                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)
                   
                 '<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.querySelector('.clear-btn').addEventListener('click', function() {
            calculatorInterface
            LawCalculatorModule.clearSelection();
                .querySelector(".clear-btn")
        });
                .addEventListener("click", function () {
       
                    LawCalculatorModule.clearSelection();
        calculatorInterface.querySelector('.calculate-btn').addEventListener('click', function() {
                });
            LawCalculatorModule.calculateSentence();
 
        });
            calculatorInterface
       
                .querySelector(".calculate-btn")
        return calculatorInterface;
                .addEventListener("click", function () {
    },
                    LawCalculatorModule.calculateSentence();
   
                });
    makeTableInteractive: function(table) {
 
        var cells = table.querySelectorAll('td');
            return calculatorInterface;
        cells.forEach(function(cell) {
        },
            var link = cell.querySelector('a[href*="#"]');
 
            if (!link) return;
        makeTableInteractive: function (table) {
           
            var cells = table.querySelectorAll("td");
            var href = link.getAttribute('href');
            cells.forEach(
            var match = href.match(/#(\d{3})/);
                function (cell) {
            if (!match) return;
                    var link = cell.querySelector('a[href*="#"]');
           
                    if (!link) return;
            var lawCode = match[1];
 
            if (!this.lawData[lawCode]) return;
                    var href = link.getAttribute("href");
           
                    var match = href.match(/#(\d{3})/);
            cell.classList.add('law-cell-calculator');
                    if (!match) return;
            cell.dataset.lawCode = lawCode;
 
           
                    var lawCode = match[1];
            // Сохраняем оригинальный обработчик клика
                    if (!this.lawData[lawCode]) return;
            var originalClickHandler = cell.onclick;
 
           
                    cell.classList.add("law-cell-calculator");
            cell.addEventListener('click', function(e) {
                    cell.dataset.lawCode = lawCode;
                e.preventDefault();
 
                e.stopPropagation();
                    // Сохраняем оригинальный обработчик клика
                LawCalculatorModule.toggleViolation(lawCode, cell);
                    var originalClickHandler = cell.onclick;
            });
 
        }.bind(this));
                    cell.addEventListener("click", function (e) {
    },
                        e.preventDefault();
   
                        e.stopPropagation();
    makeTableNormal: function(table) {
                        LawCalculatorModule.toggleViolation(lawCode, cell);
        var cells = table.querySelectorAll('.law-cell-calculator');
                    });
        cells.forEach(function(cell) {
                }.bind(this),
            cell.classList.remove('law-cell-calculator', 'selected');
            );
            // Восстанавливаем оригинальную функциональность навигации
        },
            var lawCode = cell.dataset.lawCode;
 
            if (lawCode) {
        makeTableNormal: function (table) {
                cell.addEventListener('click', function(e) {
            var cells = table.querySelectorAll(".law-cell-calculator");
                    e.preventDefault();
            cells.forEach(function (cell) {
                    window.location.hash = lawCode;
                cell.classList.remove("law-cell-calculator", "selected");
                });
                // Восстанавливаем оригинальную функциональность навигации
            }
                var lawCode = cell.dataset.lawCode;
        });
                if (lawCode) {
    },
                    cell.addEventListener("click", function (e) {
   
                        e.preventDefault();
    toggleViolation: function(lawCode, cell) {
                        window.location.hash = lawCode;
        var index = this.selectedViolations.indexOf(lawCode);
                    });
       
                }
        if (index > -1) {
            });
            // Убираем нарушение
        },
            this.selectedViolations.splice(index, 1);
 
            cell.classList.remove('selected');
        toggleViolation: function (lawCode, cell) {
        } else {
            var index = this.selectedViolations.indexOf(lawCode);
            // Добавляем нарушение
 
            this.selectedViolations.push(lawCode);
            if (index > -1) {
            cell.classList.add('selected');
                // Убираем нарушение
        }
                this.selectedViolations.splice(index, 1);
       
                cell.classList.remove("selected");
        this.updateViolationsList();
            } else {
    },
                // Добавляем нарушение
   
                this.selectedViolations.push(lawCode);
    updateViolationsList: function() {
                cell.classList.add("selected");
        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 () {
    clearSelection: function() {
            var violationsList = document.querySelector(".violations-list");
        this.selectedViolations = [];
            if (!violationsList) return;
       
 
        // Убираем выделение со всех ячеек
            violationsList.innerHTML = "";
        var selectedCells = document.querySelectorAll('.law-cell-calculator.selected');
 
        selectedCells.forEach(function(cell) {
            this.selectedViolations.forEach(
            cell.classList.remove('selected');
                function (lawCode) {
        });
                    var law = this.lawData[lawCode];
       
                    if (!law) return;
        this.updateViolationsList();
 
        this.hideResult();
                    var item = document.createElement("div");
    },
                    item.className = "violation-item";
   
                    item.innerHTML =
    calculateSentence: function() {
                        lawCode +
        if (this.selectedViolations.length === 0) {
                        " - " +
            alert('Выберите хотя бы одно нарушение');
                        law.title +
            return;
                        '<span class="remove-btn" data-law-code="' +
        }
                        lawCode +
       
                        '">×</span>';
            var result = this.calculateSentenceLogic(this.selectedViolations);
 
            this.showResult(result);
                    item.querySelector(".remove-btn").addEventListener(
        },
                        "click",
       
                        function () {
        calculateSentenceLogic: function(violations) {
                            LawCalculatorModule.removeViolation(lawCode);
            // Группируем нарушения по категориям
                        },
            var categories = {};
                    );
           
 
            violations.forEach(function(lawCode) {
                    violationsList.appendChild(item);
                var law = this.lawData[lawCode];
                }.bind(this),
                if (!law || !law.category) return;
            );
               
        },
                // Используем номер статьи как ключ категории
 
                var categoryKey = this.getCategoryKeyByLawCode(lawCode);
        removeViolation: function (lawCode) {
                if (!categories[categoryKey]) {
            var index = this.selectedViolations.indexOf(lawCode);
                    categories[categoryKey] = [];
            if (index > -1) {
                }
                this.selectedViolations.splice(index, 1);
                categories[categoryKey].push(law);
 
            }.bind(this));
                // Убираем выделение с ячейки
           
                var cell = document.querySelector(
            var totalMinTime = 0;
                    '[data-law-code="' + lawCode + '"]',
            var totalMaxTime = 0;
                );
            var totalFine = 0;
                if (cell) {
            var hasCritical = false;
                    cell.classList.remove("selected");
            var resultText = '';
                }
           
 
            // Обрабатываем каждую категорию
                this.updateViolationsList();
            Object.keys(categories).forEach(function(categoryKey) {
            }
                var laws = categories[categoryKey];
        },
                var category = laws[0].category;
 
                var categoryName = this.getCategoryName(categoryKey);
        clearSelection: function () {
               
            this.selectedViolations = [];
                if (category.isCritical) {
 
                    hasCritical = true;
            // Убираем выделение со всех ячеек
                    resultText += '' + categoryName + ': ' + (category.punishment || 'Перевод в Д-класс/казнь') + '\n';
            var selectedCells = document.querySelectorAll(
                } else {
                ".law-cell-calculator.selected",
                    // Для одной категории берем самую тяжкую статью
            );
                    var maxLaw = laws.reduce(function(max, current) {
            selectedCells.forEach(function (cell) {
                        var currentCode = parseInt(lawCode);
                cell.classList.remove("selected");
                        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;
 
                    totalFine += category.fine || 0;
        calculateSentence: function () {
                   
            if (this.selectedViolations.length === 0) {
                    var punishmentText = '';
                alert("Выберите хотя бы одно нарушение");
                    if (category.punishment) {
                return;
                        punishmentText = category.punishment;
            }
                    } else if (category.minTime !== undefined) {
 
                        punishmentText = category.minTime === category.maxTime ?  
            var result = this.calculateSentenceLogic(this.selectedViolations);
                            category.minTime + ' минут' :  
            this.showResult(result);
                            category.minTime + '-' + category.maxTime + ' минут';
        },
                    }
 
                   
        calculateSentenceLogic: function (violations) {
                    resultText += '' + categoryName + ': ' + punishmentText + ' (статья ' + lawCode + ')\n';
            // Группируем нарушения по категориям
                }
            var categories = {};
            }.bind(this));
 
           
            violations.forEach(
            if (hasCritical) {
                function (lawCode) {
                return {
                    var law = this.lawData[lawCode];
                    isCritical: true,
                    if (!law || !law.category) return;
                    text: 'КРИТИЧЕСКИЕ НАРУШЕНИЯ\n' + resultText + '\nПриговор: Перевод в Д-класс или казнь'
 
                };
                    // Используем номер статьи как ключ категории
            } else if (this.pageType === 'd-class') {
                    var categoryKey = this.getCategoryKeyByLawCode(lawCode);
                // Для Д-класса показываем штрафы и время отдельно
                    if (!categories[categoryKey]) {
                var summaryText = '';
                        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) {
                 if (totalFine > 0) {
                     summaryText += 'Общий штраф: ' + totalFine + ' кредитов\n';
                     summaryText += "Общий штраф: " + totalFine + " кредитов\n";
                 }
                 }
                 if (totalMinTime > 0 || totalMaxTime > 0) {
                 if (totalMinTime > 0 || totalMaxTime > 0) {
                     summaryText += 'Общее время: ' + this.formatTime(totalMinTime, totalMaxTime) + '\n';
                     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();
                 }
                 }
               
            });
                return {
            this.snowflakes = [];
                    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 = {
            var styles = document.getElementById("snowflakes-styles");
    fireworks: null,
            if (styles) {
    container: null,
                styles.remove();
    checkInterval: null,
            }
    isActive: false,
        },
    loaded: false,
 
    loading: false,
        toggle: function () {
    callbacks: [],
            if (this.isActive) {
   
                this.stop();
    init: function() {
            } else {
        this.startTimeCheck();
                this.init();
    },
            }
   
        },
    loadFireworks: function(callback) {
    };
        // Проверяем разные варианты экспорта библиотеки
 
        if (this.loaded && (window.Fireworks || (window.Fireworks && window.Fireworks.default))) {
    window.SnowflakesModule = SnowflakesModule;
            if (callback) callback();
 
            return;
    var FireworksModule = {
        }
        fireworks: null,
       
        container: null,
        if (callback) {
        checkInterval: null,
            this.callbacks.push(callback);
        isActive: false,
        }
        loaded: false,
       
        loading: false,
        if (this.loading) return;
        callbacks: [],
        this.loading = true;
 
       
        init: function () {
        var self = this;
            this.startTimeCheck();
       
        },
        // Загружаем библиотеку fireworks.js через UMD версию
 
        var script = document.createElement('script');
        loadFireworks: function (callback) {
        script.src = 'https://unpkg.com/fireworks-js@2/dist/index.umd.js';
            // Проверяем разные варианты экспорта библиотеки
        script.onload = function() {
            if (
            self.loaded = true;
                this.loaded &&
            self.loading = false;
                (window.Fireworks ||
           
                    (window.Fireworks && window.Fireworks.default))
            // Вызываем все колбэки
            ) {
            self.callbacks.forEach(function(cb) { cb(); });
                if (callback) callback();
            self.callbacks = [];
                return;
        };
            }
        script.onerror = function() {
 
            console.error('Ошибка загрузки fireworks.js');
            if (callback) {
            self.loading = false;
                this.callbacks.push(callback);
            self.callbacks = [];
            }
        };
 
       
            if (this.loading) return;
        document.head.appendChild(script);
            this.loading = true;
    },
 
   
            var self = this;
    getMoscowTime: function() {
 
        // МСК = UTC+3
            // Загружаем библиотеку fireworks.js через UMD версию
        var now = new Date();
            var script = document.createElement("script");
        var utc = now.getTime() + (now.getTimezoneOffset() * 60000);
            script.src = "https://unpkg.com/fireworks-js@2/dist/index.umd.js";
        var moscowTime = new Date(utc + (3 * 3600000)); // UTC+3
            script.onload = function () {
        return moscowTime;
                self.loaded = true;
    },
                self.loading = false;
   
 
    isFireworksTime: function() {
                // Вызываем все колбэки
        var moscowTime = this.getMoscowTime();
                self.callbacks.forEach(function (cb) {
        var hours = moscowTime.getHours();
                    cb();
        // С 00:00 до 01:00 по МСК
                });
        return hours === 0;
                self.callbacks = [];
    },
            };
   
            script.onerror = function () {
    startTimeCheck: function() {
                console.error("Ошибка загрузки fireworks.js");
        var self = this;
                self.loading = false;
       
                self.callbacks = [];
        // Проверяем сразу при загрузке
            };
        this.checkTime();
 
       
            document.head.appendChild(script);
        // Проверяем каждую минуту
        },
        this.checkInterval = setInterval(function() {
 
            self.checkTime();
        getMoscowTime: function () {
        }, 60000);
            // МСК = UTC+3
    },
            var now = new Date();
   
            var utc = now.getTime() + now.getTimezoneOffset() * 60000;
    checkTime: function() {
            var moscowTime = new Date(utc + 3 * 3600000); // UTC+3
        if (this.isFireworksTime()) {
            return moscowTime;
            if (!this.isActive) {
        },
                this.startFireworks();
 
            }
        isFireworksTime: function () {
        } else {
            var moscowTime = this.getMoscowTime();
            if (this.isActive) {
            var hours = moscowTime.getHours();
                this.stopFireworks();
            // С 00:00 до 01:00 по МСК
            }
            return hours === 0;
        }
        },
    },
 
   
        startTimeCheck: function () {
    getFireworksConstructor: function() {
            var self = this;
        // Пробуем разные варианты доступа к конструктору
 
        if (window.Fireworks && typeof window.Fireworks === 'function') {
            // Проверяем сразу при загрузке
            return window.Fireworks;
            this.checkTime();
        }
 
        if (window.Fireworks && window.Fireworks.default && typeof window.Fireworks.default === 'function') {
            // Проверяем каждую минуту
            return window.Fireworks.default;
            this.checkInterval = setInterval(function () {
        }
                self.checkTime();
        if (window.fireworks && typeof window.fireworks === 'function') {
            }, 60000);
            return window.fireworks;
        },
        }
 
        // Если библиотека экспортируется по-другому
        checkTime: function () {
        if (typeof Fireworks !== 'undefined' && typeof Fireworks === 'function') {
            if (this.isFireworksTime()) {
            return Fireworks;
                if (!this.isActive) {
        }
                    this.startFireworks();
        return null;
                }
    },
            } else {
   
                if (this.isActive) {
    startFireworks: function() {
                    this.stopFireworks();
        var self = this;
                }
       
            }
        this.loadFireworks(function() {
        },
            if (!self.container) {
 
                self.container = document.createElement('div');
        getFireworksConstructor: function () {
                self.container.id = 'fireworks-container';
            // Пробуем разные варианты доступа к конструктору
                self.container.style.cssText =  
            if (window.Fireworks && typeof window.Fireworks === "function") {
                    'position: fixed;' +
                return window.Fireworks;
                    'top: 0;' +
            }
                    'left: 0;' +
            if (
                    'width: 100%;' +
                window.Fireworks &&
                    'height: 100%;' +
                window.Fireworks.default &&
                    'pointer-events: none;' +
                typeof window.Fireworks.default === "function"
                    'z-index: 99998;';
            ) {
                document.body.appendChild(self.container);
                return window.Fireworks.default;
            }
            }
           
            if (window.fireworks && typeof window.fireworks === "function") {
            var FireworksConstructor = self.getFireworksConstructor();
                return window.fireworks;
           
            }
            if (!FireworksConstructor) {
            // Если библиотека экспортируется по-другому
                console.error('Не удалось найти конструктор Fireworks. Проверьте загрузку библиотеки.');
            if (
                console.log('Доступные объекты:', {
                typeof Fireworks !== "undefined" &&
                    'window.Fireworks': window.Fireworks,
                typeof Fireworks === "function"
                    'window.Fireworks.default': window.Fireworks && window.Fireworks.default,
            ) {
                    'window.fireworks': window.fireworks
                return Fireworks;
                });
            }
                return;
            return null;
            }
        },
           
 
            if (!self.fireworks) {
        startFireworks: function () {
                try {
            var self = this;
                    self.fireworks = new FireworksConstructor(self.container, {
 
                        autoresize: true,
            this.loadFireworks(function () {
                        opacity: 0.5,
                if (!self.container) {
                        // Замедляем фейерверки
                    self.container = document.createElement("div");
                        acceleration: 1.02, // Уменьшено с 1.05 для более медленного движения
                    self.container.id = "fireworks-container";
                        friction: 0.98,     // Увеличено с 0.97 для большего сопротивления
                    self.container.style.cssText =
                        gravity: 1.2,       // Уменьшено с 1.5 для более медленного падения
                        "position: fixed;" +
                        particles: 50,
                        "top: 0;" +
                        traceLength: 5,     // Увеличено с 3 для более длинного следа
                        "left: 0;" +
                        traceSpeed: 5,       // Уменьшено с 10 для более медленного следа
                        "width: 100%;" +
                        explosion: 5,
                        "height: 100%;" +
                        intensity: 30,
                        "pointer-events: none;" +
                        flickering: 50,
                        "z-index: 99998;";
                        lineStyle: 'round',
                    document.body.appendChild(self.container);
                        hue: { min: 0, max: 360 },
                }
                        // Увеличиваем задержку между запусками для более медленного темпа
 
                        delay: { min: 30, max: 60 }, // Увеличено с 15-30 до 30-60
                var FireworksConstructor = self.getFireworksConstructor();
                        rocketsPoint: { min: 50, max: 50 },
 
                        lineWidth: {  
                if (!FireworksConstructor) {
                            explosion: { min: 1, max: 3 },  
                    console.error(
                            trace: { min: 1, max: 2 }  
                        "Не удалось найти конструктор Fireworks. Проверьте загрузку библиотеки.",
                        },
                    );
                        brightness: { min: 50, max: 80 },
                    console.log("Доступные объекты:", {
                        decay: { min: 0.01, max: 0.02 }, // Уменьшено для более медленного затухания
                        "window.Fireworks": window.Fireworks,
                        mouse: { click: false, move: false, max: 1 },
                        "window.Fireworks.default":
                        // Включаем звук
                            window.Fireworks && window.Fireworks.default,
                        sound: {
                        "window.fireworks": window.fireworks,
                            enable: true,
                    });
                            files: [
                    return;
                                'https://fireworks.js.org/sounds/explosion0.mp3',
                }
                                'https://fireworks.js.org/sounds/explosion1.mp3',
 
                                'https://fireworks.js.org/sounds/explosion2.mp3'
                if (!self.fireworks) {
                            ],
                    try {
                            volume: { min: 0.3, max: 0.6 } // Громкость от 30% до 60%
                        self.fireworks = new FireworksConstructor(
                        }
                            self.container,
                    });
                            {
                } catch (e) {
                                autoresize: true,
                    console.error('Ошибка создания экземпляра Fireworks:', e);
                                opacity: 0.5,
                    return;
                                // Замедляем фейерверки
                }
                                acceleration: 1.02, // Уменьшено с 1.05 для более медленного движения
            }
                                friction: 0.98, // Увеличено с 0.97 для большего сопротивления
           
                                gravity: 1.2, // Уменьшено с 1.5 для более медленного падения
            if (self.fireworks) {
                                particles: 50,
                self.fireworks.start();
                                traceLength: 5, // Увеличено с 3 для более длинного следа
                self.isActive = true;
                                traceSpeed: 5, // Уменьшено с 10 для более медленного следа
            }
                                explosion: 5,
        });
                                intensity: 30,
    },
                                flickering: 50,
   
                                lineStyle: "round",
    stopFireworks: function() {
                                hue: { min: 0, max: 360 },
        if (this.fireworks) {
                                // Увеличиваем задержку между запусками для более медленного темпа
            this.fireworks.stop();
                                delay: { min: 30, max: 60 }, // Увеличено с 15-30 до 30-60
            this.isActive = false;
                                rocketsPoint: { min: 50, max: 50 },
        }
                                lineWidth: {
    },
                                    explosion: { min: 1, max: 3 },
   
                                    trace: { min: 1, max: 2 },
    toggleFireworks: function() {
                                },
        if (this.isActive) {
                                brightness: { min: 50, max: 80 },
            this.stopFireworks();
                                decay: { min: 0.01, max: 0.02 }, // Уменьшено для более медленного затухания
        } else {
                                mouse: { click: false, move: false, max: 1 },
            this.startFireworks();
                                // Включаем звук
        }
                                sound: {
    }
                                    enable: true,
};
                                    files: [
                                        "https://fireworks.js.org/sounds/explosion0.mp3",
window.FireworksModule = FireworksModule;
                                        "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() {
Строка 2033: Строка 2417:
         // LawCalculatorModule.init();
         // LawCalculatorModule.init();
     }
     }
   
 
     if (document.readyState === 'loading') {
     if (document.readyState === "loading") {
         document.addEventListener('DOMContentLoaded', initAllModules);
         document.addEventListener("DOMContentLoaded", initAllModules);
     } else {
     } else {
         initAllModules();
         initAllModules();
     }
     }
   
})();
})();