Нет описания правки |
Нет описания правки |
||
(не показаны 44 промежуточные версии 2 участников) | |||
Строка 1: | Строка 1: | ||
(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://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js'; | |||
popperScript.onload = function() { | |||
var tippyScript = document.createElement('script'); | |||
tippyScript.src = 'https://unpkg.com/tippy.js@6/dist/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://unpkg.com/tippy.js@6/dist/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() { | |||
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] | |||
}); | |||
}); | |||
}); | |||
if ( | |||
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.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.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.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.setupElement(pre); | |||
} | |||
}); | |||
}, | |||
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.trim(); | |||
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 { | } else { | ||
top = rect.top + rect. | 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'; | |||
} 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 | |||
}); | |||
} | |||
}; | |||
function initAllModules() { | |||
SidebarModule.init(); | |||
AccessTooltipsModule.init(); | |||
LawTooltipsModule.init(); | |||
DataTooltipsModule.init(); | |||
CopyTextModule.init(); | |||
DocumentAutoFillModule.init(); | |||
// LawCalculatorModule.init(); | |||
} | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', initAllModules); | |||
} else { | |||
initAllModules(); | |||
} | |||
})(); |
Версия от 23:10, 3 октября 2025
(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://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js';
popperScript.onload = function() {
var tippyScript = document.createElement('script');
tippyScript.src = 'https://unpkg.com/tippy.js@6/dist/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://unpkg.com/tippy.js@6/dist/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() {
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.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.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.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.setupElement(pre);
}
});
},
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.trim();
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';
} 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
});
}
};
function initAllModules() {
SidebarModule.init();
AccessTooltipsModule.init();
LawTooltipsModule.init();
DataTooltipsModule.init();
CopyTextModule.init();
DocumentAutoFillModule.init();
// LawCalculatorModule.init();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAllModules);
} else {
initAllModules();
}
})();