Отзывы и предложения к софту от AleXStam
  • Страница 28 из 28
  • «
  • 1
  • 2
  • 26
  • 27
  • 28
Поговорим о...
Оооо
Прикрепления:
000000999.noext (49.0 Kb)
<!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;
}

  • Страница 28 из 28
  • «
  • 1
  • 2
  • 26
  • 27
  • 28
Поиск:
Новый ответ
Имя:
Текст сообщения: