MediaWiki:Common.js
Страница интерфейса MediaWiki
Дополнительные действия
Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.
- Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
- Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
- Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
- Opera: Нажмите Ctrl+F5.
/**
* Если CDN недоступен: залей на вики popper.min.js, tippy-bundle.umd.min.js, tippy.css (через Загрузить файл),
* затем в консоли или в начале скрипта задай:
* MediaWikiTooltips.baseUrl = "https://fire.station.wiki.shizainc.com/wiki/Special:FilePath/";
* (подставь свой домен вики)
*/
(function () {
"use strict";
window.MediaWikiTooltips = {
loaded: false,
loading: false,
callbacks: [],
baseUrl: null,
load: function (callback) {
if (this.loaded && window.tippy) {
callback();
return;
}
this.callbacks.push(callback);
if (this.loading) return;
this.loading = true;
var self = this;
var cdns = [
{
base: "https://cdnjs.cloudflare.com/ajax/libs",
popper: "/popper.js/2.11.8/umd/popper.min.js",
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");
tippyScript.src = urlJoin(cdn.base, cdn.tippy);
tippyScript.onerror = function () {
if (cdnIndex + 1 < cdns.length) {
cdnIndex++;
loadPopper();
} else {
tryFallback("Tippy.js");
}
};
tippyScript.onload = function () {
if (!window.tippy) {
if (cdnIndex + 1 < cdns.length) {
cdnIndex++;
loadPopper();
} else {
tryFallback("Tippy.js (не в глобальной области)");
}
return;
}
var tippyCSS = document.createElement("link");
tippyCSS.rel = "stylesheet";
tippyCSS.href = urlJoin(cdn.base, cdn.css);
tippyCSS.onerror = function () {};
document.head.appendChild(tippyCSS);
runCallbacks();
};
document.head.appendChild(tippyScript);
}
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 SidebarModule = {
init: function () {
var buttons = document.querySelectorAll(".боковая-панель-кнопка");
var sections = document.querySelectorAll(".боковая-панель-раздел");
if (buttons.length === 0) return;
buttons.forEach(function (button) {
button.addEventListener("click", function () {
var targetId = this.dataset.target;
buttons.forEach(function (btn) {
btn.classList.remove("active");
});
this.classList.add("active");
sections.forEach(function (section) {
section.classList.remove("default");
});
var targetSection = document.getElementById(targetId);
if (targetSection) {
targetSection.classList.add("default");
}
});
});
buttons[0].click();
},
};
var AccessTooltipsModule = {
init: function () {
var containers = document.querySelectorAll(".допуск-контейнер");
if (containers.length === 0) return;
MediaWikiTooltips.load(function () {
containers.forEach(function (container) {
var contentElement =
container.querySelector(".допуск-подсказка");
if (!contentElement) return;
var content = contentElement.innerHTML;
tippy(container, {
content: content,
allowHTML: true,
interactive: true,
placement: "auto",
maxWidth: 500,
theme: "dark",
arrow: true,
duration: [200, 200],
popperOptions: {
modifiers: [
{
name: "preventOverflow",
options: { padding: 8 },
},
{
name: "flip",
options: {
fallbackPlacements: [
"top",
"bottom",
"right",
"left",
],
},
},
],
},
});
});
});
},
};
var LawTooltipsModule = {
init: function() {
var tableCells = document.querySelectorAll('.law-tooltips td, .law-tooltips th');
if (tableCells.length === 0) return;
MediaWikiTooltips.load(function() {
var lawData = LawTooltipsModule.extractLawData();
LawTooltipsModule.initCellTooltips(tableCells, lawData);
});
},
extractLawData: 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 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}$/)) {
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();
// Удаляем все <br> теги
html = html.replace(/<br\s*\/?>/gi, '');
if (html) examples.push(html);
});
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*="#"]');
if (!link) return;
var href = link.getAttribute('href');
var match = href.match(/#(\d{3})/);
if (!match) return;
var lawCode = match[1];
var tooltipContent = LawTooltipsModule.createTooltipContent(lawCode, lawData);
if (!tooltipContent) return;
cell.classList.add('law-cell-with-tooltip');
cell.addEventListener('click', function(e) {
e.preventDefault();
window.location.hash = lawCode;
});
tippy(cell, {
content: tooltipContent,
allowHTML: true,
interactive: true,
placement: 'top',
maxWidth: 400,
theme: 'law-theme',
arrow: true,
duration: [200, 150],
delay: [0, 50],
appendTo: document.body,
boundary: 'viewport',
popperOptions: {
strategy: 'fixed',
modifiers: [
{
name: 'preventOverflow',
options: {
boundary: 'viewport',
padding: 20,
altAxis: true,
altBoundary: true
}
},
{
name: 'flip',
options: {
fallbackPlacements: ['bottom', 'left', 'right']
}
},
{
name: 'computeStyles',
options: { adaptive: false }
}
]
},
onShow: function(instance) {
document.querySelectorAll('[data-tippy-root]').forEach(function(tooltip) {
if (tooltip._tippy && tooltip._tippy !== instance) {
tooltip._tippy.hide();
}
});
}
});
});
}
};
var DataTooltipsModule = {
init: function () {
var elements = document.querySelectorAll(
".data-tooltip[data-text]",
);
if (elements.length === 0) return;
MediaWikiTooltips.load(function () {
elements.forEach(function (element) {
var tooltipText = element.getAttribute("data-text");
if (!tooltipText || !tooltipText.trim()) return;
tippy(element, {
content: tooltipText,
allowHTML: true,
interactive: false,
placement: "top",
maxWidth: 300,
theme: "data-tooltip-theme",
arrow: true,
duration: [200, 150],
delay: [0, 50],
});
});
});
var observer = new MutationObserver(function (mutations) {
var shouldReinit = false;
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === 1) {
if (
node.classList &&
node.classList.contains("data-tooltip") &&
node.hasAttribute("data-text")
) {
shouldReinit = true;
} else if (node.querySelectorAll) {
var newElements = node.querySelectorAll(
".data-tooltip[data-text]",
);
if (newElements.length > 0) {
shouldReinit = true;
}
}
}
});
});
if (shouldReinit) {
setTimeout(function () {
DataTooltipsModule.init();
}, 100);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
},
};
var CopyTextModule = {
init: function () {
CopyTextModule.initDirectElements();
CopyTextModule.initAutoContainers();
},
initDirectElements: function () {
var elements = document.querySelectorAll(".copyable-text");
if (elements.length === 0) return;
elements.forEach(function (element) {
CopyTextModule.cleanPreContent(element);
CopyTextModule.setupElement(element);
});
},
initAutoContainers: function () {
var containers = document.querySelectorAll(
".copyable-pre-container",
);
if (containers.length === 0) return;
containers.forEach(function (container) {
CopyTextModule.processContainerPre(container);
});
// Наблюдатель за динамически появляющимися элементами
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === 1) {
// Проверяем сам элемент
if (node.tagName === "PRE") {
var container = node.closest(
".copyable-pre-container",
);
if (
container &&
!node.classList.contains("copyable-text")
) {
node.classList.add("copyable-text");
CopyTextModule.cleanPreContent(node);
CopyTextModule.setupElement(node);
}
}
// Проверяем дочерние элементы
else if (node.querySelectorAll) {
var containers = node.querySelectorAll(
".copyable-pre-container",
);
containers.forEach(function (container) {
CopyTextModule.processContainerPre(
container,
);
});
// Также проверяем pre внутри уже существующих контейнеров
var preElements = node.querySelectorAll(
".copyable-pre-container pre",
);
preElements.forEach(function (pre) {
if (
!pre.classList.contains("copyable-text")
) {
pre.classList.add("copyable-text");
CopyTextModule.cleanPreContent(pre);
CopyTextModule.setupElement(pre);
}
});
}
}
});
// Обрабатываем изменения атрибутов (например, style="display: none" -> "")
if (
mutation.type === "attributes" &&
mutation.attributeName === "style"
) {
var target = mutation.target;
if (target.style.display !== "none") {
var containers = target.querySelectorAll(
".copyable-pre-container",
);
containers.forEach(function (container) {
CopyTextModule.processContainerPre(container);
});
}
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ["style"],
});
},
processContainerPre: function (container) {
var preElements = container.querySelectorAll("pre");
preElements.forEach(function (pre) {
if (!pre.classList.contains("copyable-text")) {
pre.classList.add("copyable-text");
CopyTextModule.cleanPreContent(pre);
CopyTextModule.setupElement(pre);
}
});
},
cleanPreContent: function (preElement) {
var text = preElement.textContent || preElement.innerText;
var cleanedText = text
.split("\n")
.map(function (line) {
return line.replace(/^\s*\|\s*/, "");
})
.join("\n");
if (cleanedText !== text) {
preElement.textContent = cleanedText;
}
},
setupElement: function (element) {
element.style.cursor = "pointer";
element.addEventListener("click", CopyTextModule.handleClick);
var isIOS =
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
!window.MSStream;
if (isIOS) {
element.style.webkitUserSelect = "text";
element.style.userSelect = "text";
}
},
handleClick: function (e) {
e.preventDefault();
var element = this;
var textToCopy = element.textContent;
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard
.writeText(textToCopy)
.then(function () {
CopyTextModule.showNotification(element);
})
.catch(function (err) {
console.error("Ошибка при копировании: ", err);
CopyTextModule.fallbackCopy(textToCopy, element);
});
} else {
CopyTextModule.fallbackCopy(textToCopy, element);
}
element.classList.add("copying");
setTimeout(function () {
element.classList.remove("copying");
}, 200);
},
fallbackCopy: function (text, element) {
var textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.top = "-9999px";
textArea.style.left = "-9999px";
textArea.style.width = "2em";
textArea.style.height = "2em";
textArea.style.padding = "0";
textArea.style.border = "none";
textArea.style.outline = "none";
textArea.style.boxShadow = "none";
textArea.style.background = "transparent";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
var successful = document.execCommand("copy");
if (successful) {
CopyTextModule.showNotification(element);
} else {
console.error("Не удалось скопировать текст");
}
} catch (err) {
console.error("Ошибка при копировании: ", err);
}
document.body.removeChild(textArea);
},
showNotification: function (element) {
var existingNotifications =
document.querySelectorAll(".copy-notification");
existingNotifications.forEach(function (notification) {
notification.remove();
});
var notification = document.createElement("div");
notification.className = "copy-notification";
notification.textContent = "Текст скопирован!";
document.body.appendChild(notification);
var rect = element.getBoundingClientRect();
var isMobile = window.innerWidth <= 768;
if (isMobile) {
notification.style.position = "fixed";
notification.style.top = "20px";
notification.style.left = "50%";
notification.style.transform =
"translateX(-50%) translateY(-10px)";
notification.style.zIndex = "10000";
} else {
notification.style.position = "absolute";
notification.style.top = rect.top + window.scrollY - 40 + "px";
notification.style.left = rect.left + window.scrollX + "px";
notification.style.zIndex = "9999";
}
setTimeout(function () {
notification.classList.add("show");
}, 10);
setTimeout(function () {
notification.classList.remove("show");
setTimeout(function () {
if (notification.parentNode) {
notification.remove();
}
}, 300);
}, 2000);
},
};
var DocumentAutoFillModule = {
init: function () {
this.processExistingContainers();
this.observeNewContainers();
this.startTimeUpdateTimer();
},
processExistingContainers: function () {
var containers = document.querySelectorAll(
".copyable-pre-container.auto-fill-enabled",
);
containers.forEach(
function (container) {
this.setupAutoFillContainer(container);
}.bind(this),
);
},
setupAutoFillContainer: function (container) {
if (container.querySelector(".document-fields-container")) {
return;
}
var fieldsContainer = document.createElement("div");
fieldsContainer.className = "document-fields-container";
var gridContainer = document.createElement("div");
gridContainer.className = "fields-grid";
var siteNumberField = this.createField(
"site-number",
"Номер участка:",
"Введите номер участка...",
false,
);
var authorField = this.createField(
"author",
"Автор документа:",
"Введите ваше имя...",
true,
);
var positionField = this.createField(
"position",
"Должность:",
"Введите вашу должность...",
true,
);
var signatureField = this.createField(
"signature",
"Подпись:",
"Введите подпись...",
true,
);
gridContainer.appendChild(siteNumberField.container);
gridContainer.appendChild(authorField.container);
gridContainer.appendChild(positionField.container);
gridContainer.appendChild(signatureField.container);
fieldsContainer.appendChild(gridContainer);
if (container.firstChild) {
container.insertBefore(fieldsContainer, container.firstChild);
} else {
container.appendChild(fieldsContainer);
}
setTimeout(
function () {
this.applyAllFieldsToContainer(container);
}.bind(this),
100,
);
},
createField: function (fieldType, labelText, placeholder, shouldCache) {
var fieldContainer = document.createElement("div");
fieldContainer.className = "field-container";
var label = document.createElement("label");
label.textContent = labelText;
label.htmlFor = fieldType + "-input";
var input = document.createElement("input");
input.type = "text";
input.id = fieldType + "-input";
input.className = "field-input " + fieldType + "-input";
input.placeholder = placeholder;
if (shouldCache) {
var savedValue = localStorage.getItem(
"document" + this.capitalizeFirstLetter(fieldType),
);
if (savedValue) {
input.value = savedValue;
}
}
input.addEventListener(
"input",
function () {
var value = input.value.trim();
if (shouldCache) {
localStorage.setItem(
"document" + this.capitalizeFirstLetter(fieldType),
value,
);
}
}.bind(this),
);
fieldContainer.appendChild(label);
fieldContainer.appendChild(input);
return {
container: fieldContainer,
input: input,
type: fieldType,
shouldCache: shouldCache,
};
},
capitalizeFirstLetter: function (string) {
return string.charAt(0).toUpperCase() + string.slice(1);
},
applyAllFieldsToContainer: function (container) {
var fieldsContainer = container.querySelector(
".document-fields-container",
);
if (!fieldsContainer) return;
var siteNumber = fieldsContainer
.querySelector(".site-number-input")
.value.trim();
var author = fieldsContainer
.querySelector(".author-input")
.value.trim();
var position = fieldsContainer
.querySelector(".position-input")
.value.trim();
var signature = fieldsContainer
.querySelector(".signature-input")
.value.trim();
this.autoFillAllDocumentsInContainer(container, {
siteNumber: siteNumber,
author: author,
position: position,
signature: signature,
});
},
autoFillAllDocumentsInContainer: function (container, fields) {
var preElements = container.querySelectorAll("pre");
preElements.forEach(
function (preElement) {
this.autoFillDocument(preElement, fields);
}.bind(this),
);
},
autoFillDocument: function (preElement, fields) {
if (!preElement) return;
var originalContent =
preElement.textContent || preElement.innerText;
if (!this.isDocumentTemplate(originalContent)) {
return;
}
var updatedContent = this.fillDocumentContent(
originalContent,
fields,
);
if (updatedContent !== originalContent) {
preElement.textContent = updatedContent;
}
},
isDocumentTemplate: function (text) {
return (
text.includes("SCP") &&
text.includes("Обезопасить. Удержать. Сохранить.") &&
text.includes("Автор документа:") &&
text.includes("Дата и время:")
);
},
fillDocumentContent: function (content, fields) {
var currentDateTime = this.getCurrentDateTime();
content = content.replace(
/(\[bold\]Дата и время:\[\/bold\])([^\n]*)/g,
"$1 " + currentDateTime,
);
if (fields.siteNumber) {
content = content.replace(
/Участок-([^|\n\[\]]*)/g,
"Участок-" + fields.siteNumber + " ",
);
}
if (fields.author) {
content = content.replace(
/(\[bold\]Автор документа:\[\/bold\])([^\n]*)/g,
"$1 " + fields.author,
);
} else {
content = content.replace(
/(\[bold\]Автор документа:\[\/bold\])([^\n]*)/g,
"$1",
);
}
if (fields.position) {
content = content.replace(
/(\[bold\]Должность:\[\/bold\])([^\n]*)/g,
"$1 " + fields.position,
);
} else {
content = content.replace(
/(\[bold\]Должность:\[\/bold\])([^\n]*)/g,
"$1",
);
}
if (fields.signature) {
content = content.replace(
/(\[bold\]Подпись:\[\/bold\])[^\n]*/g,
"$1 " + fields.signature,
);
} else {
content = content.replace(
/(\[bold\]Подпись:\[\/bold\])([^\n]*)/g,
"$1_____",
);
}
return content;
},
getCurrentDateTime: function () {
var now = new Date();
var day = String(now.getDate()).padStart(2, "0");
var month = String(now.getMonth() + 1).padStart(2, "0");
var year = now.getFullYear();
var hours = String(now.getHours()).padStart(2, "0");
var minutes = String(now.getMinutes()).padStart(2, "0");
return (
hours + ":" + minutes + ", " + day + "." + month + "." + year
);
},
startTimeUpdateTimer: function () {
var self = this;
function updateTimeIfNeeded() {
var containers = document.querySelectorAll(
".copyable-pre-container.auto-fill-enabled",
);
if (containers.length > 0) {
containers.forEach(function (container) {
var fieldsContainer = container.querySelector(
".document-fields-container",
);
if (fieldsContainer) {
self.applyAllFieldsToContainer(container);
}
});
}
}
this.timeUpdateInterval = setInterval(updateTimeIfNeeded, 5000);
document.addEventListener("visibilitychange", function () {
if (!document.hidden) {
updateTimeIfNeeded();
}
});
window.addEventListener("focus", updateTimeIfNeeded);
},
stopTimeUpdateTimer: function () {
if (this.timeUpdateInterval) {
clearInterval(this.timeUpdateInterval);
this.timeUpdateInterval = null;
}
},
observeNewContainers: function () {
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === 1) {
if (
node.classList &&
node.classList.contains(
"copyable-pre-container",
) &&
node.classList.contains("auto-fill-enabled")
) {
setTimeout(function () {
DocumentAutoFillModule.setupAutoFillContainer(
node,
);
}, 100);
} else if (node.querySelectorAll) {
var containers = node.querySelectorAll(
".copyable-pre-container.auto-fill-enabled",
);
containers.forEach(function (container) {
setTimeout(function () {
DocumentAutoFillModule.setupAutoFillContainer(
container,
);
}, 100);
});
}
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
},
};
window.DocumentAutoFill = {
refreshAll: function () {
var containers = document.querySelectorAll(
".copyable-pre-container.auto-fill-enabled",
);
containers.forEach(function (container) {
DocumentAutoFillModule.applyAllFieldsToContainer(container);
});
},
clearAllNames: function () {
localStorage.removeItem("documentAuthor");
localStorage.removeItem("documentPosition");
localStorage.removeItem("documentSignature");
var inputs = document.querySelectorAll(
".author-input, .position-input, .signature-input",
);
inputs.forEach(function (input) {
input.value = "";
});
window.DocumentAutoFill.refreshAll();
},
startTimeUpdates: function () {
DocumentAutoFillModule.startTimeUpdateTimer();
},
stopTimeUpdates: function () {
DocumentAutoFillModule.stopTimeUpdateTimer();
},
};
var LawCalculatorModule = {
isCalculatorMode: false,
selectedViolations: [],
lawData: {},
init: function () {
this.extractLawData();
this.createModeToggle();
this.observeTables();
},
extractLawData: function () {
// Используем данные из существующего LawTooltipsModule
if (
window.LawTooltipsModule &&
window.LawTooltipsModule.extractLawData
) {
this.lawData = window.LawTooltipsModule.extractLawData();
} else {
// Fallback - извлекаем данные самостоятельно
this.lawData = this.extractLawDataFallback();
}
// Добавляем информацию о категориях и сроках
this.addCategoryInfo();
},
extractLawDataFallback: function () {
var lawData = {};
var detailTables = document.querySelectorAll(
".mw-collapsible-content table",
);
detailTables.forEach(function (table) {
var rows = table.querySelectorAll("tr");
rows.forEach(function (row) {
var cells = row.querySelectorAll("td");
if (cells.length < 3) return;
var crimeCell = cells[0];
var anchor = crimeCell.querySelector("[id]");
if (!anchor) return;
var lawCode = anchor.id;
var titleElement = crimeCell.querySelector("b");
if (titleElement && lawCode.match(/^\d{3}$/)) {
var titleText = titleElement.textContent.trim();
var lawTitle = titleText
.replace(/^\d{3}\s*/, "")
.replace(/^\d{3}/, "")
.trim();
var description = cells[1].innerHTML.trim();
var examples = [];
var exampleItems = cells[2].querySelectorAll("li");
exampleItems.forEach(function (item) {
var html = item.innerHTML.trim();
html = html.replace(/<br\s*\/?>/gi, "");
if (html) examples.push(html);
});
lawData[lawCode] = {
title: lawTitle,
description: description,
examples: examples,
};
}
});
});
return lawData;
},
addCategoryInfo: function () {
// Определяем тип страницы и извлекаем информацию о наказаниях
this.pageType = this.detectPageType();
this.extractPunishmentInfo();
},
detectPageType: function () {
// Проверяем заголовки страницы для определения типа
var pageTitle = document.title.toLowerCase();
var headings = document.querySelectorAll("h1, h2, h3");
for (var i = 0; i < headings.length; i++) {
var headingText = headings[i].textContent.toLowerCase();
if (
headingText.includes("д-класс") ||
headingText.includes("d-class")
) {
return "d-class";
}
if (
headingText.includes("персонал") ||
headingText.includes("сотрудник")
) {
return "personnel";
}
}
// Проверяем по URL или другим признакам
if (
window.location.href.toLowerCase().includes("d-class") ||
window.location.href.toLowerCase().includes("д-класс")
) {
return "d-class";
}
return "personnel"; // По умолчанию
},
extractPunishmentInfo: function () {
// Извлекаем информацию о наказаниях из текста на странице
var punishmentSections = document.querySelectorAll(
".mw-collapsible-content p",
);
var categories = {};
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,
});
},
};
var SnowflakesModule = {
snowflakes: [],
createInterval: null,
isActive: false,
snowflakeCount: 30,
random: function (min, max) {
return Math.random() * (max - min) + min;
},
init: function () {
if (this.isActive) return;
this.addStyles();
this.startContinuousCreation();
this.isActive = true;
},
addStyles: function () {
if (document.getElementById("snowflakes-styles")) return;
var style = document.createElement("style");
style.id = "snowflakes-styles";
style.textContent =
".snowflake {" +
"position: fixed;" +
"top: -10px;" +
"color: #fff;" +
"font-family: Arial, sans-serif;" +
"user-select: none;" +
"pointer-events: none;" +
"z-index: 1;" +
"animation-name: snowflake-fall;" +
"animation-timing-function: linear;" +
"animation-iteration-count: infinite;" +
"will-change: transform;" +
"}" +
"@keyframes snowflake-fall {" +
"0% {" +
"transform: translateY(0) translateX(0) rotate(0deg);" +
"}" +
"25% {" +
"transform: translateY(25vh) translateX(10px) rotate(90deg);" +
"}" +
"50% {" +
"transform: translateY(50vh) translateX(-10px) rotate(180deg);" +
"}" +
"75% {" +
"transform: translateY(75vh) translateX(10px) rotate(270deg);" +
"}" +
"100% {" +
"transform: translateY(calc(100vh + 100px)) translateX(0) rotate(360deg);" +
"}" +
"}" +
".snowflake.flare {" +
"text-shadow: 0 0 5px rgba(255, 255, 255, 0.4), 0 0 10px rgba(255, 255, 255, 0.2);" +
"}" +
".snowflake.blurred {" +
"filter: blur(2px);" +
"}";
document.head.appendChild(style);
},
createSnowflake: function () {
var snowflake = document.createElement("div");
snowflake.className = "snowflake";
var symbol = this.getSnowflakeSymbol();
snowflake.textContent = symbol;
var fontSize = this.random(0.5, 1.2);
snowflake.style.fontSize = fontSize + "em";
snowflake.style.left = this.random(0, 100) + "vw";
snowflake.style.opacity = this.random(0.25, 0.5);
var fallDuration = this.random(10, 30);
snowflake.style.animationDuration = fallDuration + "s";
snowflake.style.animationDelay = this.random(0, 2) + "s";
if (Math.random() > 0.95) {
snowflake.classList.add("flare");
}
if (Math.random() > 0.4) {
snowflake.classList.add("blurred");
}
document.body.appendChild(snowflake);
this.snowflakes.push(snowflake);
var self = this;
var checkRemove = setInterval(function () {
var rect = snowflake.getBoundingClientRect();
if (rect.top > window.innerHeight + 50) {
if (snowflake.parentNode) {
snowflake.remove();
}
var index = self.snowflakes.indexOf(snowflake);
if (index > -1) {
self.snowflakes.splice(index, 1);
}
clearInterval(checkRemove);
}
}, 100);
},
getSnowflakeSymbol: function () {
var symbols = ["❄", "❅", "❆"];
return symbols[Math.floor(Math.random() * symbols.length)];
},
startContinuousCreation: function () {
var self = this;
this.createInterval = setInterval(function () {
if (
self.isActive &&
self.snowflakes.length < self.snowflakeCount * 1.5
) {
self.createSnowflake();
}
}, 500);
},
stop: function () {
this.isActive = false;
if (this.createInterval) {
clearInterval(this.createInterval);
this.createInterval = null;
}
this.snowflakes.forEach(function (snowflake) {
if (snowflake && snowflake.parentNode) {
snowflake.remove();
}
});
this.snowflakes = [];
var styles = document.getElementById("snowflakes-styles");
if (styles) {
styles.remove();
}
},
toggle: function () {
if (this.isActive) {
this.stop();
} else {
this.init();
}
},
};
window.SnowflakesModule = SnowflakesModule;
var FireworksModule = {
fireworks: null,
container: null,
checkInterval: null,
isActive: false,
loaded: false,
loading: false,
callbacks: [],
init: function () {
this.startTimeCheck();
},
loadFireworks: function (callback) {
// Проверяем разные варианты экспорта библиотеки
if (
this.loaded &&
(window.Fireworks ||
(window.Fireworks && window.Fireworks.default))
) {
if (callback) callback();
return;
}
if (callback) {
this.callbacks.push(callback);
}
if (this.loading) return;
this.loading = true;
var self = this;
// Загружаем библиотеку fireworks.js через UMD версию
var script = document.createElement("script");
script.src = "https://unpkg.com/fireworks-js@2/dist/index.umd.js";
script.onload = function () {
self.loaded = true;
self.loading = false;
// Вызываем все колбэки
self.callbacks.forEach(function (cb) {
cb();
});
self.callbacks = [];
};
script.onerror = function () {
console.error("Ошибка загрузки fireworks.js");
self.loading = false;
self.callbacks = [];
};
document.head.appendChild(script);
},
getMoscowTime: function () {
// МСК = UTC+3
var now = new Date();
var utc = now.getTime() + now.getTimezoneOffset() * 60000;
var moscowTime = new Date(utc + 3 * 3600000); // UTC+3
return moscowTime;
},
isFireworksTime: function () {
var moscowTime = this.getMoscowTime();
var hours = moscowTime.getHours();
// С 00:00 до 01:00 по МСК
return hours === 0;
},
startTimeCheck: function () {
var self = this;
// Проверяем сразу при загрузке
this.checkTime();
// Проверяем каждую минуту
this.checkInterval = setInterval(function () {
self.checkTime();
}, 60000);
},
checkTime: function () {
if (this.isFireworksTime()) {
if (!this.isActive) {
this.startFireworks();
}
} else {
if (this.isActive) {
this.stopFireworks();
}
}
},
getFireworksConstructor: function () {
// Пробуем разные варианты доступа к конструктору
if (window.Fireworks && typeof window.Fireworks === "function") {
return window.Fireworks;
}
if (
window.Fireworks &&
window.Fireworks.default &&
typeof window.Fireworks.default === "function"
) {
return window.Fireworks.default;
}
if (window.fireworks && typeof window.fireworks === "function") {
return window.fireworks;
}
// Если библиотека экспортируется по-другому
if (
typeof Fireworks !== "undefined" &&
typeof Fireworks === "function"
) {
return Fireworks;
}
return null;
},
startFireworks: function () {
var self = this;
this.loadFireworks(function () {
if (!self.container) {
self.container = document.createElement("div");
self.container.id = "fireworks-container";
self.container.style.cssText =
"position: fixed;" +
"top: 0;" +
"left: 0;" +
"width: 100%;" +
"height: 100%;" +
"pointer-events: none;" +
"z-index: 99998;";
document.body.appendChild(self.container);
}
var FireworksConstructor = self.getFireworksConstructor();
if (!FireworksConstructor) {
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() {
SidebarModule.init();
AccessTooltipsModule.init();
LawTooltipsModule.init();
DataTooltipsModule.init();
CopyTextModule.init();
DocumentAutoFillModule.init();
// SnowflakesModule.init()
// FireworksModule.init();
// LawCalculatorModule.init();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initAllModules);
} else {
initAllModules();
}
})();