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