| Строка 1: |
Строка 1: |
| /**
| |
| * Если CDN недоступен: залей на вики popper.min.js, tippy-bundle.umd.min.js, tippy.css (через Загрузить файл),
| |
| * затем в консоли или в начале скрипта задай:
| |
| * MediaWikiTooltips.baseUrl = "https://fire.station.wiki.shizainc.com/wiki/Special:FilePath/";
| |
| * (подставь свой домен вики)
| |
| */
| |
| (function () { | | (function () { |
| "use strict"; | | "use strict"; |
| Строка 10: |
Строка 4: |
| window.MediaWikiTooltips = { | | window.MediaWikiTooltips = { |
| loaded: false, | | loaded: false, |
| loading: false,
| |
| callbacks: [], | | callbacks: [], |
| baseUrl: null,
| |
|
| |
|
| load: function (callback) { | | load: function (callback) { |
| Строка 26: |
Строка 18: |
|
| |
|
| var self = this; | | var self = this; |
| var cdns = [ | | var popperScript = document.createElement("script"); |
| { | | popperScript.src = |
| base: "https://cdnjs.cloudflare.com/ajax/libs",
| | "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js"; |
| popper: "/popper.js/2.11.8/umd/popper.min.js",
| | popperScript.onload = function () { |
| tippy: "/tippy.js/6.3.7/tippy-bundle.umd.min.js",
| |
| css: "/tippy.js/6.3.7/tippy.css",
| |
| },
| |
| ];
| |
| if (self.baseUrl) { | |
| var b = self.baseUrl.replace(/\/?$/, "/");
| |
| cdns = [
| |
| {
| |
| base: b,
| |
| popper: "popper.min.js",
| |
| tippy: "tippy-bundle.umd.min.js",
| |
| css: "tippy.css",
| |
| },
| |
| ];
| |
| }
| |
| var cdnIndex = 0;
| |
| | |
| var tryFallback = function (which) {
| |
| self.loading = false;
| |
| self.callbacks = [];
| |
| };
| |
| | |
| var runCallbacks = function () {
| |
| self.loaded = true;
| |
| self.loading = false;
| |
| self.callbacks.forEach(function (cb) {
| |
| try { cb(); } catch (e) {}
| |
| });
| |
| self.callbacks = [];
| |
| };
| |
| | |
| function urlJoin(base, path) {
| |
| return base.replace(/\/$/, "") + "/" + path.replace(/^\//, "");
| |
| }
| |
| | |
| function loadTippyAndCss() {
| |
| var cdn = cdns[cdnIndex];
| |
| var tippyScript = document.createElement("script"); | | var tippyScript = document.createElement("script"); |
| tippyScript.src = urlJoin(cdn.base, cdn.tippy); | | tippyScript.src = |
| tippyScript.onerror = function () {
| | "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.7/tippy-bundle.umd.min.js"; |
| if (cdnIndex + 1 < cdns.length) {
| |
| cdnIndex++;
| |
| loadPopper();
| |
| } else {
| |
| tryFallback("Tippy.js");
| |
| }
| |
| };
| |
| tippyScript.onload = function () { | | tippyScript.onload = function () { |
| if (!window.tippy) { | | self.loaded = true; |
| if (cdnIndex + 1 < cdns.length) {
| | self.loading = false; |
| cdnIndex++;
| | self.callbacks.forEach(function (cb) { |
| loadPopper();
| | cb(); |
| } else {
| | }); |
| tryFallback("Tippy.js (не в глобальной области)");
| | self.callbacks = []; |
| }
| |
| return;
| |
| }
| |
| var tippyCSS = document.createElement("link");
| |
| tippyCSS.rel = "stylesheet"; | |
| tippyCSS.href = urlJoin(cdn.base, cdn.css); | |
| tippyCSS.onerror = function () {};
| |
| document.head.appendChild(tippyCSS); | |
| runCallbacks(); | |
| }; | | }; |
| document.head.appendChild(tippyScript); | | document.head.appendChild(tippyScript); |
| } | | }; |
| | | document.head.appendChild(popperScript); |
| function loadPopper() { | |
| var cdn = cdns[cdnIndex];
| |
| var popperScript = document.createElement("script");
| |
| popperScript.src = urlJoin(cdn.base, cdn.popper);
| |
| popperScript.onerror = function () {
| |
| if (cdnIndex + 1 < cdns.length) {
| |
| cdnIndex++;
| |
| loadPopper();
| |
| } else {
| |
| tryFallback("Popper.js");
| |
| }
| |
| };
| |
| popperScript.onload = loadTippyAndCss;
| |
| document.head.appendChild(popperScript);
| |
| }
| |
|
| |
|
| loadPopper(); | | var tippyCSS = document.createElement("link"); |
| | tippyCSS.rel = "stylesheet"; |
| | tippyCSS.href = |
| | "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.7/tippy.css"; |
| | document.head.appendChild(tippyCSS); |
| }, | | }, |
| }; | | }; |
| Строка 194: |
Строка 122: |
|
| |
|
| var LawTooltipsModule = { | | var LawTooltipsModule = { |
| init: function() { | | init: function () { |
| var tableCells = document.querySelectorAll('.law-tooltips td, .law-tooltips th'); | | console.log("[LawTooltips] init()"); |
| if (tableCells.length === 0) return; | | var tableCells = document.querySelectorAll( |
| | | ".law-tooltips td, .law-tooltips th", |
| MediaWikiTooltips.load(function() { | | ); |
| | console.log( |
| | "[LawTooltips] .law-tooltips ячеек:", |
| | tableCells.length, |
| | ); |
| | if (tableCells.length === 0) { |
| | console.warn("[LawTooltips] Нет ячеек .law-tooltips — выход"); |
| | return; |
| | } |
| | |
| | function run() { |
| | console.log("[LawTooltips] run() — извлечение данных..."); |
| var lawData = LawTooltipsModule.extractLawData(); | | var lawData = LawTooltipsModule.extractLawData(); |
| LawTooltipsModule.initCellTooltips(tableCells, lawData); | | var count = Object.keys(lawData).length; |
| | console.log( |
| | "[LawTooltips] Загружено записей законов:", |
| | count, |
| | count |
| | ? Object.keys(lawData).slice(0, 10).join(", ") + |
| | (count > 10 ? "..." : "") |
| | : "", |
| | ); |
| | if (count > 0) { |
| | LawTooltipsModule.initCellTooltips(tableCells, lawData); |
| | return true; |
| | } |
| | return false; |
| | } |
| | |
| | if (typeof MediaWikiTooltips === "undefined") { |
| | console.error( |
| | "[LawTooltips] MediaWikiTooltips не найден — тултипы не загрузятся", |
| | ); |
| | run(); |
| | return; |
| | } |
| | MediaWikiTooltips.load(function () { |
| | console.log("[LawTooltips] MediaWikiTooltips.load вызван"); |
| | if (run()) { |
| | console.log("[LawTooltips] Инициализация с первой попытки"); |
| | return; |
| | } |
| | setTimeout(function () { |
| | if (run()) { |
| | console.log("[LawTooltips] Инициализация после 300ms"); |
| | return; |
| | } |
| | setTimeout(function () { |
| | if (run()) |
| | console.log( |
| | "[LawTooltips] Инициализация после 800ms", |
| | ); |
| | else |
| | console.warn( |
| | "[LawTooltips] Данные законов не найдены после всех попыток", |
| | ); |
| | }, 500); |
| | }, 300); |
| | }); |
| | }, |
| | |
| | extractLawData: function () { |
| | var lawData = {}; |
| | var containers = document.querySelectorAll(".mw-collapsible"); |
| | console.log( |
| | "[LawTooltips] extractLawData: коллапсибл-блоков:", |
| | containers.length, |
| | ); |
| | containers.forEach(function (block) { |
| | var content = block.querySelector(".mw-collapsible-content"); |
| | if (content && block.classList.contains("mw-collapsed")) { |
| | block.classList.remove("mw-collapsed"); |
| | var toggle = content.querySelector( |
| | ".mw-collapsible-toggle", |
| | ); |
| | if (toggle) toggle.setAttribute("aria-expanded", "true"); |
| | } |
| | }); |
| | |
| | var detailTables = document.querySelectorAll( |
| | ".mw-collapsible-content table", |
| | ); |
| | console.log( |
| | "[LawTooltips] extractLawData: таблиц в .mw-collapsible-content:", |
| | detailTables.length, |
| | ); |
| | detailTables.forEach(function (table, tableIndex) { |
| | if (table.classList.contains("law-tooltips")) { |
| | console.log( |
| | "[LawTooltips] extractLawData: таблица", |
| | tableIndex, |
| | "— пропуск (law-tooltips)", |
| | ); |
| | return; |
| | } |
| | var rows = table.querySelectorAll("tr"); |
| | |
| | rows.forEach(function (row) { |
| | var cells = row.querySelectorAll("td"); |
| | if (cells.length < 3) return; |
| | |
| | var crimeCell = cells[0]; |
| | var descriptionCell = cells[1]; |
| | var exampleCell = cells[2]; |
| | |
| | var anchor = crimeCell.querySelector("[id]"); |
| | if (!anchor) return; |
| | |
| | var lawCode = anchor.id; |
| | var titleElement = crimeCell.querySelector("b"); |
| | if (!titleElement || !lawCode.match(/^\d{3}$/)) return; |
| | |
| | var titleText = titleElement.textContent.trim(); |
| | var lawTitle = titleText |
| | .replace(/^\d{3}\s*/, "") |
| | .replace(/^\d{3}/, "") |
| | .trim(); |
| | var description = descriptionCell.innerHTML.trim(); |
| | |
| | var examples = []; |
| | var exampleItems = exampleCell.querySelectorAll("li"); |
| | exampleItems.forEach(function (item) { |
| | var html = item.innerHTML |
| | .trim() |
| | .replace(/<br\s*\/?>/gi, ""); |
| | if (html) examples.push(html); |
| | }); |
| | |
| | lawData[lawCode] = { |
| | title: lawTitle, |
| | description: description, |
| | examples: examples, |
| | }; |
| | }); |
| }); | | }); |
| | |
| | console.log( |
| | "[LawTooltips] extractLawData: итого кодов:", |
| | Object.keys(lawData).length, |
| | ); |
| | return lawData; |
| }, | | }, |
|
| | |
| extractLawData: function() { | | createTooltipContent: function (lawCode, lawData) { |
| var lawData = {};
| | var data = lawData[lawCode]; |
| var detailTables = document.querySelectorAll('.mw-collapsible-content table');
| | if (!data) return null; |
|
| | |
| detailTables.forEach(function(table) {
| | var html = '<div class="law-tooltip">'; |
| var rows = table.querySelectorAll('tr');
| | html += |
|
| | '<h4 class="law-tooltip-title">' + |
| rows.forEach(function(row) {
| | lawCode + |
| var cells = row.querySelectorAll('td');
| | " - " + |
| if (cells.length < 3) return;
| | data.title + |
|
| | "</h4>"; |
| var crimeCell = cells[0];
| | html += |
| var descriptionCell = cells[1];
| | '<p class="law-tooltip-description">' + |
| var exampleCell = cells[2];
| | data.description + |
|
| | "</p>"; |
| var anchor = crimeCell.querySelector('[id]');
| | |
| if (!anchor) return;
| | if (data.examples && data.examples.length > 0) { |
|
| | html += '<div class="law-tooltip-examples">'; |
| var lawCode = anchor.id;
| | html += "<strong>Примеры:</strong><ul>"; |
| var titleElement = crimeCell.querySelector('b');
| | data.examples.slice(0, 5).forEach(function (example) { |
|
| | html += "<li>" + example + "</li>"; |
| if (titleElement && lawCode.match(/^\d{3}$/)) {
| | }); |
| var titleText = titleElement.textContent.trim();
| | html += "</ul></div>"; |
| var lawTitle = titleText.replace(/^\d{3}\s*/, '').replace(/^\d{3}/, '').trim();
| | } |
| var description = descriptionCell.innerHTML.trim();
| | html += "</div>"; |
|
| | |
| var examples = [];
| | return html; |
| var exampleItems = exampleCell.querySelectorAll('li');
| | }, |
| exampleItems.forEach(function(item) {
| | |
| var html = item.innerHTML.trim();
| | initCellTooltips: function (tableCells, lawData) { |
| // Удаляем все <br> теги
| | var withLink = 0, |
| html = html.replace(/<br\s*\/?>/gi, '');
| | withMatch = 0, |
| if (html) examples.push(html);
| | withContent = 0, |
| });
| | tooltipsCreated = 0; |
|
| | tableCells.forEach(function (cell) { |
| lawData[lawCode] = {
| |
| title: lawTitle,
| |
| description: description,
| |
| examples: examples
| |
| };
| |
| }
| |
| });
| |
| });
| |
|
| |
| return lawData;
| |
| },
| |
|
| |
| createTooltipContent: function(lawCode, lawData) {
| |
| var data = lawData[lawCode];
| |
| if (!data) return null;
| |
|
| |
| var html = '<div class="law-tooltip">';
| |
| html += '<h4 class="law-tooltip-title">' + lawCode + ' - ' + data.title + '</h4>';
| |
| html += '<p class="law-tooltip-description">' + data.description + '</p>';
| |
|
| |
| if (data.examples && data.examples.length > 0) {
| |
| html += '<div class="law-tooltip-examples">';
| |
| html += '<strong>Примеры:</strong><ul>';
| |
| data.examples.slice(0, 5).forEach(function(example) {
| |
| html += '<li>' + example + '</li>'; // Теперь example содержит HTML
| |
| });
| |
| html += '</ul></div>';
| |
| }
| |
| html += '</div>';
| |
|
| |
| return html;
| |
| },
| |
|
| |
| initCellTooltips: function(tableCells, lawData) { | |
| tableCells.forEach(function(cell) { | |
| var link = cell.querySelector('a[href*="#"]'); | | var link = cell.querySelector('a[href*="#"]'); |
| if (!link) return; | | if (!link) return; |
| | | withLink++; |
| var href = link.getAttribute('href'); | | |
| | var href = link.getAttribute("href"); |
| var match = href.match(/#(\d{3})/); | | var match = href.match(/#(\d{3})/); |
| if (!match) return; | | if (!match) return; |
| | | withMatch++; |
| | |
| var lawCode = match[1]; | | var lawCode = match[1]; |
| var tooltipContent = LawTooltipsModule.createTooltipContent(lawCode, lawData); | | var tooltipContent = LawTooltipsModule.createTooltipContent( |
| | lawCode, |
| | lawData, |
| | ); |
| if (!tooltipContent) return; | | if (!tooltipContent) return; |
| | | withContent++; |
| cell.classList.add('law-cell-with-tooltip'); | | |
| | | cell.classList.add("law-cell-with-tooltip"); |
| cell.addEventListener('click', function(e) { | | tooltipsCreated++; |
| | |
| | cell.addEventListener("click", function (e) { |
| e.preventDefault(); | | e.preventDefault(); |
| window.location.hash = lawCode; | | window.location.hash = lawCode; |
| }); | | }); |
| | | |
| | if (typeof tippy === "undefined") { |
| | console.error( |
| | "[LawTooltips] tippy не найден — подключи библиотеку Tippy.js", |
| | ); |
| | return; |
| | } |
| tippy(cell, { | | tippy(cell, { |
| content: tooltipContent, | | content: tooltipContent, |
| allowHTML: true, | | allowHTML: true, |
| interactive: true, | | interactive: true, |
| placement: 'top', | | placement: "top", |
| maxWidth: 400, | | maxWidth: 400, |
| theme: 'law-theme', | | theme: "law-theme", |
| arrow: true, | | arrow: true, |
| duration: [200, 150], | | duration: [200, 150], |
| delay: [0, 50], | | delay: [0, 50], |
| appendTo: document.body, | | appendTo: document.body, |
| boundary: 'viewport', | | boundary: "viewport", |
| popperOptions: { | | popperOptions: { |
| strategy: 'fixed', | | strategy: "fixed", |
| modifiers: [ | | modifiers: [ |
| { | | { |
| name: 'preventOverflow', | | name: "preventOverflow", |
| options: { | | options: { |
| boundary: 'viewport', | | boundary: "viewport", |
| padding: 20, | | padding: 20, |
| altAxis: true, | | altAxis: true, |
| altBoundary: true | | altBoundary: true, |
| } | | }, |
| }, | | }, |
| { | | { |
| name: 'flip', | | name: "flip", |
| options: { | | options: { |
| fallbackPlacements: ['bottom', 'left', 'right'] | | fallbackPlacements: [ |
| } | | "bottom", |
| | "left", |
| | "right", |
| | ], |
| | }, |
| }, | | }, |
| { | | { |
| name: 'computeStyles', | | name: "computeStyles", |
| options: { adaptive: false } | | options: { adaptive: false }, |
| } | | }, |
| ] | | ], |
| | }, |
| | onShow: function (instance) { |
| | document |
| | .querySelectorAll("[data-tippy-root]") |
| | .forEach(function (tooltip) { |
| | if ( |
| | tooltip._tippy && |
| | tooltip._tippy !== instance |
| | ) { |
| | tooltip._tippy.hide(); |
| | } |
| | }); |
| }, | | }, |
| onShow: function(instance) {
| |
| document.querySelectorAll('[data-tippy-root]').forEach(function(tooltip) {
| |
| if (tooltip._tippy && tooltip._tippy !== instance) {
| |
| tooltip._tippy.hide();
| |
| }
| |
| });
| |
| }
| |
| }); | | }); |
| }); | | }); |
| } | | console.log( |
| | "[LawTooltips] initCellTooltips: ячеек с ссылкой", |
| | withLink, |
| | "| с #XXX", |
| | withMatch, |
| | "| с данными", |
| | withContent, |
| | "| тултипов создано", |
| | tooltipsCreated, |
| | ); |
| | }, |
| }; | | }; |
|
| |
|
| Строка 1015: |
Строка 1077: |
| stopTimeUpdates: function () { | | stopTimeUpdates: function () { |
| DocumentAutoFillModule.stopTimeUpdateTimer(); | | DocumentAutoFillModule.stopTimeUpdateTimer(); |
| },
| |
| };
| |
|
| |
| var LawCalculatorModule = {
| |
| isCalculatorMode: false,
| |
| selectedViolations: [],
| |
| lawData: {},
| |
|
| |
| init: function () {
| |
| this.extractLawData();
| |
| this.createModeToggle();
| |
| this.observeTables();
| |
| },
| |
|
| |
| extractLawData: function () {
| |
| // Используем данные из существующего LawTooltipsModule
| |
| if (
| |
| window.LawTooltipsModule &&
| |
| window.LawTooltipsModule.extractLawData
| |
| ) {
| |
| this.lawData = window.LawTooltipsModule.extractLawData();
| |
| } else {
| |
| // Fallback - извлекаем данные самостоятельно
| |
| this.lawData = this.extractLawDataFallback();
| |
| }
| |
|
| |
| // Добавляем информацию о категориях и сроках
| |
| this.addCategoryInfo();
| |
| },
| |
|
| |
| extractLawDataFallback: function () {
| |
| var lawData = {};
| |
| var detailTables = document.querySelectorAll(
| |
| ".mw-collapsible-content table",
| |
| );
| |
|
| |
| detailTables.forEach(function (table) {
| |
| var rows = table.querySelectorAll("tr");
| |
|
| |
| rows.forEach(function (row) {
| |
| var cells = row.querySelectorAll("td");
| |
| if (cells.length < 3) return;
| |
|
| |
| var crimeCell = cells[0];
| |
| var anchor = crimeCell.querySelector("[id]");
| |
| if (!anchor) return;
| |
|
| |
| var lawCode = anchor.id;
| |
| var titleElement = crimeCell.querySelector("b");
| |
|
| |
| if (titleElement && lawCode.match(/^\d{3}$/)) {
| |
| var titleText = titleElement.textContent.trim();
| |
| var lawTitle = titleText
| |
| .replace(/^\d{3}\s*/, "")
| |
| .replace(/^\d{3}/, "")
| |
| .trim();
| |
| var description = cells[1].innerHTML.trim();
| |
|
| |
| var examples = [];
| |
| var exampleItems = cells[2].querySelectorAll("li");
| |
| exampleItems.forEach(function (item) {
| |
| var html = item.innerHTML.trim();
| |
| html = html.replace(/<br\s*\/?>/gi, "");
| |
| if (html) examples.push(html);
| |
| });
| |
|
| |
| lawData[lawCode] = {
| |
| title: lawTitle,
| |
| description: description,
| |
| examples: examples,
| |
| };
| |
| }
| |
| });
| |
| });
| |
|
| |
| return lawData;
| |
| },
| |
|
| |
| addCategoryInfo: function () {
| |
| // Определяем тип страницы и извлекаем информацию о наказаниях
| |
| this.pageType = this.detectPageType();
| |
| this.extractPunishmentInfo();
| |
| },
| |
|
| |
| detectPageType: function () {
| |
| // Проверяем заголовки страницы для определения типа
| |
| var pageTitle = document.title.toLowerCase();
| |
| var headings = document.querySelectorAll("h1, h2, h3");
| |
|
| |
| for (var i = 0; i < headings.length; i++) {
| |
| var headingText = headings[i].textContent.toLowerCase();
| |
| if (
| |
| headingText.includes("д-класс") ||
| |
| headingText.includes("d-class")
| |
| ) {
| |
| return "d-class";
| |
| }
| |
| if (
| |
| headingText.includes("персонал") ||
| |
| headingText.includes("сотрудник")
| |
| ) {
| |
| return "personnel";
| |
| }
| |
| }
| |
|
| |
| // Проверяем по URL или другим признакам
| |
| if (
| |
| window.location.href.toLowerCase().includes("d-class") ||
| |
| window.location.href.toLowerCase().includes("д-класс")
| |
| ) {
| |
| return "d-class";
| |
| }
| |
|
| |
| return "personnel"; // По умолчанию
| |
| },
| |
|
| |
| extractPunishmentInfo: function () {
| |
| // Извлекаем информацию о наказаниях из текста на странице
| |
| var punishmentSections = document.querySelectorAll(
| |
| ".mw-collapsible-content p",
| |
| );
| |
| var categories = {};
| |
|
| |
| punishmentSections.forEach(
| |
| function (section) {
| |
| var text = section.textContent;
| |
| var punishmentMatch = text.match(
| |
| /Наказание[:\s]*(.+?)(?:\n|$)/i,
| |
| );
| |
|
| |
| if (punishmentMatch) {
| |
| var punishmentText = punishmentMatch[1].trim();
| |
|
| |
| var categoryInfo =
| |
| this.parsePunishmentText(punishmentText);
| |
|
| |
| if (categoryInfo) {
| |
| // Находим соответствующую категорию по цвету или другим признакам
| |
| var categoryKey =
| |
| this.findCategoryByContext(section);
| |
| if (categoryKey) {
| |
| categories[categoryKey] = categoryInfo;
| |
| }
| |
| }
| |
| }
| |
| }.bind(this),
| |
| );
| |
|
| |
| // Применяем найденную информацию к законам
| |
| this.applyCategoryInfo(categories);
| |
| },
| |
|
| |
| parsePunishmentText: function (text) {
| |
| // Парсим текст наказания
| |
| var originalText = text;
| |
| text = text.toLowerCase();
| |
|
| |
| // Проверяем на критические нарушения
| |
| if (
| |
| text.includes("д-класс") ||
| |
| text.includes("казнь") ||
| |
| text.includes("перевод")
| |
| ) {
| |
| return {
| |
| isCritical: true,
| |
| punishment: originalText.trim(),
| |
| };
| |
| }
| |
|
| |
| // Для Д-класса ищем штрафы
| |
| if (this.pageType === "d-class") {
| |
| return this.parseDClassPunishment(originalText);
| |
| }
| |
|
| |
| // Для персонала ищем временные рамки
| |
| return this.parsePersonnelPunishment(originalText);
| |
| },
| |
|
| |
| parseDClassPunishment: function (text) {
| |
| // Парсим наказания для Д-класса (штрафы, дополнительные наказания)
| |
| var lowerText = text.toLowerCase();
| |
|
| |
| // Ищем штрафы в кредитах
| |
| var creditMatch = text.match(/(\d+[\s,.]?\d*)\s*кредит/);
| |
| if (creditMatch) {
| |
| var amount = creditMatch[1].replace(/[\s,]/g, "");
| |
| return {
| |
| isCritical: false,
| |
| punishment: "Штраф " + amount + " кредитов",
| |
| fine: parseInt(amount),
| |
| };
| |
| }
| |
|
| |
| // Ищем временные наказания для Д-класса
| |
| var timeMatch = text.match(/(\d+)\s*до\s*(\d+)\s*минут/);
| |
| if (timeMatch) {
| |
| return {
| |
| minTime: parseInt(timeMatch[1]),
| |
| maxTime: parseInt(timeMatch[2]),
| |
| isCritical: false,
| |
| punishment: timeMatch[1] + "-" + timeMatch[2] + " минут",
| |
| };
| |
| }
| |
|
| |
| var rangeMatch = text.match(/от\s*(\d+)\s*до\s*(\d+)\s*минут/);
| |
| if (rangeMatch) {
| |
| return {
| |
| minTime: parseInt(rangeMatch[1]),
| |
| maxTime: parseInt(rangeMatch[2]),
| |
| isCritical: false,
| |
| punishment: rangeMatch[1] + "-" + rangeMatch[2] + " минут",
| |
| };
| |
| }
| |
|
| |
| var fixedMatch = text.match(/(\d+)\s*минут/);
| |
| if (fixedMatch) {
| |
| var time = parseInt(fixedMatch[1]);
| |
| return {
| |
| minTime: time,
| |
| maxTime: time,
| |
| isCritical: false,
| |
| punishment: time + " минут",
| |
| };
| |
| }
| |
|
| |
| // Если не нашли конкретные наказания, возвращаем текст как есть
| |
| return {
| |
| isCritical: false,
| |
| punishment: text.trim(),
| |
| };
| |
| },
| |
|
| |
| parsePersonnelPunishment: function (text) {
| |
| // Парсим наказания для персонала
| |
| var lowerText = text.toLowerCase();
| |
|
| |
| // Ищем временные рамки
| |
| var timeMatch = text.match(/(\d+)\s*до\s*(\d+)\s*минут/);
| |
| if (timeMatch) {
| |
| return {
| |
| minTime: parseInt(timeMatch[1]),
| |
| maxTime: parseInt(timeMatch[2]),
| |
| isCritical: false,
| |
| };
| |
| }
| |
|
| |
| // Ищем диапазон времени
| |
| var rangeMatch = text.match(/от\s*(\d+)\s*до\s*(\d+)\s*минут/);
| |
| if (rangeMatch) {
| |
| return {
| |
| minTime: parseInt(rangeMatch[1]),
| |
| maxTime: parseInt(rangeMatch[2]),
| |
| isCritical: false,
| |
| };
| |
| }
| |
|
| |
| // Ищем фиксированное время
| |
| var fixedMatch = text.match(/(\d+)\s*минут/);
| |
| if (fixedMatch) {
| |
| var time = parseInt(fixedMatch[1]);
| |
| return {
| |
| minTime: time,
| |
| maxTime: time,
| |
| isCritical: false,
| |
| };
| |
| }
| |
|
| |
| return null;
| |
| },
| |
|
| |
| findCategoryByContext: function (section) {
| |
| // Находим категорию по контексту (заголовок секции)
| |
| var collapsible = section.closest(".mw-collapsible");
| |
| if (!collapsible) return null;
| |
|
| |
| var toggle = collapsible.querySelector(".mw-collapsible-toggle");
| |
| if (!toggle) return null;
| |
|
| |
| var titleElement = toggle.querySelector(".рамка-название");
| |
| if (!titleElement) return null;
| |
|
| |
| var title = titleElement.textContent.trim();
| |
|
| |
| // Определяем диапазон статей по заголовку
| |
| if (title.includes("Лёгкие")) return "100-109";
| |
| if (title.includes("Средние")) return "200-211";
| |
| if (title.includes("Тяжкие") && !title.includes("Особо"))
| |
| return "300-311";
| |
| if (title.includes("Особо тяжкие")) return "400-411";
| |
| if (title.includes("Критические")) return "500-511";
| |
|
| |
| return null;
| |
| },
| |
|
| |
| applyCategoryInfo: function (categories) {
| |
| // Применяем информацию о категориях к законам
| |
| Object.keys(this.lawData).forEach(
| |
| function (lawCode) {
| |
| var code = parseInt(lawCode);
| |
| var categoryKey = null;
| |
|
| |
| // Определяем категорию по номеру статьи
| |
| if (code >= 100 && code <= 109) categoryKey = "100-109";
| |
| else if (code >= 200 && code <= 211)
| |
| categoryKey = "200-211";
| |
| else if (code >= 300 && code <= 311)
| |
| categoryKey = "300-311";
| |
| else if (code >= 400 && code <= 411)
| |
| categoryKey = "400-411";
| |
| else if (code >= 500 && code <= 511)
| |
| categoryKey = "500-511";
| |
|
| |
| if (categoryKey && categories[categoryKey]) {
| |
| this.lawData[lawCode].category =
| |
| categories[categoryKey];
| |
| }
| |
| }.bind(this),
| |
| );
| |
| },
| |
|
| |
| createModeToggle: function () {
| |
| var tables = document.querySelectorAll(
| |
| ".citizen-table-wrapper table",
| |
| );
| |
|
| |
| tables.forEach(
| |
| function (table) {
| |
| var wrapper = table.closest(".citizen-table-wrapper");
| |
| if (!wrapper || wrapper.querySelector(".law-mode-toggle"))
| |
| return;
| |
|
| |
| var toggleContainer = document.createElement("div");
| |
| toggleContainer.className = "law-mode-toggle";
| |
| toggleContainer.innerHTML =
| |
| '<div class="mode-toggle-buttons">' +
| |
| '<button class="mode-btn active" data-mode="navigation">Навигация</button>' +
| |
| '<button class="mode-btn" data-mode="calculator">Калькулятор</button>' +
| |
| "</div>";
| |
|
| |
| wrapper.appendChild(toggleContainer);
| |
|
| |
| // Добавляем стили
| |
| this.addToggleStyles();
| |
|
| |
| // Обработчики событий
| |
| var buttons = toggleContainer.querySelectorAll(".mode-btn");
| |
| buttons.forEach(function (btn) {
| |
| btn.addEventListener("click", function () {
| |
| buttons.forEach(function (b) {
| |
| b.classList.remove("active");
| |
| });
| |
| this.classList.add("active");
| |
|
| |
| var mode = this.dataset.mode;
| |
| LawCalculatorModule.setMode(mode, table);
| |
| });
| |
| });
| |
| }.bind(this),
| |
| );
| |
| },
| |
|
| |
| addToggleStyles: function () {
| |
| if (document.getElementById("law-calculator-styles")) return;
| |
|
| |
| var style = document.createElement("style");
| |
| style.id = "law-calculator-styles";
| |
| style.textContent =
| |
| ".law-mode-toggle {" +
| |
| "margin: 10px 0;" +
| |
| "text-align: center;" +
| |
| "}" +
| |
| ".mode-toggle-buttons {" +
| |
| "display: inline-flex;" +
| |
| "background: #f0f0f0;" +
| |
| "border-radius: 5px;" +
| |
| "padding: 2px;" +
| |
| "border: 1px solid #ccc;" +
| |
| "}" +
| |
| ".mode-btn {" +
| |
| "padding: 8px 16px;" +
| |
| "border: none;" +
| |
| "background: transparent;" +
| |
| "cursor: pointer;" +
| |
| "border-radius: 3px;" +
| |
| "font-size: 14px;" +
| |
| "transition: all 0.2s;" +
| |
| "}" +
| |
| ".mode-btn.active {" +
| |
| "background: #007cba;" +
| |
| "color: white;" +
| |
| "}" +
| |
| ".mode-btn:hover:not(.active) {" +
| |
| "background: #e0e0e0;" +
| |
| "}" +
| |
| ".calculator-interface {" +
| |
| "margin-top: 15px;" +
| |
| "padding: 15px;" +
| |
| "background: #f9f9f9;" +
| |
| "border: 1px solid #ddd;" +
| |
| "border-radius: 5px;" +
| |
| "display: none;" +
| |
| "}" +
| |
| ".calculator-interface.active {" +
| |
| "display: block;" +
| |
| "}" +
| |
| ".selected-violations {" +
| |
| "margin: 10px 0;" +
| |
| "}" +
| |
| ".violation-item {" +
| |
| "display: inline-block;" +
| |
| "margin: 5px;" +
| |
| "padding: 5px 10px;" +
| |
| "background: #e3f2fd;" +
| |
| "border: 1px solid #2196f3;" +
| |
| "border-radius: 15px;" +
| |
| "font-size: 12px;" +
| |
| "}" +
| |
| ".violation-item .remove-btn {" +
| |
| "margin-left: 5px;" +
| |
| "cursor: pointer;" +
| |
| "color: #f44336;" +
| |
| "font-weight: bold;" +
| |
| "}" +
| |
| ".calculation-result {" +
| |
| "margin-top: 15px;" +
| |
| "padding: 10px;" +
| |
| "background: #fff3cd;" +
| |
| "border: 1px solid #ffeaa7;" +
| |
| "border-radius: 5px;" +
| |
| "font-weight: bold;" +
| |
| "white-space: pre-line;" +
| |
| "}" +
| |
| ".calculation-result.critical {" +
| |
| "background: #f8d7da;" +
| |
| "border-color: #f5c6cb;" +
| |
| "color: #721c24;" +
| |
| "}" +
| |
| ".law-cell-calculator {" +
| |
| "cursor: pointer;" +
| |
| "transition: background-color 0.2s;" +
| |
| "}" +
| |
| ".law-cell-calculator:hover {" +
| |
| "background-color: rgba(0, 124, 186, 0.1) !important;" +
| |
| "}" +
| |
| ".law-cell-calculator.selected {" +
| |
| "background-color: rgba(0, 124, 186, 0.3) !important;" +
| |
| "}" +
| |
| ".calculator-actions {" +
| |
| "margin-top: 10px;" +
| |
| "}" +
| |
| ".calculator-actions button {" +
| |
| "padding: 8px 16px;" +
| |
| "border: 1px solid #ccc;" +
| |
| "background: white;" +
| |
| "cursor: pointer;" +
| |
| "border-radius: 3px;" +
| |
| "font-size: 14px;" +
| |
| "}" +
| |
| ".calculator-actions button:hover {" +
| |
| "background: #f0f0f0;" +
| |
| "}" +
| |
| ".calculator-actions .calculate-btn {" +
| |
| "background: #007cba;" +
| |
| "color: white;" +
| |
| "border-color: #007cba;" +
| |
| "}" +
| |
| ".calculator-actions .calculate-btn:hover {" +
| |
| "background: #005a87;" +
| |
| "}";
| |
|
| |
| document.head.appendChild(style);
| |
| },
| |
|
| |
| setMode: function (mode, table) {
| |
| this.isCalculatorMode = mode === "calculator";
| |
|
| |
| var wrapper = table.closest(".citizen-table-wrapper");
| |
| var calculatorInterface = wrapper.querySelector(
| |
| ".calculator-interface",
| |
| );
| |
|
| |
| if (this.isCalculatorMode) {
| |
| if (!calculatorInterface) {
| |
| calculatorInterface = this.createCalculatorInterface();
| |
| wrapper.appendChild(calculatorInterface);
| |
| }
| |
| calculatorInterface.classList.add("active");
| |
| this.makeTableInteractive(table);
| |
| } else {
| |
| if (calculatorInterface) {
| |
| calculatorInterface.classList.remove("active");
| |
| }
| |
| this.makeTableNormal(table);
| |
| }
| |
| },
| |
|
| |
| createCalculatorInterface: function () {
| |
| var calculatorInterface = document.createElement("div");
| |
| calculatorInterface.className = "calculator-interface";
| |
| calculatorInterface.innerHTML =
| |
| "<h4>Калькулятор сроков заключения</h4>" +
| |
| "<p>Выберите нарушения, кликая по статьям в таблице выше:</p>" +
| |
| '<div class="selected-violations">' +
| |
| '<div class="violations-list"></div>' +
| |
| "</div>" +
| |
| '<div class="calculation-result" style="display: none;"></div>' +
| |
| '<div class="calculator-actions">' +
| |
| '<button class="clear-btn" style="margin-right: 10px;">Очистить</button>' +
| |
| '<button class="calculate-btn">Рассчитать</button>' +
| |
| "</div>";
| |
|
| |
| // Обработчики событий
| |
| calculatorInterface
| |
| .querySelector(".clear-btn")
| |
| .addEventListener("click", function () {
| |
| LawCalculatorModule.clearSelection();
| |
| });
| |
|
| |
| calculatorInterface
| |
| .querySelector(".calculate-btn")
| |
| .addEventListener("click", function () {
| |
| LawCalculatorModule.calculateSentence();
| |
| });
| |
|
| |
| return calculatorInterface;
| |
| },
| |
|
| |
| makeTableInteractive: function (table) {
| |
| var cells = table.querySelectorAll("td");
| |
| cells.forEach(
| |
| function (cell) {
| |
| var link = cell.querySelector('a[href*="#"]');
| |
| if (!link) return;
| |
|
| |
| var href = link.getAttribute("href");
| |
| var match = href.match(/#(\d{3})/);
| |
| if (!match) return;
| |
|
| |
| var lawCode = match[1];
| |
| if (!this.lawData[lawCode]) return;
| |
|
| |
| cell.classList.add("law-cell-calculator");
| |
| cell.dataset.lawCode = lawCode;
| |
|
| |
| // Сохраняем оригинальный обработчик клика
| |
| var originalClickHandler = cell.onclick;
| |
|
| |
| cell.addEventListener("click", function (e) {
| |
| e.preventDefault();
| |
| e.stopPropagation();
| |
| LawCalculatorModule.toggleViolation(lawCode, cell);
| |
| });
| |
| }.bind(this),
| |
| );
| |
| },
| |
|
| |
| makeTableNormal: function (table) {
| |
| var cells = table.querySelectorAll(".law-cell-calculator");
| |
| cells.forEach(function (cell) {
| |
| cell.classList.remove("law-cell-calculator", "selected");
| |
| // Восстанавливаем оригинальную функциональность навигации
| |
| var lawCode = cell.dataset.lawCode;
| |
| if (lawCode) {
| |
| cell.addEventListener("click", function (e) {
| |
| e.preventDefault();
| |
| window.location.hash = lawCode;
| |
| });
| |
| }
| |
| });
| |
| },
| |
|
| |
| toggleViolation: function (lawCode, cell) {
| |
| var index = this.selectedViolations.indexOf(lawCode);
| |
|
| |
| if (index > -1) {
| |
| // Убираем нарушение
| |
| this.selectedViolations.splice(index, 1);
| |
| cell.classList.remove("selected");
| |
| } else {
| |
| // Добавляем нарушение
| |
| this.selectedViolations.push(lawCode);
| |
| cell.classList.add("selected");
| |
| }
| |
|
| |
| this.updateViolationsList();
| |
| },
| |
|
| |
| updateViolationsList: function () {
| |
| var violationsList = document.querySelector(".violations-list");
| |
| if (!violationsList) return;
| |
|
| |
| violationsList.innerHTML = "";
| |
|
| |
| this.selectedViolations.forEach(
| |
| function (lawCode) {
| |
| var law = this.lawData[lawCode];
| |
| if (!law) return;
| |
|
| |
| var item = document.createElement("div");
| |
| item.className = "violation-item";
| |
| item.innerHTML =
| |
| lawCode +
| |
| " - " +
| |
| law.title +
| |
| '<span class="remove-btn" data-law-code="' +
| |
| lawCode +
| |
| '">×</span>';
| |
|
| |
| item.querySelector(".remove-btn").addEventListener(
| |
| "click",
| |
| function () {
| |
| LawCalculatorModule.removeViolation(lawCode);
| |
| },
| |
| );
| |
|
| |
| violationsList.appendChild(item);
| |
| }.bind(this),
| |
| );
| |
| },
| |
|
| |
| removeViolation: function (lawCode) {
| |
| var index = this.selectedViolations.indexOf(lawCode);
| |
| if (index > -1) {
| |
| this.selectedViolations.splice(index, 1);
| |
|
| |
| // Убираем выделение с ячейки
| |
| var cell = document.querySelector(
| |
| '[data-law-code="' + lawCode + '"]',
| |
| );
| |
| if (cell) {
| |
| cell.classList.remove("selected");
| |
| }
| |
|
| |
| this.updateViolationsList();
| |
| }
| |
| },
| |
|
| |
| clearSelection: function () {
| |
| this.selectedViolations = [];
| |
|
| |
| // Убираем выделение со всех ячеек
| |
| var selectedCells = document.querySelectorAll(
| |
| ".law-cell-calculator.selected",
| |
| );
| |
| selectedCells.forEach(function (cell) {
| |
| cell.classList.remove("selected");
| |
| });
| |
|
| |
| this.updateViolationsList();
| |
| this.hideResult();
| |
| },
| |
|
| |
| calculateSentence: function () {
| |
| if (this.selectedViolations.length === 0) {
| |
| alert("Выберите хотя бы одно нарушение");
| |
| return;
| |
| }
| |
|
| |
| var result = this.calculateSentenceLogic(this.selectedViolations);
| |
| this.showResult(result);
| |
| },
| |
|
| |
| calculateSentenceLogic: function (violations) {
| |
| // Группируем нарушения по категориям
| |
| var categories = {};
| |
|
| |
| violations.forEach(
| |
| function (lawCode) {
| |
| var law = this.lawData[lawCode];
| |
| if (!law || !law.category) return;
| |
|
| |
| // Используем номер статьи как ключ категории
| |
| var categoryKey = this.getCategoryKeyByLawCode(lawCode);
| |
| if (!categories[categoryKey]) {
| |
| categories[categoryKey] = [];
| |
| }
| |
| categories[categoryKey].push(law);
| |
| }.bind(this),
| |
| );
| |
|
| |
| var totalMinTime = 0;
| |
| var totalMaxTime = 0;
| |
| var totalFine = 0;
| |
| var hasCritical = false;
| |
| var resultText = "";
| |
|
| |
| // Обрабатываем каждую категорию
| |
| Object.keys(categories).forEach(
| |
| function (categoryKey) {
| |
| var laws = categories[categoryKey];
| |
| var category = laws[0].category;
| |
| var categoryName = this.getCategoryName(categoryKey);
| |
|
| |
| if (category.isCritical) {
| |
| hasCritical = true;
| |
| resultText +=
| |
| "• " +
| |
| categoryName +
| |
| ": " +
| |
| (category.punishment || "Перевод в Д-класс/казнь") +
| |
| "\n";
| |
| } else {
| |
| // Для одной категории берем самую тяжкую статью
| |
| var maxLaw = laws.reduce(function (max, current) {
| |
| var currentCode = parseInt(lawCode);
| |
| var maxCode = parseInt(max.title.match(/\d{3}/)[0]);
| |
| return currentCode > maxCode ? current : max;
| |
| });
| |
|
| |
| totalMinTime += category.minTime || 0;
| |
| totalMaxTime += category.maxTime || 0;
| |
| totalFine += category.fine || 0;
| |
|
| |
| var punishmentText = "";
| |
| if (category.punishment) {
| |
| punishmentText = category.punishment;
| |
| } else if (category.minTime !== undefined) {
| |
| punishmentText =
| |
| category.minTime === category.maxTime
| |
| ? category.minTime + " минут"
| |
| : category.minTime +
| |
| "-" +
| |
| category.maxTime +
| |
| " минут";
| |
| }
| |
|
| |
| resultText +=
| |
| "• " +
| |
| categoryName +
| |
| ": " +
| |
| punishmentText +
| |
| " (статья " +
| |
| lawCode +
| |
| ")\n";
| |
| }
| |
| }.bind(this),
| |
| );
| |
|
| |
| if (hasCritical) {
| |
| return {
| |
| isCritical: true,
| |
| text:
| |
| "КРИТИЧЕСКИЕ НАРУШЕНИЯ\n" +
| |
| resultText +
| |
| "\nПриговор: Перевод в Д-класс или казнь",
| |
| };
| |
| } else if (this.pageType === "d-class") {
| |
| // Для Д-класса показываем штрафы и время отдельно
| |
| var summaryText = "";
| |
| if (totalFine > 0) {
| |
| summaryText += "Общий штраф: " + totalFine + " кредитов\n";
| |
| }
| |
| if (totalMinTime > 0 || totalMaxTime > 0) {
| |
| summaryText +=
| |
| "Общее время: " +
| |
| this.formatTime(totalMinTime, totalMaxTime) +
| |
| "\n";
| |
| }
| |
|
| |
| return {
| |
| isCritical: false,
| |
| text:
| |
| "РАСЧЕТ НАКАЗАНИЙ Д-КЛАССА\n" +
| |
| resultText +
| |
| "\n" +
| |
| summaryText,
| |
| };
| |
| } else if (totalMinTime > 0 || totalMaxTime > 0) {
| |
| var timeText = this.formatTime(totalMinTime, totalMaxTime);
| |
| return {
| |
| isCritical: false,
| |
| text:
| |
| "РАСЧЕТ СРОКА ЗАКЛЮЧЕНИЯ\n" +
| |
| resultText +
| |
| "\nОбщий срок: " +
| |
| timeText,
| |
| };
| |
| } else {
| |
| return {
| |
| isCritical: false,
| |
| text:
| |
| "РАСЧЕТ НАКАЗАНИЙ\n" +
| |
| resultText +
| |
| "\nНе удалось определить наказания",
| |
| };
| |
| }
| |
| },
| |
|
| |
| getCategoryKeyByLawCode: function (lawCode) {
| |
| var code = parseInt(lawCode);
| |
| if (code >= 100 && code <= 109) return "100-109";
| |
| else if (code >= 200 && code <= 211) return "200-211";
| |
| else if (code >= 300 && code <= 311) return "300-311";
| |
| else if (code >= 400 && code <= 411) return "400-411";
| |
| else if (code >= 500 && code <= 511) return "500-511";
| |
| return "unknown";
| |
| },
| |
|
| |
| getCategoryName: function (categoryKey) {
| |
| var names = {
| |
| "100-109": "Лёгкие нарушения",
| |
| "200-211": "Средние нарушения",
| |
| "300-311": "Тяжкие нарушения",
| |
| "400-411": "Особо тяжкие нарушения",
| |
| "500-511": "Критические нарушения",
| |
| };
| |
| return names[categoryKey] || "Неизвестная категория";
| |
| },
| |
|
| |
| formatTime: function (minTime, maxTime) {
| |
| if (minTime === maxTime) {
| |
| return this.formatMinutes(minTime);
| |
| } else {
| |
| return (
| |
| this.formatMinutes(minTime) +
| |
| " - " +
| |
| this.formatMinutes(maxTime)
| |
| );
| |
| }
| |
| },
| |
|
| |
| formatMinutes: function (minutes) {
| |
| if (minutes < 60) {
| |
| return minutes + " минут";
| |
| } else {
| |
| var hours = Math.floor(minutes / 60);
| |
| var remainingMinutes = minutes % 60;
| |
| var result = hours + " час";
| |
| if (hours > 1 && hours < 5) result += "а";
| |
| if (hours >= 5) result += "ов";
| |
| if (remainingMinutes > 0) {
| |
| result += " " + remainingMinutes + " минут";
| |
| }
| |
| return result;
| |
| }
| |
| },
| |
|
| |
| showResult: function (result) {
| |
| var resultDiv = document.querySelector(".calculation-result");
| |
| if (!resultDiv) return;
| |
|
| |
| resultDiv.textContent = result.text;
| |
| resultDiv.className =
| |
| "calculation-result" + (result.isCritical ? " critical" : "");
| |
| resultDiv.style.display = "block";
| |
| },
| |
|
| |
| hideResult: function () {
| |
| var resultDiv = document.querySelector(".calculation-result");
| |
| if (resultDiv) {
| |
| resultDiv.style.display = "none";
| |
| }
| |
| },
| |
|
| |
| observeTables: function () {
| |
| // Наблюдаем за динамически добавляемыми таблицами
| |
| var observer = new MutationObserver(function (mutations) {
| |
| mutations.forEach(function (mutation) {
| |
| mutation.addedNodes.forEach(function (node) {
| |
| if (node.nodeType === 1) {
| |
| if (
| |
| node.classList &&
| |
| node.classList.contains("citizen-table-wrapper")
| |
| ) {
| |
| setTimeout(function () {
| |
| LawCalculatorModule.createModeToggle();
| |
| }, 100);
| |
| } else if (node.querySelectorAll) {
| |
| var tables = node.querySelectorAll(
| |
| ".citizen-table-wrapper",
| |
| );
| |
| if (tables.length > 0) {
| |
| setTimeout(function () {
| |
| LawCalculatorModule.createModeToggle();
| |
| }, 100);
| |
| }
| |
| }
| |
| }
| |
| });
| |
| });
| |
| });
| |
|
| |
| observer.observe(document.body, {
| |
| childList: true,
| |
| subtree: true,
| |
| });
| |
| }, | | }, |
| }; | | }; |
| Строка 2059: |
Строка 1235: |
|
| |
|
| window.SnowflakesModule = SnowflakesModule; | | window.SnowflakesModule = SnowflakesModule; |
|
| |
| var FireworksModule = {
| |
| fireworks: null,
| |
| container: null,
| |
| checkInterval: null,
| |
| isActive: false,
| |
| loaded: false,
| |
| loading: false,
| |
| callbacks: [],
| |
|
| |
| init: function () {
| |
| this.startTimeCheck();
| |
| },
| |
|
| |
| loadFireworks: function (callback) {
| |
| // Проверяем разные варианты экспорта библиотеки
| |
| if (
| |
| this.loaded &&
| |
| (window.Fireworks ||
| |
| (window.Fireworks && window.Fireworks.default))
| |
| ) {
| |
| if (callback) callback();
| |
| return;
| |
| }
| |
|
| |
| if (callback) {
| |
| this.callbacks.push(callback);
| |
| }
| |
|
| |
| if (this.loading) return;
| |
| this.loading = true;
| |
|
| |
| var self = this;
| |
|
| |
| // Загружаем библиотеку fireworks.js через UMD версию
| |
| var script = document.createElement("script");
| |
| script.src = "https://unpkg.com/fireworks-js@2/dist/index.umd.js";
| |
| script.onload = function () {
| |
| self.loaded = true;
| |
| self.loading = false;
| |
|
| |
| // Вызываем все колбэки
| |
| self.callbacks.forEach(function (cb) {
| |
| cb();
| |
| });
| |
| self.callbacks = [];
| |
| };
| |
| script.onerror = function () {
| |
| console.error("Ошибка загрузки fireworks.js");
| |
| self.loading = false;
| |
| self.callbacks = [];
| |
| };
| |
|
| |
| document.head.appendChild(script);
| |
| },
| |
|
| |
| getMoscowTime: function () {
| |
| // МСК = UTC+3
| |
| var now = new Date();
| |
| var utc = now.getTime() + now.getTimezoneOffset() * 60000;
| |
| var moscowTime = new Date(utc + 3 * 3600000); // UTC+3
| |
| return moscowTime;
| |
| },
| |
|
| |
| isFireworksTime: function () {
| |
| var moscowTime = this.getMoscowTime();
| |
| var hours = moscowTime.getHours();
| |
| // С 00:00 до 01:00 по МСК
| |
| return hours === 0;
| |
| },
| |
|
| |
| startTimeCheck: function () {
| |
| var self = this;
| |
|
| |
| // Проверяем сразу при загрузке
| |
| this.checkTime();
| |
|
| |
| // Проверяем каждую минуту
| |
| this.checkInterval = setInterval(function () {
| |
| self.checkTime();
| |
| }, 60000);
| |
| },
| |
|
| |
| checkTime: function () {
| |
| if (this.isFireworksTime()) {
| |
| if (!this.isActive) {
| |
| this.startFireworks();
| |
| }
| |
| } else {
| |
| if (this.isActive) {
| |
| this.stopFireworks();
| |
| }
| |
| }
| |
| },
| |
|
| |
| getFireworksConstructor: function () {
| |
| // Пробуем разные варианты доступа к конструктору
| |
| if (window.Fireworks && typeof window.Fireworks === "function") {
| |
| return window.Fireworks;
| |
| }
| |
| if (
| |
| window.Fireworks &&
| |
| window.Fireworks.default &&
| |
| typeof window.Fireworks.default === "function"
| |
| ) {
| |
| return window.Fireworks.default;
| |
| }
| |
| if (window.fireworks && typeof window.fireworks === "function") {
| |
| return window.fireworks;
| |
| }
| |
| // Если библиотека экспортируется по-другому
| |
| if (
| |
| typeof Fireworks !== "undefined" &&
| |
| typeof Fireworks === "function"
| |
| ) {
| |
| return Fireworks;
| |
| }
| |
| return null;
| |
| },
| |
|
| |
| startFireworks: function () {
| |
| var self = this;
| |
|
| |
| this.loadFireworks(function () {
| |
| if (!self.container) {
| |
| self.container = document.createElement("div");
| |
| self.container.id = "fireworks-container";
| |
| self.container.style.cssText =
| |
| "position: fixed;" +
| |
| "top: 0;" +
| |
| "left: 0;" +
| |
| "width: 100%;" +
| |
| "height: 100%;" +
| |
| "pointer-events: none;" +
| |
| "z-index: 99998;";
| |
| document.body.appendChild(self.container);
| |
| }
| |
|
| |
| var FireworksConstructor = self.getFireworksConstructor();
| |
|
| |
| if (!FireworksConstructor) {
| |
| return;
| |
| }
| |
|
| |
| if (!self.fireworks) {
| |
| try {
| |
| self.fireworks = new FireworksConstructor(
| |
| self.container,
| |
| {
| |
| autoresize: true,
| |
| opacity: 0.5,
| |
| // Замедляем фейерверки
| |
| acceleration: 1.02, // Уменьшено с 1.05 для более медленного движения
| |
| friction: 0.98, // Увеличено с 0.97 для большего сопротивления
| |
| gravity: 1.2, // Уменьшено с 1.5 для более медленного падения
| |
| particles: 50,
| |
| traceLength: 5, // Увеличено с 3 для более длинного следа
| |
| traceSpeed: 5, // Уменьшено с 10 для более медленного следа
| |
| explosion: 5,
| |
| intensity: 30,
| |
| flickering: 50,
| |
| lineStyle: "round",
| |
| hue: { min: 0, max: 360 },
| |
| // Увеличиваем задержку между запусками для более медленного темпа
| |
| delay: { min: 30, max: 60 }, // Увеличено с 15-30 до 30-60
| |
| rocketsPoint: { min: 50, max: 50 },
| |
| lineWidth: {
| |
| explosion: { min: 1, max: 3 },
| |
| trace: { min: 1, max: 2 },
| |
| },
| |
| brightness: { min: 50, max: 80 },
| |
| decay: { min: 0.01, max: 0.02 }, // Уменьшено для более медленного затухания
| |
| mouse: { click: false, move: false, max: 1 },
| |
| // Включаем звук
| |
| sound: {
| |
| enable: true,
| |
| files: [
| |
| "https://fireworks.js.org/sounds/explosion0.mp3",
| |
| "https://fireworks.js.org/sounds/explosion1.mp3",
| |
| "https://fireworks.js.org/sounds/explosion2.mp3",
| |
| ],
| |
| volume: { min: 0.3, max: 0.6 }, // Громкость от 30% до 60%
| |
| },
| |
| },
| |
| );
| |
| } catch (e) {
| |
| console.error(
| |
| "Ошибка создания экземпляра Fireworks:",
| |
| e,
| |
| );
| |
| return;
| |
| }
| |
| }
| |
|
| |
| if (self.fireworks) {
| |
| self.fireworks.start();
| |
| self.isActive = true;
| |
| }
| |
| });
| |
| },
| |
|
| |
| stopFireworks: function () {
| |
| if (this.fireworks) {
| |
| this.fireworks.stop();
| |
| this.isActive = false;
| |
| }
| |
| },
| |
|
| |
| toggleFireworks: function () {
| |
| if (this.isActive) {
| |
| this.stopFireworks();
| |
| } else {
| |
| this.startFireworks();
| |
| }
| |
| },
| |
| };
| |
|
| |
| window.FireworksModule = FireworksModule;
| |
|
| |
|
| function initAllModules() { | | function initAllModules() { |
| Строка 2286: |
Строка 1244: |
| DocumentAutoFillModule.init(); | | DocumentAutoFillModule.init(); |
| // SnowflakesModule.init() | | // SnowflakesModule.init() |
| // FireworksModule.init();
| |
| // LawCalculatorModule.init();
| |
| } | | } |
|
| |
|