Mediawiki:common.js
| Строка 1: | Строка 1: | ||
(function () { | (function() { | ||
'use strict'; | |||
window.MediaWikiTooltips = { | window.MediaWikiTooltips = { | ||
loaded: false, | loaded: false, | ||
callbacks: [], | callbacks: [], | ||
load: function (callback) { | load: function(callback) { | ||
if (this.loaded && window.tippy) { | if (this.loaded && window.tippy) { | ||
callback(); | callback(); | ||
return; | return; | ||
} | } | ||
this.callbacks.push(callback); | this.callbacks.push(callback); | ||
if (this.loading) return; | if (this.loading) return; | ||
this.loading = true; | this.loading = true; | ||
var self = this; | var self = this; | ||
var popperScript = document.createElement( | var popperScript = document.createElement('script'); | ||
popperScript.src = | popperScript.src = 'https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js'; | ||
popperScript.onload = function() { | |||
popperScript.onload = function () { | var tippyScript = document.createElement('script'); | ||
var tippyScript = document.createElement( | tippyScript.src = 'https://unpkg.com/tippy.js@6/dist/tippy-bundle.umd.min.js'; | ||
tippyScript.src = | tippyScript.onload = function() { | ||
tippyScript.onload = function () { | |||
self.loaded = true; | self.loaded = true; | ||
self.loading = false; | self.loading = false; | ||
self.callbacks.forEach(function (cb) { | self.callbacks.forEach(function(cb) { cb(); }); | ||
self.callbacks = []; | self.callbacks = []; | ||
}; | }; | ||
| Строка 36: | Строка 32: | ||
}; | }; | ||
document.head.appendChild(popperScript); | document.head.appendChild(popperScript); | ||
var tippyCSS = document.createElement( | var tippyCSS = document.createElement('link'); | ||
tippyCSS.rel = | tippyCSS.rel = 'stylesheet'; | ||
tippyCSS.href = | tippyCSS.href = 'https://unpkg.com/tippy.js@6/dist/tippy.css'; | ||
document.head.appendChild(tippyCSS); | document.head.appendChild(tippyCSS); | ||
} | } | ||
}; | }; | ||
var SidebarModule = { | var SidebarModule = { | ||
init: function () { | init: function() { | ||
var buttons = document.querySelectorAll( | var buttons = document.querySelectorAll('.боковая-панель-кнопка'); | ||
var sections = document.querySelectorAll( | var sections = document.querySelectorAll('.боковая-панель-раздел'); | ||
if (buttons.length === 0) return; | if (buttons.length === 0) return; | ||
buttons.forEach(function (button) { | buttons.forEach(function(button) { | ||
button.addEventListener( | button.addEventListener('click', function() { | ||
var targetId = this.dataset.target; | var targetId = this.dataset.target; | ||
buttons.forEach(function (btn) { | buttons.forEach(function(btn) { btn.classList.remove('active'); }); | ||
this.classList.add('active'); | |||
this.classList.add( | sections.forEach(function(section) { section.classList.remove('default'); }); | ||
sections.forEach(function (section) { | |||
var targetSection = document.getElementById(targetId); | var targetSection = document.getElementById(targetId); | ||
if (targetSection) { | if (targetSection) { | ||
targetSection.classList.add( | targetSection.classList.add('default'); | ||
} | } | ||
}); | }); | ||
}); | }); | ||
buttons[0].click(); | buttons[0].click(); | ||
} | } | ||
}; | }; | ||
var AccessTooltipsModule = { | var AccessTooltipsModule = { | ||
init: function () { | init: function() { | ||
var containers = document.querySelectorAll( | var containers = document.querySelectorAll('.допуск-контейнер'); | ||
if (containers.length === 0) return; | if (containers.length === 0) return; | ||
MediaWikiTooltips.load(function () { | MediaWikiTooltips.load(function() { | ||
containers.forEach(function (container) { | containers.forEach(function(container) { | ||
var contentElement = | var contentElement = container.querySelector('.допуск-подсказка'); | ||
if (!contentElement) return; | if (!contentElement) return; | ||
var content = contentElement.innerHTML; | var content = contentElement.innerHTML; | ||
tippy(container, { | tippy(container, { | ||
| Строка 91: | Строка 81: | ||
allowHTML: true, | allowHTML: true, | ||
interactive: true, | interactive: true, | ||
placement: | placement: 'auto', | ||
maxWidth: 500, | maxWidth: 500, | ||
theme: | theme: 'dark', | ||
arrow: true, | arrow: true, | ||
duration: [200, 200], | duration: [200, 200], | ||
| Строка 99: | Строка 89: | ||
modifiers: [ | modifiers: [ | ||
{ | { | ||
name: | name: 'preventOverflow', | ||
options: { padding: 8 } | options: { padding: 8 } | ||
}, | }, | ||
{ | { | ||
name: | name: 'flip', | ||
options: { | options: { | ||
fallbackPlacements: [ | fallbackPlacements: ['top', 'bottom', 'right', 'left'] | ||
} | |||
} | |||
] | |||
} | |||
} | |||
} | |||
] | |||
} | |||
}); | }); | ||
}); | }); | ||
}); | }); | ||
} | } | ||
}; | }; | ||
var LawTooltipsModule = { | var LawTooltipsModule = { | ||
init: function () { | init: function() { | ||
var tableCells = document.querySelectorAll('.law-tooltips td, .law-tooltips th'); | |||
var tableCells = document.querySelectorAll( | if (tableCells.length === 0) return; | ||
MediaWikiTooltips.load(function() { | |||
if (tableCells.length === 0) | |||
function | |||
var lawData = LawTooltipsModule.extractLawData(); | var lawData = LawTooltipsModule.extractLawData(); | ||
LawTooltipsModule.initCellTooltips(tableCells, lawData); | |||
}); | }); | ||
}, | }, | ||
extractLawData: function () { | 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) { | |||
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; | ||
var href = link.getAttribute('href'); | |||
var href = link.getAttribute( | |||
var match = href.match(/#(\d{3})/); | var match = href.match(/#(\d{3})/); | ||
if (!match) return; | if (!match) return; | ||
var lawCode = match[1]; | var lawCode = match[1]; | ||
var tooltipContent = LawTooltipsModule.createTooltipContent( | var tooltipContent = LawTooltipsModule.createTooltipContent(lawCode, lawData); | ||
if (!tooltipContent) return; | if (!tooltipContent) return; | ||
cell.classList.add('law-cell-with-tooltip'); | |||
cell.classList.add( | |||
cell.addEventListener('click', function(e) { | |||
cell.addEventListener( | |||
e.preventDefault(); | e.preventDefault(); | ||
window.location.hash = lawCode; | window.location.hash = lawCode; | ||
}); | }); | ||
tippy(cell, { | tippy(cell, { | ||
content: tooltipContent, | content: tooltipContent, | ||
allowHTML: true, | allowHTML: true, | ||
interactive: true, | interactive: true, | ||
placement: | placement: 'top', | ||
maxWidth: 400, | maxWidth: 400, | ||
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: | boundary: 'viewport', | ||
popperOptions: { | popperOptions: { | ||
strategy: | strategy: 'fixed', | ||
modifiers: [ | modifiers: [ | ||
{ | { | ||
name: | name: 'preventOverflow', | ||
options: { | options: { | ||
boundary: | boundary: 'viewport', | ||
padding: 20, | padding: 20, | ||
altAxis: true, | altAxis: true, | ||
altBoundary: true | altBoundary: true | ||
} | } | ||
}, | }, | ||
{ | { | ||
name: | name: 'flip', | ||
options: { | options: { | ||
fallbackPlacements: [ | fallbackPlacements: ['bottom', 'left', 'right'] | ||
} | |||
} | |||
}, | }, | ||
{ | { | ||
name: | 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(); | |||
} | |||
}); | |||
} | |||
}); | }); | ||
}); | }); | ||
} | |||
} | |||
}; | }; | ||
var DataTooltipsModule = { | var DataTooltipsModule = { | ||
init: function () { | init: function() { | ||
var elements = document.querySelectorAll( | var elements = document.querySelectorAll('.data-tooltip[data-text]'); | ||
if (elements.length === 0) return; | if (elements.length === 0) return; | ||
MediaWikiTooltips.load(function () { | MediaWikiTooltips.load(function() { | ||
elements.forEach(function (element) { | elements.forEach(function(element) { | ||
var tooltipText = element.getAttribute( | var tooltipText = element.getAttribute('data-text'); | ||
if (!tooltipText || !tooltipText.trim()) return; | if (!tooltipText || !tooltipText.trim()) return; | ||
tippy(element, { | tippy(element, { | ||
content: tooltipText, | content: tooltipText, | ||
allowHTML: true, | allowHTML: true, | ||
interactive: false, | interactive: false, | ||
placement: | placement: 'top', | ||
maxWidth: 300, | maxWidth: 300, | ||
theme: | theme: 'data-tooltip-theme', | ||
arrow: true, | arrow: true, | ||
duration: [200, 150], | duration: [200, 150], | ||
delay: [0, 50] | delay: [0, 50] | ||
}); | }); | ||
}); | }); | ||
}); | }); | ||
var observer = new MutationObserver(function (mutations) { | var observer = new MutationObserver(function(mutations) { | ||
var shouldReinit = false; | var shouldReinit = false; | ||
mutations.forEach(function (mutation) { | mutations.forEach(function(mutation) { | ||
mutation.addedNodes.forEach(function (node) { | mutation.addedNodes.forEach(function(node) { | ||
if (node.nodeType === 1) { | if (node.nodeType === 1) { | ||
if ( | if (node.classList && node.classList.contains('data-tooltip') && node.hasAttribute('data-text')) { | ||
shouldReinit = true; | shouldReinit = true; | ||
} else if (node.querySelectorAll) { | } else if (node.querySelectorAll) { | ||
var newElements = node.querySelectorAll( | var newElements = node.querySelectorAll('.data-tooltip[data-text]'); | ||
if (newElements.length > 0) { | if (newElements.length > 0) { | ||
shouldReinit = true; | shouldReinit = true; | ||
| Строка 450: | Строка 293: | ||
}); | }); | ||
}); | }); | ||
if (shouldReinit) { | if (shouldReinit) { | ||
setTimeout(function () { | setTimeout(function() { DataTooltipsModule.init(); }, 100); | ||
} | } | ||
}); | }); | ||
observer.observe(document.body, { | observer.observe(document.body, { | ||
childList: true, | childList: true, | ||
subtree: true | subtree: true | ||
}); | }); | ||
} | } | ||
}; | }; | ||
var CopyTextModule = { | |||
init: function() { | |||
CopyTextModule.initDirectElements(); | |||
CopyTextModule.initAutoContainers(); | |||
}, | |||
initDirectElements: function() { | |||
var elements = document.querySelectorAll('.copyable-text'); | |||
if (elements.length === 0) return; | |||
elements.forEach(function(element) { | |||
CopyTextModule.cleanPreContent(element); | |||
CopyTextModule.setupElement(element); | |||
}); | |||
}, | |||
initAutoContainers: function() { | |||
var containers = document.querySelectorAll('.copyable-pre-container'); | |||
if (containers.length === 0) return; | |||
containers.forEach(function(container) { | |||
CopyTextModule.processContainerPre(container); | |||
}); | |||
// Наблюдатель за динамически появляющимися элементами | |||
var observer = new MutationObserver(function(mutations) { | |||
mutations.forEach(function(mutation) { | |||
mutation.addedNodes.forEach(function(node) { | |||
if (node.nodeType === 1) { | |||
// Проверяем сам элемент | |||
if (node.tagName === 'PRE') { | |||
var container = node.closest('.copyable-pre-container'); | |||
if (container && !node.classList.contains('copyable-text')) { | |||
node.classList.add('copyable-text'); | |||
CopyTextModule.cleanPreContent(node); | |||
CopyTextModule.setupElement(node); | |||
} | |||
} | |||
// Проверяем дочерние элементы | |||
else if (node.querySelectorAll) { | |||
var containers = node.querySelectorAll('.copyable-pre-container'); | |||
containers.forEach(function(container) { | |||
CopyTextModule.processContainerPre(container); | |||
}); | |||
// Также проверяем pre внутри уже существующих контейнеров | |||
var preElements = node.querySelectorAll('.copyable-pre-container pre'); | |||
preElements.forEach(function(pre) { | |||
if (!pre.classList.contains('copyable-text')) { | |||
pre.classList.add('copyable-text'); | |||
CopyTextModule.cleanPreContent(pre); | |||
CopyTextModule.setupElement(pre); | |||
} | |||
}); | |||
} | |||
} | |||
}); | |||
// Обрабатываем изменения атрибутов (например, style="display: none" -> "") | |||
if (mutation.type === 'attributes' && mutation.attributeName === 'style') { | |||
var target = mutation.target; | |||
if (target.style.display !== 'none') { | |||
var containers = target.querySelectorAll('.copyable-pre-container'); | |||
containers.forEach(function(container) { | |||
CopyTextModule.processContainerPre(container); | |||
}); | |||
} | |||
} | |||
}); | |||
}); | |||
observer.observe(document.body, { | |||
childList: true, | |||
subtree: true, | |||
attributes: true, | |||
attributeFilter: ['style'] | |||
}); | |||
}, | |||
processContainerPre: function(container) { | |||
var preElements = container.querySelectorAll('pre'); | |||
preElements.forEach(function(pre) { | |||
if (!pre.classList.contains('copyable-text')) { | |||
pre.classList.add('copyable-text'); | |||
CopyTextModule.cleanPreContent(pre); | |||
CopyTextModule.setupElement(pre); | |||
} | |||
}); | |||
}, | |||
cleanPreContent: function(preElement) { | |||
var text = preElement.textContent || preElement.innerText; | |||
var cleanedText = text.split('\n').map(function(line) { | |||
return line.replace(/^\s*\|\s*/, ''); | |||
}).join('\n'); | |||
if (cleanedText !== text) { | |||
preElement.textContent = cleanedText; | |||
} | |||
}, | |||
setupElement: function(element) { | |||
element.style.cursor = 'pointer'; | |||
element.addEventListener('click', CopyTextModule.handleClick); | |||
var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; | |||
if (isIOS) { | |||
element.style.webkitUserSelect = 'text'; | |||
element.style.userSelect = 'text'; | |||
} | |||
}, | |||
handleClick: function(e) { | |||
e.preventDefault(); | |||
var element = this; | |||
var textToCopy = element.textContent; | |||
if (navigator.clipboard && window.isSecureContext) { | |||
navigator.clipboard.writeText(textToCopy).then(function() { | |||
CopyTextModule.showNotification(element); | |||
}).catch(function(err) { | |||
console.error('Ошибка при копировании: ', err); | |||
CopyTextModule.fallbackCopy(textToCopy, element); | |||
}); | |||
} else { | |||
CopyTextModule.fallbackCopy(textToCopy, element); | |||
} | |||
element.classList.add('copying'); | |||
setTimeout(function() { | |||
element.classList.remove('copying'); | |||
}, 200); | |||
}, | |||
fallbackCopy: function(text, element) { | |||
var textArea = document.createElement('textarea'); | |||
textArea.value = text; | |||
textArea.style.position = 'fixed'; | |||
textArea.style.top = '-9999px'; | |||
textArea.style.left = '-9999px'; | |||
textArea.style.width = '2em'; | |||
textArea.style.height = '2em'; | |||
textArea.style.padding = '0'; | |||
textArea.style.border = 'none'; | |||
textArea.style.outline = 'none'; | |||
textArea.style.boxShadow = 'none'; | |||
textArea.style.background = 'transparent'; | |||
document.body.appendChild(textArea); | |||
textArea.focus(); | |||
textArea.select(); | |||
try { | |||
var successful = document.execCommand('copy'); | |||
if (successful) { | |||
CopyTextModule.showNotification(element); | |||
} else { | |||
console.error('Не удалось скопировать текст'); | |||
} | |||
} catch (err) { | |||
console.error('Ошибка при копировании: ', err); | |||
} | |||
document.body.removeChild(textArea); | |||
}, | |||
showNotification: function(element) { | |||
var existingNotifications = document.querySelectorAll('.copy-notification'); | |||
existingNotifications.forEach(function(notification) { | |||
notification.remove(); | |||
}); | |||
var notification = document.createElement('div'); | |||
notification.className = 'copy-notification'; | |||
notification.textContent = 'Текст скопирован!'; | |||
document.body.appendChild(notification); | |||
var rect = element.getBoundingClientRect(); | |||
var isMobile = window.innerWidth <= 768; | |||
if (isMobile) { | |||
notification.style.position = 'fixed'; | |||
notification.style.top = '20px'; | |||
notification.style.left = '50%'; | |||
notification.style.transform = 'translateX(-50%) translateY(-10px)'; | |||
notification.style.zIndex = '10000'; | |||
} else { | |||
notification.style.position = 'absolute'; | |||
notification.style.top = (rect.top + window.scrollY - 40) + 'px'; | |||
notification.style.left = (rect.left + window.scrollX) + 'px'; | |||
notification.style.zIndex = '9999'; | |||
} | |||
setTimeout(function() { | |||
notification.classList.add('show'); | |||
}, 10); | |||
setTimeout(function() { | |||
notification.classList.remove('show'); | |||
setTimeout(function() { | |||
if (notification.parentNode) { | |||
notification.remove(); | |||
} | |||
}, 300); | |||
}, 2000); | |||
} | |||
}; | |||
var DocumentAutoFillModule = { | |||
init: function() { | |||
this.processExistingContainers(); | |||
this.observeNewContainers(); | |||
this.startTimeUpdateTimer(); | |||
}, | |||
processExistingContainers: function() { | |||
var containers = document.querySelectorAll('.copyable-pre-container.auto-fill-enabled'); | |||
containers.forEach(function(container) { | |||
this.setupAutoFillContainer(container); | |||
}.bind(this)); | |||
}, | |||
setupAutoFillContainer: function(container) { | |||
if (container.querySelector('.document-fields-container')) { | |||
return; | |||
} | |||
var fieldsContainer = document.createElement('div'); | |||
fieldsContainer.className = 'document-fields-container'; | |||
var gridContainer = document.createElement('div'); | |||
gridContainer.className = 'fields-grid'; | |||
var siteNumberField = this.createField('site-number', 'Номер участка:', 'Введите номер участка...', false); | |||
var authorField = this.createField('author', 'Автор документа:', 'Введите ваше имя...', true); | |||
var positionField = this.createField('position', 'Должность:', 'Введите вашу должность...', true); | |||
var signatureField = this.createField('signature', 'Подпись:', 'Введите подпись...', true); | |||
gridContainer.appendChild(siteNumberField.container); | |||
gridContainer.appendChild(authorField.container); | |||
gridContainer.appendChild(positionField.container); | |||
gridContainer.appendChild(signatureField.container); | |||
fieldsContainer.appendChild(gridContainer); | |||
if (container.firstChild) { | |||
container.insertBefore(fieldsContainer, container.firstChild); | |||
} else { | |||
container.appendChild(fieldsContainer); | |||
} | |||
setTimeout(function() { | |||
this.applyAllFieldsToContainer(container); | |||
}.bind(this), 100); | |||
}, | |||
createField: function(fieldType, labelText, placeholder, shouldCache) { | |||
var fieldContainer = document.createElement('div'); | |||
fieldContainer.className = 'field-container'; | |||
var label = document.createElement('label'); | |||
label.textContent = labelText; | |||
label.htmlFor = fieldType + '-input'; | |||
var input = document.createElement('input'); | |||
input.type = 'text'; | |||
input.id = fieldType + '-input'; | |||
input.className = 'field-input ' + fieldType + '-input'; | |||
input.placeholder = placeholder; | |||
if (shouldCache) { | |||
var savedValue = localStorage.getItem('document' + this.capitalizeFirstLetter(fieldType)); | |||
if (savedValue) { | |||
input.value = savedValue; | |||
} | |||
} | |||
input.addEventListener('input', function() { | |||
var value = input.value.trim(); | |||
if (shouldCache) { | |||
localStorage.setItem('document' + this.capitalizeFirstLetter(fieldType), value); | |||
} | |||
}.bind(this)); | |||
fieldContainer.appendChild(label); | |||
fieldContainer.appendChild(input); | |||
return { | |||
container: fieldContainer, | |||
input: input, | |||
type: fieldType, | |||
shouldCache: shouldCache | |||
}; | |||
}, | |||
capitalizeFirstLetter: function(string) { | |||
return string.charAt(0).toUpperCase() + string.slice(1); | |||
}, | |||
applyAllFieldsToContainer: function(container) { | |||
var fieldsContainer = container.querySelector('.document-fields-container'); | |||
if (!fieldsContainer) return; | |||
var siteNumber = fieldsContainer.querySelector('.site-number-input').value.trim(); | |||
var author = fieldsContainer.querySelector('.author-input').value.trim(); | |||
var position = fieldsContainer.querySelector('.position-input').value.trim(); | |||
var signature = fieldsContainer.querySelector('.signature-input').value.trim(); | |||
this.autoFillAllDocumentsInContainer(container, { | |||
siteNumber: siteNumber, | |||
author: author, | |||
position: position, | |||
signature: signature | |||
}); | |||
}, | |||
autoFillAllDocumentsInContainer: function(container, fields) { | |||
var preElements = container.querySelectorAll('pre'); | |||
preElements.forEach(function(preElement) { | |||
this.autoFillDocument(preElement, fields); | |||
}.bind(this)); | |||
}, | |||
autoFillDocument: function(preElement, fields) { | |||
if (!preElement) return; | |||
var originalContent = preElement.textContent || preElement.innerText; | |||
if (!this.isDocumentTemplate(originalContent)) { | |||
return; | |||
} | |||
var updatedContent = this.fillDocumentContent(originalContent, fields); | |||
if (updatedContent !== originalContent) { | |||
preElement.textContent = updatedContent; | |||
} | |||
}, | |||
isDocumentTemplate: function(text) { | |||
return text.includes('SCP') && | |||
text.includes('Обезопасить. Удержать. Сохранить.') && | |||
text.includes('Автор документа:') && | |||
text.includes('Дата и время:'); | |||
}, | |||
fillDocumentContent: function(content, fields) { | |||
var currentDateTime = this.getCurrentDateTime(); | |||
content = content.replace( | |||
/(\[bold\]Дата и время:\[\/bold\])([^\n]*)/g, | |||
'$1 ' + currentDateTime | |||
); | |||
if (fields.siteNumber) { | |||
content = content.replace( | |||
/Участок-([^|\n\[\]]*)/g, | |||
'Участок-' + fields.siteNumber + ' ' | |||
); | |||
} | |||
if (fields.author) { | |||
content = content.replace( | |||
/(\[bold\]Автор документа:\[\/bold\])([^\n]*)/g, | |||
'$1 ' + fields.author | |||
); | |||
} else { | |||
content = content.replace( | |||
/(\[bold\]Автор документа:\[\/bold\])([^\n]*)/g, | |||
'$1' | |||
); | |||
} | |||
if (fields.position) { | |||
content = content.replace( | |||
/(\[bold\]Должность:\[\/bold\])([^\n]*)/g, | |||
'$1 ' + fields.position | |||
); | |||
} else { | |||
content = content.replace( | |||
/(\[bold\]Должность:\[\/bold\])([^\n]*)/g, | |||
'$1' | |||
); | |||
} | |||
if (fields.signature) { | |||
content = content.replace( | |||
/(\[bold\]Подпись:\[\/bold\])[^\n]*/g, | |||
'$1 ' + fields.signature | |||
); | |||
} else { | |||
content = content.replace( | |||
/(\[bold\]Подпись:\[\/bold\])([^\n]*)/g, | |||
'$1_____' | |||
); | |||
} | |||
return content; | |||
}, | |||
getCurrentDateTime: function() { | |||
var now = new Date(); | |||
var day = String(now.getDate()).padStart(2, '0'); | |||
var month = String(now.getMonth() + 1).padStart(2, '0'); | |||
var year = now.getFullYear(); | |||
var hours = String(now.getHours()).padStart(2, '0'); | |||
var minutes = String(now.getMinutes()).padStart(2, '0'); | |||
return hours + ':' + minutes + ', ' + day + '.' + month + '.' + year; | |||
}, | |||
startTimeUpdateTimer: function() { | |||
var self = this; | |||
function updateTimeIfNeeded() { | |||
var containers = document.querySelectorAll('.copyable-pre-container.auto-fill-enabled'); | |||
if (containers.length > 0) { | |||
containers.forEach(function(container) { | |||
var fieldsContainer = container.querySelector('.document-fields-container'); | |||
if (fieldsContainer) { | |||
self.applyAllFieldsToContainer(container); | |||
} | |||
}); | |||
} | |||
} | |||
this.timeUpdateInterval = setInterval(updateTimeIfNeeded, 5000); | |||
document.addEventListener('visibilitychange', function() { | |||
if (!document.hidden) { | |||
updateTimeIfNeeded(); | |||
} | |||
}); | |||
window.addEventListener('focus', updateTimeIfNeeded); | |||
}, | |||
stopTimeUpdateTimer: function() { | |||
if (this.timeUpdateInterval) { | |||
clearInterval(this.timeUpdateInterval); | |||
this.timeUpdateInterval = null; | |||
} | |||
}, | |||
observeNewContainers: function() { | |||
var observer = new MutationObserver(function(mutations) { | |||
mutations.forEach(function(mutation) { | |||
mutation.addedNodes.forEach(function(node) { | |||
if (node.nodeType === 1) { | |||
if (node.classList && | |||
node.classList.contains('copyable-pre-container') && | |||
node.classList.contains('auto-fill-enabled')) { | |||
setTimeout(function() { | |||
DocumentAutoFillModule.setupAutoFillContainer(node); | |||
}, 100); | |||
} | |||
else if (node.querySelectorAll) { | |||
var containers = node.querySelectorAll('.copyable-pre-container.auto-fill-enabled'); | |||
containers.forEach(function(container) { | |||
setTimeout(function() { | |||
DocumentAutoFillModule.setupAutoFillContainer(container); | |||
}, 100); | |||
}); | |||
} | |||
} | |||
}); | |||
}); | |||
}); | |||
observer.observe(document.body, { | |||
childList: true, | |||
subtree: true | |||
}); | |||
} | |||
}; | |||
window.DocumentAutoFill = { | |||
refreshAll: function() { | |||
var containers = document.querySelectorAll('.copyable-pre-container.auto-fill-enabled'); | |||
containers.forEach(function(container) { | |||
DocumentAutoFillModule.applyAllFieldsToContainer(container); | |||
}); | |||
}, | |||
clearAllNames: function() { | |||
localStorage.removeItem('documentAuthor'); | |||
localStorage.removeItem('documentPosition'); | |||
localStorage.removeItem('documentSignature'); | |||
var inputs = document.querySelectorAll('.author-input, .position-input, .signature-input'); | |||
inputs.forEach(function(input) { | |||
input.value = ''; | |||
}); | |||
window.DocumentAutoFill.refreshAll(); | |||
}, | |||
startTimeUpdates: function() { | |||
DocumentAutoFillModule.startTimeUpdateTimer(); | |||
}, | |||
stopTimeUpdates: function() { | |||
DocumentAutoFillModule.stopTimeUpdateTimer(); | |||
} | |||
}; | |||
var LawCalculatorModule = { | |||
isCalculatorMode: false, | |||
selectedViolations: [], | |||
lawData: {}, | |||
init: function() { | |||
this.extractLawData(); | |||
this.createModeToggle(); | |||
this.observeTables(); | |||
}, | |||
extractLawData: function() { | |||
// Используем данные из существующего LawTooltipsModule | |||
if (window.LawTooltipsModule && window.LawTooltipsModule.extractLawData) { | |||
this.lawData = window.LawTooltipsModule.extractLawData(); | |||
} else { | |||
// Fallback - извлекаем данные самостоятельно | |||
this.lawData = this.extractLawDataFallback(); | |||
} | |||
// Добавляем информацию о категориях и сроках | |||
this.addCategoryInfo(); | |||
}, | |||
extractLawDataFallback: function() { | |||
var lawData = {}; | |||
var detailTables = document.querySelectorAll('.mw-collapsible-content table'); | |||
detailTables.forEach(function(table) { | |||
var rows = table.querySelectorAll('tr'); | |||
rows.forEach(function(row) { | |||
var cells = row.querySelectorAll('td'); | |||
if (cells.length < 3) return; | |||
var crimeCell = cells[0]; | |||
var anchor = crimeCell.querySelector('[id]'); | |||
if (!anchor) return; | |||
var lawCode = anchor.id; | |||
var titleElement = crimeCell.querySelector('b'); | |||
if (titleElement && lawCode.match(/^\d{3}$/)) { | |||
var titleText = titleElement.textContent.trim(); | |||
var lawTitle = titleText.replace(/^\d{3}\s*/, '').replace(/^\d{3}/, '').trim(); | |||
var description = cells[1].innerHTML.trim(); | |||
var examples = []; | |||
var exampleItems = cells[2].querySelectorAll('li'); | |||
exampleItems.forEach(function(item) { | |||
var html = item.innerHTML.trim(); | |||
html = html.replace(/<br\s*\/?>/gi, ''); | |||
if (html) examples.push(html); | |||
}); | |||
lawData[lawCode] = { | |||
title: lawTitle, | |||
description: description, | |||
examples: examples | |||
}; | |||
} | |||
}); | |||
}); | |||
return lawData; | |||
}, | |||
addCategoryInfo: function() { | |||
// Определяем тип страницы и извлекаем информацию о наказаниях | |||
this.pageType = this.detectPageType(); | |||
this.extractPunishmentInfo(); | |||
}, | |||
detectPageType: function() { | |||
// Проверяем заголовки страницы для определения типа | |||
var pageTitle = document.title.toLowerCase(); | |||
var headings = document.querySelectorAll('h1, h2, h3'); | |||
for (var i = 0; i < headings.length; i++) { | |||
var headingText = headings[i].textContent.toLowerCase(); | |||
if (headingText.includes('д-класс') || headingText.includes('d-class')) { | |||
return 'd-class'; | |||
} | |||
if (headingText.includes('персонал') || headingText.includes('сотрудник')) { | |||
return 'personnel'; | |||
} | |||
} | |||
// Проверяем по URL или другим признакам | |||
if (window.location.href.toLowerCase().includes('d-class') || | |||
window.location.href.toLowerCase().includes('д-класс')) { | |||
return 'd-class'; | |||
} | |||
return 'personnel'; // По умолчанию | |||
}, | |||
extractPunishmentInfo: function() { | |||
// Извлекаем информацию о наказаниях из текста на странице | |||
var punishmentSections = document.querySelectorAll('.mw-collapsible-content p'); | |||
var categories = {}; | |||
console.log('LawCalculator: Найдено секций с наказаниями:', punishmentSections.length); | |||
console.log('LawCalculator: Тип страницы:', this.pageType); | |||
punishmentSections.forEach(function(section) { | |||
var text = section.textContent; | |||
var punishmentMatch = text.match(/Наказание[:\s]*(.+?)(?:\n|$)/i); | |||
if (punishmentMatch) { | |||
var punishmentText = punishmentMatch[1].trim(); | |||
console.log('LawCalculator: Найдено наказание:', punishmentText); | |||
var categoryInfo = this.parsePunishmentText(punishmentText); | |||
if (categoryInfo) { | |||
// Находим соответствующую категорию по цвету или другим признакам | |||
var categoryKey = this.findCategoryByContext(section); | |||
if (categoryKey) { | |||
categories[categoryKey] = categoryInfo; | |||
console.log('LawCalculator: Применено к категории:', categoryKey, categoryInfo); | |||
} | |||
} | |||
} | |||
}.bind(this)); | |||
console.log('LawCalculator: Итоговые категории:', categories); | |||
// Применяем найденную информацию к законам | |||
this.applyCategoryInfo(categories); | |||
}, | |||
parsePunishmentText: function(text) { | |||
// Парсим текст наказания | |||
var originalText = text; | |||
text = text.toLowerCase(); | |||
// Проверяем на критические нарушения | |||
if (text.includes('д-класс') || text.includes('казнь') || text.includes('перевод')) { | |||
return { | |||
isCritical: true, | |||
punishment: originalText.trim() | |||
}; | |||
} | |||
// Для Д-класса ищем штрафы | |||
if (this.pageType === 'd-class') { | |||
return this.parseDClassPunishment(originalText); | |||
} | |||
// Для персонала ищем временные рамки | |||
return this.parsePersonnelPunishment(originalText); | |||
}, | |||
parseDClassPunishment: function(text) { | |||
// Парсим наказания для Д-класса (штрафы, дополнительные наказания) | |||
var lowerText = text.toLowerCase(); | |||
// Ищем штрафы в кредитах | |||
var creditMatch = text.match(/(\d+[\s,.]?\d*)\s*кредит/); | |||
if (creditMatch) { | |||
var amount = creditMatch[1].replace(/[\s,]/g, ''); | |||
return { | |||
isCritical: false, | |||
punishment: 'Штраф ' + amount + ' кредитов', | |||
fine: parseInt(amount) | |||
}; | |||
} | |||
// Ищем временные наказания для Д-класса | |||
var timeMatch = text.match(/(\d+)\s*до\s*(\d+)\s*минут/); | |||
if (timeMatch) { | |||
return { | |||
minTime: parseInt(timeMatch[1]), | |||
maxTime: parseInt(timeMatch[2]), | |||
isCritical: false, | |||
punishment: timeMatch[1] + '-' + timeMatch[2] + ' минут' | |||
}; | |||
} | |||
var rangeMatch = text.match(/от\s*(\d+)\s*до\s*(\d+)\s*минут/); | |||
if (rangeMatch) { | |||
return { | |||
minTime: parseInt(rangeMatch[1]), | |||
maxTime: parseInt(rangeMatch[2]), | |||
isCritical: false, | |||
punishment: rangeMatch[1] + '-' + rangeMatch[2] + ' минут' | |||
}; | |||
} | |||
var fixedMatch = text.match(/(\d+)\s*минут/); | |||
if (fixedMatch) { | |||
var time = parseInt(fixedMatch[1]); | |||
return { | |||
minTime: time, | |||
maxTime: time, | |||
isCritical: false, | |||
punishment: time + ' минут' | |||
}; | |||
} | |||
// Если не нашли конкретные наказания, возвращаем текст как есть | |||
return { | |||
isCritical: false, | |||
punishment: text.trim() | |||
}; | |||
}, | |||
parsePersonnelPunishment: function(text) { | |||
// Парсим наказания для персонала | |||
var lowerText = text.toLowerCase(); | |||
// Ищем временные рамки | |||
var timeMatch = text.match(/(\d+)\s*до\s*(\d+)\s*минут/); | |||
if (timeMatch) { | |||
return { | |||
minTime: parseInt(timeMatch[1]), | |||
maxTime: parseInt(timeMatch[2]), | |||
isCritical: false | |||
}; | |||
} | |||
// Ищем диапазон времени | |||
var rangeMatch = text.match(/от\s*(\d+)\s*до\s*(\d+)\s*минут/); | |||
if (rangeMatch) { | |||
return { | |||
minTime: parseInt(rangeMatch[1]), | |||
maxTime: parseInt(rangeMatch[2]), | |||
isCritical: false | |||
}; | |||
} | |||
// Ищем фиксированное время | |||
var fixedMatch = text.match(/(\d+)\s*минут/); | |||
if (fixedMatch) { | |||
var time = parseInt(fixedMatch[1]); | |||
return { | |||
minTime: time, | |||
maxTime: time, | |||
isCritical: false | |||
}; | |||
} | |||
return null; | |||
}, | |||
findCategoryByContext: function(section) { | |||
// Находим категорию по контексту (заголовок секции) | |||
var collapsible = section.closest('.mw-collapsible'); | |||
if (!collapsible) return null; | |||
var toggle = collapsible.querySelector('.mw-collapsible-toggle'); | |||
if (!toggle) return null; | |||
var titleElement = toggle.querySelector('.рамка-название'); | |||
if (!titleElement) return null; | |||
var title = titleElement.textContent.trim(); | |||
// Определяем диапазон статей по заголовку | |||
if (title.includes('Лёгкие')) return '100-109'; | |||
if (title.includes('Средние')) return '200-211'; | |||
if (title.includes('Тяжкие') && !title.includes('Особо')) return '300-311'; | |||
if (title.includes('Особо тяжкие')) return '400-411'; | |||
if (title.includes('Критические')) return '500-511'; | |||
return null; | |||
}, | |||
applyCategoryInfo: function(categories) { | |||
// Применяем информацию о категориях к законам | |||
Object.keys(this.lawData).forEach(function(lawCode) { | |||
var code = parseInt(lawCode); | |||
var categoryKey = null; | |||
// Определяем категорию по номеру статьи | |||
if (code >= 100 && code <= 109) categoryKey = '100-109'; | |||
else if (code >= 200 && code <= 211) categoryKey = '200-211'; | |||
else if (code >= 300 && code <= 311) categoryKey = '300-311'; | |||
else if (code >= 400 && code <= 411) categoryKey = '400-411'; | |||
else if (code >= 500 && code <= 511) categoryKey = '500-511'; | |||
if (categoryKey && categories[categoryKey]) { | |||
this.lawData[lawCode].category = categories[categoryKey]; | |||
} | |||
}.bind(this)); | |||
}, | |||
createModeToggle: function() { | |||
var tables = document.querySelectorAll('.citizen-table-wrapper table'); | |||
tables.forEach(function(table) { | |||
var wrapper = table.closest('.citizen-table-wrapper'); | |||
if (!wrapper || wrapper.querySelector('.law-mode-toggle')) return; | |||
var toggleContainer = document.createElement('div'); | |||
toggleContainer.className = 'law-mode-toggle'; | |||
toggleContainer.innerHTML = | |||
'<div class="mode-toggle-buttons">' + | |||
'<button class="mode-btn active" data-mode="navigation">Навигация</button>' + | |||
'<button class="mode-btn" data-mode="calculator">Калькулятор</button>' + | |||
'</div>'; | |||
wrapper.appendChild(toggleContainer); | |||
// Добавляем стили | |||
this.addToggleStyles(); | |||
// Обработчики событий | |||
var buttons = toggleContainer.querySelectorAll('.mode-btn'); | |||
buttons.forEach(function(btn) { | |||
btn.addEventListener('click', function() { | |||
buttons.forEach(function(b) { b.classList.remove('active'); }); | |||
this.classList.add('active'); | |||
var mode = this.dataset.mode; | |||
LawCalculatorModule.setMode(mode, table); | |||
}); | |||
}); | |||
}.bind(this)); | |||
}, | |||
addToggleStyles: function() { | |||
if (document.getElementById('law-calculator-styles')) return; | |||
var style = document.createElement('style'); | |||
style.id = 'law-calculator-styles'; | |||
style.textContent = | |||
'.law-mode-toggle {' + | |||
'margin: 10px 0;' + | |||
'text-align: center;' + | |||
'}' + | |||
'.mode-toggle-buttons {' + | |||
'display: inline-flex;' + | |||
'background: #f0f0f0;' + | |||
'border-radius: 5px;' + | |||
'padding: 2px;' + | |||
'border: 1px solid #ccc;' + | |||
'}' + | |||
'.mode-btn {' + | |||
'padding: 8px 16px;' + | |||
'border: none;' + | |||
'background: transparent;' + | |||
'cursor: pointer;' + | |||
'border-radius: 3px;' + | |||
'font-size: 14px;' + | |||
'transition: all 0.2s;' + | |||
'}' + | |||
'.mode-btn.active {' + | |||
'background: #007cba;' + | |||
'color: white;' + | |||
'}' + | |||
'.mode-btn:hover:not(.active) {' + | |||
'background: #e0e0e0;' + | |||
'}' + | |||
'.calculator-interface {' + | |||
'margin-top: 15px;' + | |||
'padding: 15px;' + | |||
'background: #f9f9f9;' + | |||
'border: 1px solid #ddd;' + | |||
'border-radius: 5px;' + | |||
'display: none;' + | |||
'}' + | |||
'.calculator-interface.active {' + | |||
'display: block;' + | |||
'}' + | |||
'.selected-violations {' + | |||
'margin: 10px 0;' + | |||
'}' + | |||
'.violation-item {' + | |||
'display: inline-block;' + | |||
'margin: 5px;' + | |||
'padding: 5px 10px;' + | |||
'background: #e3f2fd;' + | |||
'border: 1px solid #2196f3;' + | |||
'border-radius: 15px;' + | |||
'font-size: 12px;' + | |||
'}' + | |||
'.violation-item .remove-btn {' + | |||
'margin-left: 5px;' + | |||
'cursor: pointer;' + | |||
'color: #f44336;' + | |||
'font-weight: bold;' + | |||
'}' + | |||
'.calculation-result {' + | |||
'margin-top: 15px;' + | |||
'padding: 10px;' + | |||
'background: #fff3cd;' + | |||
'border: 1px solid #ffeaa7;' + | |||
'border-radius: 5px;' + | |||
'font-weight: bold;' + | |||
'white-space: pre-line;' + | |||
'}' + | |||
'.calculation-result.critical {' + | |||
'background: #f8d7da;' + | |||
'border-color: #f5c6cb;' + | |||
'color: #721c24;' + | |||
'}' + | |||
'.law-cell-calculator {' + | |||
'cursor: pointer;' + | |||
'transition: background-color 0.2s;' + | |||
'}' + | |||
'.law-cell-calculator:hover {' + | |||
'background-color: rgba(0, 124, 186, 0.1) !important;' + | |||
'}' + | |||
'.law-cell-calculator.selected {' + | |||
'background-color: rgba(0, 124, 186, 0.3) !important;' + | |||
'}' + | |||
'.calculator-actions {' + | |||
'margin-top: 10px;' + | |||
'}' + | |||
'.calculator-actions button {' + | |||
'padding: 8px 16px;' + | |||
'border: 1px solid #ccc;' + | |||
'background: white;' + | |||
'cursor: pointer;' + | |||
'border-radius: 3px;' + | |||
'font-size: 14px;' + | |||
'}' + | |||
'.calculator-actions button:hover {' + | |||
'background: #f0f0f0;' + | |||
'}' + | |||
'.calculator-actions .calculate-btn {' + | |||
'background: #007cba;' + | |||
'color: white;' + | |||
'border-color: #007cba;' + | |||
'}' + | |||
'.calculator-actions .calculate-btn:hover {' + | |||
'background: #005a87;' + | |||
'}'; | |||
document.head.appendChild(style); | |||
}, | |||
setMode: function(mode, table) { | |||
this.isCalculatorMode = (mode === 'calculator'); | |||
var wrapper = table.closest('.citizen-table-wrapper'); | |||
var calculatorInterface = wrapper.querySelector('.calculator-interface'); | |||
if (this.isCalculatorMode) { | |||
if (!calculatorInterface) { | |||
calculatorInterface = this.createCalculatorInterface(); | |||
wrapper.appendChild(calculatorInterface); | |||
} | |||
calculatorInterface.classList.add('active'); | |||
this.makeTableInteractive(table); | |||
} else { | |||
if (calculatorInterface) { | |||
calculatorInterface.classList.remove('active'); | |||
} | |||
this.makeTableNormal(table); | |||
} | |||
}, | |||
createCalculatorInterface: function() { | |||
var calculatorInterface = document.createElement('div'); | |||
calculatorInterface.className = 'calculator-interface'; | |||
calculatorInterface.innerHTML = | |||
'<h4>Калькулятор сроков заключения</h4>' + | |||
'<p>Выберите нарушения, кликая по статьям в таблице выше:</p>' + | |||
'<div class="selected-violations">' + | |||
'<div class="violations-list"></div>' + | |||
'</div>' + | |||
'<div class="calculation-result" style="display: none;"></div>' + | |||
'<div class="calculator-actions">' + | |||
'<button class="clear-btn" style="margin-right: 10px;">Очистить</button>' + | |||
'<button class="calculate-btn">Рассчитать</button>' + | |||
'</div>'; | |||
// Обработчики событий | |||
calculatorInterface.querySelector('.clear-btn').addEventListener('click', function() { | |||
LawCalculatorModule.clearSelection(); | |||
}); | |||
calculatorInterface.querySelector('.calculate-btn').addEventListener('click', function() { | |||
LawCalculatorModule.calculateSentence(); | |||
}); | |||
return calculatorInterface; | |||
}, | |||
makeTableInteractive: function(table) { | |||
var cells = table.querySelectorAll('td'); | |||
cells.forEach(function(cell) { | |||
var link = cell.querySelector('a[href*="#"]'); | |||
if (!link) return; | |||
var href = link.getAttribute('href'); | |||
var match = href.match(/#(\d{3})/); | |||
if (!match) return; | |||
var lawCode = match[1]; | |||
if (!this.lawData[lawCode]) return; | |||
cell.classList.add('law-cell-calculator'); | |||
cell.dataset.lawCode = lawCode; | |||
// Сохраняем оригинальный обработчик клика | |||
var originalClickHandler = cell.onclick; | |||
cell.addEventListener('click', function(e) { | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
LawCalculatorModule.toggleViolation(lawCode, cell); | |||
}); | |||
}.bind(this)); | |||
}, | |||
makeTableNormal: function(table) { | |||
var cells = table.querySelectorAll('.law-cell-calculator'); | |||
cells.forEach(function(cell) { | |||
cell.classList.remove('law-cell-calculator', 'selected'); | |||
// Восстанавливаем оригинальную функциональность навигации | |||
var lawCode = cell.dataset.lawCode; | |||
if (lawCode) { | |||
cell.addEventListener('click', function(e) { | |||
e.preventDefault(); | |||
window.location.hash = lawCode; | |||
}); | |||
} | |||
}); | |||
}, | |||
toggleViolation: function(lawCode, cell) { | |||
var index = this.selectedViolations.indexOf(lawCode); | |||
if (index > -1) { | |||
// Убираем нарушение | |||
this.selectedViolations.splice(index, 1); | |||
cell.classList.remove('selected'); | |||
} else { | |||
// Добавляем нарушение | |||
this.selectedViolations.push(lawCode); | |||
cell.classList.add('selected'); | |||
} | |||
this.updateViolationsList(); | |||
}, | |||
updateViolationsList: function() { | |||
var violationsList = document.querySelector('.violations-list'); | |||
if (!violationsList) return; | |||
violationsList.innerHTML = ''; | |||
this.selectedViolations.forEach(function(lawCode) { | |||
var law = this.lawData[lawCode]; | |||
if (!law) return; | |||
var item = document.createElement('div'); | |||
item.className = 'violation-item'; | |||
item.innerHTML = | |||
lawCode + ' - ' + law.title + | |||
'<span class="remove-btn" data-law-code="' + lawCode + '">×</span>'; | |||
item.querySelector('.remove-btn').addEventListener('click', function() { | |||
LawCalculatorModule.removeViolation(lawCode); | |||
}); | |||
violationsList.appendChild(item); | |||
}.bind(this)); | |||
}, | |||
removeViolation: function(lawCode) { | |||
var index = this.selectedViolations.indexOf(lawCode); | |||
if (index > -1) { | |||
this.selectedViolations.splice(index, 1); | |||
// Убираем выделение с ячейки | |||
var cell = document.querySelector('[data-law-code="' + lawCode + '"]'); | |||
if (cell) { | |||
cell.classList.remove('selected'); | |||
} | } | ||
this.updateViolationsList(); | |||
} | |||
}, | |||
clearSelection: function() { | |||
this.selectedViolations = []; | |||
// Убираем выделение со всех ячеек | |||
var selectedCells = document.querySelectorAll('.law-cell-calculator.selected'); | |||
selectedCells.forEach(function(cell) { | |||
cell.classList.remove('selected'); | |||
}); | |||
this.updateViolationsList(); | |||
this.hideResult(); | |||
}, | |||
calculateSentence: function() { | |||
if (this.selectedViolations.length === 0) { | |||
alert('Выберите хотя бы одно нарушение'); | |||
return; | |||
} | |||
var result = this.calculateSentenceLogic(this.selectedViolations); | |||
this.showResult(result); | |||
}, | |||
calculateSentenceLogic: function(violations) { | |||
// Группируем нарушения по категориям | |||
var categories = {}; | |||
violations.forEach(function(lawCode) { | |||
var law = this.lawData[lawCode]; | |||
if (!law || !law.category) return; | |||
// Используем номер статьи как ключ категории | |||
var categoryKey = this.getCategoryKeyByLawCode(lawCode); | |||
if (!categories[categoryKey]) { | |||
categories[categoryKey] = []; | |||
} | |||
categories[categoryKey].push(law); | |||
}.bind(this)); | |||
var totalMinTime = 0; | |||
var totalMaxTime = 0; | |||
var totalFine = 0; | |||
var hasCritical = false; | |||
var resultText = ''; | |||
// Обрабатываем каждую категорию | |||
Object.keys(categories).forEach(function(categoryKey) { | |||
var laws = categories[categoryKey]; | |||
var category = laws[0].category; | |||
var categoryName = this.getCategoryName(categoryKey); | |||
if (category.isCritical) { | |||
hasCritical = true; | |||
resultText += '• ' + categoryName + ': ' + (category.punishment || 'Перевод в Д-класс/казнь') + '\n'; | |||
if ( | |||
} else { | } else { | ||
// Для одной категории берем самую тяжкую статью | |||
var maxLaw = laws.reduce(function(max, current) { | |||
var currentCode = parseInt(lawCode); | |||
var maxCode = parseInt(max.title.match(/\d{3}/)[0]); | |||
return currentCode > maxCode ? current : max; | |||
}); | |||
totalMinTime += category.minTime || 0; | |||
totalMaxTime += category.maxTime || 0; | |||
totalFine += category.fine || 0; | |||
var punishmentText = ''; | |||
if (category.punishment) { | |||
punishmentText = category.punishment; | |||
} else if (category.minTime !== undefined) { | |||
punishmentText = category.minTime === category.maxTime ? | |||
category.minTime + ' минут' : | |||
category.minTime + '-' + category.maxTime + ' минут'; | |||
} | |||
resultText += '• ' + categoryName + ': ' + punishmentText + ' (статья ' + lawCode + ')\n'; | |||
} | |||
}.bind(this)); | |||
if (hasCritical) { | |||
return { | |||
isCritical: true, | |||
text: 'КРИТИЧЕСКИЕ НАРУШЕНИЯ\n' + resultText + '\nПриговор: Перевод в Д-класс или казнь' | |||
}; | |||
} else if (this.pageType === 'd-class') { | |||
// Для Д-класса показываем штрафы и время отдельно | |||
var summaryText = ''; | |||
if (totalFine > 0) { | |||
summaryText += 'Общий штраф: ' + totalFine + ' кредитов\n'; | |||
} | } | ||
if (totalMinTime > 0 || totalMaxTime > 0) { | |||
summaryText += 'Общее время: ' + this.formatTime(totalMinTime, totalMaxTime) + '\n'; | |||
} | } | ||
return { | |||
isCritical: false, | |||
text: 'РАСЧЕТ НАКАЗАНИЙ Д-КЛАССА\n' + resultText + '\n' + summaryText | |||
}; | |||
} else if (totalMinTime > 0 || totalMaxTime > 0) { | |||
var timeText = this.formatTime(totalMinTime, totalMaxTime); | |||
return { | |||
isCritical: false, | |||
text: 'РАСЧЕТ СРОКА ЗАКЛЮЧЕНИЯ\n' + resultText + '\nОбщий срок: ' + timeText | |||
}; | |||
} else { | |||
return { | |||
isCritical: false, | |||
text: 'РАСЧЕТ НАКАЗАНИЙ\n' + resultText + '\nНе удалось определить наказания' | |||
}; | |||
} | |||
}, | |||
getCategoryKeyByLawCode: function(lawCode) { | |||
var code = parseInt(lawCode); | |||
if (code >= 100 && code <= 109) return '100-109'; | |||
else if (code >= 200 && code <= 211) return '200-211'; | |||
else if (code >= 300 && code <= 311) return '300-311'; | |||
else if (code >= 400 && code <= 411) return '400-411'; | |||
else if (code >= 500 && code <= 511) return '500-511'; | |||
return 'unknown'; | |||
}, | |||
getCategoryName: function(categoryKey) { | |||
var names = { | |||
'100-109': 'Лёгкие нарушения', | |||
'200-211': 'Средние нарушения', | |||
'300-311': 'Тяжкие нарушения', | |||
'400-411': 'Особо тяжкие нарушения', | |||
'500-511': 'Критические нарушения' | |||
}; | |||
return names[categoryKey] || 'Неизвестная категория'; | |||
}, | |||
formatTime: function(minTime, maxTime) { | |||
if (minTime === maxTime) { | |||
return this.formatMinutes(minTime); | |||
} else { | |||
return this.formatMinutes(minTime) + ' - ' + this.formatMinutes(maxTime); | |||
} | |||
}, | |||
formatMinutes: function(minutes) { | |||
if (minutes < 60) { | |||
return minutes + ' минут'; | |||
} else { | |||
var hours = Math.floor(minutes / 60); | |||
var remainingMinutes = minutes % 60; | |||
var result = hours + ' час'; | |||
if (hours > 1 && hours < 5) result += 'а'; | |||
if (hours >= 5) result += 'ов'; | |||
if (remainingMinutes > 0) { | |||
result += ' ' + remainingMinutes + ' минут'; | |||
} | |||
return result; | |||
} | |||
}, | |||
showResult: function(result) { | |||
var resultDiv = document.querySelector('.calculation-result'); | |||
if (!resultDiv) return; | |||
resultDiv.textContent = result.text; | |||
resultDiv.className = 'calculation-result' + (result.isCritical ? ' critical' : ''); | |||
resultDiv.style.display = 'block'; | |||
}, | |||
hideResult: function() { | |||
var resultDiv = document.querySelector('.calculation-result'); | |||
if (resultDiv) { | |||
resultDiv.style.display = 'none'; | |||
} | |||
}, | |||
observeTables: function() { | |||
// Наблюдаем за динамически добавляемыми таблицами | |||
var observer = new MutationObserver(function(mutations) { | |||
mutations.forEach(function(mutation) { | |||
mutation.addedNodes.forEach(function(node) { | |||
if (node.nodeType === 1) { | |||
if (node.classList && node.classList.contains('citizen-table-wrapper')) { | |||
setTimeout(function() { | |||
LawCalculatorModule.createModeToggle(); | |||
}, 100); | |||
} else if (node.querySelectorAll) { | |||
var tables = node.querySelectorAll('.citizen-table-wrapper'); | |||
if (tables.length > 0) { | |||
setTimeout(function() { | |||
LawCalculatorModule.createModeToggle(); | |||
}, 100); | |||
} | |||
} | |||
} | |||
}); | |||
}); | |||
}); | |||
observer.observe(document.body, { | |||
childList: true, | |||
subtree: true | |||
}); | |||
} | |||
}; | |||
var SnowflakesModule = { | |||
snowflakes: [], | |||
createInterval: null, | |||
isActive: false, | |||
snowflakeCount: 30, | |||
random: function (min, max) { | |||
return Math.random() * (max - min) + min; | |||
}, | |||
init: function () { | |||
if (this.isActive) return; | |||
this.addStyles(); | |||
this.startContinuousCreation(); | |||
this.isActive = true; | |||
}, | |||
addStyles: function () { | |||
if (document.getElementById("snowflakes-styles")) return; | |||
var style = document.createElement("style"); | |||
style.id = "snowflakes-styles"; | |||
style.textContent = | |||
".snowflake {" + | |||
"position: fixed;" + | |||
"top: -10px;" + | |||
"color: #fff;" + | |||
"font-family: Arial, sans-serif;" + | |||
"user-select: none;" + | |||
"pointer-events: none;" + | |||
"z-index: 1;" + | |||
"animation-name: snowflake-fall;" + | |||
"animation-timing-function: linear;" + | |||
"animation-iteration-count: infinite;" + | |||
"will-change: transform;" + | |||
"}" + | |||
"@keyframes snowflake-fall {" + | |||
"0% {" + | |||
"transform: translateY(0) translateX(0) rotate(0deg);" + | |||
"}" + | |||
"25% {" + | |||
"transform: translateY(25vh) translateX(10px) rotate(90deg);" + | |||
"}" + | |||
"50% {" + | |||
"transform: translateY(50vh) translateX(-10px) rotate(180deg);" + | |||
"}" + | |||
"75% {" + | |||
"transform: translateY(75vh) translateX(10px) rotate(270deg);" + | |||
"}" + | |||
"100% {" + | |||
"transform: translateY(calc(100vh + 100px)) translateX(0) rotate(360deg);" + | |||
"}" + | |||
"}" + | |||
".snowflake.flare {" + | |||
"text-shadow: 0 0 5px rgba(255, 255, 255, 0.4), 0 0 10px rgba(255, 255, 255, 0.2);" + | |||
"}" + | |||
".snowflake.blurred {" + | |||
"filter: blur(2px);" + | |||
"}"; | |||
document.head.appendChild(style); | |||
}, | |||
createSnowflake: function () { | |||
var snowflake = document.createElement("div"); | |||
snowflake.className = "snowflake"; | |||
var symbol = this.getSnowflakeSymbol(); | |||
snowflake.textContent = symbol; | |||
var fontSize = this.random(0.5, 1.2); | |||
snowflake.style.fontSize = fontSize + "em"; | |||
snowflake.style.left = this.random(0, 100) + "vw"; | |||
snowflake.style.opacity = this.random(0.25, 0.5); | |||
var fallDuration = this.random(10, 30); | |||
snowflake.style.animationDuration = fallDuration + "s"; | |||
snowflake.style.animationDelay = this.random(0, 2) + "s"; | |||
if (Math.random() > 0.95) { | |||
snowflake.classList.add("flare"); | |||
} | |||
if (Math.random() > 0.4) { | |||
snowflake.classList.add("blurred"); | |||
} | |||
document.body.appendChild(snowflake); | |||
this.snowflakes.push(snowflake); | |||
var self = this; | |||
var checkRemove = setInterval(function () { | |||
var rect = snowflake.getBoundingClientRect(); | |||
if (rect.top > window.innerHeight + 50) { | |||
if (snowflake.parentNode) { | |||
snowflake.remove(); | |||
} | |||
var index = self.snowflakes.indexOf(snowflake); | |||
if (index > -1) { | |||
self.snowflakes.splice(index, 1); | |||
} | |||
clearInterval(checkRemove); | |||
} | |||
}, 100); | |||
}, | |||
getSnowflakeSymbol: function () { | |||
var symbols = ["❄", "❅", "❆"]; | |||
return symbols[Math.floor(Math.random() * symbols.length)]; | |||
}, | |||
startContinuousCreation: function () { | |||
var self = this; | |||
this.createInterval = setInterval(function () { | |||
if ( | |||
self.isActive && | |||
self.snowflakes.length < self.snowflakeCount * 1.5 | |||
) { | |||
self.createSnowflake(); | |||
} | |||
}, 500); | |||
}, | |||
stop: function () { | |||
this.isActive = false; | |||
if (this.createInterval) { | |||
clearInterval(this.createInterval); | |||
this.createInterval = null; | |||
} | |||
this.snowflakes.forEach(function (snowflake) { | |||
if (snowflake && snowflake.parentNode) { | |||
snowflake.remove(); | |||
} | |||
}); | |||
this.snowflakes = []; | |||
var styles = document.getElementById("snowflakes-styles"); | |||
if (styles) { | |||
styles.remove(); | |||
} | |||
}, | |||
toggle: function () { | |||
if (this.isActive) { | |||
this.stop(); | |||
} else { | |||
this.init(); | |||
} | |||
}, | |||
}; | |||
window.SnowflakesModule = SnowflakesModule; | |||
var FireworksModule = { | |||
fireworks: null, | |||
container: null, | |||
checkInterval: null, | |||
isActive: false, | |||
loaded: false, | |||
loading: false, | |||
callbacks: [], | |||
init: function() { | |||
this.startTimeCheck(); | |||
}, | |||
loadFireworks: function(callback) { | |||
// Проверяем разные варианты экспорта библиотеки | |||
if (this.loaded && (window.Fireworks || (window.Fireworks && window.Fireworks.default))) { | |||
if (callback) callback(); | |||
return; | |||
} | |||
if (callback) { | |||
this.callbacks.push(callback); | |||
} | |||
if (this.loading) return; | |||
this.loading = true; | |||
var self = this; | |||
// Загружаем библиотеку fireworks.js через UMD версию | |||
var script = document.createElement('script'); | |||
script.src = 'https://unpkg.com/fireworks-js@2/dist/index.umd.js'; | |||
script.onload = function() { | |||
self.loaded = true; | |||
self.loading = false; | |||
// Вызываем все колбэки | |||
self.callbacks.forEach(function(cb) { cb(); }); | |||
self.callbacks = []; | |||
}; | |||
script.onerror = function() { | |||
console.error('Ошибка загрузки fireworks.js'); | |||
self.loading = false; | |||
self.callbacks = []; | |||
}; | |||
document.head.appendChild(script); | |||
}, | |||
getMoscowTime: function() { | |||
// МСК = UTC+3 | |||
var now = new Date(); | |||
var utc = now.getTime() + (now.getTimezoneOffset() * 60000); | |||
var moscowTime = new Date(utc + (3 * 3600000)); // UTC+3 | |||
return moscowTime; | |||
}, | |||
isFireworksTime: function() { | |||
var moscowTime = this.getMoscowTime(); | |||
var hours = moscowTime.getHours(); | |||
// С 00:00 до 01:00 по МСК | |||
return hours === 0; | |||
}, | |||
startTimeCheck: function() { | |||
var self = this; | |||
// Проверяем сразу при загрузке | |||
this.checkTime(); | |||
// Проверяем каждую минуту | |||
this.checkInterval = setInterval(function() { | |||
self.checkTime(); | |||
}, 60000); | |||
}, | |||
checkTime: function() { | |||
if (this.isFireworksTime()) { | |||
if (!this.isActive) { | |||
this.startFireworks(); | |||
} | |||
} else { | |||
if (this.isActive) { | |||
this.stopFireworks(); | |||
} | |||
} | |||
}, | |||
getFireworksConstructor: function() { | |||
// Пробуем разные варианты доступа к конструктору | |||
if (window.Fireworks && typeof window.Fireworks === 'function') { | |||
return window.Fireworks; | |||
} | |||
if (window.Fireworks && window.Fireworks.default && typeof window.Fireworks.default === 'function') { | |||
return window.Fireworks.default; | |||
} | |||
if (window.fireworks && typeof window.fireworks === 'function') { | |||
return window.fireworks; | |||
} | |||
// Если библиотека экспортируется по-другому | |||
if (typeof Fireworks !== 'undefined' && typeof Fireworks === 'function') { | |||
return Fireworks; | |||
} | |||
return null; | |||
}, | |||
startFireworks: function() { | |||
var self = this; | |||
this.loadFireworks(function() { | |||
if (!self.container) { | |||
self.container = document.createElement('div'); | |||
self.container.id = 'fireworks-container'; | |||
self.container.style.cssText = | |||
'position: fixed;' + | |||
'top: 0;' + | |||
'left: 0;' + | |||
'width: 100%;' + | |||
'height: 100%;' + | |||
'pointer-events: none;' + | |||
'z-index: 99998;'; | |||
document.body.appendChild(self.container); | |||
} | |||
var FireworksConstructor = self.getFireworksConstructor(); | |||
if (!FireworksConstructor) { | |||
console.error('Не удалось найти конструктор Fireworks. Проверьте загрузку библиотеки.'); | |||
console.log('Доступные объекты:', { | |||
'window.Fireworks': window.Fireworks, | |||
'window.Fireworks.default': window.Fireworks && window.Fireworks.default, | |||
'window.fireworks': window.fireworks | |||
}); | |||
return; | |||
} | |||
if (!self.fireworks) { | |||
try { | |||
self.fireworks = new FireworksConstructor(self.container, { | |||
autoresize: true, | |||
opacity: 0.5, | |||
// Замедляем фейерверки | |||
acceleration: 1.02, // Уменьшено с 1.05 для более медленного движения | |||
friction: 0.98, // Увеличено с 0.97 для большего сопротивления | |||
gravity: 1.2, // Уменьшено с 1.5 для более медленного падения | |||
particles: 50, | |||
traceLength: 5, // Увеличено с 3 для более длинного следа | |||
traceSpeed: 5, // Уменьшено с 10 для более медленного следа | |||
explosion: 5, | |||
intensity: 30, | |||
flickering: 50, | |||
lineStyle: 'round', | |||
hue: { min: 0, max: 360 }, | |||
// Увеличиваем задержку между запусками для более медленного темпа | |||
delay: { min: 30, max: 60 }, // Увеличено с 15-30 до 30-60 | |||
rocketsPoint: { min: 50, max: 50 }, | |||
lineWidth: { | |||
explosion: { min: 1, max: 3 }, | |||
trace: { min: 1, max: 2 } | |||
}, | |||
brightness: { min: 50, max: 80 }, | |||
decay: { min: 0.01, max: 0.02 }, // Уменьшено для более медленного затухания | |||
mouse: { click: false, move: false, max: 1 }, | |||
// Включаем звук | |||
sound: { | |||
enable: true, | |||
files: [ | |||
'https://fireworks.js.org/sounds/explosion0.mp3', | |||
'https://fireworks.js.org/sounds/explosion1.mp3', | |||
'https://fireworks.js.org/sounds/explosion2.mp3' | |||
], | |||
volume: { min: 0.3, max: 0.6 } // Громкость от 30% до 60% | |||
} | |||
}); | |||
} catch (e) { | |||
console.error('Ошибка создания экземпляра Fireworks:', e); | |||
return; | |||
} | |||
} | |||
if (self.fireworks) { | |||
self.fireworks.start(); | |||
self.isActive = true; | |||
} | |||
}); | |||
}, | |||
stopFireworks: function() { | |||
if (this.fireworks) { | |||
this.fireworks.stop(); | |||
this.isActive = false; | |||
} | |||
}, | |||
toggleFireworks: function() { | |||
if (this.isActive) { | |||
this.stopFireworks(); | |||
} else { | |||
this.startFireworks(); | |||
} | |||
} | |||
}; | |||
window.FireworksModule = FireworksModule; | |||
function initAllModules() { | function initAllModules() { | ||
| Строка 1244: | Строка 1970: | ||
DocumentAutoFillModule.init(); | DocumentAutoFillModule.init(); | ||
// SnowflakesModule.init() | // SnowflakesModule.init() | ||
// FireworksModule.init(); | |||
// LawCalculatorModule.init(); | |||
} | } | ||
if (document.readyState === | if (document.readyState === 'loading') { | ||
document.addEventListener( | document.addEventListener('DOMContentLoaded', initAllModules); | ||
} else { | } else { | ||
initAllModules(); | initAllModules(); | ||
} | } | ||
})(); | })(); | ||