|
Поговорим о...
|
|
|
|
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Оригинальные стили с минимальными изменениями */ .icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; /* Фиксированная ширина как у панелей */ margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; /* Фиксированная ширина */ max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; /* Фиксированная ширина колонки */ max-width: 100%; }
/* Темная тема - только если Hugo активирует её */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; /* Иконки защищены от фильтров */ filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> // Используем оригинальные имена функций let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для разбивки больших групп на части function iconComparisonSplitLargeGroup(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; for (let i = 0; i < icons.length; i += maxItems) { chunks.push(icons.slice(i, i + maxItems)); } return chunks; }
// Функция для расчета примерной высоты группы function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки function iconComparisonCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); // Создаем колонки const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); // Массив для отслеживания высоты каждой колонки const columnHeights = Array(availableColumns).fill(0); // Распределяем группы по колонкам (алгоритм "наименьшая высота") groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = iconComparisonCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой (masonry компоновка) function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = iconComparisonSplitLargeGroup(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = iconComparisonCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
// Функция для создания секции группы function iconComparisonCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
// Остальные функции с оригинальными названиями function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0czcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; groupSelect.innerHTML += '<option value="no_group">Прочие</option>'; groups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); groupSelect.style.display = groups.length > 0 ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>
|
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Базовые стили для html и body вне Hugo */ html, body { margin: 0; padding: 0; width: 100%; overflow-x: hidden; } body { background-color: #fff; font-family: Arial, sans-serif; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; width: 100%; min-height: 100vh; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; width: 100%; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; width: 100%; }
.icon-comparison-group { break-inside: avoid; width: 400px; /* Фиксированная ширина */ max-width: 100%; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; /* Фиксированная ширина как у панели */ max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; max-width: 100%; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; /* Фиксированная ширина колонки */ max-width: 100%; }
/* Темная тема - только если Hugo активирует её */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; /* Иконки защищены от фильтров */ filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для ширины 800px и выше - 2 колонки */ @media (min-width: 800px) { .icon-comparison-masonry-column { width: calc(50% - 6px); max-width: 400px; } }
/* Для ширины 1200px и выше - 3 колонки */ @media (min-width: 1200px) { .icon-comparison-wrapper { max-width: 1200px; } .icon-comparison-masonry-column { width: calc(33.333% - 8px); max-width: 400px; } }
/* Для ширины 1400px и выше - 4 колонки */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } .icon-comparison-masonry-column { width: calc(25% - 9px); max-width: 400px; } }
/* Для ширины 1600px и выше */ @media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> // Используем оригинальные имена функций let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для разбивки больших групп на части function iconComparisonSplitLargeGroup(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; for (let i = 0; i < icons.length; i += maxItems) { chunks.push(icons.slice(i, i + maxItems)); } return chunks; }
// Функция для расчета примерной высоты группы function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки - ИСПРАВЛЕННАЯ function iconComparisonCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; // Определяем количество колонок на основе ширины контейнера let columnCount; if (containerWidth >= 1400) { columnCount = 4; } else if (containerWidth >= 1200) { columnCount = 3; } else if (containerWidth >= 800) { columnCount = 2; } else { columnCount = 1; } console.log(`Container width: ${containerWidth}, Columns: ${columnCount}`); // Создаем колонки с фиксированной шириной const columns = Array.from({ length: columnCount }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; // Рассчитываем ширину колонки в зависимости от их количества let columnWidth; if (containerWidth >= 1400 && columnCount === 4) { columnWidth = 'calc(25% - 9px)'; } else if (containerWidth >= 1200 && columnCount === 3) { columnWidth = 'calc(33.333% - 8px)'; } else if (containerWidth >= 800 && columnCount === 2) { columnWidth = 'calc(50% - 6px)'; } else { columnWidth = '100%'; } col.style.width = columnWidth; col.style.maxWidth = '400px'; col.style.minWidth = '300px'; return col; }); // Массив для отслеживания высоты каждой колонки const columnHeights = Array(columnCount).fill(0); // Распределяем группы по колонкам (алгоритм "наименьшая высота") groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = iconComparisonCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой (masonry компоновка) function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = iconComparisonSplitLargeGroup(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = iconComparisonCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
// Функция для создания секции группы - ИСПРАВЛЕННАЯ function iconComparisonCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; section.style.width = '100%'; section.style.maxWidth = '400px'; section.style.marginBottom = '12px'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; header.style.width = '100%'; header.style.maxWidth = '400px'; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; column.style.width = '100%'; column.style.maxWidth = '400px'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
// Остальные функции с оригинальными названиями function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; groupSelect.innerHTML += '<option value="no_group">Прочие</option>'; groups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); groupSelect.style.display = groups.length > 0 ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>
|
добавил сброс стилей и все заработало везде: * { margin: 0; padding: 0; box-sizing: border-box; }
|
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
function iconComparisonSplitLargeGroup(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; for (let i = 0; i < icons.length; i += maxItems) { chunks.push(icons.slice(i, i + maxItems)); } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = iconComparisonCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = iconComparisonSplitLargeGroup(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length) }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length) }); }); } allGroupChunks.sort((a, b) => b.height - a.height); const columns = iconComparisonCreateMasonryLayout(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; groupSelect.innerHTML += '<option value="no_group">Прочие</option>'; groups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); groupSelect.style.display = groups.length > 0 ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>Добавлено (2025-12-01, 16:30) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Убраны pixelated rendering свойства */ /* image-rendering: -webkit-optimize-contrast; */ /* image-rendering: crisp-edges; */ /* image-rendering: pixelated; */ /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; /* Добавляем плавное сглаживание */ image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
function iconComparisonSplitLargeGroup(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; for (let i = 0; i < icons.length; i += maxItems) { chunks.push(icons.slice(i, i + maxItems)); } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = iconComparisonCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = iconComparisonSplitLargeGroup(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length) }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length) }); }); } allGroupChunks.sort((a, b) => b.height - a.height); const columns = iconComparisonCreateMasonryLayout(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; // Используем сглаживание при рисовании на canvas ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; groupSelect.innerHTML += '<option value="no_group">Прочие</option>'; groups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); groupSelect.style.display = groups.length > 0 ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>
|
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
/* Убрали .icon-comparison-body - теперь стили применяются к внутренним элементам */ .icon-comparison-wrapper { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; min-height: 100vh; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { /* Убрали filter: none */ }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* ====== ИСПРАВЛЕННАЯ ТЕМНАЯ ТЕМА ====== */ /* Теперь используем обертку для темной темы вместо body */ body.dark .icon-comparison-wrapper, body[data-theme="dark"] .icon-comparison-wrapper, .dark-mode .icon-comparison-wrapper { background-color: #1a1a1a; color: #e0e0e0; }
body.dark .icon-comparison-title, body[data-theme="dark"] .icon-comparison-title, .dark-mode .icon-comparison-title { color: #e0e0e0; }
body.dark .icon-comparison-search, body[data-theme="dark"] .icon-comparison-search, .dark-mode .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
body.dark .icon-comparison-controls, body[data-theme="dark"] .icon-comparison-controls, .dark-mode .icon-comparison-controls { background: #333333; border-color: #404040; }
body.dark .icon-comparison-stats, body[data-theme="dark"] .icon-comparison-stats, .dark-mode .icon-comparison-stats { color: #e0e0e0; }
body.dark .icon-comparison-group-select, body[data-theme="dark"] .icon-comparison-group-select, .dark-mode .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
body.dark .icon-comparison-toggle, body[data-theme="dark"] .icon-comparison-toggle, .dark-mode .icon-comparison-toggle { color: #e0e0e0; }
body.dark .icon-comparison-group-title, body[data-theme="dark"] .icon-comparison-group-title, .dark-mode .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
body.dark .icon-comparison-panel, body[data-theme="dark"] .icon-comparison-panel, .dark-mode .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
body.dark .icon-comparison-panel-header, body[data-theme="dark"] .icon-comparison-panel-header, .dark-mode .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
body.dark .icon-comparison-header-label, body[data-theme="dark"] .icon-comparison-header-label, .dark-mode .icon-comparison-header-label { color: #e0e0e0; }
body.dark .icon-comparison-row, body[data-theme="dark"] .icon-comparison-row, .dark-mode .icon-comparison-row { border-bottom-color: #3a3a3a; }
.icon-comparison-image { background-color: #f5f5f5; }
body.dark .icon-comparison-image, body[data-theme="dark"] .icon-comparison-image, .dark-mode .icon-comparison-image { background-color: #2a2a2a; }
body.dark .icon-comparison-arrow, body[data-theme="dark"] .icon-comparison-arrow, .dark-mode .icon-comparison-arrow { color: #888; }
body.dark .icon-comparison-name, body[data-theme="dark"] .icon-comparison-name, .dark-mode .icon-comparison-name { color: #e0e0e0; }
body.dark .icon-comparison-loading, body[data-theme="dark"] .icon-comparison-loading, .dark-mode .icon-comparison-loading, body.dark .icon-comparison-no-results, body[data-theme="dark"] .icon-comparison-no-results, .dark-mode .icon-comparison-no-results { color: #999; }
/* ====== ВАЖНО: ЗАЩИТА ОТ ИНВЕРТИРОВАНИЯ ИКОНОК ====== */ /* Эти правила защитят PNG иконки от изменения в темной теме */ body.dark .icon-comparison-image, body[data-theme="dark"] .icon-comparison-image, .dark-mode .icon-comparison-image { /* Нейтрализуем любые фильтры темной темы для изображений */ filter: brightness(100%) contrast(100%) saturate(100%) !important; -webkit-filter: brightness(100%) contrast(100%) saturate(100%) !important; }
/* Ядерная защита - изоляция изображений от любых влияний */ body.dark .icon-comparison-panel, body[data-theme="dark"] .icon-comparison-panel, .dark-mode .icon-comparison-panel { isolation: isolate; position: relative; z-index: 1; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
function iconComparisonSplitLargeGroup(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; for (let i = 0; i < icons.length; i += maxItems) { chunks.push(icons.slice(i, i + maxItems)); } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = iconComparisonCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = iconComparisonSplitLargeGroup(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length) }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length) }); }); } allGroupChunks.sort((a, b) => b.height - a.height); const columns = iconComparisonCreateMasonryLayout(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; groupSelect.innerHTML += '<option value="no_group">Прочие</option>'; groups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); groupSelect.style.display = groups.length > 0 ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>Добавлено (2025-12-02, 07:38) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
/* Основные стили для wrapper */ .icon-comparison-wrapper { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; min-height: 100vh; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
/* Основной контейнер для сетки */ .icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
/* Стили для Masonry раскладки */ .icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Группа иконок */ .icon-comparison-group { break-inside: avoid; margin-bottom: 12px; width: 100%; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Панель с иконками */ .icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 100%; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
/* ====== ТЕМНАЯ ТЕМА ДЛЯ HUGO ====== */ /* Hugo обычно добавляет dark класс на body */ body.dark .icon-comparison-wrapper { background-color: #1a1a1a; color: #e0e0e0; }
body.dark .icon-comparison-title { color: #e0e0e0; }
body.dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
body.dark .icon-comparison-controls { background: #333333; border-color: #404040; }
body.dark .icon-comparison-stats { color: #e0e0e0; }
body.dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
body.dark .icon-comparison-toggle { color: #e0e0e0; }
body.dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
body.dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
body.dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
body.dark .icon-comparison-header-label { color: #e0e0e0; }
body.dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
body.dark .icon-comparison-image { background-color: #2a2a2a; }
body.dark .icon-comparison-arrow { color: #888; }
body.dark .icon-comparison-name { color: #e0e0e0; }
body.dark .icon-comparison-loading, body.dark .icon-comparison-no-results { color: #999; }
/* Защита PNG иконок от инвертирования в темной теме */ body.dark .icon-comparison-image { filter: brightness(100%) contrast(100%) saturate(100%) !important; -webkit-filter: brightness(100%) contrast(100%) saturate(100%) !important; }
/* Изоляция для защиты от наследования фильтров */ body.dark .icon-comparison-panel { isolation: isolate; }
/* ====== АДАПТИВНОСТЬ ====== */ @media (max-width: 768px) { .icon-comparison-wrapper { padding: 0 10px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; padding: 8px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; max-width: 180px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 150px; } .icon-comparison-masonry-column { width: 100%; } }
/* Для средних экранов (1-2 колонки) */ @media (min-width: 481px) and (max-width: 900px) { .icon-comparison-masonry-column { width: 100%; max-width: 500px; } }
/* Для широких экранов (3+ колонки) */ @media (min-width: 901px) and (max-width: 1400px) { .icon-comparison-columns { justify-content: flex-start; } }
@media (min-width: 1401px) { .icon-comparison-wrapper { max-width: 1400px; margin: 0 auto; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
function iconComparisonSplitLargeGroup(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; for (let i = 0; i < icons.length; i += maxItems) { chunks.push(icons.slice(i, i + maxItems)); } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; // Автоматически определяем количество колонок let availableColumns = 1; if (containerWidth >= 850) { availableColumns = 2; } if (containerWidth >= 1250) { availableColumns = 3; } if (containerWidth >= 1650) { availableColumns = 4; } console.log(`Container width: ${containerWidth}, Columns: ${availableColumns}`); const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); // Сортируем группы по высоте (от самой высокой к самой низкой) const sortedGroups = [...groupsChunks].sort((a, b) => b.height - a.height); sortedGroups.forEach(group => { // Находим колонку с минимальной высотой let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = iconComparisonCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = iconComparisonSplitLargeGroup(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length) }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length) }); }); } const columns = iconComparisonCreateMasonryLayout(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; groupSelect.innerHTML += '<option value="no_group">Прочие</option>'; groups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); groupSelect.style.display = groups.length > 0 ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html> Добавлено (2025-12-02, 07:49) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Убраны pixelated rendering свойства */ /* image-rendering: -webkit-optimize-contrast; */ /* image-rendering: crisp-edges; */ /* image-rendering: pixelated; */ /* Добавляем плавное сглаживание */ image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; /* ЗАЩИТА ДЛЯ ИЗОБРАЖЕНИЙ - не позволяем темной теме изменять PNG */ filter: brightness(1) contrast(1) saturate(1) !important; -webkit-filter: brightness(1) contrast(1) saturate(1) !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
function iconComparisonSplitLargeGroup(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; for (let i = 0; i < icons.length; i += maxItems) { chunks.push(icons.slice(i, i + maxItems)); } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = iconComparisonCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = iconComparisonSplitLargeGroup(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length) }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length) }); }); } allGroupChunks.sort((a, b) => b.height - a.height); const columns = iconComparisonCreateMasonryLayout(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; // Используем сглаживание при рисовании на canvas ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzIiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; groupSelect.innerHTML += '<option value="no_group">Прочие</option>'; groups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); groupSelect.style.display = groups.length > 0 ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html> Добавлено (2025-12-02, 07:55) --------------------------------------------- /* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
/* ... остальные стили темной темы ... */
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; /* МОЩНАЯ ЗАЩИТА ДЛЯ ИЗОБРАЖЕНИЙ - полная нейтрализация любых фильтров */ filter: none !important; -webkit-filter: none !important; /* Дополнительная защита от CSS трансформаций */ transform: none !important; -webkit-transform: none !important; /* Защита от любых цветовых изменений */ mix-blend-mode: normal !important; /* Принудительно отключаем все возможные фильтры темной темы */ backdrop-filter: none !important; -webkit-backdrop-filter: none !important; /* Сохраняем оригинальные цвета */ forced-color-adjust: none !important; color-adjust: exact !important; -webkit-print-color-adjust: exact !important; }
/* Ядерная защита - создаем контекст где фильтры не применяются */ .dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { /* Изолируем панели от родительских фильтров */ isolation: isolate; position: relative; z-index: 1; }
/* Специальная защита для изображений внутри панелей */ .dark-mode .icon-comparison-panel .icon-comparison-image, [data-theme="dark"] .icon-comparison-panel .icon-comparison-image, .dark .icon-comparison-panel .icon-comparison-image { /* Двойная защита - более специфичный селектор */ filter: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><filter id="identity"><feComponentTransfer/></filter></svg>#identity') !important; -webkit-filter: none !important; }
/* Защита на уровне всей страницы если Hugo применяет фильтры к body */ .dark-mode body, [data-theme="dark"] body, .dark body { /* Проверяем, не применяются ли фильтры к body */ }
/* Альтернативный вариант если Hugo использует invert() фильтр */ @media (prefers-color-scheme: dark) { .dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { /* Отменяем инвертирование */ filter: invert(0%) hue-rotate(0deg) brightness(100%) contrast(100%) saturate(100%) !important; -webkit-filter: invert(0%) hue-rotate(0deg) brightness(100%) contrast(100%) saturate(100%) !important; } } Добавлено (2025-12-02, 07:57) --------------------------------------------- // Дополнительная JavaScript защита для изображений в темной теме function iconComparisonFixDarkThemeImages() { // Проверяем, активна ли темная тема const isDarkTheme = document.body.classList.contains('dark') || document.body.classList.contains('dark-mode') || document.body.getAttribute('data-theme') === 'dark'; if (isDarkTheme) { // Находим все изображения иконок const images = document.querySelectorAll('.icon-comparison-image'); images.forEach(img => { // Принудительно отключаем любые фильтры img.style.filter = 'none'; img.style.webkitFilter = 'none'; img.style.backdropFilter = 'none'; img.style.webkitBackdropFilter = 'none'; // Сбрасываем любые CSS трансформации img.style.transform = 'none'; img.style.webkitTransform = 'none'; // Защищаем от наследования фильтров img.style.mixBlendMode = 'normal'; // Создаем клон изображения чтобы сбросить стили const parent = img.parentNode; const clone = img.cloneNode(true); parent.replaceChild(clone, img); // Применяем защиту к клону clone.style.filter = 'none !important'; clone.style.webkitFilter = 'none !important'; }); // Также защищаем контейнеры const panels = document.querySelectorAll('.icon-comparison-panel'); panels.forEach(panel => { panel.style.filter = 'none'; panel.style.webkitFilter = 'none'; panel.style.isolation = 'isolate'; }); } }
// Вызываем при загрузке и при изменении темы window.addEventListener('DOMContentLoaded', function() { iconComparisonFixDarkThemeImages(); // Отслеживаем изменения в DOM (на случай динамического переключения темы) const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'attributes' && (mutation.attributeName === 'class' || mutation.attributeName === 'data-theme')) { setTimeout(iconComparisonFixDarkThemeImages, 100); } }); }); // Начинаем наблюдать за изменениями в body observer.observe(document.body, { attributes: true, attributeFilter: ['class', 'data-theme'] }); });
|
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Убраны pixelated rendering свойства */ /* image-rendering: -webkit-optimize-contrast; */ /* image-rendering: crisp-edges; */ /* image-rendering: pixelated; */ /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; /* Добавляем плавное сглаживание */ image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя групп */ .icon-comparison-group-separator { width: 100%; height: 1px; background-color: #ddd; margin: 10px 0; border: none; }
.dark-mode .icon-comparison-group-separator, [data-theme="dark"] .icon-comparison-group-separator, .dark .icon-comparison-group-separator { background-color: #404040; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } .icon-comparison-group-separator { margin: 8px 0; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-group-separator { margin: 6px 0; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
function iconComparisonSplitLargeGroup(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; for (let i = 0; i < icons.length; i += maxItems) { chunks.push(icons.slice(i, i + maxItems)); } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = iconComparisonCreateGroupSection(group.name, group.icons, group.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; // Сортировка групп по алфавиту const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы (отсортированные по алфавиту) sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroup(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); // Добавляем разделитель, если есть и обычные группы, и группа "Прочие" if (sortedGroupNames.length > 0 && noGroup.length > 0) { allGroupChunks.push({ name: 'separator', icons: [], height: 21, // Высота разделителя (1px + margin) isSeparator: true }); } // Добавляем группу "Прочие" в конец if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } // Сортировка по высоте для masonry раскладки const regularGroups = allGroupChunks.filter(g => !g.isSeparator); regularGroups.sort((a, b) => b.height - a.height); // Вставляем разделитель на правильную позицию const separatorIndex = allGroupChunks.findIndex(g => g.isSeparator); if (separatorIndex !== -1) { // Находим позицию, куда вставить разделитель let separatorInserted = false; const finalGroups = []; for (let i = 0; i < regularGroups.length; i++) { finalGroups.push(regularGroups[i]); // Если это последняя обычная группа и есть группа "Прочие" после нее if (!separatorInserted && i === regularGroups.filter(g => !g.isNoGroupSection).length - 1 && allGroupChunks.some(g => g.isNoGroupSection)) { finalGroups.push(allGroupChunks[separatorIndex]); separatorInserted = true; } } // Если разделитель не был вставлен, но должен быть if (!separatorInserted && separatorIndex !== -1) { finalGroups.push(allGroupChunks[separatorIndex]); } // Добавляем оставшиеся группы (должны быть только "Прочие") const remainingGroups = regularGroups.filter(g => !finalGroups.includes(g)); finalGroups.push(...remainingGroups); const columns = iconComparisonCreateMasonryLayout(finalGroups); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); } else { // Если нет разделителя, просто используем отсортированные группы const columns = iconComparisonCreateMasonryLayout(regularGroups); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); } iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { if (groupName === 'separator') { const separator = document.createElement('hr'); separator.className = 'icon-comparison-group-separator'; return separator; } const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; // Используем сглаживание при рисовании на canvas ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; // Сортировка групп по алфавиту const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); // Добавляем группу "Прочие" в конец списка const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (hasNoGroup) { const option = document.createElement('option'); option.value = 'no_group'; option.textContent = 'Прочие'; groupSelect.appendChild(option); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>Добавлено (2025-12-03, 10:31) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Убраны pixelated rendering свойства */ /* image-rendering: -webkit-optimize-contrast; */ /* image-rendering: crisp-edges; */ /* image-rendering: pixelated; */ /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; /* Добавляем плавное сглаживание */ image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя групп */ .icon-comparison-group-separator { width: 100%; height: 1px; background-color: #ddd; margin: 10px 0; border: none; }
.dark-mode .icon-comparison-group-separator, [data-theme="dark"] .icon-comparison-group-separator, .dark .icon-comparison-group-separator { background-color: #404040; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } .icon-comparison-group-separator { margin: 8px 0; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-group-separator { margin: 6px 0; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { // Для небольших групп (до 30 элементов) делим пополам const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { // Для больших групп используем оптимальное разбиение const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { // Если у группы только один чанк const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { // Если у группы несколько чанков, пытаемся разместить их рядом let bestDistribution = null; let bestHeightDiff = Infinity; // Пробуем разные способы распределения чанков по колонкам for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; // Проверяем, не слишком ли неравномерное распределение const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { // Максимально допустимая разница высот canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } // Если не нашли хорошего распределения, используем стандартный алгоритм if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { // Используем найденное оптимальное распределение group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); // Добавляем разделители groupsChunks.forEach(chunk => { if (chunk.isSeparator) { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, false); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; // Сортировка групп по алфавиту const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы (отсортированные по алфавиту) sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); // Добавляем разделитель, если есть и обычные группы, и группа "Прочие" if (sortedGroupNames.length > 0 && noGroup.length > 0) { allGroupChunks.push({ name: 'separator', icons: [], height: 21, // Высота разделителя (1px + margin) isSeparator: true }); } // Добавляем группу "Прочие" в конец if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { if (groupName === 'separator') { const separator = document.createElement('hr'); separator.className = 'icon-comparison-group-separator'; return separator; } const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; // Используем сглаживание при рисовании на canvas ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; // Сортировка групп по алфавиту const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); // Добавляем группу "Прочие" в конец списка const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (hasNoGroup) { const option = document.createElement('option'); option.value = 'no_group'; option.textContent = 'Прочие'; groupSelect.appendChild(option); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html> Добавлено (2025-12-03, 10:47) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Убраны pixelated rendering свойства */ /* image-rendering: -webkit-optimize-contrast; */ /* image-rendering: crisp-edges; */ /* image-rendering: pixelated; */ /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; /* Добавляем плавное сглаживание */ image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Убраны стили для разделителя групп в визуальном отображении */
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { // Для небольших групп (до 30 элементов) делим пополам const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { // Для больших групп используем оптимальное разбиение const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { // Если у группы только один чанк const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { // Если у группы несколько чанков, пытаемся разместить их рядом let bestDistribution = null; let bestHeightDiff = Infinity; // Пробуем разные способы распределения чанков по колонкам for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; // Проверяем, не слишком ли неравномерное распределение const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { // Максимально допустимая разница высот canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } // Если не нашли хорошего распределения, используем стандартный алгоритм if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { // Используем найденное оптимальное распределение group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; // Сортировка групп по алфавиту const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы (отсортированные по алфавиту) sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); // НЕ добавляем разделитель в визуальное отображение // Добавляем группу "Прочие" в конец if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; // Используем сглаживание при рисовании на canvas ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0czcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; // Сортировка групп по алфавиту const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } // Проверяем, есть ли группа "Прочие" const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); // Добавляем разделитель и группу "Прочие" в конец списка, если есть обычные группы и группа "Прочие" if (sortedGroups.length > 0 && hasNoGroup) { // Добавляем разделитель const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); // Добавляем группу "Прочие" const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { // Если есть только группа "Прочие" const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (sortedGroups.length > 0) { // Если есть только обычные группы - ничего не добавляем } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>
|
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { // Если у группы только один чанк const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { // Если у группы несколько чанков, пытаемся разместить их рядом let bestDistribution = null; let bestHeightDiff = Infinity; // Пробуем разные способы распределения чанков по колонкам for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; // Проверяем, не слишком ли неравномерное распределение const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { // Максимально допустимая разница высот canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } // Если не нашли хорошего распределения, используем стандартный алгоритм if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { // Используем найденное оптимальное распределение group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; // Сортировка групп по алфавиту const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы (отсортированные по алфавиту) sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); // Добавляем группу "Прочие" в конец if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); // Активируем кнопку экспорта если есть что экспортировать const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; // Используем сглаживание при рисовании на canvas ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); // Активируем кнопку экспорта если есть что экспортировать const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; // Сортировка групп по алфавиту const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } // Проверяем, есть ли группа "Прочие" const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); // Добавляем разделитель и группу "Прочие" в конец списка, если есть обычные группы и группа "Прочие" if (sortedGroups.length > 0 && hasNoGroup) { // Добавляем разделитель const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); // Добавляем группу "Прочие" const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { // Если есть только группа "Прочие" const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (sortedGroups.length > 0) { // Если есть только обычные группы - ничего не добавляем } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); // Активируем кнопку экспорта если есть что экспортировать const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonAllIcons.length === 0; }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
function iconComparisonSetupExport() { const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.addEventListener('click', function() { if (!this.disabled) { iconComparisonExportToPDF(); } }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupExport(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>Добавлено (2025-12-03, 12:49) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { display: flex; justify-content: center; align-items: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-export-btn { padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.icon-comparison-export-btn:hover { background-color: #218838; transform: translateY(-1px); }
.icon-comparison-export-btn:active { transform: translateY(0); }
.icon-comparison-export-btn:disabled { background-color: #6c757d; cursor: not-allowed; transform: none; }
.icon-comparison-export-btn svg { width: 16px; height: 16px; fill: currentColor; }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; filter: none !important; -webkit-filter: none !important; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
/* Стили для сообщения о загрузке PDF */ .icon-comparison-pdf-loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px 30px; border-radius: 8px; z-index: 10000; display: none; flex-direction: column; align-items: center; gap: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); }
.icon-comparison-pdf-loading.show { display: flex; }
.icon-comparison-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: icon-comparison-spin 1s ease-in-out infinite; }
@keyframes icon-comparison-spin { to { transform: rotate(360deg); } }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-export-btn, [data-theme="dark"] .icon-comparison-export-btn, .dark .icon-comparison-export-btn { background-color: #2e7d32; }
.dark-mode .icon-comparison-export-btn:hover, [data-theme="dark"] .icon-comparison-export-btn:hover, .dark .icon-comparison-export-btn:hover { background-color: #1b5e20; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-search-area { flex-direction: column; align-items: stretch; } .icon-comparison-search { width: 100%; max-width: 100%; } .icon-comparison-export-btn { width: 100%; justify-content: center; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
@media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-export-btn { padding: 8px 12px; font-size: 13px; } }
@media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> <!-- Подключаем библиотеки для генерации PDF --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> <button id="iconComparisonExportBtn" class="icon-comparison-export-btn" disabled> <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт в PDF </button> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<!-- Оверлей для отображения загрузки PDF --> <div id="iconComparisonPdfLoading" class="icon-comparison-pdf-loading"> <div class="icon-comparison-spinner"></div> <div>Идет генерация PDF...</div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для разбиения большого контента на страницы А4 function iconComparisonSplitContentForA4(contentElements, maxHeight = 1120) { // 1120px ~ A4 height minus margins const pages = []; let currentPage = []; let currentPageHeight = 0; contentElements.forEach(element => { const elementHeight = element.offsetHeight || 0; // Если элемент сам по себе больше страницы, разбиваем его if (elementHeight > maxHeight) { // Для очень больших элементов просто добавляем на отдельную страницу if (currentPage.length > 0) { pages.push([...currentPage]); currentPage = []; currentPageHeight = 0; } pages.push([element]); } // Если элемент помещается на текущую страницу else if (currentPageHeight + elementHeight <= maxHeight) { currentPage.push(element); currentPageHeight += elementHeight; } // Если не помещается, начинаем новую страницу else { if (currentPage.length > 0) { pages.push([...currentPage]); } currentPage = [element]; currentPageHeight = elementHeight; } }); // Добавляем последнюю страницу, если в ней есть элементы if (currentPage.length > 0) { pages.push(currentPage); } return pages; }
// Функция для создания заголовка PDF function iconComparisonCreatePdfHeader(pdf, searchTerm = '', groupName = '', totalIcons = 0, shownIcons = 0) { // Заголовок pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text('Сравнение иконок ИСИХОГИ', 105, 20, { align: 'center' }); // Информация о фильтрах pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); let infoText = `Всего иконок: ${totalIcons} | Отображено: ${shownIcons}`; if (searchTerm) { infoText += ` | Поиск: "${searchTerm}"`; } if (groupName && groupName !== 'Все группы') { const displayGroupName = groupName === 'no_group' ? 'Прочие' : groupName; infoText += ` | Группа: ${displayGroupName}`; } pdf.text(infoText, 105, 30, { align: 'center' }); // Дата экспорта const now = new Date(); const dateStr = now.toLocaleDateString('ru-RU') + ' ' + now.toLocaleTimeString('ru-RU'); pdf.text(`Экспорт: ${dateStr}`, 105, 40, { align: 'center' }); return 45; // Возвращаем Y-координату после заголовка }
// Функция для экспорта в PDF async function iconComparisonExportToPDF() { const exportBtn = document.getElementById('iconComparisonExportBtn'); const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); try { // Блокируем кнопку и показываем индикатор загрузки exportBtn.disabled = true; exportBtn.textContent = 'Генерация...'; loadingOverlay.classList.add('show'); const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; // Создаем временный контейнер для генерации PDF const tempContainer = document.createElement('div'); tempContainer.style.position = 'absolute'; tempContainer.style.left = '-9999px'; tempContainer.style.top = '-9999px'; tempContainer.style.width = '794px'; // Ширина A4 в пикселях (210mm) tempContainer.style.backgroundColor = 'white'; tempContainer.style.padding = '20px'; tempContainer.style.boxSizing = 'border-box'; document.body.appendChild(tempContainer); // Копируем контент для PDF const originalContainer = document.getElementById('iconComparisonContainer'); if (iconComparisonShowGroups) { // Для режима с группами - создаем оптимизированный PDF контент const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); // Создаем заголовок const title = document.createElement('h2'); title.textContent = 'Сравнение иконок ИСИХОГИ'; title.style.fontSize = '18px'; title.style.textAlign = 'center'; title.style.marginBottom = '10px'; tempContainer.appendChild(title); // Информация const info = document.createElement('div'); info.textContent = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) info.textContent += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') info.textContent += ` | Группа: ${groupName}`; info.style.fontSize = '11px'; info.style.textAlign = 'center'; info.style.marginBottom = '15px'; info.style.color = '#666'; tempContainer.appendChild(info); // Сортировка групп по алфавиту const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; // Заголовок группы const groupHeader = document.createElement('div'); groupHeader.textContent = groupName; groupHeader.style.fontWeight = 'bold'; groupHeader.style.fontSize = '13px'; groupHeader.style.backgroundColor = '#f8f9fa'; groupHeader.style.padding = '6px 10px'; groupHeader.style.borderRadius = '4px'; groupHeader.style.marginTop = '15px'; groupHeader.style.marginBottom = '8px'; tempContainer.appendChild(groupHeader); // Создаем таблицу для группы (оптимизированную для PDF) const table = document.createElement('table'); table.style.width = '100%'; table.style.borderCollapse = 'collapse'; table.style.fontSize = '10px'; // Заголовок таблицы const thead = document.createElement('thead'); thead.innerHTML = ` <tr> <th style="width: 60px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;">ДО</th> <th style="width: 20px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;"></th> <th style="width: 60px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;">ПОСЛЕ</th> <th style="text-align: left; padding: 4px; border-bottom: 1px solid #ddd;">Название</th> </tr> `; table.appendChild(thead); // Тело таблицы const tbody = document.createElement('tbody'); icons.forEach(icon => { const row = document.createElement('tr'); row.style.borderBottom = '1px solid #f0f0f0'; const beforeCell = document.createElement('td'); beforeCell.style.textAlign = 'center'; beforeCell.style.padding = '4px'; beforeCell.style.verticalAlign = 'middle'; beforeCell.innerHTML = `<img src="${icon.before}" style="width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px;">`; const arrowCell = document.createElement('td'); arrowCell.style.textAlign = 'center'; arrowCell.style.padding = '4px'; arrowCell.style.verticalAlign = 'middle'; arrowCell.textContent = '→'; const afterCell = document.createElement('td'); afterCell.style.textAlign = 'center'; afterCell.style.padding = '4px'; afterCell.style.verticalAlign = 'middle'; afterCell.innerHTML = `<img src="${icon.after}" style="width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px;">`; const nameCell = document.createElement('td'); nameCell.style.padding = '4px 8px'; nameCell.style.verticalAlign = 'middle'; nameCell.textContent = icon.name; row.appendChild(beforeCell); row.appendChild(arrowCell); row.appendChild(afterCell); row.appendChild(nameCell); tbody.appendChild(row); }); table.appendChild(tbody); tempContainer.appendChild(table); }); // Добавляем группу "Прочие", если есть if (noGroup.length > 0) { // Заголовок группы const groupHeader = document.createElement('div'); groupHeader.textContent = 'Прочие иконки'; groupHeader.style.fontWeight = 'bold'; groupHeader.style.fontSize = '13px'; groupHeader.style.backgroundColor = '#f8f9fa'; groupHeader.style.padding = '6px 10px'; groupHeader.style.borderRadius = '4px'; groupHeader.style.marginTop = '15px'; groupHeader.style.marginBottom = '8px'; tempContainer.appendChild(groupHeader); // Таблица для группы "Прочие" const table = document.createElement('table'); table.style.width = '100%'; table.style.borderCollapse = 'collapse'; table.style.fontSize = '10px'; const thead = document.createElement('thead'); thead.innerHTML = ` <tr> <th style="width: 60px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;">ДО</th> <th style="width: 20px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;"></th> <th style="width: 60px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;">ПОСЛЕ</th> <th style="text-align: left; padding: 4px; border-bottom: 1px solid #ddd;">Название</th> </tr> `; table.appendChild(thead); const tbody = document.createElement('tbody'); noGroup.forEach(icon => { const row = document.createElement('tr'); row.style.borderBottom = '1px solid #f0f0f0'; const beforeCell = document.createElement('td'); beforeCell.style.textAlign = 'center'; beforeCell.style.padding = '4px'; beforeCell.style.verticalAlign = 'middle'; beforeCell.innerHTML = `<img src="${icon.before}" style="width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px;">`; const arrowCell = document.createElement('td'); arrowCell.style.textAlign = 'center'; arrowCell.style.padding = '4px'; arrowCell.style.verticalAlign = 'middle'; arrowCell.textContent = '→'; const afterCell = document.createElement('td'); afterCell.style.textAlign = 'center'; afterCell.style.padding = '4px'; afterCell.style.verticalAlign = 'middle'; afterCell.innerHTML = `<img src="${icon.after}" style="width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px;">`; const nameCell = document.createElement('td'); nameCell.style.padding = '4px 8px'; nameCell.style.verticalAlign = 'middle'; nameCell.textContent = icon.name; row.appendChild(beforeCell); row.appendChild(arrowCell); row.appendChild(afterCell); row.appendChild(nameCell); tbody.appendChild(row); }); table.appendChild(tbody); tempContainer.appendChild(table); } } else { // Для режима без групп const title = document.createElement('h2'); title.textContent = 'Сравнение иконок ИСИХОГИ'; title.style.fontSize = '18px'; title.style.textAlign = 'center'; title.style.marginBottom = '10px'; tempContainer.appendChild(title); const info = document.createElement('div'); info.textContent = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) info.textContent += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') info.textContent += ` | Группа: ${groupName}`; info.style.fontSize = '11px'; info.style.textAlign = 'center'; info.style.marginBottom = '20px'; info.style.color = '#666'; tempContainer.appendChild(info); // Создаем общую таблицу для всех иконок const table = document.createElement('table'); table.style.width = '100%'; table.style.borderCollapse = 'collapse'; table.style.fontSize = '10px'; const thead = document.createElement('thead'); thead.innerHTML = ` <tr> <th style="width: 60px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;">ДО</th> <th style="width: 20px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;"></th> <th style="width: 60px; text-align: center; padding: 4px; border-bottom: 1px solid #ddd;">ПОСЛЕ</th> <th style="text-align: left; padding: 4px; border-bottom: 1px solid #ddd;">Название</th> </tr> `; table.appendChild(thead); const tbody = document.createElement('tbody'); const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) return groupA.localeCompare(groupB); return a.name.localeCompare(b.name); }); sortedIcons.forEach(icon => { const row = document.createElement('tr'); row.style.borderBottom = '1px solid #f0f0f0'; const beforeCell = document.createElement('td'); beforeCell.style.textAlign = 'center'; beforeCell.style.padding = '4px'; beforeCell.style.verticalAlign = 'middle'; beforeCell.innerHTML = `<img src="${icon.before}" style="width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px;">`; const arrowCell = document.createElement('td'); arrowCell.style.textAlign = 'center'; arrowCell.style.padding = '4px'; arrowCell.style.verticalAlign = 'middle'; arrowCell.textContent = '→'; const afterCell = document.createElement('td'); afterCell.style.textAlign = 'center'; afterCell.style.padding = '4px'; afterCell.style.verticalAlign = 'middle'; afterCell.innerHTML = `<img src="${icon.after}" style="width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px;">`; const nameCell = document.createElement('td'); nameCell.style.padding = '4px 8px'; nameCell.style.verticalAlign = 'middle'; nameCell.textContent = icon.name; row.appendChild(beforeCell); row.appendChild(arrowCell); row.appendChild(afterCell); row.appendChild(nameCell); tbody.appendChild(row); }); table.appendChild(tbody); tempContainer.appendChild(table); } // Даем время на отрисовку изображений await new Promise(resolve => setTimeout(resolve, 500)); // Создаем PDF const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); // Размеры страницы А4 const pageWidth = 210; // mm const pageHeight = 297; // mm const margin = 10; // mm const contentWidth = pageWidth - (margin * 2); const contentHeight = pageHeight - (margin * 2); // Получаем контент как изображение const canvas = await html2canvas(tempContainer, { scale: 2, // Увеличиваем качество useCORS: true, // Для кросс-доменных изображений allowTaint: true, backgroundColor: '#ffffff' }); // Удаляем временный контейнер document.body.removeChild(tempContainer); // Размеры изображения const imgWidth = contentWidth; const imgHeight = (canvas.height * contentWidth) / canvas.width; // Рассчитываем количество страниц let totalHeight = imgHeight; let position = 0; let pageNumber = 1; while (position < totalHeight) { if (position > 0) { pdf.addPage(); } // Вычисляем видимую часть изображения для текущей страницы const srcY = position * (canvas.height / imgHeight); const srcHeight = Math.min(contentHeight * (canvas.height / imgHeight), canvas.height - srcY); // Создаем временный canvas для текущей страницы const tempCanvas = document.createElement('canvas'); tempCanvas.width = canvas.width; tempCanvas.height = srcHeight; const ctx = tempCanvas.getContext('2d'); ctx.drawImage(canvas, 0, srcY, canvas.width, srcHeight, 0, 0, canvas.width, srcHeight); // Конвертируем в данные URL const imgData = tempCanvas.toDataURL('image/jpeg', 0.9); // Добавляем изображение на страницу pdf.addImage(imgData, 'JPEG', margin, margin, imgWidth, contentHeight); // Добавляем номер страницы pdf.setFontSize(9); pdf.setFont('helvetica', 'normal'); pdf.text(`Страница ${pageNumber}`, pageWidth / 2, pageHeight - 5, { align: 'center' }); position += contentHeight; pageNumber++; } // Сохраняем PDF const fileName = `Сравнение_иконок_ИСИХОГИ_${new Date().toISOString().slice(0, 10)}.pdf`; pdf.save(fileName); } catch (error) { console.error('Ошибка при генерации PDF:', error); alert('Произошла ошибка при генерации PDF. Пожалуйста, попробуйте еще раз.'); } finally { // Восстанавливаем кнопку и скрываем индикатор exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт в PDF `; loadingOverlay.classList.remove('show'); } }
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { // Для небольших групп (до 30 элементов) делим пополам const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { // Для больших групп используем оптимальное разбиение const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); console.log(`Container width: ${containerWidth}, Column width: ${columnWidth}, Columns: ${availableColumns}`); // Группируем чанки по исходной группе
|
index.html:1035 Ошибка при генерации PDF: SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported. at iconComparisonExportToPDF (index.html:1016:28)
|
function tryNextProxy() { if (attemptIndex >= proxyAttempts.length) { reject(new Error('Не удалось загрузить изображение')); return; } const proxySrc = proxyAttempts[attemptIndex]; attemptIndex++; console.log('Попытка загрузки через прокси:', proxySrc); // Создаем новый Image для каждой попытки const proxyImg = new Image(); proxyImg.crossOrigin = 'Anonymous'; proxyImg.onload = function() { try { const canvas = document.createElement('canvas'); canvas.width = proxyImg.naturalWidth || proxyImg.width; canvas.height = proxyImg.naturalHeight || proxyImg.height; const ctx = canvas.getContext('2d'); ctx.drawImage(proxyImg, 0, 0); const dataURL = canvas.toDataURL('image/png'); resolve(dataURL); } catch (error) { tryNextProxy(); } }; proxyImg.onerror = function() { tryNextProxy(); }; proxyImg.src = proxySrc; } // Начинаем попытки загрузки tryNextProxy(); }); }
// Функция для создания текстового PDF (без изображений) в случае CORS ошибок async function iconComparisonCreateTextPDF() { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; // Заголовок pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text('Сравнение иконок ИСИХОГИ', 105, 20, { align: 'center' }); // Информация pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); let infoText = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) infoText += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') infoText += ` | Группа: ${groupName}`; pdf.text(infoText, 105, 30, { align: 'center' }); // Дата const now = new Date(); const dateStr = now.toLocaleDateString('ru-RU') + ' ' + now.toLocaleTimeString('ru-RU'); pdf.text(`Экспорт: ${dateStr}`, 105, 40, { align: 'center' }); // Предупреждение об отсутствии изображений pdf.setFontSize(12); pdf.setTextColor(255, 0, 0); pdf.text('ВНИМАНИЕ: Изображения не добавлены из-за ограничений CORS', 105, 50, { align: 'center' }); pdf.setTextColor(0, 0, 0); let yPos = 65; const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); // Добавляем группы const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); for (const groupName of sortedGroupNames) { if (yPos > 270) { pdf.addPage(); yPos = 20; } pdf.setFontSize(13); pdf.setFont('helvetica', 'bold'); pdf.text(groupName, 20, yPos); yPos += 8; pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); const icons = groups[groupName]; for (const icon of icons) { if (yPos > 280) { pdf.addPage(); yPos = 20; } pdf.text(`• ${icon.name}`, 25, yPos); pdf.text(` До: ${icon.before}`, 30, yPos + 4); pdf.text(` После: ${icon.after}`, 30, yPos + 8); yPos += 15; } yPos += 5; } // Добавляем группу "Прочие" if (noGroup.length > 0) { if (yPos > 270) { pdf.addPage(); yPos = 20; } pdf.setFontSize(13); pdf.setFont('helvetica', 'bold'); pdf.text('Прочие иконки', 20, yPos); yPos += 8; pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); for (const icon of noGroup) { if (yPos > 280) { pdf.addPage(); yPos = 20; } pdf.text(`• ${icon.name}`, 25, yPos); pdf.text(` До: ${icon.before}`, 30, yPos + 4); pdf.text(` После: ${icon.after}`, 30, yPos + 8); yPos += 15; } } // Номера страниц const pageCount = pdf.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { pdf.setPage(i); pdf.setFontSize(9); pdf.text(`Страница ${i} из ${pageCount}`, 105, 287, { align: 'center' }); } return pdf; }
// Основная функция для экспорта в PDF async function iconComparisonExportToPDF() { const exportBtn = document.getElementById('iconComparisonExportBtn'); const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); try { exportBtn.disabled = true; exportBtn.textContent = 'Генерация...'; loadingOverlay.classList.add('show'); // Пытаемся создать PDF с изображениями try { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; // Заголовок pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text('Сравнение иконок ИСИХОГИ', 105, 15, { align: 'center' }); // Информация pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); let infoText = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) infoText += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') infoText += ` | Группа: ${groupName}`; pdf.text(infoText, 105, 22, { align: 'center' }); // Дата const now = new Date(); const dateStr = now.toLocaleDateString('ru-RU') + ' ' + now.toLocaleTimeString('ru-RU'); pdf.text(`Экспорт: ${dateStr}`, 105, 29, { align: 'center' }); let yPos = 40; const pageWidth = 210; const margin = 10; const contentWidth = pageWidth - (margin * 2); const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Размеры для изображений в PDF const imgSize = 12; // мм const imgMargin = 2; // мм const rowHeight = 15; // мм // Добавляем обычные группы for (const groupName of sortedGroupNames) { if (yPos > 260) { pdf.addPage(); yPos = 20; } // Заголовок группы pdf.setFontSize(12); pdf.setFont('helvetica', 'bold'); pdf.text(groupName, margin, yPos); yPos += 8; const icons = groups[groupName]; // Добавляем иконки группы for (const icon of icons) { if (yPos > 270) { pdf.addPage(); yPos = 20; } // Пытаемся загрузить изображения try { const beforeDataURL = await iconComparisonLoadImageForPDF(icon.before); const afterDataURL = await iconComparisonLoadImageForPDF(icon.after); // Добавляем иконки pdf.addImage(beforeDataURL, 'PNG', margin, yPos, imgSize, imgSize); pdf.addImage(afterDataURL, 'PNG', margin + imgSize + imgMargin, yPos, imgSize, imgSize); // Добавляем стрелку и текст pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); pdf.text('→', margin + imgSize + (imgMargin/2), yPos + (imgSize/2) + 2); pdf.text(icon.name, margin + (imgSize * 2) + (imgMargin * 2), yPos + (imgSize/2) + 2); } catch (error) { // Если не удалось загрузить изображения, добавляем только текст pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); pdf.text(`• ${icon.name}`, margin, yPos + 6); pdf.setFontSize(8); pdf.text(` До: ${icon.before}`, margin + 5, yPos + 11); pdf.text(` После: ${icon.after}`, margin + 5, yPos + 16); } yPos += rowHeight; } yPos += 5; // Отступ между группами } // Добавляем группу "Прочие" if (noGroup.length > 0) { if (yPos > 260) { pdf.addPage(); yPos = 20; } // Заголовок группы pdf.setFontSize(12); pdf.setFont('helvetica', 'bold'); pdf.text('Прочие иконки', margin, yPos); yPos += 8; // Добавляем иконки группы "Прочие" for (const icon of noGroup) { if (yPos > 270) { pdf.addPage(); yPos = 20; } try { const beforeDataURL = await iconComparisonLoadImageForPDF(icon.before); const afterDataURL = await iconComparisonLoadImageForPDF(icon.after); pdf.addImage(beforeDataURL, 'PNG', margin, yPos, imgSize, imgSize); pdf.addImage(afterDataURL, 'PNG', margin + imgSize + imgMargin, yPos, imgSize, imgSize); pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); pdf.text('→', margin + imgSize + (imgMargin/2), yPos + (imgSize/2) + 2); pdf.text(icon.name, margin + (imgSize * 2) + (imgMargin * 2), yPos + (imgSize/2) + 2); } catch (error) { pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); pdf.text(`• ${icon.name}`, margin, yPos + 6); pdf.setFontSize(8); pdf.text(` До: ${icon.before}`, margin + 5, yPos + 11); pdf.text(` После: ${icon.after}`, margin + 5, yPos + 16); } yPos += rowHeight; } } // Добавляем номера страниц const pageCount = pdf.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { pdf.setPage(i); pdf.setFontSize(9); pdf.text(`Страница ${i} из ${pageCount}`, 105, 287, { align: 'center' }); } // Сохраняем PDF const fileName = `Сравнение_иконок_ИСИХОГИ_${new Date().toISOString().slice(0, 10)}.pdf`; pdf.save(fileName); } catch (imageError) { console.warn('Не удалось создать PDF с изображениями, создаем текстовый PDF:', imageError); // Если не удалось создать PDF с изображениями, создаем текстовый PDF const textPDF = await iconComparisonCreateTextPDF(); const fileName = `Сравнение_иконок_ИСИХОГИ_текст_${new Date().toISOString().slice(0, 10)}.pdf`; textPDF.save(fileName); // Показываем предупреждение alert('Изображения не удалось добавить в PDF из-за ограничений безопасности. Создан текстовый PDF со ссылками на изображения.'); } } catch (error) { console.error('Ошибка при генерации PDF:', error); alert('Произошла ошибка при генерации PDF. Пожалуйста, попробуйте еще раз.'); } finally { exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт в PDF `; loadingOverlay.classList.remove('show'); } }
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { let bestDistribution = null; let bestHeightDiff = Infinity; for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (sortedGroups.length > 0 && hasNoGroup) { const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonAllIcons.length === 0; }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
function iconComparisonSetupExport() { const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.addEventListener('click', function() { if (!this.disabled) { iconComparisonExportToPDF(); } }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupExport(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>Добавлено (2025-12-03, 13:28) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { display: flex; justify-content: center; align-items: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-export-btn { padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.icon-comparison-export-btn:hover { background-color: #218838; transform: translateY(-1px); }
.icon-comparison-export-btn:active { transform: translateY(0); }
.icon-comparison-export-btn:disabled { background-color: #6c757d; cursor: not-allowed; transform: none; }
.icon-comparison-export-btn svg { width: 16px; height: 16px; fill: currentColor; }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; filter: none !important; -webkit-filter: none !important; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
/* Стили для сообщения о загрузке PDF */ .icon-comparison-pdf-loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px 30px; border-radius: 8px; z-index: 10000; display: none; flex-direction: column; align-items: center; gap: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); }
.icon-comparison-pdf-loading.show { display: flex; }
.icon-comparison-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: icon-comparison-spin 1s ease-in-out infinite; }
@keyframes icon-comparison-spin { to { transform: rotate(360deg); } }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-export-btn, [data-theme="dark"] .icon-comparison-export-btn, .dark .icon-comparison-export-btn { background-color: #2e7d32; }
.dark-mode .icon-comparison-export-btn:hover, [data-theme="dark"] .icon-comparison-export-btn:hover, .dark .icon-comparison-export-btn:hover { background-color: #1b5e20; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-search-area { flex-direction: column; align-items: stretch; } .icon-comparison-search { width: 100%; max-width: 100%; } .icon-comparison-export-btn { width: 100%; justify-content: center; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
@media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-export-btn { padding: 8px 12px; font-size: 13px; } }
@media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> <!-- Подключаем библиотеки для генерации PDF --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> <button id="iconComparisonExportBtn" class="icon-comparison-export-btn" disabled> <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт в PDF </button> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<!-- Оверлей для отображения загрузки PDF --> <div id="iconComparisonPdfLoading" class="icon-comparison-pdf-loading"> <div class="icon-comparison-spinner"></div> <div>Идет генерация PDF...</div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для загрузки изображения через прокси для обхода CORS async function iconComparisonLoadImageForPDF(src) { return new Promise((resolve, reject) => { // Если изображение уже загружено и имеет data URL, используем его if (src.startsWith('data:')) { resolve(src); return; } const img = new Image(); img.crossOrigin = 'Anonymous'; // Попытка обойти CORS img.onload = function() { try { // Создаем canvas для конвертации изображения в data URL const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth || img.width; canvas.height = img.naturalHeight || img.height; const ctx = canvas.getContext('2d'); // Обработка BMP изображений (удаление малинового фона) if (src.toLowerCase().endsWith('.bmp')) { ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; // Устанавливаем прозрачность } } ctx.putImageData(imageData, 0, 0); } else { ctx.drawImage(img, 0, 0); } // Получаем data URL const dataURL = canvas.toDataURL('image/png'); resolve(dataURL); } catch (error) { console.warn('Ошибка обработки изображения для PDF:', src, error); // В случае ошибки возвращаем placeholder resolve('data:image/svg+xml;base64,' + btoa(` <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> <rect width="32" height="32" fill="#f5f5f5"/> <text x="16" y="16" text-anchor="middle" dominant-baseline="middle" font-size="10" fill="#999">?</text> </svg> `)); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения для PDF:', src); // Возвращаем placeholder в случае ошибки resolve('data:image/svg+xml;base64,' + btoa(` <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> <rect width="32" height="32" fill="#f5f5f5"/> <text x="16" y="16" text-anchor="middle" dominant-baseline="middle" font-size="10" fill="#999">X</text> </svg> `)); }; // Пытаемся загрузить изображение с разными стратегиями let proxyAttempts = [ src, // Прямая загрузка `https://cors-anywhere.herokuapp.com/${src}`, // CORS прокси `https://api.allorigins.win/raw?url=${encodeURIComponent(src)}` // Альтернативный прокси ]; let attemptIndex = 0;
|
index.html:1 Access to image at 'file:///Z:/GDB/%D0%A0%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0/%D0%A1_%D0%BA%D0%BE%D0%BC%D0%BF%D0%B0_%D0%90%D0%BB%D0%B5%D0%BA%D1%81%D0%B5%D0%B9/Web-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8/%D0%A1%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B8%D0%BA%D0%BE%D0%BD%D0%BE%D0%BA/images/before/SelectGISMethod_no_bg.png' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: chrome, chrome-extension, chrome-untrusted, data, http, https, isolated-app. SelectGISMethod_no_bg.png:1 Failed to load resource: net::ERR_FAILED index.html:686 Попытка загрузки через прокси: https://cors-anywhere.herokuapp.com/images/before/SelectGISMethod_no_bg.png index.html:1 Access to image at 'https://cors-anywhere.herokuapp.com/images/before/SelectGISMethod_no_bg.png' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. SelectGISMethod_no_bg.png:1 Failed to load resource: net::ERR_FAILED
|
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { let bestDistribution = null; let bestHeightDiff = Infinity; for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMSLjU1IzMgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (sortedGroups.length > 0 && hasNoGroup) { const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonAllIcons.length === 0; }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
function iconComparisonSetupExport() { const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.addEventListener('click', function() { if (!this.disabled) { iconComparisonExportToPDF(); } }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupExport(); iconComparisonSetupResizeHandler(); }); </script> </body> </html>Добавлено (2025-12-03, 13:49) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { display: flex; justify-content: center; align-items: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-export-btn { padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.icon-comparison-export-btn:hover { background-color: #218838; transform: translateY(-1px); }
.icon-comparison-export-btn:active { transform: translateY(0); }
.icon-comparison-export-btn:disabled { background-color: #6c757d; cursor: not-allowed; transform: none; }
.icon-comparison-export-btn svg { width: 16px; height: 16px; fill: currentColor; }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; filter: none !important; -webkit-filter: none !important; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
/* Стили для сообщения о загрузке PDF */ .icon-comparison-pdf-loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px 30px; border-radius: 8px; z-index: 10000; display: none; flex-direction: column; align-items: center; gap: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); }
.icon-comparison-pdf-loading.show { display: flex; }
.icon-comparison-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: icon-comparison-spin 1s ease-in-out infinite; }
@keyframes icon-comparison-spin { to { transform: rotate(360deg); } }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-export-btn, [data-theme="dark"] .icon-comparison-export-btn, .dark .icon-comparison-export-btn { background-color: #2e7d32; }
.dark-mode .icon-comparison-export-btn:hover, [data-theme="dark"] .icon-comparison-export-btn:hover, .dark .icon-comparison-export-btn:hover { background-color: #1b5e20; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-search-area { flex-direction: column; align-items: stretch; } .icon-comparison-search { width: 100%; max-width: 100%; } .icon-comparison-export-btn { width: 100%; justify-content: center; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
@media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-export-btn { padding: 8px 12px; font-size: 13px; } }
@media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> <!-- Подключаем библиотеку для генерации PDF --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> <button id="iconComparisonExportBtn" class="icon-comparison-export-btn" disabled> <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт в PDF </button> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<!-- Оверлей для отображения загрузки PDF --> <div id="iconComparisonPdfLoading" class="icon-comparison-pdf-loading"> <div class="icon-comparison-spinner"></div> <div>Идет генерация PDF...</div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для создания текстового PDF async function iconComparisonCreateTextPDF() { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; // Заголовок pdf.setFontSize(16); pdf.setFont('helvetica', 'bold'); pdf.text('Сравнение иконок ИСИХОГИ', 105, 15, { align: 'center' }); // Информация pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); let infoText = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) infoText += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') infoText += ` | Группа: ${groupName}`; pdf.text(infoText, 105, 22, { align: 'center' }); // Дата const now = new Date(); const dateStr = now.toLocaleDateString('ru-RU') + ' ' + now.toLocaleTimeString('ru-RU'); pdf.text(`Экспорт: ${dateStr}`, 105, 29, { align: 'center' }); // Инструкция pdf.setFontSize(9); pdf.setTextColor(100, 100, 100); pdf.text('Для просмотра изображений откройте веб-страницу в браузере', 105, 36, { align: 'center' }); pdf.setTextColor(0, 0, 0); let yPos = 45; const pageWidth = 210; const margin = 10; const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы for (const groupName of sortedGroupNames) { if (yPos > 270) { pdf.addPage(); yPos = 20; } // Заголовок группы pdf.setFontSize(12); pdf.setFont('helvetica', 'bold'); pdf.text(groupName, margin, yPos); yPos += 8; const icons = groups[groupName]; // Добавляем иконки группы pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); // Заголовок таблицы pdf.setFont('helvetica', 'bold'); pdf.text('Название', margin, yPos); pdf.text('Изображение "До"', margin + 70, yPos); pdf.text('Изображение "После"', margin + 120, yPos); yPos += 6; pdf.setFont('helvetica', 'normal'); // Горизонтальная линия под заголовком pdf.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; for (const icon of icons) { if (yPos > 280) { pdf.addPage(); yPos = 20; // Повторяем заголовок на новой странице pdf.setFontSize(12); pdf.setFont('helvetica', 'bold'); pdf.text(groupName, margin, yPos); yPos += 8; pdf.setFontSize(10); pdf.setFont('helvetica', 'bold'); pdf.text('Название', margin, yPos); pdf.text('Изображение "До"', margin + 70, yPos); pdf.text('Изображение "После"', margin + 120, yPos); yPos += 6; pdf.setFont('helvetica', 'normal'); pdf.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; } // Название иконки const nameLines = pdf.splitTextToSize(icon.name, 60); pdf.text(nameLines, margin, yPos); // Ссылки на изображения pdf.setFontSize(9); pdf.setTextColor(0, 0, 255); const beforePath = icon.before; const afterPath = icon.after; // Обрезаем длинные пути для лучшего отображения const maxPathLength = 30; let beforeDisplay = beforePath; if (beforePath.length > maxPathLength) { beforeDisplay = '...' + beforePath.substring(beforePath.length - maxPathLength); } let afterDisplay = afterPath; if (afterPath.length > maxPathLength) { afterDisplay = '...' + afterPath.substring(afterPath.length - maxPathLength); } const beforeLines = pdf.splitTextToSize(beforeDisplay, 40); const afterLines = pdf.splitTextToSize(afterDisplay, 40); pdf.text(beforeLines, margin + 70, yPos); pdf.text(afterLines, margin + 120, yPos); pdf.setTextColor(0, 0, 0); pdf.setFontSize(10); // Вычисляем высоту строки (берем максимальную высоту из всех колонок) const nameHeight = nameLines.length * 5; const beforeHeight = beforeLines.length * 4.5; const afterHeight = afterLines.length * 4.5; const rowHeight = Math.max(nameHeight, beforeHeight, afterHeight) + 2; yPos += rowHeight; // Горизонтальная разделительная линия if (yPos < 280) { pdf.line(margin, yPos - 1, pageWidth - margin, yPos - 1); } } yPos += 10; // Отступ между группами } // Добавляем группу "Прочие" if (noGroup.length > 0) { if (yPos > 260) { pdf.addPage(); yPos = 20; } // Заголовок группы pdf.setFontSize(12); pdf.setFont('helvetica', 'bold'); pdf.text('Прочие иконки', margin, yPos); yPos += 8; // Заголовок таблицы pdf.setFontSize(10); pdf.setFont('helvetica', 'bold'); pdf.text('Название', margin, yPos); pdf.text('Изображение "До"', margin + 70, yPos); pdf.text('Изображение "После"', margin + 120, yPos); yPos += 6; pdf.setFont('helvetica', 'normal'); pdf.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; // Добавляем иконки группы "Прочие" for (const icon of noGroup) { if (yPos > 280) { pdf.addPage(); yPos = 20; // Повторяем заголовок на новой странице pdf.setFontSize(12); pdf.setFont('helvetica', 'bold'); pdf.text('Прочие иконки', margin, yPos); yPos += 8; pdf.setFontSize(10); pdf.setFont('helvetica', 'bold'); pdf.text('Название', margin, yPos); pdf.text('Изображение "До"', margin + 70, yPos); pdf.text('Изображение "После"', margin + 120, yPos); yPos += 6; pdf.setFont('helvetica', 'normal'); pdf.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; } // Название иконки const nameLines = pdf.splitTextToSize(icon.name, 60); pdf.text(nameLines, margin, yPos); // Ссылки на изображения pdf.setFontSize(9); pdf.setTextColor(0, 0, 255); const beforePath = icon.before; const afterPath = icon.after; // Обрезаем длинные пути const maxPathLength = 30; let beforeDisplay = beforePath; if (beforePath.length > maxPathLength) { beforeDisplay = '...' + beforePath.substring(beforePath.length - maxPathLength); } let afterDisplay = afterPath; if (afterPath.length > maxPathLength) { afterDisplay = '...' + afterPath.substring(afterPath.length - maxPathLength); } const beforeLines = pdf.splitTextToSize(beforeDisplay, 40); const afterLines = pdf.splitTextToSize(afterDisplay, 40); pdf.text(beforeLines, margin + 70, yPos); pdf.text(afterLines, margin + 120, yPos); pdf.setTextColor(0, 0, 0); pdf.setFontSize(10); // Вычисляем высоту строки const nameHeight = nameLines.length * 5; const beforeHeight = beforeLines.length * 4.5; const afterHeight = afterLines.length * 4.5; const rowHeight = Math.max(nameHeight, beforeHeight, afterHeight) + 2; yPos += rowHeight; // Горизонтальная разделительная линия if (yPos < 280) { pdf.line(margin, yPos - 1, pageWidth - margin, yPos - 1); } } } // Добавляем номера страниц const pageCount = pdf.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { pdf.setPage(i); pdf.setFontSize(9); pdf.text(`Страница ${i} из ${pageCount}`, 105, 287, { align: 'center' }); } return pdf; }
// Основная функция для экспорта в PDF async function iconComparisonExportToPDF() { const exportBtn = document.getElementById('iconComparisonExportBtn'); const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); try { exportBtn.disabled = true; exportBtn.textContent = 'Генерация...'; loadingOverlay.classList.add('show'); // Создаем текстовый PDF const pdf = await iconComparisonCreateTextPDF(); // Сохраняем PDF const fileName = `Сравнение_иконок_ИСИХОГИ_${new Date().toISOString().slice(0, 10)}.pdf`; pdf.save(fileName); // Показываем информационное сообщение setTimeout(() => { alert('PDF успешно создан!\n\nИзображения не включены в PDF из-за ограничений безопасности при работе с локальными файлами.\n\nДля просмотра изображений используйте веб-страницу в браузере.'); }, 500); } catch (error) { console.error('Ошибка при генерации PDF:', error); alert('Произошла ошибка при генерации PDF. Пожалуйста, попробуйте еще раз.\n\nОшибка: ' + error.message); } finally { exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт в PDF `; loadingOverlay.classList.remove('show'); } }
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; } Добавлено (2025-12-03, 14:04) --------------------------------------------- // Отображаем только имя файла из пути const getFileName = (path) => { try { const parts = path.split('/'); const fileName = parts[parts.length - 1]; if (fileName.length > 30) { return fileName.substring(0, 27) + '...'; } return fileName; } catch (e) { return path; } }; const beforeDisplay = getFileName(beforePath); const afterDisplay = getFileName(afterPath); const beforeLines = pdf.splitTextToSize(beforeDisplay, column2Width - 5); const afterLines = pdf.splitTextToSize(afterDisplay, column3Width - 5); // Добавляем текст в колонки pdf.text(nameLines, margin, yPos); pdf.setFontSize(9); pdf.setTextColor(0, 0, 255); pdf.text(beforeLines, margin + column1Width, yPos); pdf.text(afterLines, margin + column1Width + column2Width, yPos); pdf.setTextColor(0, 0, 0); pdf.setFontSize(10); // Вычисляем высоту строки const lineHeight = 5; const nameHeight = nameLines.length * lineHeight; const beforeHeight = beforeLines.length * (lineHeight - 1); const afterHeight = afterLines.length * (lineHeight - 1); const rowHeight = Math.max(nameHeight, beforeHeight, afterHeight) + 2; // Разделительная линия между строками if (yPos + rowHeight < 280) { pdf.setDrawColor(240, 240, 240); pdf.line(margin, yPos + rowHeight - 1, pageWidth - margin, yPos + rowHeight - 1); } yPos += rowHeight; } } // Добавляем номера страниц const pageCount = pdf.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { pdf.setPage(i); pdf.setFontSize(9); pdf.text(`Страница ${i} из ${pageCount}`, 105, 287, { align: 'center' }); } return pdf; }
// Основная функция для экспорта в PDF async function iconComparisonExportToPDF() { const exportBtn = document.getElementById('iconComparisonExportBtn'); const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); try { exportBtn.disabled = true; exportBtn.textContent = 'Генерация...'; loadingOverlay.classList.add('show'); // Создаем PDF с поддержкой кириллицы const pdf = await iconComparisonCreateTextPDF(); // Сохраняем PDF const fileName = `Сравнение_иконок_ИСИХОГИ_${new Date().toISOString().slice(0, 10)}.pdf`; pdf.save(fileName); // Показываем информационное сообщение setTimeout(() => { alert('PDF успешно создан!\n\nИзображения не включены в PDF из-за ограничений безопасности при работе с локальными файлами.\n\nДля просмотра изображений используйте веб-страницу в браузере.'); }, 500); } catch (error) { console.error('Ошибка при генерации PDF:', error); alert('Произошла ошибка при генерации PDF. Пожалуйста, попробуйте еще раз.\n\nОшибка: ' + error.message); } finally { exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт в PDF `; loadingOverlay.classList.remove('show'); } }
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { let bestDistribution = null; let bestHeightDiff = Infinity; for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (sortedGroups.length > 0 && hasNoGroup) { const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonAllIcons.length === 0; }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
function iconComparisonSetupExport() { const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.addEventListener('click', function() { if (!this.disabled) { iconComparisonExportToPDF(); } }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupExport(); iconComparisonSetupResizeHandler(); }); </script> </body> </html> Добавлено (2025-12-03, 14:04) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { display: flex; justify-content: center; align-items: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-export-btn { padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.icon-comparison-export-btn:hover { background-color: #218838; transform: translateY(-1px); }
.icon-comparison-export-btn:active { transform: translateY(0); }
.icon-comparison-export-btn:disabled { background-color: #6c757d; cursor: not-allowed; transform: none; }
.icon-comparison-export-btn svg { width: 16px; height: 16px; fill: currentColor; }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; filter: none !important; -webkit-filter: none !important; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
/* Стили для сообщения о загрузке PDF */ .icon-comparison-pdf-loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px 30px; border-radius: 8px; z-index: 10000; display: none; flex-direction: column; align-items: center; gap: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); }
.icon-comparison-pdf-loading.show { display: flex; }
.icon-comparison-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: icon-comparison-spin 1s ease-in-out infinite; }
@keyframes icon-comparison-spin { to { transform: rotate(360deg); } }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-export-btn, [data-theme="dark"] .icon-comparison-export-btn, .dark .icon-comparison-export-btn { background-color: #2e7d32; }
.dark-mode .icon-comparison-export-btn:hover, [data-theme="dark"] .icon-comparison-export-btn:hover, .dark .icon-comparison-export-btn:hover { background-color: #1b5e20; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-search-area { flex-direction: column; align-items: stretch; } .icon-comparison-search { width: 100%; max-width: 100%; } .icon-comparison-export-btn { width: 100%; justify-content: center; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
@media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-export-btn { padding: 8px 12px; font-size: 13px; } }
@media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> <!-- Подключаем библиотеку для генерации PDF с поддержкой кириллицы --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <!-- Подключаем шрифт для поддержки кириллицы --> <script src="https://cdn.jsdelivr.net/npm/jspdf-customfonts@latest/dist/jspdf.customfonts.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@mrrio/jspdf/dist/polyfills.umd.js"></script> <script src="https://cdn.jsdelivr.net/npm/@mrrio/jspdf/dist/jspdf.umd.js"></script> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> <button id="iconComparisonExportBtn" class="icon-comparison-export-btn" disabled> <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт в PDF </button> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<!-- Оверлей для отображения загрузки PDF --> <div id="iconComparisonPdfLoading" class="icon-comparison-pdf-loading"> <div class="icon-comparison-spinner"></div> <div>Идет генерация PDF...</div> </div>
<!-- Скрытый элемент для кириллического шрифта --> <div id="cyrillicFont" style="display: none; font-family: 'DejaVu Sans';">АаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя</div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для добавления кириллического шрифта в PDF async function addCyrillicFontToPDF(pdf) { try { // Базовый шрифт с поддержкой кириллицы pdf.setFont("helvetica"); // Если доступны кастомные шрифты, используем их if (pdf.addFont) { try { // Пробуем использовать шрифт DejaVu (поддерживает кириллицу) pdf.addFont('https://cdn.jsdelivr.net/npm/@obvious/jspdf/dist/DejaVuSans.ttf', 'DejaVu', 'normal'); pdf.setFont('DejaVu'); } catch (e) { console.log('Не удалось загрузить шрифт DejaVu, используем стандартный'); } } return pdf; } catch (error) { console.warn('Ошибка при добавлении шрифта:', error); return pdf; } }
// Функция для корректного отображения кириллического текста в PDF function encodeCyrillicText(text) { if (!text) return ''; // Простая замена проблемных символов const replacements = { '№': '№', 'ё': 'е', 'Ё': 'Е', '—': '-', '«': '"', '»': '"', '„': '"', '“': '"' }; let result = text; for (const [key, value] of Object.entries(replacements)) { result = result.replace(new RegExp(key, 'g'), value); } return result; }
// Функция для создания текстового PDF с поддержкой кириллицы async function iconComparisonCreateTextPDF() { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4', compress: true }); // Добавляем поддержку кириллицы await addCyrillicFontToPDF(pdf); const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; // Заголовок pdf.setFontSize(16); pdf.setFont("helvetica", "bold"); const title = encodeCyrillicText('Сравнение иконок ИСИХОГИ'); pdf.text(title, 105, 15, { align: 'center' }); // Информация pdf.setFontSize(10); pdf.setFont("helvetica", "normal"); let infoText = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) infoText += ` | Поиск: "${encodeCyrillicText(searchTerm)}"`; if (groupName !== 'Все группы') infoText += ` | Группа: ${encodeCyrillicText(groupName)}`; pdf.text(infoText, 105, 22, { align: 'center' }); // Дата const now = new Date(); const dateStr = now.toLocaleDateString('ru-RU') + ' ' + now.toLocaleTimeString('ru-RU'); pdf.text(`Экспорт: ${encodeCyrillicText(dateStr)}`, 105, 29, { align: 'center' }); // Инструкция pdf.setFontSize(9); pdf.setTextColor(100, 100, 100); pdf.text('Для просмотра изображений откройте веб-страницу в браузере', 105, 36, { align: 'center' }); pdf.setTextColor(0, 0, 0); let yPos = 45; const pageWidth = 210; const margin = 10; const column1Width = 60; // Название const column2Width = 60; // Изображение "До" const column3Width = 60; // Изображение "После" const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы for (const groupName of sortedGroupNames) { if (yPos > 270) { pdf.addPage(); yPos = 20; await addCyrillicFontToPDF(pdf); } // Заголовок группы pdf.setFontSize(12); pdf.setFont("helvetica", "bold"); pdf.text(encodeCyrillicText(groupName), margin, yPos); yPos += 8; const icons = groups[groupName]; // Заголовок таблицы pdf.setFontSize(10); pdf.setFont("helvetica", "bold"); pdf.text('Название', margin, yPos); pdf.text('Изображение "До"', margin + column1Width, yPos); pdf.text('Изображение "После"', margin + column1Width + column2Width, yPos); yPos += 6; // Горизонтальная линия под заголовком pdf.setDrawColor(200, 200, 200); pdf.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; pdf.setFont("helvetica", "normal"); for (const icon of icons) { if (yPos > 280) { pdf.addPage(); yPos = 20; await addCyrillicFontToPDF(pdf); // Повторяем заголовок на новой странице pdf.setFontSize(12); pdf.setFont("helvetica", "bold"); pdf.text(encodeCyrillicText(groupName), margin, yPos); yPos += 8; pdf.setFontSize(10); pdf.setFont("helvetica", "bold"); pdf.text('Название', margin, yPos); pdf.text('Изображение "До"', margin + column1Width, yPos); pdf.text('Изображение "После"', margin + column1Width + column2Width, yPos); yPos += 6; pdf.setDrawColor(200, 200, 200); pdf.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; pdf.setFont("helvetica", "normal"); } // Название иконки const iconName = encodeCyrillicText(icon.name); const nameLines = pdf.splitTextToSize(iconName, column1Width - 5); // Пути к изображениям (обрезаем для отображения) const beforePath = encodeCyrillicText(icon.before); const afterPath = encodeCyrillicText(icon.after); // Отображаем только имя файла из пути const getFileName = (path) => { try { const parts = path.split('/'); const fileName = parts[parts.length - 1]; // Обрезаем длинные имена файлов if (fileName.length > 30) { return fileName.substring(0, 27) + '...'; } return fileName; } catch (e) { return path; } }; const beforeDisplay = getFileName(beforePath); const afterDisplay = getFileName(afterPath); const beforeLines = pdf.splitTextToSize(beforeDisplay, column2Width - 5); const afterLines = pdf.splitTextToSize(afterDisplay, column3Width - 5); // Добавляем текст в колонки pdf.text(nameLines, margin, yPos); pdf.setFontSize(9); pdf.setTextColor(0, 0, 255); pdf.text(beforeLines, margin + column1Width, yPos); pdf.text(afterLines, margin + column1Width + column2Width, yPos); pdf.setTextColor(0, 0, 0); pdf.setFontSize(10); // Вычисляем высоту строки (берем максимальную высоту из всех колонок) const lineHeight = 5; const nameHeight = nameLines.length * lineHeight; const beforeHeight = beforeLines.length * (lineHeight - 1); const afterHeight = afterLines.length * (lineHeight - 1); const rowHeight = Math.max(nameHeight, beforeHeight, afterHeight) + 2; // Разделительная линия между строками if (yPos + rowHeight < 280) { pdf.setDrawColor(240, 240, 240); pdf.line(margin, yPos + rowHeight - 1, pageWidth - margin, yPos + rowHeight - 1); } yPos += rowHeight; } yPos += 10; // Отступ между группами } // Добавляем группу "Прочие" if (noGroup.length > 0) { if (yPos > 260) { pdf.addPage(); yPos = 20; await addCyrillicFontToPDF(pdf); } // Заголовок группы pdf.setFontSize(12); pdf.setFont("helvetica", "bold"); pdf.text('Прочие иконки', margin, yPos); yPos += 8; // Заголовок таблицы pdf.setFontSize(10); pdf.setFont("helvetica", "bold"); pdf.text('Название', margin, yPos); pdf.text('Изображение "До"', margin + column1Width, yPos); pdf.text('Изображение "После"', margin + column1Width + column2Width, yPos); yPos += 6; pdf.setDrawColor(200, 200, 200); pdf.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; pdf.setFont("helvetica", "normal"); // Добавляем иконки группы "Прочие" for (const icon of noGroup) { if (yPos > 280) { pdf.addPage(); yPos = 20; await addCyrillicFontToPDF(pdf); // Повторяем заголовок на новой странице pdf.setFontSize(12); pdf.setFont("helvetica", "bold"); pdf.text('Прочие иконки', margin, yPos); yPos += 8; pdf.setFontSize(10); pdf.setFont("helvetica", "bold"); pdf.text('Название', margin, yPos); pdf.text('Изображение "До"', margin + column1Width, yPos); pdf.text('Изображение "После"', margin + column1Width + column2Width, yPos); yPos += 6; pdf.setDrawColor(200, 200, 200); pdf.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; pdf.setFont("helvetica", "normal"); } // Название иконки const iconName = encodeCyrillicText(icon.name); const nameLines = pdf.splitTextToSize(iconName, column1Width - 5); // Пути к изображениям const beforePath = encodeCyrillicText(icon.before); const afterPath = encodeCyrillicText(icon.after); Добавлено (2025-12-03, 14:27) --------------------------------------------- // Заголовок группы const groupDiv = document.createElement('div'); groupDiv.className = 'print-group'; const groupTitle = document.createElement('h2'); groupTitle.className = 'print-group-title'; groupTitle.textContent = groupName; groupDiv.appendChild(groupTitle); // Таблица для иконок группы const table = document.createElement('table'); table.className = 'print-icons-table'; // Заголовок таблицы const thead = document.createElement('thead'); thead.innerHTML = ` <tr> <th style="width: 50px;">ДО</th> <th style="width: 20px;"></th> <th style="width: 50px;">ПОСЛЕ</th> <th style="text-align: left;">Название иконки</th> </tr> `; table.appendChild(thead); const tbody = document.createElement('tbody'); // Добавляем иконки в таблицу icons.forEach(icon => { const row = document.createElement('tr'); // Ячейка "ДО" const beforeCell = document.createElement('td'); beforeCell.style.textAlign = 'center'; beforeCell.style.padding = '4px'; const beforeImg = document.createElement('img'); beforeImg.className = 'print-icon-image'; beforeImg.src = icon.before; beforeImg.alt = `${icon.name} (до)`; beforeImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> <rect width="40" height="40" fill="#f5f5f5"/> <text x="20" y="22" text-anchor="middle" font-family="Arial" font-size="12" fill="#999">?</text> </svg> `); }; beforeCell.appendChild(beforeImg); // Ячейка стрелки const arrowCell = document.createElement('td'); arrowCell.className = 'print-arrow'; arrowCell.textContent = '→'; // Ячейка "ПОСЛЕ" const afterCell = document.createElement('td'); afterCell.style.textAlign = 'center'; afterCell.style.padding = '4px'; const afterImg = document.createElement('img'); afterImg.className = 'print-icon-image'; afterImg.src = icon.after; afterImg.alt = `${icon.name} (после)`; afterImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> <rect width="40" height="40" fill="#f5f5f5"/> <text x="20" y="22" text-anchor="middle" font-family="Arial" font-size="12" fill="#999">?</text> </svg> `); }; afterCell.appendChild(afterImg); // Ячейка названия const nameCell = document.createElement('td'); nameCell.className = 'print-icon-name'; nameCell.textContent = icon.name; nameCell.style.paddingLeft = '10px'; row.appendChild(beforeCell); row.appendChild(arrowCell); row.appendChild(afterCell); row.appendChild(nameCell); tbody.appendChild(row); }); table.appendChild(tbody); groupDiv.appendChild(table); currentPage.appendChild(groupDiv); currentPageRows += icons.length + 2; // +2 для заголовка группы и отступов } // Добавляем группу "Прочие" if (noGroup.length > 0) { // Проверяем, нужно ли начинать новую страницу if (currentPageRows + noGroup.length + 2 > maxRowsPerPage && currentPageRows > 0) { // Добавляем футер на текущую страницу const footer = document.createElement('div'); footer.className = 'print-footer'; footer.textContent = `Страница ${document.querySelectorAll('.print-page').length}`; currentPage.appendChild(footer); // Создаем новую страницу currentPage = document.createElement('div'); currentPage.className = 'print-page'; printContainer.appendChild(currentPage); currentPageRows = 0; } // Заголовок группы "Прочие" const groupDiv = document.createElement('div'); groupDiv.className = 'print-group'; const groupTitle = document.createElement('h2'); groupTitle.className = 'print-group-title'; groupTitle.textContent = 'Прочие иконки'; groupDiv.appendChild(groupTitle); // Таблица для иконок группы "Прочие" const table = document.createElement('table'); table.className = 'print-icons-table'; // Заголовок таблицы const thead = document.createElement('thead'); thead.innerHTML = ` <tr> <th style="width: 50px;">ДО</th> <th style="width: 20px;"></th> <th style="width: 50px;">ПОСЛЕ</th> <th style="text-align: left;">Название иконки</th> </tr> `; table.appendChild(thead); const tbody = document.createElement('tbody'); // Добавляем иконки в таблицу noGroup.forEach(icon => { const row = document.createElement('tr'); // Ячейка "ДО" const beforeCell = document.createElement('td'); beforeCell.style.textAlign = 'center'; beforeCell.style.padding = '4px'; const beforeImg = document.createElement('img'); beforeImg.className = 'print-icon-image'; beforeImg.src = icon.before; beforeImg.alt = `${icon.name} (до)`; beforeImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> <rect width="40" height="40" fill="#f5f5f5"/> <text x="20" y="22" text-anchor="middle" font-family="Arial" font-size="12" fill="#999">?</text> </svg> `); }; beforeCell.appendChild(beforeImg); // Ячейка стрелки const arrowCell = document.createElement('td'); arrowCell.className = 'print-arrow'; arrowCell.textContent = '→'; // Ячейка "ПОСЛЕ" const afterCell = document.createElement('td'); afterCell.style.textAlign = 'center'; afterCell.style.padding = '4px'; const afterImg = document.createElement('img'); afterImg.className = 'print-icon-image'; afterImg.src = icon.after; afterImg.alt = `${icon.name} (после)`; afterImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> <rect width="40" height="40" fill="#f5f5f5"/> <text x="20" y="22" text-anchor="middle" font-family="Arial" font-size="12" fill="#999">?</text> </svg> `); }; afterCell.appendChild(afterImg); // Ячейка названия const nameCell = document.createElement('td'); nameCell.className = 'print-icon-name'; nameCell.textContent = icon.name; nameCell.style.paddingLeft = '10px'; row.appendChild(beforeCell); row.appendChild(arrowCell); row.appendChild(afterCell); row.appendChild(nameCell); tbody.appendChild(row); }); table.appendChild(tbody); groupDiv.appendChild(table); currentPage.appendChild(groupDiv); } // Добавляем футер на последнюю страницу const totalPages = document.querySelectorAll('.print-page').length; const lastPage = document.querySelectorAll('.print-page')[totalPages - 1]; const footer = document.createElement('div'); footer.className = 'print-footer'; footer.textContent = `Страница ${totalPages} из ${totalPages} | Всего иконок: ${iconComparisonFilteredIcons.length}`; lastPage.appendChild(footer); // Даем время на загрузку изображений setTimeout(() => { // Открываем диалог печати window.print(); // Удаляем временный контейнер document.body.removeChild(printContainer); // Восстанавливаем кнопку exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать `; loadingOverlay.classList.remove('show'); // Показываем инструкцию setTimeout(() => { alert('Открыт диалог печати.\n\nДля сохранения в PDF:\n1. В диалоге печати выберите "Сохранить как PDF"\n2. Установите параметры:\n - Размер бумаги: A4\n - Поля: минимальные\n - Масштаб: 100%\n3. Нажмите "Сохранить"\n\nДля печати на бумаге:\n1. Выберите принтер\n2. Нажмите "Печать"'); }, 100); }, 1000); } catch (error) { console.error('Ошибка при подготовке к печати:', error); alert('Произошла ошибка при подготовке к печати. Пожалуйста, попробуйте еще раз.\n\nОшибка: ' + error.message); // Восстанавливаем кнопку const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать `; const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); loadingOverlay.classList.remove('show'); } }
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { let bestDistribution = null; let bestHeightDiff = Infinity; for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (sortedGroups.length > 0 && hasNoGroup) { const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonAllIcons.length === 0; }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
function iconComparisonSetupExport() { const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.addEventListener('click', function() { if (!this.disabled) { iconComparisonCreatePrintPage(); } }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupExport(); iconComparisonSetupResizeHandler(); }); </script> </body> </html> Добавлено (2025-12-03, 14:28) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { display: flex; justify-content: center; align-items: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-export-btn { padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.icon-comparison-export-btn:hover { background-color: #218838; transform: translateY(-1px); }
.icon-comparison-export-btn:active { transform: translateY(0); }
.icon-comparison-export-btn:disabled { background-color: #6c757d; cursor: not-allowed; transform: none; }
.icon-comparison-export-btn svg { width: 16px; height: 16px; fill: currentColor; }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; filter: none !important; -webkit-filter: none !important; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
/* Стили для сообщения о загрузке PDF */ .icon-comparison-pdf-loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px 30px; border-radius: 8px; z-index: 10000; display: none; flex-direction: column; align-items: center; gap: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); }
.icon-comparison-pdf-loading.show { display: flex; }
.icon-comparison-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: icon-comparison-spin 1s ease-in-out infinite; }
@keyframes icon-comparison-spin { to { transform: rotate(360deg); } }
/* Стили для страницы печати */ @media print { body * { visibility: hidden; } #icon-comparison-print-content, #icon-comparison-print-content * { visibility: visible; } #icon-comparison-print-content { position: absolute; left: 0; top: 0; width: 100%; padding: 20px; background: white; font-family: Arial, sans-serif; } .print-page { page-break-after: always; padding: 20px; } .print-page:last-child { page-break-after: avoid; } .print-header { text-align: center; margin-bottom: 20px; border-bottom: 2px solid #333; padding-bottom: 10px; } .print-title { font-size: 24px; font-weight: bold; margin-bottom: 10px; } .print-subtitle { font-size: 14px; color: #666; margin-bottom: 5px; } .print-group { margin-bottom: 20px; page-break-inside: avoid; } .print-group-title { font-size: 18px; font-weight: bold; background-color: #f5f5f5; padding: 8px 12px; border-radius: 4px; margin-bottom: 10px; border-left: 4px solid #007bff; } .print-icons-table { width: 100%; border-collapse: collapse; margin-bottom: 15px; } .print-icons-table th { background-color: #f8f9fa; padding: 8px; text-align: center; border-bottom: 2px solid #dee2e6; font-size: 12px; font-weight: bold; } .print-icons-table td { padding: 8px; border-bottom: 1px solid #dee2e6; vertical-align: middle; } .print-icon-name { font-size: 12px; font-weight: normal; } .print-icon-image { width: 40px; height: 40px; object-fit: contain; display: block; margin: 0 auto; background-color: #f5f5f5; padding: 2px; border-radius: 3px; } .print-arrow { text-align: center; font-weight: bold; font-size: 14px; } .print-footer { text-align: center; font-size: 10px; color: #666; margin-top: 20px; padding-top: 10px; border-top: 1px solid #ddd; } }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-export-btn, [data-theme="dark"] .icon-comparison-export-btn, .dark .icon-comparison-export-btn { background-color: #2e7d32; }
.dark-mode .icon-comparison-export-btn:hover, [data-theme="dark"] .icon-comparison-export-btn:hover, .dark .icon-comparison-export-btn:hover { background-color: #1b5e20; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-search-area { flex-direction: column; align-items: stretch; } .icon-comparison-search { width: 100%; max-width: 100%; } .icon-comparison-export-btn { width: 100%; justify-content: center; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
@media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-export-btn { padding: 8px 12px; font-size: 13px; } }
@media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> <button id="iconComparisonExportBtn" class="icon-comparison-export-btn" disabled> <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать </button> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<!-- Оверлей для отображения загрузки PDF --> <div id="iconComparisonPdfLoading" class="icon-comparison-pdf-loading"> <div class="icon-comparison-spinner"></div> <div>Подготовка к печати...</div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для создания страницы для печати/экспорта function iconComparisonCreatePrintPage() { const exportBtn = document.getElementById('iconComparisonExportBtn'); const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); try { exportBtn.disabled = true; exportBtn.textContent = 'Подготовка...'; loadingOverlay.classList.add('show'); // Создаем временный контейнер для печати const printContainer = document.createElement('div'); printContainer.id = 'icon-comparison-print-content'; printContainer.style.position = 'absolute'; printContainer.style.left = '-9999px'; printContainer.style.top = '0'; printContainer.style.width = '210mm'; // A4 ширина printContainer.style.padding = '20px'; printContainer.style.backgroundColor = 'white'; printContainer.style.fontFamily = 'Arial, sans-serif'; document.body.appendChild(printContainer); const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Заголовок документа const header = document.createElement('div'); header.className = 'print-header'; const title = document.createElement('h1'); title.className = 'print-title'; title.textContent = 'Сравнение иконок ИСИХОГИ'; header.appendChild(title); const subtitle = document.createElement('div'); subtitle.className = 'print-subtitle'; let subtitleText = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) subtitleText += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') subtitleText += ` | Группа: ${groupName}`; subtitle.textContent = subtitleText; header.appendChild(subtitle); const date = document.createElement('div'); date.className = 'print-subtitle'; const now = new Date(); date.textContent = `Дата экспорта: ${now.toLocaleDateString('ru-RU')} ${now.toLocaleTimeString('ru-RU')}`; header.appendChild(date); printContainer.appendChild(header); // Расчет максимального количества строк на страницу А4 const maxRowsPerPage = 25; // Примерное количество строк на страницу let currentPageRows = 0; let currentPage = document.createElement('div'); currentPage.className = 'print-page'; printContainer.appendChild(currentPage); // Добавляем обычные группы for (const groupName of sortedGroupNames) { const icons = groups[groupName]; // Если группа большая, проверяем, нужно ли начинать новую страницу if (currentPageRows + icons.length + 2 > maxRowsPerPage && currentPageRows > 0) { // Добавляем футер на текущую страницу const footer = document.createElement('div'); footer.className = 'print-footer'; footer.textContent = `Страница ${document.querySelectorAll('.print-page').length}`; currentPage.appendChild(footer); // Создаем новую страницу currentPage = document.createElement('div'); currentPage.className = 'print-page'; printContainer.appendChild(currentPage); currentPageRows = 0; }
Добавлено (2025-12-03, 14:46) --------------------------------------------- function iconComparisonCreatePrintContent() { const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Создаем контейнер для печати const printContainer = document.createElement('div'); printContainer.className = 'print-container'; // Рассчитываем количество иконок на страницу (примерно 20-25 на колонку) const iconsPerColumn = 15; const iconsPerPage = iconsPerColumn * 2; // 2 колонки // Собираем все иконки для печати const allIconsForPrint = []; // Добавляем иконки из обычных групп for (const groupName of sortedGroupNames) { const icons = groups[groupName]; allIconsForPrint.push({ type: 'group', name: groupName, icons: icons }); } // Добавляем группу "Прочие" if (noGroup.length > 0) { allIconsForPrint.push({ type: 'group', name: 'Прочие иконки', icons: noGroup }); } // Разбиваем на страницы let currentPageIcons = 0; let currentPageGroups = []; let currentGroupIndex = 0; let currentIconIndex = 0; const pages = []; while (currentGroupIndex < allIconsForPrint.length) { const currentGroup = allIconsForPrint[currentGroupIndex]; // Если это начало страницы или группа помещается полностью if (currentPageIcons === 0 || (currentPageIcons + currentGroup.icons.length - currentIconIndex) <= iconsPerPage) { // Добавляем группу (полностью или частично) const remainingIcons = currentGroup.icons.length - currentIconIndex; const iconsToAdd = Math.min(remainingIcons, iconsPerPage - currentPageIcons); const groupCopy = { type: currentGroup.type, name: currentGroup.name, icons: currentGroup.icons.slice(currentIconIndex, currentIconIndex + iconsToAdd) }; currentPageGroups.push(groupCopy); currentPageIcons += iconsToAdd; currentIconIndex += iconsToAdd; // Если группа закончилась, переходим к следующей if (currentIconIndex >= currentGroup.icons.length) { currentGroupIndex++; currentIconIndex = 0; } // Если страница заполнена, создаем новую if (currentPageIcons >= iconsPerPage) { pages.push([...currentPageGroups]); currentPageGroups = []; currentPageIcons = 0; } } else { // Группа не помещается, создаем новую страницу pages.push([...currentPageGroups]); currentPageGroups = []; currentPageIcons = 0; } } // Добавляем последнюю страницу, если в ней есть данные if (currentPageGroups.length > 0) { pages.push(currentPageGroups); } // Создаем страницы для печати pages.forEach((pageGroups, pageIndex) => { const page = document.createElement('div'); page.className = 'print-page'; // Заголовок страницы const header = document.createElement('div'); header.className = 'print-header'; const title = document.createElement('h1'); title.className = 'print-title'; title.textContent = 'Сравнение иконок ИСИХОГИ'; header.appendChild(title); const subtitle = document.createElement('div'); subtitle.className = 'print-subtitle'; let subtitleText = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) subtitleText += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') subtitleText += ` | Группа: ${groupName}`; subtitle.textContent = subtitleText; header.appendChild(subtitle); const date = document.createElement('div'); date.className = 'print-subtitle'; const now = new Date(); date.textContent = `Дата экспорта: ${now.toLocaleDateString('ru-RU')} ${now.toLocaleTimeString('ru-RU')}`; header.appendChild(date); page.appendChild(header); // Разделяем группы на две колонки const twoColumns = document.createElement('div'); twoColumns.className = 'print-two-columns'; const leftColumn = document.createElement('div'); leftColumn.className = 'print-column'; const rightColumn = document.createElement('div'); rightColumn.className = 'print-column'; // Распределяем группы по колонкам let currentColumn = leftColumn; let columnIconCount = 0; const maxIconsPerColumn = iconsPerColumn; pageGroups.forEach(group => { // Если текущая колонка заполнена, переключаемся на правую if (columnIconCount + group.icons.length > maxIconsPerColumn && currentColumn === leftColumn) { twoColumns.appendChild(leftColumn); currentColumn = rightColumn; columnIconCount = 0; } // Создаем группу const groupDiv = document.createElement('div'); groupDiv.className = 'print-group'; const groupTitle = document.createElement('h2'); groupTitle.className = 'print-group-title'; groupTitle.textContent = group.name; groupDiv.appendChild(groupTitle); // Добавляем иконки в группу group.icons.forEach(icon => { const iconItem = document.createElement('div'); iconItem.className = 'print-icon-item'; // Контейнер для изображений const imagesContainer = document.createElement('div'); imagesContainer.className = 'print-icon-images'; // Изображение "ДО" const beforeImg = document.createElement('img'); beforeImg.className = 'print-icon-image'; beforeImg.src = icon.before; beforeImg.alt = `${icon.name} (до)`; beforeImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="48" height="48" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <rect width="48" height="48" fill="#f5f5f5"/> <text x="24" y="28" text-anchor="middle" font-family="Arial" font-size="14" fill="#999">ДО</text> </svg> `); }; // Стрелка const arrow = document.createElement('div'); arrow.className = 'print-arrow'; arrow.textContent = '→'; // Изображение "ПОСЛЕ" const afterImg = document.createElement('img'); afterImg.className = 'print-icon-image'; afterImg.src = icon.after; afterImg.alt = `${icon.name} (после)`; afterImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="48" height="48" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <rect width="48" height="48" fill="#f5f5f5"/> <text x="24" y="28" text-anchor="middle" font-family="Arial" font-size="14" fill="#999">ПОСЛЕ</text> </svg> `); }; imagesContainer.appendChild(beforeImg); imagesContainer.appendChild(arrow); imagesContainer.appendChild(afterImg); // Информация об иконке const infoContainer = document.createElement('div'); infoContainer.className = 'print-icon-info'; const name = document.createElement('div'); name.className = 'print-icon-name'; name.textContent = icon.name; const path = document.createElement('div'); path.className = 'print-icon-path'; path.textContent = `${icon.before} → ${icon.after}`; infoContainer.appendChild(name); infoContainer.appendChild(path); iconItem.appendChild(imagesContainer); iconItem.appendChild(infoContainer); groupDiv.appendChild(iconItem); }); currentColumn.appendChild(groupDiv); columnIconCount += group.icons.length; }); // Добавляем правую колонку, если в ней есть данные if (rightColumn.children.length > 0) { twoColumns.appendChild(rightColumn); } else if (leftColumn.children.length > 0) { // Если только левая колонка, добавляем ее twoColumns.appendChild(leftColumn); } page.appendChild(twoColumns); // Футер страницы const footer = document.createElement('div'); footer.className = 'print-footer'; footer.textContent = 'Сравнение иконок ИСИХОГИ - Старая и новая версии'; page.appendChild(footer); // Номер страницы const pageNumber = document.createElement('div'); pageNumber.className = 'print-page-number'; pageNumber.textContent = `Страница ${pageIndex + 1} из ${pages.length}`; page.appendChild(pageNumber); printContainer.appendChild(page); }); return printContainer; }
// Функция для показа предпросмотра печати function iconComparisonShowPrintPreview() { const exportBtn = document.getElementById('iconComparisonExportBtn'); const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); try { exportBtn.disabled = true; exportBtn.textContent = 'Подготовка...'; loadingOverlay.classList.add('show'); // Создаем контент для печати const printContent = iconComparisonCreatePrintContent(); // Вставляем в модальное окно предпросмотра const previewBody = document.getElementById('printPreviewBody'); previewBody.innerHTML = ''; previewBody.appendChild(printContent); // Показываем модальное окно const previewModal = document.getElementById('iconComparisonPrintPreview'); previewModal.classList.add('show'); // Даем время на загрузку изображений setTimeout(() => { // Восстанавливаем кнопку exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать `; loadingOverlay.classList.remove('show'); }, 500); } catch (error) { console.error('Ошибка при подготовке к печати:', error); alert('Произошла ошибка при подготовке к печати. Пожалуйста, попробуйте еще раз.\n\nОшибка: ' + error.message); // Восстанавливаем кнопку const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать `; const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); loadingOverlay.classList.remove('show'); } }
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { let bestDistribution = null; let bestHeightDiff = Infinity; for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE8IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (sortedGroups.length > 0 && hasNoGroup) { const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonAllIcons.length === 0; }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
function iconComparisonSetupExport() { const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.addEventListener('click', function() { if (!this.disabled) { iconComparisonShowPrintPreview(); } }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupExport(); iconComparisonSetupResizeHandler(); }); </script> </body> </html> Добавлено (2025-12-03, 14:47) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { display: flex; justify-content: center; align-items: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-export-btn { padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.icon-comparison-export-btn:hover { background-color: #218838; transform: translateY(-1px); }
.icon-comparison-export-btn:active { transform: translateY(0); }
.icon-comparison-export-btn:disabled { background-color: #6c757d; cursor: not-allowed; transform: none; }
.icon-comparison-export-btn svg { width: 16px; height: 16px; fill: currentColor; }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; filter: none !important; -webkit-filter: none !important; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
/* Стили для сообщения о загрузке PDF */ .icon-comparison-pdf-loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px 30px; border-radius: 8px; z-index: 10000; display: none; flex-direction: column; align-items: center; gap: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); }
.icon-comparison-pdf-loading.show { display: flex; }
.icon-comparison-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: icon-comparison-spin 1s ease-in-out infinite; }
@keyframes icon-comparison-spin { to { transform: rotate(360deg); } }
/* Стили для страницы печати */ @media print { body * { visibility: hidden; } .print-container, .print-container * { visibility: visible; } .print-container { position: absolute; left: 0; top: 0; width: 100%; background: white; font-family: Arial, sans-serif; padding: 0 !important; margin: 0 !important; } .print-page { width: 210mm; min-height: 297mm; padding: 15mm; margin: 0 auto; page-break-after: always; box-sizing: border-box; } .print-page:last-child { page-break-after: avoid; } .print-header { text-align: center; margin-bottom: 10mm; border-bottom: 1px solid #333; padding-bottom: 5mm; } .print-title { font-size: 20pt; font-weight: bold; margin-bottom: 3mm; } .print-subtitle { font-size: 10pt; color: #666; margin-bottom: 2mm; } .print-two-columns { display: flex; gap: 10mm; margin-bottom: 10mm; } .print-column { flex: 1; width: 50%; } .print-group { margin-bottom: 8mm; page-break-inside: avoid; } .print-group-title { font-size: 14pt; font-weight: bold; background-color: #f5f5f5; padding: 3mm 4mm; border-radius: 2mm; margin-bottom: 4mm; border-left: 3mm solid #007bff; } .print-icon-item { display: flex; align-items: center; margin-bottom: 3mm; padding: 2mm; border-bottom: 1px dashed #eee; page-break-inside: avoid; } .print-icon-images { display: flex; align-items: center; gap: 2mm; min-width: 45mm; flex-shrink: 0; } .print-icon-image { width: 12mm; height: 12mm; object-fit: contain; display: block; background-color: #f5f5f5; padding: 1mm; border-radius: 1mm; border: 1px solid #ddd; } .print-arrow { font-weight: bold; font-size: 10pt; color: #666; margin: 0 1mm; } .print-icon-info { flex: 1; padding-left: 3mm; } .print-icon-name { font-size: 10pt; font-weight: bold; margin-bottom: 1mm; word-break: break-word; } .print-icon-path { font-size: 8pt; color: #666; font-family: 'Courier New', monospace; word-break: break-all; } .print-footer { text-align: center; font-size: 8pt; color: #666; margin-top: 10mm; padding-top: 3mm; border-top: 1px solid #ddd; position: absolute; bottom: 15mm; left: 15mm; right: 15mm; } .print-page-number { position: absolute; bottom: 10mm; right: 15mm; font-size: 9pt; color: #666; } /* Скрываем ненужные элементы при печати */ .no-print { display: none !important; } }
/* Предпросмотр печати */ .print-preview-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 10001; display: none; justify-content: center; align-items: center; } .print-preview-modal.show { display: flex; } .print-preview-content { background: white; width: 90%; height: 90%; border-radius: 8px; display: flex; flex-direction: column; overflow: hidden; } .print-preview-header { padding: 15px; background: #f8f9fa; border-bottom: 1px solid #dee2e6; display: flex; justify-content: space-between; align-items: center; } .print-preview-title { font-size: 18px; font-weight: bold; } .print-preview-close { background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; } .print-preview-body { flex: 1; overflow: auto; padding: 20px; background: #f5f5f5; } .print-preview-actions { padding: 15px; background: #f8f9fa; border-top: 1px solid #dee2e6; display: flex; gap: 10px; justify-content: flex-end; } .print-preview-btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; } .print-preview-btn.print { background: #28a745; color: white; } .print-preview-btn.cancel { background: #6c757d; color: white; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-export-btn, [data-theme="dark"] .icon-comparison-export-btn, .dark .icon-comparison-export-btn { background-color: #2e7d32; }
.dark-mode .icon-comparison-export-btn:hover, [data-theme="dark"] .icon-comparison-export-btn:hover, .dark .icon-comparison-export-btn:hover { background-color: #1b5e20; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-search-area { flex-direction: column; align-items: stretch; } .icon-comparison-search { width: 100%; max-width: 100%; } .icon-comparison-export-btn { width: 100%; justify-content: center; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } .print-preview-content { width: 100%; height: 100%; border-radius: 0; } }
@media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-export-btn { padding: 8px 12px; font-size: 13px; } @media print { .print-two-columns { flex-direction: column; gap: 5mm; } .print-column { width: 100%; } .print-icon-images { min-width: 35mm; } .print-icon-image { width: 10mm; height: 10mm; } } }
@media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> <button id="iconComparisonExportBtn" class="icon-comparison-export-btn" disabled> <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать </button> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<!-- Оверлей для отображения загрузки --> <div id="iconComparisonPdfLoading" class="icon-comparison-pdf-loading"> <div class="icon-comparison-spinner"></div> <div>Подготовка к печати...</div> </div>
<!-- Модальное окно предпросмотра --> <div id="iconComparisonPrintPreview" class="print-preview-modal"> <div class="print-preview-content"> <div class="print-preview-header"> <div class="print-preview-title">Предпросмотр печати</div> <button class="print-preview-close" onclick="iconComparisonClosePrintPreview()">✕ Закрыть</button> </div> <div class="print-preview-body" id="printPreviewBody"> <!-- Сюда будет вставлен предпросмотр --> </div> <div class="print-preview-actions"> <button class="print-preview-btn cancel" onclick="iconComparisonClosePrintPreview()">Отмена</button> <button class="print-preview-btn print" onclick="iconComparisonPrintFromPreview()">Печать</button> </div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для закрытия предпросмотра function iconComparisonClosePrintPreview() { const previewModal = document.getElementById('iconComparisonPrintPreview'); previewModal.classList.remove('show'); }
// Функция для печати из предпросмотра function iconComparisonPrintFromPreview() { window.print(); }
// Функция для создания контента для печати (двухколоночный макет) Добавлено (2025-12-03, 15:11) --------------------------------------------- // Функция для расчета высоты группы function calculateGroupHeight(iconsCount) { // Приблизительная высота: заголовок группы + иконки const groupHeaderHeight = 10; // mm const iconHeight = 15; // mm на иконку return groupHeaderHeight + (iconsCount * iconHeight); } const maxPageHeight = 250; // mm (297mm - отступы) while (currentGroupIndex < allIconsForPrint.length) { const currentGroup = allIconsForPrint[currentGroupIndex]; // Проверяем, помещается ли вся группа на текущую страницу const groupRemainingIcons = currentGroup.icons.length - currentIconIndex; const groupRemainingHeight = calculateGroupHeight(groupRemainingIcons); if (currentPageIcons === 0 || (calculateGroupHeight(currentPageIcons) + groupRemainingHeight) <= maxPageHeight) { // Группа помещается полностью или частично const remainingHeight = maxPageHeight - calculateGroupHeight(currentPageIcons); const maxIconsForRemainingHeight = Math.floor((remainingHeight - 10) / 15); // 10mm на заголовок const iconsToAdd = Math.min(groupRemainingIcons, maxIconsForRemainingHeight); if (iconsToAdd > 0) { const groupCopy = { type: currentGroup.type, name: currentGroup.name, icons: currentGroup.icons.slice(currentIconIndex, currentIconIndex + iconsToAdd) }; currentPageGroups.push(groupCopy); currentPageIcons += iconsToAdd; currentIconIndex += iconsToAdd; // Если группа закончилась, переходим к следующей if (currentIconIndex >= currentGroup.icons.length) { currentGroupIndex++; currentIconIndex = 0; } } // Если страница заполнена или группа не помещается, создаем новую страницу if (currentPageIcons >= iconsPerPage || (groupRemainingIcons > 0 && iconsToAdd === 0)) { if (currentPageGroups.length > 0) { pages.push([...currentPageGroups]); } currentPageGroups = []; currentPageIcons = 0; } } else { // Группа не помещается, создаем новую страницу if (currentPageGroups.length > 0) { pages.push([...currentPageGroups]); } currentPageGroups = []; currentPageIcons = 0; } } // Добавляем последнюю страницу, если в ней есть данные if (currentPageGroups.length > 0) { pages.push(currentPageGroups); } // Создаем страницы для печати pages.forEach((pageGroups, pageIndex) => { const page = document.createElement('div'); page.className = 'print-page'; // Заголовок страницы - ТОЛЬКО НА ПЕРВОЙ СТРАНИЦЕ if (pageIndex === 0) { const header = document.createElement('div'); header.className = 'print-header'; const title = document.createElement('h1'); title.className = 'print-title'; title.textContent = 'Сравнение иконок ИСИХОГИ'; header.appendChild(title); const subtitle = document.createElement('div'); subtitle.className = 'print-subtitle'; let subtitleText = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) subtitleText += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') subtitleText += ` | Группа: ${groupName}`; subtitle.textContent = subtitleText; header.appendChild(subtitle); // УБИРАЕМ ДАТУ ЭКСПОРТА // const date = document.createElement('div'); // date.className = 'print-subtitle'; // const now = new Date(); // date.textContent = `Дата экспорта: ${now.toLocaleDateString('ru-RU')} ${now.toLocaleTimeString('ru-RU')}`; // header.appendChild(date); page.appendChild(header); } // Разделяем группы на две колонки const twoColumns = document.createElement('div'); twoColumns.className = 'print-two-columns'; const leftColumn = document.createElement('div'); leftColumn.className = 'print-column'; const rightColumn = document.createElement('div'); rightColumn.className = 'print-column'; // Распределяем группы по колонкам let currentColumn = leftColumn; let columnIconCount = 0; const maxIconsPerColumn = iconsPerColumn; pageGroups.forEach(group => { // Если текущая колонка заполнена, переключаемся на правую if (columnIconCount + group.icons.length > maxIconsPerColumn && currentColumn === leftColumn) { twoColumns.appendChild(leftColumn); currentColumn = rightColumn; columnIconCount = 0; } // Создаем группу const groupDiv = document.createElement('div'); groupDiv.className = 'print-group'; const groupTitle = document.createElement('h2'); groupTitle.className = 'print-group-title'; groupTitle.textContent = group.name; groupDiv.appendChild(groupTitle); // Добавляем иконки в группу group.icons.forEach(icon => { const iconItem = document.createElement('div'); iconItem.className = 'print-icon-item'; // Контейнер для изображений const imagesContainer = document.createElement('div'); imagesContainer.className = 'print-icon-images'; // Изображение "ДО" const beforeImg = document.createElement('img'); beforeImg.className = 'print-icon-image'; beforeImg.src = icon.before; beforeImg.alt = `${icon.name} (до)`; beforeImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="48" height="48" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <rect width="48" height="48" fill="#f5f5f5"/> <text x="24" y="28" text-anchor="middle" font-family="Arial" font-size="14" fill="#999">ДО</text> </svg> `); }; // Стрелка const arrow = document.createElement('div'); arrow.className = 'print-arrow'; arrow.textContent = '→'; // Изображение "ПОСЛЕ" const afterImg = document.createElement('img'); afterImg.className = 'print-icon-image'; afterImg.src = icon.after; afterImg.alt = `${icon.name} (после)`; afterImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="48" height="48" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <rect width="48" height="48" fill="#f5f5f5"/> <text x="24" y="28" text-anchor="middle" font-family="Arial" font-size="14" fill="#999">ПОСЛЕ</text> </svg> `); }; imagesContainer.appendChild(beforeImg); imagesContainer.appendChild(arrow); imagesContainer.appendChild(afterImg); // Информация об иконке - БЕЗ ПУТЕЙ К ФАЙЛАМ const infoContainer = document.createElement('div'); infoContainer.className = 'print-icon-info'; const name = document.createElement('div'); name.className = 'print-icon-name'; name.textContent = icon.name; // УБИРАЕМ ПУТИ К ФАЙЛАМ // const path = document.createElement('div'); // path.className = 'print-icon-path'; // path.textContent = `${icon.before} → ${icon.after}`; infoContainer.appendChild(name); // infoContainer.appendChild(path); iconItem.appendChild(imagesContainer); iconItem.appendChild(infoContainer); groupDiv.appendChild(iconItem); }); currentColumn.appendChild(groupDiv); columnIconCount += group.icons.length; }); // Добавляем правую колонку, если в ней есть данные if (rightColumn.children.length > 0) { twoColumns.appendChild(rightColumn); } else if (leftColumn.children.length > 0) { // Если только левая колонка, добавляем ее twoColumns.appendChild(leftColumn); } page.appendChild(twoColumns); // Футер страницы const footer = document.createElement('div'); footer.className = 'print-footer'; footer.textContent = 'Сравнение иконок ИСИХОГИ - Старая и новая версии'; page.appendChild(footer); // Номер страницы const pageNumber = document.createElement('div'); pageNumber.className = 'print-page-number'; pageNumber.textContent = `Страница ${pageIndex + 1} из ${pages.length}`; page.appendChild(pageNumber); printContainer.appendChild(page); }); return printContainer; }
// Функция для показа предпросмотра печати function iconComparisonShowPrintPreview() { const exportBtn = document.getElementById('iconComparisonExportBtn'); const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); try { exportBtn.disabled = true; exportBtn.textContent = 'Подготовка...'; loadingOverlay.classList.add('show'); // Создаем контент для печати const printContent = iconComparisonCreatePrintContent(); // Вставляем в модальное окно предпросмотра const previewBody = document.getElementById('printPreviewBody'); previewBody.innerHTML = ''; previewBody.appendChild(printContent); // Показываем модальное окно const previewModal = document.getElementById('iconComparisonPrintPreview'); previewModal.classList.add('show'); // Даем время на загрузку изображений setTimeout(() => { // Восстанавливаем кнопку exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать `; loadingOverlay.classList.remove('show'); }, 500); } catch (error) { console.error('Ошибка при подготовке к печати:', error); alert('Произошла ошибка при подготовке к печати. Пожалуйста, попробуйте еще раз.\n\nОшибка: ' + error.message); // Восстанавливаем кнопку const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать `; const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); loadingOverlay.classList.remove('show'); } }
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { let bestDistribution = null; let bestHeightDiff = Infinity; for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (sortedGroups.length > 0 && hasNoGroup) { const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonAllIcons.length === 0; }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
function iconComparisonSetupExport() { const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.addEventListener('click', function() { if (!this.disabled) { iconComparisonShowPrintPreview(); } }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupExport(); iconComparisonSetupResizeHandler(); }); </script> </body> </html> Добавлено (2025-12-03, 15:12) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { display: flex; justify-content: center; align-items: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-export-btn { padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.icon-comparison-export-btn:hover { background-color: #218838; transform: translateY(-1px); }
.icon-comparison-export-btn:active { transform: translateY(0); }
.icon-comparison-export-btn:disabled { background-color: #6c757d; cursor: not-allowed; transform: none; }
.icon-comparison-export-btn svg { width: 16px; height: 16px; fill: currentColor; }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; filter: none !important; -webkit-filter: none !important; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
/* Стили для сообщения о загрузке PDF */ .icon-comparison-pdf-loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px 30px; border-radius: 8px; z-index: 10000; display: none; flex-direction: column; align-items: center; gap: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); }
.icon-comparison-pdf-loading.show { display: flex; }
.icon-comparison-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: icon-comparison-spin 1s ease-in-out infinite; }
@keyframes icon-comparison-spin { to { transform: rotate(360deg); } }
/* Стили для страницы печати - ИСПРАВЛЕННЫЕ */ @media print { body * { visibility: hidden !important; } .print-container, .print-container * { visibility: visible !important; } .print-container { position: absolute !important; left: 0 !important; top: 0 !important; width: 100% !important; background: white !important; font-family: Arial, sans-serif !important; padding: 0 !important; margin: 0 !important; page-break-inside: avoid !important; } .print-page { width: 210mm !important; min-height: 297mm !important; padding: 15mm 15mm 20mm 15mm !important; margin: 0 auto !important; page-break-after: always !important; page-break-inside: avoid !important; box-sizing: border-box !important; background: white !important; position: relative !important; } .print-page:last-child { page-break-after: avoid !important; } .print-header { text-align: center; margin-bottom: 8mm; padding-bottom: 4mm; border-bottom: 2px solid #333; position: relative; } .print-title { font-size: 24pt; font-weight: bold; margin-bottom: 2mm; color: #333; } .print-subtitle { font-size: 12pt; color: #666; margin-bottom: 2mm; } .print-two-columns { display: flex; gap: 10mm; margin-bottom: 5mm; page-break-inside: avoid; } .print-column { flex: 1; width: 50%; page-break-inside: avoid; } .print-group { margin-bottom: 6mm; page-break-inside: avoid; break-inside: avoid; } .print-group-title { font-size: 14pt; font-weight: bold; background-color: #f5f5f5; padding: 2mm 3mm; border-radius: 2mm; margin-bottom: 3mm; /* УБИРАЕМ синюю полоску */ border-left: none !important; page-break-after: avoid; } .print-icon-item { display: flex; align-items: center; margin-bottom: 2mm; padding: 1mm 0; page-break-inside: avoid; break-inside: avoid; min-height: 15mm; } .print-icon-images { display: flex; align-items: center; gap: 2mm; min-width: 35mm; flex-shrink: 0; page-break-inside: avoid; } .print-icon-image { width: 12mm !important; height: 12mm !important; object-fit: contain !important; display: block !important; background-color: #f5f5f5 !important; padding: 1mm !important; border-radius: 1mm !important; border: 1px solid #ddd !important; page-break-inside: avoid; } .print-arrow { font-weight: bold; font-size: 10pt; color: #666; margin: 0 1mm; } .print-icon-info { flex: 1; padding-left: 3mm; page-break-inside: avoid; } .print-icon-name { font-size: 10pt; font-weight: bold; margin-bottom: 0.5mm; word-break: break-word; color: #333; } /* УБИРАЕМ ПУТИ К ФАЙЛАМ */ .print-icon-path { display: none !important; } .print-footer { text-align: center; font-size: 9pt; color: #666; padding-top: 3mm; border-top: 1px solid #ddd; position: absolute; bottom: 10mm; left: 15mm; right: 15mm; } .print-page-number { position: absolute; bottom: 8mm; right: 15mm; font-size: 10pt; color: #666; } /* Скрываем ненужные элементы при печати */ .no-print { display: none !important; } }
/* Предпросмотр печати */ .print-preview-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 10001; display: none; justify-content: center; align-items: center; } .print-preview-modal.show { display: flex; } .print-preview-content { background: white; width: 90%; height: 90%; border-radius: 8px; display: flex; flex-direction: column; overflow: hidden; } .print-preview-header { padding: 15px; background: #f8f9fa; border-bottom: 1px solid #dee2e6; display: flex; justify-content: space-between; align-items: center; } .print-preview-title { font-size: 18px; font-weight: bold; } .print-preview-close { background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; } .print-preview-body { flex: 1; overflow: auto; padding: 20px; background: #f5f5f5; } .print-preview-actions { padding: 15px; background: #f8f9fa; border-top: 1px solid #dee2e6; display: flex; gap: 10px; justify-content: flex-end; } .print-preview-btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; } .print-preview-btn.print { background: #28a745; color: white; } .print-preview-btn.cancel { background: #6c757d; color: white; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-export-btn, [data-theme="dark"] .icon-comparison-export-btn, .dark .icon-comparison-export-btn { background-color: #2e7d32; }
.dark-mode .icon-comparison-export-btn:hover, [data-theme="dark"] .icon-comparison-export-btn:hover, .dark .icon-comparison-export-btn:hover { background-color: #1b5e20; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-search-area { flex-direction: column; align-items: stretch; } .icon-comparison-search { width: 100%; max-width: 100%; } .icon-comparison-export-btn { width: 100%; justify-content: center; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } .print-preview-content { width: 100%; height: 100%; border-radius: 0; } }
@media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } .icon-comparison-export-btn { padding: 8px 12px; font-size: 13px; } @media print { .print-two-columns { flex-direction: column; gap: 5mm; } .print-column { width: 100%; } .print-icon-images { min-width: 35mm; } .print-icon-image { width: 10mm; height: 10mm; } } }
@media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> <button id="iconComparisonExportBtn" class="icon-comparison-export-btn" disabled> <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать </button> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<!-- Оверлей для отображения загрузки --> <div id="iconComparisonPdfLoading" class="icon-comparison-pdf-loading"> <div class="icon-comparison-spinner"></div> <div>Подготовка к печати...</div> </div>
<!-- Модальное окно предпросмотра --> <div id="iconComparisonPrintPreview" class="print-preview-modal"> <div class="print-preview-content"> <div class="print-preview-header"> <div class="print-preview-title">Предпросмотр печати</div> <button class="print-preview-close" onclick="iconComparisonClosePrintPreview()">✕ Закрыть</button> </div> <div class="print-preview-body" id="printPreviewBody"> <!-- Сюда будет вставлен предпросмотр --> </div> <div class="print-preview-actions"> <button class="print-preview-btn cancel" onclick="iconComparisonClosePrintPreview()">Отмена</button> <button class="print-preview-btn print" onclick="iconComparisonPrintFromPreview()">Печать</button> </div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для закрытия предпросмотра function iconComparisonClosePrintPreview() { const previewModal = document.getElementById('iconComparisonPrintPreview'); previewModal.classList.remove('show'); }
// Функция для печати из предпросмотра function iconComparisonPrintFromPreview() { window.print(); }
// Функция для создания контента для печати (двухколоночный макет) - ИСПРАВЛЕННАЯ function iconComparisonCreatePrintContent() { const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Создаем контейнер для печати const printContainer = document.createElement('div'); printContainer.className = 'print-container'; // Рассчитываем количество иконок на страницу (оптимизировано для предотвращения наложений) const iconsPerColumn = 12; // Уменьшено для предотвращения наложений const iconsPerPage = iconsPerColumn * 2; // Собираем все иконки для печати const allIconsForPrint = []; // Добавляем иконки из обычных групп for (const groupName of sortedGroupNames) { const icons = groups[groupName]; allIconsForPrint.push({ type: 'group', name: groupName, icons: icons }); } // Добавляем группу "Прочие" if (noGroup.length > 0) { allIconsForPrint.push({ type: 'group', name: 'Прочие иконки', icons: noGroup }); } // Разбиваем на страницы с учетом высоты элементов let currentPageIcons = 0; let currentPageGroups = []; let currentGroupIndex = 0; let currentIconIndex = 0; const pages = [];
Добавлено (2025-12-03, 16:02) --------------------------------------------- // Добавляем часть группы currentPage.push({ name: partGroupName, icons: partIcons, column: currentColumn, height: partGroupHeight }); currentColumnHeight += partGroupHeight; currentPageHeight = Math.max(currentPageHeight, currentColumnHeight); // Если колонка заполнена, переходим на другую колонку if (currentColumnHeight >= maxPageHeight - (currentPage.length === 1 ? headerHeight : 0)) { if (currentColumn === 'left') { currentColumn = 'right'; currentColumnHeight = 0; } else { // Завершаем страницу pages.push([...currentPage]); currentPage = []; currentColumn = 'left'; currentColumnHeight = 0; currentPageHeight = 0; } } } } else { // Группа помещается в одну колонку // Проверяем, помещается ли группа в текущую колонку const availableHeight = maxPageHeight - (currentPage.length === 0 ? headerHeight : 0); if (currentColumnHeight + groupHeight > availableHeight) { // Переходим на другую колонку или страницу if (currentColumn === 'left') { currentColumn = 'right'; currentColumnHeight = 0; } else { // Завершаем текущую страницу if (currentPage.length > 0) { pages.push([...currentPage]); } currentPage = []; currentColumn = 'left'; currentColumnHeight = 0; currentPageHeight = 0; } } // Добавляем группу currentPage.push({ name: group.name, icons: group.icons, column: currentColumn, height: groupHeight }); currentColumnHeight += groupHeight; currentPageHeight = Math.max(currentPageHeight, currentColumnHeight); // Если колонка заполнена, переходим на другую колонку if (currentColumnHeight >= availableHeight) { if (currentColumn === 'left') { currentColumn = 'right'; currentColumnHeight = 0; } else { // Завершаем страницу pages.push([...currentPage]); currentPage = []; currentColumn = 'left'; currentColumnHeight = 0; currentPageHeight = 0; } } } } // Добавляем последнюю страницу, если в ней есть данные if (currentPage.length > 0) { pages.push(currentPage); } // Создаем HTML для страниц pages.forEach((pageGroups, pageIndex) => { const page = document.createElement('div'); page.className = 'print-page'; // Заголовок страницы - ТОЛЬКО НА ПЕРВОЙ СТРАНИЦЕ if (pageIndex === 0) { const header = document.createElement('div'); header.className = 'print-header'; const title = document.createElement('h1'); title.className = 'print-title'; title.textContent = 'Сравнение иконок ИСИХОГИ'; header.appendChild(title); const subtitle = document.createElement('div'); subtitle.className = 'print-subtitle'; let subtitleText = `Всего иконок: ${iconComparisonAllIcons.length} | Отображено: ${iconComparisonFilteredIcons.length}`; if (searchTerm) subtitleText += ` | Поиск: "${searchTerm}"`; if (groupName !== 'Все группы') subtitleText += ` | Группа: ${groupName}`; subtitle.textContent = subtitleText; header.appendChild(subtitle); page.appendChild(header); } // Разделяем на две колонки const twoColumns = document.createElement('div'); twoColumns.className = 'print-two-columns'; const leftColumn = document.createElement('div'); leftColumn.className = 'print-column'; const rightColumn = document.createElement('div'); rightColumn.className = 'print-column'; // Распределяем группы по колонкам pageGroups.forEach(groupData => { const column = groupData.column === 'left' ? leftColumn : rightColumn; // Создаем группу const groupDiv = document.createElement('div'); groupDiv.className = 'print-group'; const groupTitle = document.createElement('h2'); groupTitle.className = 'print-group-title'; groupTitle.textContent = groupData.name; groupDiv.appendChild(groupTitle); // Добавляем иконки в группу groupData.icons.forEach(icon => { const iconItem = document.createElement('div'); iconItem.className = 'print-icon-item'; // Контейнер для изображений const imagesContainer = document.createElement('div'); imagesContainer.className = 'print-icon-images'; // Изображение "ДО" const beforeImg = document.createElement('img'); beforeImg.className = 'print-icon-image'; beforeImg.src = icon.before; beforeImg.alt = `${icon.name} (до)`; beforeImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="48" height="48" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <rect width="48" height="48" fill="#f5f5f5"/> <text x="24" y="28" text-anchor="middle" font-family="Arial" font-size="14" fill="#999">ДО</text> </svg> `); }; // Стрелка const arrow = document.createElement('div'); arrow.className = 'print-arrow'; arrow.textContent = '→'; // Изображение "ПОСЛЕ" const afterImg = document.createElement('img'); afterImg.className = 'print-icon-image'; afterImg.src = icon.after; afterImg.alt = `${icon.name} (после)`; afterImg.onerror = function() { this.src = 'data:image/svg+xml;base64,' + btoa(` <svg width="48" height="48" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <rect width="48" height="48" fill="#f5f5f5"/> <text x="24" y="28" text-anchor="middle" font-family="Arial" font-size="14" fill="#999">ПОСЛЕ</text> </svg> `); }; imagesContainer.appendChild(beforeImg); imagesContainer.appendChild(arrow); imagesContainer.appendChild(afterImg); // Информация об иконке const infoContainer = document.createElement('div'); infoContainer.className = 'print-icon-info'; const name = document.createElement('div'); name.className = 'print-icon-name'; name.textContent = icon.name; infoContainer.appendChild(name); iconItem.appendChild(imagesContainer); iconItem.appendChild(infoContainer); groupDiv.appendChild(iconItem); }); column.appendChild(groupDiv); }); // Добавляем колонки if (leftColumn.children.length > 0) { twoColumns.appendChild(leftColumn); } if (rightColumn.children.length > 0) { twoColumns.appendChild(rightColumn); } page.appendChild(twoColumns); // Футер и номер страницы const footer = document.createElement('div'); footer.className = 'print-footer'; footer.textContent = 'Сравнение иконок ИСИХОГИ - Старая и новая версии'; page.appendChild(footer); const pageNumber = document.createElement('div'); pageNumber.className = 'print-page-number'; pageNumber.textContent = `Страница ${pageIndex + 1} из ${pages.length}`; page.appendChild(pageNumber); printContainer.appendChild(page); }); return printContainer; }
// Функция для показа предпросмотра печати function iconComparisonShowPrintPreview() { const exportBtn = document.getElementById('iconComparisonExportBtn'); const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); try { exportBtn.disabled = true; exportBtn.textContent = 'Подготовка...'; loadingOverlay.classList.add('show'); // Создаем контент для печати const printContent = iconComparisonCreatePrintContent(); // Вставляем в модальное окно предпросмотра const previewBody = document.getElementById('printPreviewBody'); previewBody.innerHTML = ''; // Создаем контейнер для предпросмотра const previewContainer = document.createElement('div'); previewContainer.className = 'print-preview-container'; previewContainer.appendChild(printContent.cloneNode(true)); previewBody.appendChild(previewContainer); // Показываем модальное окно const previewModal = document.getElementById('iconComparisonPrintPreview'); previewModal.classList.add('show'); // Даем время на загрузку изображений setTimeout(() => { // Восстанавливаем кнопку exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать `; loadingOverlay.classList.remove('show'); }, 500); } catch (error) { console.error('Ошибка при подготовке к печати:', error); alert('Произошла ошибка при подготовке к печати. Пожалуйста, попробуйте еще раз.\n\nОшибка: ' + error.message); // Восстанавливаем кнопку const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = false; exportBtn.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать `; const loadingOverlay = document.getElementById('iconComparisonPdfLoading'); loadingOverlay.classList.remove('show'); } }
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; const maxColumns = Math.floor(1600 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(availableColumns).fill(0); const columnChunks = Array(availableColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { let bestDistribution = null; let bestHeightDiff = Infinity; for (let startCol = 0; startCol < availableColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % availableColumns; tempColumnHeights[colIndex] += chunk.height; const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % availableColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) { iconComparisonRenderWithGroups(); } }, 250); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE8IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } iconComparisonUpdateStats(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonFilteredIcons.length === 0; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); if (sortedGroups.length > 0 && hasNoGroup) { const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.disabled = iconComparisonAllIcons.length === 0; }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
function iconComparisonSetupExport() { const exportBtn = document.getElementById('iconComparisonExportBtn'); exportBtn.addEventListener('click', function() { if (!this.disabled) { iconComparisonShowPrintPreview(); } }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupExport(); iconComparisonSetupResizeHandler(); }); </script> </body> </html> Добавлено (2025-12-03, 16:03) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { display: flex; justify-content: center; align-items: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-export-btn { padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.icon-comparison-export-btn:hover { background-color: #218838; transform: translateY(-1px); }
.icon-comparison-export-btn:active { transform: translateY(0); }
.icon-comparison-export-btn:disabled { background-color: #6c757d; cursor: not-allowed; transform: none; }
.icon-comparison-export-btn svg { width: 16px; height: 16px; fill: currentColor; }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; filter: none !important; -webkit-filter: none !important; image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
/* Стили для сообщения о загрузке PDF */ .icon-comparison-pdf-loading { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px 30px; border-radius: 8px; z-index: 10000; display: none; flex-direction: column; align-items: center; gap: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); }
.icon-comparison-pdf-loading.show { display: flex; }
.icon-comparison-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: icon-comparison-spin 1s ease-in-out infinite; }
@keyframes icon-comparison-spin { to { transform: rotate(360deg); } }
/* Стили для страницы печати - ПОЛНОСТЬЮ ПЕРЕРАБОТАННЫЕ */ @media print { body, html { margin: 0 !important; padding: 0 !important; width: 100% !important; height: auto !important; overflow: visible !important; background: white !important; } body * { visibility: hidden; } .print-container { visibility: visible !important; display: block !important; position: relative !important; width: 100% !important; height: auto !important; margin: 0 !important; padding: 0 !important; background: white !important; font-family: Arial, sans-serif !important; page-break-inside: avoid !important; } .print-page { width: 210mm !important; min-height: 297mm !important; height: 297mm !important; padding: 10mm 15mm 10mm 15mm !important; margin: 0 auto !important; page-break-after: always !important; display: block !important; position: relative !important; overflow: visible !important; background: white !important; box-sizing: border-box !important; break-inside: avoid !important; } .print-page:last-child { page-break-after: avoid !important; } .print-header { text-align: center; margin-bottom: 8mm; padding-bottom: 4mm; border-bottom: 2px solid #333; position: relative; break-after: avoid; } .print-title { font-size: 24pt; font-weight: bold; margin-bottom: 2mm; color: #333; } .print-subtitle { font-size: 12pt; color: #666; margin-bottom: 2mm; } .print-two-columns { display: flex !important; gap: 10mm; margin-bottom: 0; page-break-inside: avoid; break-inside: avoid; min-height: 240mm; height: auto; position: relative; } .print-column { flex: 1; width: 50%; page-break-inside: avoid; break-inside: avoid; height: auto; position: relative; } .print-group { margin-bottom: 6mm; page-break-inside: avoid; break-inside: avoid; position: relative; } .print-group-title { font-size: 14pt; font-weight: bold; background-color: #f5f5f5; padding: 2mm 3mm; border-radius: 2mm; margin-bottom: 3mm; page-break-after: avoid; break-after: avoid; position: relative; } .print-icon-item { display: flex !important; align-items: center !important; margin-bottom: 3mm !important; padding: 0 !important; page-break-inside: avoid !important; break-inside: avoid !important; min-height: 18mm !important; position: relative !important; width: 100% !important; } .print-icon-images { display: flex !important; align-items: center !important; gap: 2mm !important; min-width: 40mm !important; flex-shrink: 0 !important; page-break-inside: avoid !important; position: relative !important; } .print-icon-image { width: 15mm !important; height: 15mm !important; object-fit: contain !important; display: block !important; background-color: #f5f5f5 !important; padding: 1mm !important; border-radius: 1mm !important; border: 1px solid #ddd !important; page-break-inside: avoid !important; position: relative !important; } .print-arrow { font-weight: bold; font-size: 10pt; color: #666; margin: 0 1mm; display: block !important; } .print-icon-info { flex: 1; padding-left: 3mm; page-break-inside: avoid; break-inside: avoid; position: relative; } .print-icon-name { font-size: 11pt !important; font-weight: bold !important; margin-bottom: 0.5mm !important; word-break: break-word !important; color: #333 !important; line-height: 1.3 !important; display: block !important; } /* УБИРАЕМ ПУТИ К ФАЙЛАМ */ .print-icon-path { display: none !important; } .print-footer { text-align: center; font-size: 9pt; color: #666; padding-top: 3mm; border-top: 1px solid #ddd; position: absolute; bottom: 5mm; left: 15mm; right: 15mm; height: 10mm; display: block !important; } .print-page-number { position: absolute; bottom: 5mm; right: 15mm; font-size: 10pt; color: #666; display: block !important; } /* Скрываем ненужные элементы при печати */ .no-print, .print-preview-modal, .icon-comparison-pdf-loading { display: none !important; visibility: hidden !important; } }
/* Предпросмотр печати */ .print-preview-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 10001; display: none; justify-content: center; align-items: center; } .print-preview-modal.show { display: flex; } .print-preview-content { background: white; width: 90%; height: 90%; border-radius: 8px; display: flex; flex-direction: column; overflow: hidden; } .print-preview-header { padding: 15px; background: #f8f9fa; border-bottom: 1px solid #dee2e6; display: flex; justify-content: space-between; align-items: center; } .print-preview-title { font-size: 18px; font-weight: bold; } .print-preview-close { background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; } .print-preview-body { flex: 1; overflow: auto; padding: 20px; background: #f5f5f5; } .print-preview-actions { padding: 15px; background: #f8f9fa; border-top: 1px solid #dee2e6; display: flex; gap: 10px; justify-content: flex-end; } .print-preview-btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; } .print-preview-btn.print { background: #28a745; color: white; } .print-preview-btn.cancel { background: #6c757d; color: white; }
/* Стили для предпросмотра (не для печати) */ .print-preview-container { background: white; padding: 20px; width: 210mm; margin: 0 auto; box-shadow: 0 0 20px rgba(0,0,0,0.1); box-sizing: border-box; } .print-preview-page { width: 210mm; min-height: 297mm; padding: 15mm; margin-bottom: 20px; background: white; border: 1px solid #ddd; box-sizing: border-box; page-break-after: always; position: relative; display: flex; flex-direction: column; } .print-preview-page:last-child { page-break-after: avoid; margin-bottom: 0; } .print-preview-two-columns { display: flex; gap: 15mm; flex: 1; } .print-preview-column { flex: 1; width: 50%; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-export-btn, [data-theme="dark"] .icon-comparison-export-btn, .dark .icon-comparison-export-btn { background-color: #2e7d32; }
.dark-mode .icon-comparison-export-btn:hover, [data-theme="dark"] .icon-comparison-export-btn:hover, .dark .icon-comparison-export-btn:hover { background-color: #1b5e20; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-search-area { flex-direction: column; align-items: stretch; } .icon-comparison-search { width: 100%; max-width: 100%; } .icon-comparison-export-btn { width: 100%; justify-content: center; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } .print-preview-content { width: 100%; height: 100%; border-radius: 0; } .print-preview-container, .print-preview-page { width: 100%; min-height: auto; padding: 10px; } .print-preview-two-columns { flex-direction: column; gap: 10px; } .print-preview-column { width: 100%; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> <button id="iconComparisonExportBtn" class="icon-comparison-export-btn" disabled> <svg viewBox="0 0 24 24"> <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/> </svg> Экспорт/Печать </button> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<!-- Оверлей для отображения загрузки --> <div id="iconComparisonPdfLoading" class="icon-comparison-pdf-loading"> <div class="icon-comparison-spinner"></div> <div>Подготовка к печати...</div> </div>
<!-- Модальное окно предпросмотра --> <div id="iconComparisonPrintPreview" class="print-preview-modal"> <div class="print-preview-content"> <div class="print-preview-header"> <div class="print-preview-title">Предпросмотр печати</div> <button class="print-preview-close" onclick="iconComparisonClosePrintPreview()">✕ Закрыть</button> </div> <div class="print-preview-body" id="printPreviewBody"> <!-- Сюда будет вставлен предпросмотр --> </div> <div class="print-preview-actions"> <button class="print-preview-btn cancel" onclick="iconComparisonClosePrintPreview()">Отмена</button> <button class="print-preview-btn print" onclick="iconComparisonPrintFromPreview()">Печать</button> </div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true;
// Функция для закрытия предпросмотра function iconComparisonClosePrintPreview() { const previewModal = document.getElementById('iconComparisonPrintPreview'); previewModal.classList.remove('show'); }
// Функция для печати из предпросмотра function iconComparisonPrintFromPreview() { window.print(); }
// Функция для создания контента для печати - ПОЛНОСТЬЮ ПЕРЕРАБОТАННАЯ function iconComparisonCreatePrintContent() { const searchTerm = document.getElementById('iconComparisonSearch').value; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; const groupName = selectedGroup ? (selectedGroup === 'no_group' ? 'Прочие' : document.getElementById('iconComparisonGroupSelect').options[document.getElementById('iconComparisonGroupSelect').selectedIndex].text) : 'Все группы'; const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Создаем контейнер для печати const printContainer = document.createElement('div'); printContainer.className = 'print-container'; // Собираем все группы с иконками const allGroupsForPrint = []; // Добавляем иконки из обычных групп for (const groupName of sortedGroupNames) { allGroupsForPrint.push({ name: groupName, icons: groups[groupName] }); } // Добавляем группу "Прочие" if (noGroup.length > 0) { allGroupsForPrint.push({ name: 'Прочие иконки', icons: noGroup }); } // Функция для расчета высоты группы в мм function calculateGroupHeightInMM(iconsCount) { // 18mm на каждую иконку + 10mm на заголовок группы return 10 + (iconsCount * 18); } // Максимальная высота страницы (297mm - 20mm отступы) const maxPageHeight = 277; // mm const columnWidth = 85; // мм (половина ширины страницы минус gap) // Создаем страницы let currentPage = []; let currentPageHeight = 0; let currentColumn = 'left'; let currentColumnHeight = 0; let pages = []; // Рассчитываем высоту для заголовка (только на первой странице) const headerHeight = 20; // мм for (let g = 0; g < allGroupsForPrint.length; g++) { const group = allGroupsForPrint[g]; const groupHeight = calculateGroupHeightInMM(group.icons.length); // Если группа слишком большая для одной колонки, разбиваем ее if (groupHeight > maxPageHeight - headerHeight) { // Разбиваем группу на части const maxIconsPerColumn = Math.floor((maxPageHeight - 10) / 18); // 10mm на заголовок группы const parts = Math.ceil(group.icons.length / maxIconsPerColumn); for (let part = 0; part < parts; part++) { const startIdx = part * maxIconsPerColumn; const endIdx = Math.min(startIdx + maxIconsPerColumn, group.icons.length); const partIcons = group.icons.slice(startIdx, endIdx); const partGroupName = parts > 1 ? `${group.name} (${part + 1}/${parts})` : group.name; const partGroupHeight = calculateGroupHeightInMM(partIcons.length); // Проверяем, помещается ли часть группы в текущую колонку if (currentColumnHeight + partGroupHeight > maxPageHeight - (currentPage.length === 0 ? headerHeight : 0)) { // Переходим на другую колонку или страницу if (currentColumn === 'left') { currentColumn = 'right'; currentColumnHeight = 0; } else { // Завершаем текущую страницу if (currentPage.length > 0) { pages.push([...currentPage]); } currentPage = []; currentColumn = 'left'; currentColumnHeight = 0; currentPageHeight = 0; } }
|
Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { // Если у группы только один чанк const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { // Если у группы несколько чанков, пытаемся разместить их рядом let bestDistribution = null; let bestHeightDiff = Infinity; // Пробуем разные способы распределения чанков по колонкам for (let startCol = 0; startCol < maxColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % maxColumns; tempColumnHeights[colIndex] += chunk.height; // Проверяем, не слишком ли неравномерное распределение const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { // Максимально допустимая разница высот canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } // Если не нашли хорошего распределения, используем стандартный алгоритм if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { // Используем найденное оптимальное распределение group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % maxColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonCreatePrintLayout(groupsChunks) { // Для печати всегда создаем 2 колонки const columns = [ document.createElement('div'), document.createElement('div') ]; columns[0].className = 'icon-comparison-masonry-column'; columns[1].className = 'icon-comparison-masonry-column'; // Добавляем стили для печати прямо в элементы columns[0].style.width = 'calc(50% - 4px)'; columns[0].style.maxWidth = 'calc(50% - 4px)'; columns[1].style.width = 'calc(50% - 4px)'; columns[1].style.maxWidth = 'calc(50% - 4px)'; const columnHeights = [0, 0]; // Распределяем группы по двум колонкам попеременно groupsChunks.forEach((chunk, index) => { const columnIndex = index % 2; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); // Добавляем стили для печати в группу section.style.width = '100%'; section.querySelector('.icon-comparison-group-title').style.width = '100%'; section.querySelector('.icon-comparison-panel').style.width = '100%'; columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; // Сортировка групп по алфавиту const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы (отсортированные по алфавиту) sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); // НЕ добавляем разделитель в визуальное отображение // Добавляем группу "Прочие" в конец if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; // Добавляем стили для печати если нужно if (isPrintMode) { columnsContainer.style.justifyContent = 'space-between'; columnsContainer.style.gap = '8px'; } // Добавляем только непустые колонки columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); // Центрируем колонки если они не занимают всю ширину (только не в режиме печати) if (!isPrintMode) { const totalColumnsWidth = columns.filter(c => c.children.length > 0).length * 400 + (columns.filter(c => c.children.length > 0).length - 1) * 12; if (totalColumnsWidth <= container.clientWidth) { columnsContainer.style.justifyContent = 'center'; } else { columnsContainer.style.justifyContent = 'flex-start'; } } container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); // Для режима печати используем двухколоночную раскладку if (isPrintMode) { // Добавляем специальный класс для печати без групп container.classList.add('print-no-groups'); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); // Для печати ограничиваем максимум 2 колонки const printColumnCount = Math.min(columnCount, 2); for (let i = 0; i < printColumnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); // Для печати добавляем инлайн стили if (isPrintMode) { column.style.width = 'calc(50% - 4px)'; column.style.maxWidth = 'calc(50% - 4px)'; column.style.float = i % 2 === 0 ? 'left' : 'right'; column.style.marginRight = i % 2 === 0 ? '8px' : '0'; column.style.marginBottom = '8px'; } container.appendChild(column); } // Добавляем очистку потока const clearfix = document.createElement('div'); clearfix.style.clear = 'both'; container.appendChild(clearfix); } else { // Оригинальная логика для обычного просмотра container.classList.remove('print-no-groups'); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } } iconComparisonUpdateStats(); }
function iconComparisonSetupPrintHandler() { // Обработчик для режима печати window.addEventListener('beforeprint', function() { console.log('Before print - activating print mode'); isPrintMode = true; if (iconComparisonFilteredIcons.length > 0) { // Небольшая задержка для применения CSS стилей печати setTimeout(() => { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }, 50); } }); window.addEventListener('afterprint', function() { console.log('After print - deactivating print mode'); isPrintMode = false; if (iconComparisonFilteredIcons.length > 0) { // Небольшая задержка для возврата к обычным стилям setTimeout(() => { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }, 50); } }); }
function iconComparisonSetupResizeHandler() { let resizeTimeout; let lastWidth = 0; function handleResize() { if (isPrintMode) return; // Не обрабатываем ресайз в режиме печати const currentWidth = document.getElementById('iconComparisonContainer').clientWidth; // Перестраиваем только если ширина изменилась значительно (более 20px) if (Math.abs(currentWidth - lastWidth) > 20) { lastWidth = currentWidth; if (iconComparisonFilteredIcons.length > 0) { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } } } } window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(handleResize, 150); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; // Используем сглаживание при рисовании на canvas ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; // Сортировка групп по алфавиту const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } // Проверяем, есть ли группа "Прочие" const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); // Добавляем разделитель и группу "Прочие" в конец списка, если есть обычные группы и группа "Прочие" if (sortedGroups.length > 0 && hasNoGroup) { // Добавляем разделитель const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); // Добавляем группу "Прочие" const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { // Если есть только группа "Прочие" const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (sortedGroups.length > 0) { // Если есть только обычные группы - ничего не добавляем } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); iconComparisonSetupPrintHandler(); }); </script> </body> </html>Добавлено (2025-12-04, 07:22) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; page-break-inside: avoid; /* Важно для печати */ }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; page-break-inside: avoid; /* Важно для печати */ }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Убраны pixelated rendering свойства */ /* image-rendering: -webkit-optimize-contrast; */ /* image-rendering: crisp-edges; */ /* image-rendering: pixelated; */ /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; /* Добавляем плавное сглаживание */ image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: flex-start; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для печати - ОЧЕНЬ ВАЖНО: они должны быть ДО других медиа-запросов */ @media print { .icon-comparison-body { padding: 0; font-size: 10px; background-color: white !important; } .icon-comparison-controls { display: none !important; } .icon-comparison-search-area { display: none !important; } .icon-comparison-title { font-size: 14px; margin-bottom: 10px; color: black !important; } .icon-comparison-wrapper { max-width: 100% !important; margin: 0 !important; padding: 0 !important; } .icon-comparison-grid { display: block !important; } .icon-comparison-columns { display: flex !important; flex-wrap: wrap !important; gap: 8px !important; width: 100% !important; justify-content: space-between !important; } .icon-comparison-masonry-column { width: calc(50% - 4px) !important; max-width: calc(50% - 4px) !important; gap: 8px !important; } .icon-comparison-group { width: 100% !important; margin-bottom: 8px !important; break-inside: avoid !important; page-break-inside: avoid !important; } .icon-comparison-group-title { width: 100% !important; font-size: 11px !important; padding: 4px 6px !important; margin-bottom: 4px !important; background: #f0f0f0 !important; color: black !important; } .icon-comparison-panel { width: 100% !important; max-width: 100% !important; padding: 4px !important; break-inside: avoid !important; page-break-inside: avoid !important; border: 1px solid #ccc !important; background: white !important; } /* Специальные стили для режима без групп при печати */ .print-no-groups .icon-comparison-panel { width: calc(50% - 4px) !important; max-width: calc(50% - 4px) !important; float: left !important; margin-right: 8px !important; margin-bottom: 8px !important; } .print-no-groups .icon-comparison-panel:nth-child(2n) { margin-right: 0 !important; float: right !important; } .print-no-groups .icon-comparison-grid { display: block !important; overflow: hidden !important; } .icon-comparison-panel-header { padding: 2px !important; margin-bottom: 2px !important; gap: 4px !important; border-bottom: 1px solid #ccc !important; } .icon-comparison-header-label { font-size: 9px !important; color: black !important; } .icon-comparison-row { padding: 2px !important; min-height: 30px !important; gap: 4px !important; border-bottom: 1px solid #eee !important; } .icon-comparison-image { width: 28px !important; height: 28px !important; padding: 1px !important; background-color: #f0f0f0 !important; filter: none !important; -webkit-filter: none !important; } .icon-comparison-arrow { font-size: 11px !important; color: #666 !important; } .icon-comparison-name { font-size: 10px !important; max-width: 180px !important; color: black !important; } .icon-comparison-bmp-processed { filter: none !important; } /* Отключаем все эффекты темной темы при печати */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body, .dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title, .dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name, .dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { background-color: white !important; color: black !important; } }
/* Убраны стили для разделителя групп в визуальном отображении */
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true; let isPrintMode = false;
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { // Для небольших групп (до 30 элементов) делим пополам const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { // Для больших групп используем оптимальное разбиение const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); // Для режима печати используем специальную логику if (isPrintMode) { return iconComparisonCreatePrintLayout(groupsChunks); } const containerWidth = container.clientWidth; // Используем точный расчет с учетом дробных пикселей при масштабировании const panelWidth = 400; // Фиксированная ширина панели const gap = 12; // Расстояние между колонками const panelWithGap = panelWidth + gap; // Рассчитываем максимальное количество колонок с запасом для дробных значений // Добавляем небольшой буфер (5px) для учета погрешности при масштабировании const maxColumns = Math.max(1, Math.floor((containerWidth + 5) / panelWithGap)); console.log(`Container width: ${containerWidth}, Panel width: ${panelWidth}, Gap: ${gap}, Max columns: ${maxColumns}, Print mode: ${isPrintMode}`); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: maxColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(maxColumns).fill(0); const columnChunks = Array(maxColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков
Добавлено (2025-12-04, 08:03) --------------------------------------------- function iconComparisonGroupChunksByOriginalGroup(groupsChunks) { const groupedByOriginal = {}; groupsChunks.forEach((chunk, index) => { if (!groupedByOriginal[chunk.originalGroup]) { groupedByOriginal[chunk.originalGroup] = { chunks: [], totalHeight: 0 }; } groupedByOriginal[chunk.originalGroup].chunks.push({ ...chunk, originalIndex: index }); groupedByOriginal[chunk.originalGroup].totalHeight += chunk.height; }); return groupedByOriginal; }
function iconComparisonCreateMasonryLayoutOptimized(groupsChunks) { const container = document.getElementById('iconComparisonContainer'); // Для режима печати используем специальную логику if (isPrintMode && iconComparisonShowGroups) { return iconComparisonCreatePrintLayout(groupsChunks); } const containerWidth = container.clientWidth; // Используем точный расчет с учетом дробных пикселей при масштабировании const panelWidth = 400; // Фиксированная ширина панели const gap = 12; // Расстояние между колонками const panelWithGap = panelWidth + gap; // Рассчитываем максимальное количество колонок с запасом для дробных значений // Добавляем небольшой буфер (5px) для учета погрешности при масштабировании const maxColumns = Math.max(1, Math.floor((containerWidth + 5) / panelWithGap)); console.log(`Container width: ${containerWidth}, Panel width: ${panelWidth}, Gap: ${gap}, Max columns: ${maxColumns}, Print mode: ${isPrintMode}`); // Группируем чанки по исходной группе const groupedByOriginal = iconComparisonGroupChunksByOriginalGroup(groupsChunks); // Создаем массив для распределения const columns = Array.from({ length: maxColumns }, () => { const col = document.createElement('div'); col.className = 'icon-comparison-masonry-column'; return col; }); const columnHeights = Array(maxColumns).fill(0); const columnChunks = Array(maxColumns).fill([]).map(() => []); // Функция для нахождения колонки с минимальной высотой const findMinHeightColumn = () => { let minHeight = Math.min(...columnHeights); return columnHeights.indexOf(minHeight); }; // Распределяем группы чанков Object.values(groupedByOriginal).forEach(group => { if (group.chunks.length === 1) { // Если у группы только один чанк const chunk = group.chunks[0]; const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); } else { // Если у группы несколько чанков, пытаемся разместить их рядом let bestDistribution = null; let bestHeightDiff = Infinity; // Пробуем разные способы распределения чанков по колонкам for (let startCol = 0; startCol < maxColumns; startCol++) { const tempColumnHeights = [...columnHeights]; let canFit = true; for (let i = 0; i < group.chunks.length; i++) { const chunk = group.chunks[i]; const colIndex = (startCol + i) % maxColumns; tempColumnHeights[colIndex] += chunk.height; // Проверяем, не слишком ли неравномерное распределение const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); if (maxHeight - minHeight > 500) { // Максимально допустимая разница высот canFit = false; break; } } if (canFit) { const maxHeight = Math.max(...tempColumnHeights); const minHeight = Math.min(...tempColumnHeights); const heightDiff = maxHeight - minHeight; if (heightDiff < bestHeightDiff) { bestHeightDiff = heightDiff; bestDistribution = { startCol: startCol, heights: tempColumnHeights }; } } } // Если не нашли хорошего распределения, используем стандартный алгоритм if (!bestDistribution) { group.chunks.forEach(chunk => { const columnIndex = findMinHeightColumn(); const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; columnChunks[columnIndex].push(chunk.originalIndex); }); } else { // Используем найденное оптимальное распределение group.chunks.forEach((chunk, i) => { const colIndex = (bestDistribution.startCol + i) % maxColumns; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); columns[colIndex].appendChild(section); columnHeights[colIndex] = bestDistribution.heights[colIndex]; columnChunks[colIndex].push(chunk.originalIndex); }); } } }); return columns; }
function iconComparisonCreatePrintLayout(groupsChunks) { // Для печати всегда создаем 2 колонки const columns = [ document.createElement('div'), document.createElement('div') ]; columns[0].className = 'icon-comparison-masonry-column'; columns[1].className = 'icon-comparison-masonry-column'; // Добавляем стили для печати прямо в элементы columns[0].style.width = 'calc(50% - 4px)'; columns[0].style.maxWidth = 'calc(50% - 4px)'; columns[1].style.width = 'calc(50% - 4px)'; columns[1].style.maxWidth = 'calc(50% - 4px)'; const columnHeights = [0, 0]; // Распределяем группы по двум колонкам попеременно groupsChunks.forEach((chunk, index) => { const columnIndex = index % 2; const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection); // Добавляем стили для печати в группу section.style.width = '100%'; section.querySelector('.icon-comparison-group-title').style.width = '100%'; section.querySelector('.icon-comparison-panel').style.width = '100%'; columns[columnIndex].appendChild(section); columnHeights[columnIndex] += chunk.height; }); return columns; }
function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; // Сортировка групп по алфавиту const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы (отсортированные по алфавиту) sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: false }); }); }); // НЕ добавляем разделитель в визуальное отображение // Добавляем группу "Прочие" в конец if (noGroup.length > 0) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', height: iconComparisonCalculateGroupHeight(chunk.length), isNoGroupSection: true }); }); } const columns = iconComparisonCreateMasonryLayoutOptimized(allGroupChunks); container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; // Добавляем стили для печати если нужно if (isPrintMode) { columnsContainer.style.justifyContent = 'space-between'; columnsContainer.style.gap = '8px'; } // Добавляем только непустые колонки columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); // Центрируем колонки если они не занимают всю ширину (только не в режиме печати) if (!isPrintMode) { const totalColumnsWidth = columns.filter(c => c.children.length > 0).length * 400 + (columns.filter(c => c.children.length > 0).length - 1) * 12; if (totalColumnsWidth <= container.clientWidth) { columnsContainer.style.justifyContent = 'center'; } else { columnsContainer.style.justifyContent = 'flex-start'; } } container.appendChild(columnsContainer); iconComparisonUpdateStats(); }
function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false) { const section = document.createElement('div'); section.className = 'icon-comparison-group'; const header = document.createElement('div'); header.className = 'icon-comparison-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconComparisonCreateIconColumn(icons); section.appendChild(column); return section; }
function iconComparisonCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-comparison-panel'; const header = document.createElement('div'); header.className = 'icon-comparison-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-comparison-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-comparison-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-comparison-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-comparison-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; nameLabel.style.whiteSpace = 'nowrap'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(iconComparisonCreateIconRow(icon)); }); return column; }
function iconComparisonRenderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconComparisonUpdateStats(); return; } const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => { const groupA = a.group || 'zzz'; const groupB = b.group || 'zzz'; if (groupA !== groupB) { return groupA.localeCompare(groupB); } return a.name.localeCompare(b.name); }); // Для режима печати используем двухколоночную раскладку с оптимизацией if (isPrintMode) { // Добавляем специальный класс для печати без групп container.classList.add('print-no-groups'); // Рассчитываем оптимальное количество иконок в панели для печати // Чем больше иконок, тем больше иконок в каждой панели для экономии места const totalIcons = sortedIcons.length; let itemsPerColumn; if (totalIcons <= 50) { itemsPerColumn = 20; // Для малого количества } else if (totalIcons <= 200) { itemsPerColumn = 30; // Для среднего количества } else if (totalIcons <= 500) { itemsPerColumn = 40; // Для большого количества } else { itemsPerColumn = 50; // Для очень большого количества } const columnCount = Math.ceil(totalIcons / itemsPerColumn); console.log(`Print mode without groups: ${totalIcons} icons, ${itemsPerColumn} per panel, ${columnCount} panels`); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); // Для печати добавляем инлайн стили для двухколоночной раскладки column.style.width = 'calc(50% - 4px)'; column.style.maxWidth = 'calc(50% - 4px)'; column.style.float = i % 2 === 0 ? 'left' : 'right'; column.style.marginRight = i % 2 === 0 ? '8px' : '0'; column.style.marginBottom = '8px'; // Для печати уменьшаем размеры внутри панели if (isPrintMode) { const header = column.querySelector('.icon-comparison-panel-header'); if (header) { header.style.padding = '2px'; header.style.marginBottom = '2px'; header.style.gap = '4px'; } const rows = column.querySelectorAll('.icon-comparison-row'); rows.forEach(row => { row.style.padding = '2px'; row.style.minHeight = '28px'; row.style.gap = '3px'; }); const images = column.querySelectorAll('.icon-comparison-image'); images.forEach(img => { img.style.width = '24px'; img.style.height = '24px'; img.style.padding = '1px'; }); const arrows = column.querySelectorAll('.icon-comparison-arrow'); arrows.forEach(arrow => { arrow.style.fontSize = '10px'; }); const names = column.querySelectorAll('.icon-comparison-name'); names.forEach(name => { name.style.fontSize = '9px'; name.style.maxWidth = '150px'; }); } container.appendChild(column); } // Добавляем очистку потока после всех панелей const clearfix = document.createElement('div'); clearfix.style.clear = 'both'; clearfix.style.height = '0'; clearfix.style.overflow = 'hidden'; container.appendChild(clearfix); } else { // Оригинальная логика для обычного просмотра container.classList.remove('print-no-groups'); const itemsPerColumn = 20; const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn); for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = iconComparisonCreateIconColumn(columnIcons); container.appendChild(column); } } iconComparisonUpdateStats(); }
function iconComparisonSetupPrintHandler() { // Обработчик для режима печати window.addEventListener('beforeprint', function() { console.log('Before print - activating print mode'); isPrintMode = true; if (iconComparisonFilteredIcons.length > 0) { // Небольшая задержка для применения CSS стилей печати setTimeout(() => { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }, 50); } }); window.addEventListener('afterprint', function() { console.log('After print - deactivating print mode'); isPrintMode = false; if (iconComparisonFilteredIcons.length > 0) { // Небольшая задержка для возврата к обычным стилям setTimeout(() => { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }, 50); } }); }
function iconComparisonSetupResizeHandler() { let resizeTimeout; let lastWidth = 0; function handleResize() { if (isPrintMode) return; // Не обрабатываем ресайз в режиме печати const currentWidth = document.getElementById('iconComparisonContainer').clientWidth; // Перестраиваем только если ширина изменилась значительно (более 20px) if (Math.abs(currentWidth - lastWidth) > 20) { lastWidth = currentWidth; if (iconComparisonFilteredIcons.length > 0) { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } } } } window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(handleResize, 150); }); }
function iconComparisonProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; // Используем сглаживание при рисовании на canvas ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) { data[i + 3] = 0; } } ctx.putImageData(imageData, 0, 0); const processedImg = new Image(); processedImg.onload = () => resolve({ element: processedImg, processed: true }); processedImg.src = canvas.toDataURL('image/png'); } catch (error) { console.warn('Ошибка обработки BMP:', error); resolve({ element: img, processed: false }); } }); }
function iconComparisonLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconComparisonProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { console.warn('Ошибка обработки изображения:', error, src); img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { console.warn('Ошибка загрузки изображения:', src); const placeholder = new Image(); placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function iconComparisonGroupIcons(icons) { const groups = {}; const noGroup = []; icons.forEach(icon => { if (icon.group && icon.group.trim() !== '') { if (!groups[icon.group]) { groups[icon.group] = []; } groups[icon.group].push(icon); } else { noGroup.push(icon); } }); return { groups, noGroup }; }
function iconComparisonCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconComparisonLoadImage(icon.before, `${icon.name} (до)`), iconComparisonLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : ''); afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-comparison-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-comparison-info'; const name = document.createElement('div'); name.className = 'icon-comparison-name'; name.textContent = icon.name; name.title = icon.name; infoDiv.appendChild(name); row.appendChild(beforeContainer); row.appendChild(arrow); row.appendChild(afterContainer); row.appendChild(infoDiv); return row; }
function iconComparisonFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconComparisonAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { if (selectedGroup === 'no_group') { filtered = filtered.filter(icon => !icon.group || icon.group.trim() === ''); } else { filtered = filtered.filter(icon => icon.group === selectedGroup); } } iconComparisonFilteredIcons = filtered; iconComparisonRenderFilteredIcons(); }
function iconComparisonRenderFilteredIcons() { if (iconComparisonShowGroups) { iconComparisonRenderWithGroups(); } else { iconComparisonRenderWithoutGroups(); } }
function iconComparisonUpdateStats() { document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length; document.getElementById('iconComparisonShownCount').textContent = iconComparisonFilteredIcons.length; }
function iconComparisonInitGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</option>'; // Сортировка групп по алфавиту const sortedGroups = [...groups].sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы if (sortedGroups.length > 0) { sortedGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; groupSelect.appendChild(option); }); } // Проверяем, есть ли группа "Прочие" const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === ''); // Добавляем разделитель и группу "Прочие" в конец списка, если есть обычные группы и группа "Прочие" if (sortedGroups.length > 0 && hasNoGroup) { // Добавляем разделитель const separatorOption = document.createElement('option'); separatorOption.disabled = true; separatorOption.className = 'icon-comparison-option-separator'; separatorOption.textContent = '────────────'; groupSelect.appendChild(separatorOption); // Добавляем группу "Прочие" const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (hasNoGroup) { // Если есть только группа "Прочие" const noGroupOption = document.createElement('option'); noGroupOption.value = 'no_group'; noGroupOption.textContent = 'Прочие'; groupSelect.appendChild(noGroupOption); } else if (sortedGroups.length > 0) { // Если есть только обычные группы - ничего не добавляем } groupSelect.style.display = (sortedGroups.length > 0 || hasNoGroup) ? 'block' : 'none'; groupSelect.addEventListener('change', function() { const searchTerm = document.getElementById('iconComparisonSearch').value; iconComparisonFilterIcons(searchTerm, this.value); }); }
function iconComparisonRenderIcons(images, groups) { iconComparisonAllIcons = images; iconComparisonFilteredIcons = [...iconComparisonAllIcons]; iconComparisonAvailableGroups = groups; iconComparisonInitGroupSelect(groups); iconComparisonRenderFilteredIcons(); }
async function iconComparisonLoadIcons() { try { const script = document.createElement('script'); script.src = 'image_list.js'; document.head.appendChild(script); script.onload = function() { try { const data = loadBeforeAfterImages(); if (data && data.images) { const processedImages = data.images.map(icon => { const displayName = icon.name.replace(/^32x32_/, ''); return { ...icon, name: displayName }; }); iconComparisonRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconComparisonContainer').innerHTML = '<div class="icon-comparison-loading">Ошибка загрузки иконок</div>'; } }
function iconComparisonSetupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; iconComparisonFilterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function iconComparisonSetupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { iconComparisonShowGroups = this.checked; iconComparisonRenderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { iconComparisonLoadIcons(); iconComparisonSetupSearch(); iconComparisonSetupToggle(); iconComparisonSetupResizeHandler(); iconComparisonSetupPrintHandler(); }); </script> </body> </html> Добавлено (2025-12-04, 08:06) --------------------------------------------- <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Сравнение иконок ИСИХОГИ</title> <style> /* Ваш сброс стилей - самое важное исправление! */ * { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; transition: background-color 0.3s ease, color 0.3s ease; }
.icon-comparison-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-comparison-title { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-comparison-search-area { margin: 15px 0; text-align: center; }
.icon-comparison-search { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-checkbox { width: 16px; height: 16px; }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; width: 400px; margin-bottom: 12px; page-break-inside: avoid; /* Важно для печати */ }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; width: 400px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; page-break-inside: avoid; /* Важно для печати */ }
.icon-comparison-panel-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid #f0f0f0; min-height: 36px; }
.icon-comparison-row:last-child { border-bottom: none; }
.icon-comparison-image { width: 32px; height: 32px; object-fit: contain; background-color: #f5f5f5; border-radius: 3px; padding: 2px; box-sizing: border-box; /* Убраны pixelated rendering свойства */ /* image-rendering: -webkit-optimize-contrast; */ /* image-rendering: crisp-edges; */ /* image-rendering: pixelated; */ /* Защита от фильтров темной темы */ filter: none !important; -webkit-filter: none !important; /* Добавляем плавное сглаживание */ image-rendering: auto; -ms-interpolation-mode: bicubic; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-comparison-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-comparison-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: flex-start; }
.icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 400px; max-width: 100%; }
/* Стили для печати - ОЧЕНЬ ВАЖНО: они должны быть ДО других медиа-запросов */ @media print { .icon-comparison-body { padding: 0; font-size: 10px; background-color: white !important; } .icon-comparison-controls { display: none !important; } .icon-comparison-search-area { display: none !important; } .icon-comparison-title { font-size: 14px; margin-bottom: 10px; color: black !important; } .icon-comparison-wrapper { max-width: 100% !important; margin: 0 !important; padding: 0 !important; } .icon-comparison-grid { display: block !important; } .icon-comparison-columns { display: flex !important; flex-wrap: wrap !important; gap: 8px !important; width: 100% !important; justify-content: space-between !important; } .icon-comparison-masonry-column { width: calc(50% - 4px) !important; max-width: calc(50% - 4px) !important; gap: 8px !important; } .icon-comparison-group { width: 100% !important; margin-bottom: 8px !important; break-inside: avoid !important; page-break-inside: avoid !important; } .icon-comparison-group-title { width: 100% !important; font-size: 11px !important; padding: 4px 6px !important; margin-bottom: 4px !important; background: #f0f0f0 !important; color: black !important; } .icon-comparison-panel { width: 100% !important; max-width: 100% !important; padding: 4px !important; break-inside: avoid !important; page-break-inside: avoid !important; border: 1px solid #ccc !important; background: white !important; } /* Специальные стили для режима без групп при печати */ .print-no-groups .icon-comparison-panel { width: calc(50% - 4px) !important; max-width: calc(50% - 4px) !important; float: left !important; margin-right: 8px !important; margin-bottom: 8px !important; } .print-no-groups .icon-comparison-panel:nth-child(2n) { margin-right: 0 !important; float: right !important; } .print-no-groups .icon-comparison-grid { display: block !important; overflow: hidden !important; } .icon-comparison-panel-header { padding: 2px !important; margin-bottom: 2px !important; gap: 4px !important; border-bottom: 1px solid #ccc !important; } .icon-comparison-header-label { font-size: 9px !important; color: black !important; } .icon-comparison-row { padding: 2px !important; min-height: 28px !important; gap: 3px !important; border-bottom: 1px solid #eee !important; } .icon-comparison-image { width: 24px !important; height: 24px !important; padding: 1px !important; background-color: #f0f0f0 !important; filter: none !important; -webkit-filter: none !important; } .icon-comparison-arrow { font-size: 10px !important; color: #666 !important; } .icon-comparison-name { font-size: 9px !important; max-width: 150px !important; color: black !important; } .icon-comparison-bmp-processed { filter: none !important; } /* Отключаем все эффекты темной темы при печати */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body, .dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title, .dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name, .dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { background-color: white !important; color: black !important; } }
/* Убраны стили для разделителя групп в визуальном отображении */
/* Стили для разделителя в выпадающем списке */ .icon-comparison-option-separator { border-top: 1px solid #ddd; margin: 4px 0; pointer-events: none; }
.dark-mode .icon-comparison-option-separator, [data-theme="dark"] .icon-comparison-option-separator, .dark .icon-comparison-option-separator { border-top-color: #555; }
/* Темная тема для Hugo */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-search, [data-theme="dark"] .icon-comparison-search, .dark .icon-comparison-search { background-color: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-controls, [data-theme="dark"] .icon-comparison-controls, .dark .icon-comparison-controls { background: #333333; border-color: #404040; }
.dark-mode .icon-comparison-stats, [data-theme="dark"] .icon-comparison-stats, .dark .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-group-select, [data-theme="dark"] .icon-comparison-group-select, .dark .icon-comparison-group-select { background: #2d2d2d; color: #e0e0e0; border-color: #555; }
.dark-mode .icon-comparison-toggle, [data-theme="dark"] .icon-comparison-toggle, .dark .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title, [data-theme="dark"] .icon-comparison-group-title, .dark .icon-comparison-group-title { background: #3d3d3d; color: #cccccc; }
.dark-mode .icon-comparison-panel, [data-theme="dark"] .icon-comparison-panel, .dark .icon-comparison-panel { background: #2d2d2d; border-color: #404040; }
.dark-mode .icon-comparison-panel-header, [data-theme="dark"] .icon-comparison-panel-header, .dark .icon-comparison-panel-header { border-bottom-color: #404040; color: #e0e0e0; }
.dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row, [data-theme="dark"] .icon-comparison-row, .dark .icon-comparison-row { border-bottom-color: #3a3a3a; }
.dark-mode .icon-comparison-image, [data-theme="dark"] .icon-comparison-image, .dark .icon-comparison-image { background-color: #2a2a2a; filter: none !important; -webkit-filter: none !important; }
.dark-mode .icon-comparison-arrow, [data-theme="dark"] .icon-comparison-arrow, .dark .icon-comparison-arrow { color: #888; }
.dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading, [data-theme="dark"] .icon-comparison-loading, .dark .icon-comparison-loading, .dark-mode .icon-comparison-no-results, [data-theme="dark"] .icon-comparison-no-results, .dark .icon-comparison-no-results { color: #999; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-comparison-wrapper { max-width: 100%; padding: 0 10px; } .icon-comparison-panel { width: 100%; max-width: 400px; } .icon-comparison-search { width: 100%; max-width: 300px; } .icon-comparison-controls { flex-wrap: wrap; gap: 10px; } .icon-comparison-panel-header { gap: 4px; padding: 4px 2px; } .icon-comparison-header-label { font-size: 10px; } .icon-comparison-name { font-size: 11px; } .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-comparison-panel { width: 100%; max-width: 100%; } .icon-comparison-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-comparison-header-label { font-size: 9px; } .icon-comparison-arrow { font-size: 12px; } .icon-comparison-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-comparison-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-comparison-wrapper { max-width: 1600px; } } </style> </head> <body class="icon-comparison-body"> <div class="icon-comparison-wrapper"> <h1 class="icon-comparison-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего иконок для сравнения: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <select id="iconComparisonGroupSelect" class="icon-comparison-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div>
<div class="icon-comparison-grid" id="iconComparisonContainer"> <div class="icon-comparison-loading">Загрузка иконок...</div> </div> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true; let isPrintMode = false;
function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { // Для небольших групп (до 30 элементов) делим пополам const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { // Для больших групп используем оптимальное разбиение const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; }
function iconComparisonCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
|