|
Поговорим о...
|
|
<!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; }
body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; }
.container { max-width: 1200px; margin: 0 auto; }
h1 { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.search-container { margin: 15px 0; text-align: center; }
.icon-search-input { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; }
.icon-search-input:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.controls-row { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.stats { font-size: 12px; }
.group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.show-groups-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.toggle-checkbox { width: 16px; height: 16px; }
.comparison-container { display: flex; flex-wrap: wrap; gap: 15px; align-items: flex-start; justify-content: center; }
.group-section { break-inside: avoid; }
.group-header { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; }
.icon-column { border: 1px solid #e0e0e0; border-radius: 5px; padding: 8px; background: #fafafa; width: 420px; /* Увеличили ширину с 380px до 420px */ flex-shrink: 0; }
.column-header { display: grid; grid-template-columns: 32px 20px 32px 1fr; gap: 8px; align-items: center; padding: 5px 3px; margin-bottom: 5px; border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-row { display: grid; grid-template-columns: 32px 20px 32px 1fr; gap: 8px; align-items: center; padding: 6px 3px; border-bottom: 1px solid #f0f0f0; min-height: 40px; }
.icon-row:last-child { border-bottom: none; }
.icon-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; }
.bmp-processed { filter: none; }
.arrow { text-align: center; color: #999; font-size: 14px; font-weight: bold; white-space: nowrap; }
.icon-info { padding-left: 8px; padding-right: 5px; min-width: 0; width: 100%; }
.icon-name { color: #333; line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; /* Увеличили шрифт с 11px до 12px */ max-width: 240px; /* Увеличили максимальную ширину */ }
.loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.columns-container { display: flex; flex-wrap: wrap; gap: 15px; width: 100%; justify-content: center; }
.masonry-column { display: flex; flex-direction: column; gap: 15px; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .container { max-width: 100%; padding: 0 10px; } .icon-column { width: 100%; max-width: 420px; /* Обновили максимальную ширину */ } .icon-search-input { width: 100%; max-width: 300px; } .controls-row { flex-wrap: wrap; gap: 10px; } .column-header { gap: 4px; padding: 5px 2px; } .header-label { font-size: 10px; } .icon-name { font-size: 11px; /* Чуть меньше на мобильных, но больше чем было */ } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-column { width: 100%; max-width: 100%; } .column-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .header-label { font-size: 9px; } .arrow { font-size: 12px; } .icon-name { font-size: 11px; max-width: 200px; } } </style> </head> <body> <div class="container"> <h1>Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="search-container"> <input type="text" id="iconSearchInput" class="icon-search-input" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="controls-row"> <div class="stats"> Всего иконок для сравнения: <span id="totalCount">0</span> | Отображено: <span id="shownCount">0</span> </div> <select id="groupSelect" class="group-select" style="display: none;"> <option value="">Все группы</option> </select> <div class="show-groups-toggle"> <input type="checkbox" id="showGroupsToggle" class="toggle-checkbox" checked> <label for="showGroupsToggle">Показывать группы</label> </div> </div>
<div class="comparison-container" id="iconsContainer"> <div class="loading">Загрузка иконок...</div> </div> </div>
<script> let allIcons = []; let filteredIcons = []; let availableGroups = []; let showGroups = true;
// Функция для разбивки больших групп на части function splitLargeGroup(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 calculateGroupHeight(iconsCount) { const headerHeight = 40; const rowHeight = 32; const padding = 16; return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки function createAdaptiveMasonryLayout(groupsChunks) { const container = document.getElementById('iconsContainer'); const containerWidth = container.clientWidth; const columnWidth = 420 + 15; // Обновили ширину колонки с учетом gap const maxColumns = Math.floor(1200 / columnWidth); const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Создаем колонки const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = '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 = createGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой (masonry компоновка) function renderIconsWithGroups() { const container = document.getElementById('iconsContainer'); container.innerHTML = ''; if (filteredIcons.length === 0) { container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; updateStats(); return; } // Группируем иконки const { groups, noGroup } = groupIcons(filteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = splitLargeGroup(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: calculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы if (noGroup.length > 0) { const noGroupChunks = splitLargeGroup(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: calculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = createAdaptiveMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'columns-container'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); updateStats(); }
// Функция для создания секции группы function createGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'group-section'; const header = document.createElement('div'); header.className = 'group-header'; header.textContent = groupName; section.appendChild(header); const column = createIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function createIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-column'; const header = document.createElement('div'); header.className = 'column-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = '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(createIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function setupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (showGroups && filteredIcons.length > 0) { renderIconsWithGroups(); } }, 250); }); }
// Остальные функции без изменений function processBMPWithPinkBackground(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 loadImageWithBMPProcessing(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await processBMPWithPinkBackground(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 groupIcons(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 createIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ loadImageWithBMPProcessing(icon.before, `${icon.name} (до)`), loadImageWithBMPProcessing(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-image' + (beforeResult.processed ? ' bmp-processed' : ''); afterImg.className = 'icon-image' + (afterResult.processed ? ' bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-info'; const name = document.createElement('div'); name.className = 'icon-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 renderIconsWithoutGroups() { const container = document.getElementById('iconsContainer'); container.innerHTML = ''; if (filteredIcons.length === 0) { container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; updateStats(); return; } const sortedIcons = [...filteredIcons].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 = createIconColumn(columnIcons); container.appendChild(column); } updateStats(); }
function filterIcons(searchTerm, selectedGroup = '') { let filtered = [...allIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { filtered = filtered.filter(icon => icon.group === selectedGroup); } filteredIcons = filtered; renderFilteredIcons(); }
function renderFilteredIcons() { if (showGroups) { renderIconsWithGroups(); } else { renderIconsWithoutGroups(); } }
function updateStats() { document.getElementById('totalCount').textContent = allIcons.length; document.getElementById('shownCount').textContent = filteredIcons.length; }
function initGroupSelect(groups) { const groupSelect = document.getElementById('groupSelect'); groupSelect.innerHTML = '<option value="">Все группы</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('iconSearchInput').value; filterIcons(searchTerm, this.value); }); }
function renderIcons(images, groups) { allIcons = images; filteredIcons = [...allIcons]; availableGroups = groups; initGroupSelect(groups); renderFilteredIcons(); }
async function loadIcons() { 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 }; }); renderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconsContainer').innerHTML = '<div class="loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconsContainer').innerHTML = '<div class="loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconsContainer').innerHTML = '<div class="loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconsContainer').innerHTML = '<div class="loading">Ошибка загрузки иконок</div>'; } }
function setupSearch() { const searchInput = document.getElementById('iconSearchInput'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('groupSelect').value; filterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('groupSelect').value; filterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function setupGroupsToggle() { const toggle = document.getElementById('showGroupsToggle'); toggle.addEventListener('change', function() { showGroups = this.checked; renderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { loadIcons(); setupSearch(); setupGroupsToggle(); setupResizeHandler(); }); </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; }
body { font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; }
.container { max-width: 1200px; margin: 0 auto; }
h1 { text-align: center; color: #333; font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.search-container { margin: 15px 0; text-align: center; }
.icon-search-input { width: 300px; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; }
.icon-search-input:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.controls-row { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: #f0f0f0; border-radius: 4px; border: 1px solid #ddd; }
.stats { font-size: 12px; }
.group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; }
.show-groups-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.toggle-checkbox { width: 16px; height: 16px; }
.comparison-container { display: flex; flex-wrap: wrap; gap: 12px; /* Уменьшили gap с 15px до 12px */ align-items: flex-start; justify-content: center; }
.group-section { break-inside: avoid; }
.group-header { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; }
.icon-column { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; /* Уменьшили padding с 8px до 6px */ background: #fafafa; width: 400px; /* Уменьшили ширину с 420px до 400px */ flex-shrink: 0; }
.column-header { display: grid; grid-template-columns: 32px 18px 32px 1fr; /* Уменьшили gap в заголовке */ gap: 6px; /* Уменьшили gap с 8px до 6px */ align-items: center; padding: 4px 2px; /* Уменьшили padding */ margin-bottom: 4px; /* Уменьшили отступ */ border-bottom: 1px solid #ddd; font-weight: bold; text-align: center; }
.header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; /* Уменьшили gap */ gap: 6px; /* Уменьшили gap с 8px до 6px */ align-items: center; padding: 4px 2px; /* Уменьшили padding */ border-bottom: 1px solid #f0f0f0; min-height: 36px; /* Уменьшили минимальную высоту */ }
.icon-row:last-child { border-bottom: none; }
.icon-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; }
.bmp-processed { filter: none; }
.arrow { text-align: center; color: #999; font-size: 13px; /* Уменьшили шрифт стрелки */ font-weight: bold; white-space: nowrap; }
.icon-info { padding-left: 6px; /* Уменьшили padding */ padding-right: 3px; /* Уменьшили padding */ min-width: 0; width: 100%; }
.icon-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; /* Немного уменьшили максимальную ширину */ }
.loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.columns-container { display: flex; flex-wrap: wrap; gap: 12px; /* Уменьшили gap с 15px до 12px */ width: 100%; justify-content: center; }
.masonry-column { display: flex; flex-direction: column; gap: 12px; /* Уменьшили gap с 15px до 12px */ }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .container { max-width: 100%; padding: 0 10px; } .icon-column { width: 100%; max-width: 400px; } .icon-search-input { width: 100%; max-width: 300px; } .controls-row { flex-wrap: wrap; gap: 10px; } .column-header { gap: 4px; padding: 4px 2px; } .header-label { font-size: 10px; } .icon-name { font-size: 11px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-column { width: 100%; max-width: 100%; } .column-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .header-label { font-size: 9px; } .arrow { font-size: 12px; } .icon-name { font-size: 11px; max-width: 200px; } } </style> </head> <body> <div class="container"> <h1>Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="search-container"> <input type="text" id="iconSearchInput" class="icon-search-input" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="controls-row"> <div class="stats"> Всего иконок для сравнения: <span id="totalCount">0</span> | Отображено: <span id="shownCount">0</span> </div> <select id="groupSelect" class="group-select" style="display: none;"> <option value="">Все группы</option> </select> <div class="show-groups-toggle"> <input type="checkbox" id="showGroupsToggle" class="toggle-checkbox" checked> <label for="showGroupsToggle">Показывать группы</label> </div> </div>
<div class="comparison-container" id="iconsContainer"> <div class="loading">Загрузка иконок...</div> </div> </div>
<script> let allIcons = []; let filteredIcons = []; let availableGroups = []; let showGroups = true;
// Функция для разбивки больших групп на части function splitLargeGroup(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 calculateGroupHeight(iconsCount) { const headerHeight = 36; // Уменьшили высоту заголовка const rowHeight = 30; // Уменьшили высоту строки const padding = 12; // Уменьшили padding return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки function createAdaptiveMasonryLayout(groupsChunks) { const container = document.getElementById('iconsContainer'); const containerWidth = container.clientWidth; const columnWidth = 400 + 12; // Обновили ширину колонки с учетом уменьшенного gap const maxColumns = Math.floor(1200 / 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 = '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 = createGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой (masonry компоновка) function renderIconsWithGroups() { const container = document.getElementById('iconsContainer'); container.innerHTML = ''; if (filteredIcons.length === 0) { container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; updateStats(); return; } // Группируем иконки const { groups, noGroup } = groupIcons(filteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = splitLargeGroup(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: calculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы if (noGroup.length > 0) { const noGroupChunks = splitLargeGroup(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: calculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = createAdaptiveMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'columns-container'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); updateStats(); }
// Функция для создания секции группы function createGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'group-section'; const header = document.createElement('div'); header.className = 'group-header'; header.textContent = groupName; section.appendChild(header); const column = createIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function createIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-column'; const header = document.createElement('div'); header.className = 'column-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = '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(createIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function setupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (showGroups && filteredIcons.length > 0) { renderIconsWithGroups(); } }, 250); }); }
// Остальные функции без изменений function processBMPWithPinkBackground(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 loadImageWithBMPProcessing(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await processBMPWithPinkBackground(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+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDI1LjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function groupIcons(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 createIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ loadImageWithBMPProcessing(icon.before, `${icon.name} (до)`), loadImageWithBMPProcessing(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-image' + (beforeResult.processed ? ' bmp-processed' : ''); afterImg.className = 'icon-image' + (afterResult.processed ? ' bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-info'; const name = document.createElement('div'); name.className = 'icon-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 renderIconsWithoutGroups() { const container = document.getElementById('iconsContainer'); container.innerHTML = ''; if (filteredIcons.length === 0) { container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; updateStats(); return; } const sortedIcons = [...filteredIcons].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 = createIconColumn(columnIcons); container.appendChild(column); } updateStats(); }
function filterIcons(searchTerm, selectedGroup = '') { let filtered = [...allIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { filtered = filtered.filter(icon => icon.group === selectedGroup); } filteredIcons = filtered; renderFilteredIcons(); }
function renderFilteredIcons() { if (showGroups) { renderIconsWithGroups(); } else { renderIconsWithoutGroups(); } }
function updateStats() { document.getElementById('totalCount').textContent = allIcons.length; document.getElementById('shownCount').textContent = filteredIcons.length; }
function initGroupSelect(groups) { const groupSelect = document.getElementById('groupSelect'); groupSelect.innerHTML = '<option value="">Все группы</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('iconSearchInput').value; filterIcons(searchTerm, this.value); }); }
function renderIcons(images, groups) { allIcons = images; filteredIcons = [...allIcons]; availableGroups = groups; initGroupSelect(groups); renderFilteredIcons(); }
async function loadIcons() { 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 }; }); renderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('iconsContainer').innerHTML = '<div class="loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('iconsContainer').innerHTML = '<div class="loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('iconsContainer').innerHTML = '<div class="loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('iconsContainer').innerHTML = '<div class="loading">Ошибка загрузки иконок</div>'; } }
function setupSearch() { const searchInput = document.getElementById('iconSearchInput'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('groupSelect').value; filterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('groupSelect').value; filterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function setupGroupsToggle() { const toggle = document.getElementById('showGroupsToggle'); toggle.addEventListener('change', function() { showGroups = this.checked; renderFilteredIcons(); }); }
window.addEventListener('DOMContentLoaded', function() { loadIcons(); setupSearch(); setupGroupsToggle(); setupResizeHandler(); }); </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> .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; }
.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; }
.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; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; }
.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; }
.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; }
/* Адаптивность для мобильных устройств */ @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; } }
/* Для очень маленьких экранов */ @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> </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+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) { 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>'; 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> .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-body.dark-theme { background-color: #1a1a1a; color: #e0e0e0; }
.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; transition: color 0.3s ease; }
.dark-theme .icon-comparison-title { color: #e0e0e0; }
.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: white; color: #333; transition: all 0.3s ease; }
.dark-theme .icon-comparison-search { background-color: #2d2d2d; border-color: #555; color: #e0e0e0; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.dark-theme .icon-comparison-search:focus { border-color: #4dabf7; box-shadow: 0 0 0 2px rgba(77, 171, 247, 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; transition: all 0.3s ease; }
.dark-theme .icon-comparison-controls { background: #2d2d2d; border-color: #444; }
.icon-comparison-stats { font-size: 12px; transition: color 0.3s ease; }
.dark-theme .icon-comparison-stats { color: #e0e0e0; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; color: #333; transition: all 0.3s ease; }
.dark-theme .icon-comparison-group-select { background: #2d2d2d; border-color: #555; color: #e0e0e0; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; transition: color 0.3s ease; }
.dark-theme .icon-comparison-toggle { color: #e0e0e0; }
.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; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; transition: all 0.3s ease; }
.dark-theme .icon-comparison-group-title { background: #2d2d2d; color: #e0e0e0; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; transition: all 0.3s ease; }
.dark-theme .icon-comparison-panel { background: #2d2d2d; border-color: #444; }
.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; transition: all 0.3s ease; }
.dark-theme .icon-comparison-panel-header { border-bottom-color: #555; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; transition: color 0.3s ease; }
.dark-theme .icon-comparison-header-label { color: #e0e0e0; }
.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; transition: all 0.3s ease; }
.dark-theme .icon-comparison-row { border-bottom-color: #444; }
.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: transparent; /* Для темных иконок на светлом фоне и наоборот */ mix-blend-mode: multiply; }
.dark-theme .icon-comparison-image { /* Инвертируем только темные иконки, светлые остаются как есть */ filter: brightness(1.2); mix-blend-mode: screen; }
/* Специальная обработка для обработанных BMP */ .icon-comparison-bmp-processed { filter: none; }
.dark-theme .icon-comparison-bmp-processed { /* Для обработанных BMP сохраняем оригинальный вид */ filter: none; mix-blend-mode: normal; }
.icon-comparison-arrow { text-align: center; color: #999; font-size: 13px; font-weight: bold; white-space: nowrap; transition: color 0.3s ease; }
.dark-theme .icon-comparison-arrow { color: #777; }
.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; transition: color 0.3s ease; }
.dark-theme .icon-comparison-name { color: #e0e0e0; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; transition: color 0.3s ease; }
.dark-theme .icon-comparison-loading { color: #999; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; transition: color 0.3s ease; }
.dark-theme .icon-comparison-no-results { color: #999; }
.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; }
/* Индикатор темы (для отладки) */ .icon-comparison-theme-indicator { position: fixed; bottom: 10px; right: 10px; padding: 5px 10px; background: #007bff; color: white; border-radius: 3px; font-size: 10px; z-index: 1000; opacity: 0.7; }
/* Адаптивность для мобильных устройств */ @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; } }
/* Для очень маленьких экранов */ @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> </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="iconComparisonThemeIndicator" class="icon-comparison-theme-indicator" style="display: none;"> Тема: <span id="iconComparisonThemeStatus">светлая</span> </div>
<script> let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true; let iconComparisonIsDarkTheme = false;
// Функция для проверки и применения темы Hugo function iconComparisonDetectAndApplyTheme() { const body = document.querySelector('.icon-comparison-body'); const indicator = document.getElementById('iconComparisonThemeIndicator'); // Проверяем наличие класса dark-theme на корневом элементе Hugo const htmlElement = document.documentElement; const hugoBody = document.body; // Различные способы определения темной темы в Hugo const isDarkByClass = htmlElement.classList.contains('dark') || hugoBody.classList.contains('dark') || htmlElement.classList.contains('dark-mode') || hugoBody.classList.contains('dark-mode') || htmlElement.classList.contains('theme-dark') || hugoBody.classList.contains('theme-dark'); // Проверяем атрибуты data-theme const isDarkByAttr = htmlElement.getAttribute('data-theme') === 'dark' || hugoBody.getAttribute('data-theme') === 'dark'; // Проверяем CSS переменные const computedStyle = window.getComputedStyle(htmlElement); const bgColor = computedStyle.backgroundColor; const rgb = bgColor.match(/\d+/g); if (rgb) { const brightness = (parseInt(rgb[0]) * 299 + parseInt(rgb[1]) * 587 + parseInt(rgb[2]) * 114) / 1000; const isDarkByColor = brightness < 128; if (isDarkByColor) { iconComparisonIsDarkTheme = true; } } // Применяем тему если нашли признаки темной темы if (isDarkByClass || isDarkByAttr || iconComparisonIsDarkTheme) { body.classList.add('dark-theme'); iconComparisonIsDarkTheme = true; // Обновляем индикатор if (indicator) { indicator.querySelector('#iconComparisonThemeStatus').textContent = 'тёмная'; indicator.style.display = 'block'; } } else { body.classList.remove('dark-theme'); iconComparisonIsDarkTheme = false; // Обновляем индикатор if (indicator) { indicator.querySelector('#iconComparisonThemeStatus').textContent = 'светлая'; indicator.style.display = 'block'; } } }
// Наблюдатель за изменениями темы function iconComparisonSetupThemeObserver() { // Создаем наблюдатель за изменениями атрибутов const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'attributes' && (mutation.attributeName === 'class' || mutation.attributeName === 'data-theme')) { iconComparisonDetectAndApplyTheme(); } }); });
// Начинаем наблюдение за корневым элементом и body observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'data-theme'] }); observer.observe(document.body, { attributes: true, attributeFilter: ['class', 'data-theme'] });
// Также слушаем изменения CSS переменных window.addEventListener('storage', iconComparisonDetectAndApplyTheme); // Проверяем тему при загрузке setTimeout(iconComparisonDetectAndApplyTheme, 100); // И периодически проверяем (на случай динамического переключения) setInterval(iconComparisonDetectAndApplyTheme, 1000); }
// Функция для разбивки больших групп на части 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))); // Создаем колонки 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+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) { 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>'; 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() { // Сначала настраиваем определение темы iconComparisonSetupThemeObserver(); // Затем загружаем остальное 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> .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; }
/* Темная тема - упрощенная версия */ .icon-comparison-body.dark-mode { background-color: #1a1a1a; color: #e0e0e0; }
.dark-mode .icon-comparison-title { color: #e0e0e0; }
.dark-mode .icon-comparison-controls { background: #2d2d2d; border-color: #444; }
.dark-mode .icon-comparison-stats { color: #e0e0e0; }
.dark-mode .icon-comparison-search { background-color: #2d2d2d; border-color: #555; color: #e0e0e0; }
.dark-mode .icon-comparison-group-select { background: #2d2d2d; border-color: #555; color: #e0e0e0; }
.dark-mode .icon-comparison-toggle { color: #e0e0e0; }
.dark-mode .icon-comparison-group-title { background: #2d2d2d; color: #e0e0e0; }
.dark-mode .icon-comparison-panel { background: #2d2d2d; border-color: #444; }
.dark-mode .icon-comparison-panel-header { border-bottom-color: #555; }
.dark-mode .icon-comparison-header-label { color: #e0e0e0; }
.dark-mode .icon-comparison-row { border-bottom-color: #444; }
.dark-mode .icon-comparison-arrow { color: #777; }
.dark-mode .icon-comparison-name { color: #e0e0e0; }
.dark-mode .icon-comparison-loading { color: #999; }
.dark-mode .icon-comparison-no-results { color: #999; }
/* Основные стили */ .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: white; color: #333; }
.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; color: #333; }
.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; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; }
.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; }
.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; }
/* Адаптивность */ @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; } }
@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; } } </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> </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 iconComparisonCheckDarkTheme() { const body = document.querySelector('.icon-comparison-body'); // Проверяем несколько способов определения темной темы if (document.documentElement.classList.contains('dark') || document.body.classList.contains('dark') || document.documentElement.classList.contains('dark-mode') || document.body.classList.contains('dark-mode') || document.documentElement.getAttribute('data-theme') === 'dark' || document.body.getAttribute('data-theme') === 'dark') { body.classList.add('dark-mode'); return true; } // Проверяем по цвету фона const computedStyle = window.getComputedStyle(document.documentElement); const bgColor = computedStyle.backgroundColor; if (bgColor) { const rgb = bgColor.match(/\d+/g); if (rgb) { const brightness = (parseInt(rgb[0]) * 299 + parseInt(rgb[1]) * 587 + parseInt(rgb[2]) * 114) / 1000; if (brightness < 128) { body.classList.add('dark-mode'); return true; } } } // Если не темная тема - убираем класс body.classList.remove('dark-mode'); return false; }
// Функция для разбивки больших групп на части 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))); // Создаем колонки 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); // Создаем 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); }); }
// Обработка BMP 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) { 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>'; 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() { // Простая проверка темы при загрузке setTimeout(iconComparisonCheckDarkTheme, 100); // Загружаем основной функционал 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> /* Наши стили полностью изолированы */ .icon-comparison-page { margin: 0; padding: 0; box-sizing: border-box; font-family: Arial, sans-serif; background-color: #fff; padding: 10px; font-size: 11px; color: #333; /* Изолируем наши стили от Hugo */ all: initial; font-family: Arial, sans-serif !important; }
.icon-comparison-page * { box-sizing: border-box; }
/* Светлая тема по умолчанию */ .icon-comparison-page[data-theme="light"] { background-color: #fff; color: #333; }
/* Темная тема */ .icon-comparison-page[data-theme="dark"] { background-color: #1a1a1a; color: #e0e0e0; }
.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-page[data-theme="dark"] .icon-comparison-title { color: #e0e0e0; }
.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: white; color: #333; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-search { background-color: #2d2d2d; border-color: #555; color: #e0e0e0; }
.icon-comparison-search:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.icon-comparison-page[data-theme="dark"] .icon-comparison-search:focus { border-color: #4dabf7; box-shadow: 0 0 0 2px rgba(77, 171, 247, 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-page[data-theme="dark"] .icon-comparison-controls { background: #2d2d2d; border-color: #444; }
.icon-comparison-stats { font-size: 12px; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-stats { color: #e0e0e0; }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; background: white; color: #333; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-group-select { background: #2d2d2d; border-color: #555; color: #e0e0e0; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-toggle { color: #e0e0e0; }
.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; }
.icon-comparison-group-title { background: #f8f9fa; padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: #495057; font-size: 13px; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-group-title { background: #2d2d2d; color: #e0e0e0; }
.icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 5px; padding: 6px; background: #fafafa; width: 400px; flex-shrink: 0; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-panel { background: #2d2d2d; border-color: #444; }
.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-page[data-theme="dark"] .icon-comparison-panel-header { border-bottom-color: #555; }
.icon-comparison-header-label { font-size: 11px; color: #333; white-space: nowrap; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-header-label { color: #e0e0e0; }
.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-page[data-theme="dark"] .icon-comparison-row { border-bottom-color: #444; }
.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: transparent; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-image { /* Небольшая коррекция для темной темы */ filter: brightness(1.1); }
.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-page[data-theme="dark"] .icon-comparison-arrow { color: #777; }
.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-page[data-theme="dark"] .icon-comparison-name { color: #e0e0e0; }
.icon-comparison-loading { text-align: center; padding: 20px; color: #666; width: 100%; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-loading { color: #999; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: #666; width: 100%; font-size: 14px; }
.icon-comparison-page[data-theme="dark"] .icon-comparison-no-results { color: #999; }
.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; }
/* Адаптивность для мобильных устройств */ @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; } }
/* Для очень маленьких экранов */ @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-page" data-theme="light"> <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> </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> // Глобальные переменные (изолированные) window.iconComparisonData = { allIcons: [], filteredIcons: [], availableGroups: [], showGroups: true, isDarkTheme: false };
// Простая функция определения темы function detectTheme() { const pageElement = document.querySelector('.icon-comparison-page'); // Если страница открыта отдельно - всегда светлая тема if (!window.parent || window.parent === window) { pageElement.setAttribute('data-theme', 'light'); return; } try { // Пробуем определить тему из родительского окна (Hugo) const parentHtml = window.parent.document.documentElement; const parentBody = window.parent.document.body; // Проверяем различные признаки темной темы const isDark = parentHtml.classList.contains('dark') || parentHtml.classList.contains('dark-mode') || parentHtml.classList.contains('theme-dark') || parentBody.classList.contains('dark') || parentBody.classList.contains('dark-mode') || parentBody.classList.contains('theme-dark') || parentHtml.getAttribute('data-theme') === 'dark' || parentBody.getAttribute('data-theme') === 'dark' || window.parent.localStorage.getItem('theme') === 'dark' || window.parent.localStorage.getItem('color-scheme') === 'dark'; if (isDark) { pageElement.setAttribute('data-theme', 'dark'); window.iconComparisonData.isDarkTheme = true; } else { pageElement.setAttribute('data-theme', 'light'); window.iconComparisonData.isDarkTheme = false; } } catch (error) { // Если нет доступа к родительскому окну (CORS) или ошибка pageElement.setAttribute('data-theme', 'light'); window.iconComparisonData.isDarkTheme = false; } }
// Функция для разбивки больших групп на части function splitLargeGroup(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 calculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки function createMasonryLayout(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 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 = createGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой (masonry компоновка) function renderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (window.iconComparisonData.filteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; updateStats(); return; } // Группируем иконки const { groups, noGroup } = groupIcons(window.iconComparisonData.filteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = splitLargeGroup(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: calculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы if (noGroup.length > 0) { const noGroupChunks = splitLargeGroup(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: calculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = createMasonryLayout(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); updateStats(); }
// Функция для создания секции группы function createGroupSection(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 = createIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function createIconColumn(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(createIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function setupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (window.iconComparisonData.showGroups && window.iconComparisonData.filteredIcons.length > 0) { renderWithGroups(); } }, 250); }); }
// Остальные функции function processBMP(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 loadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await processBMP(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 groupIcons(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 createIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-comparison-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ loadImage(icon.before, `${icon.name} (до)`), loadImage(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 renderWithoutGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; if (window.iconComparisonData.filteredIcons.length === 0) { container.innerHTML = '<div class="icon-comparison-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; updateStats(); return; } const sortedIcons = [...window.iconComparisonData.filteredIcons].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 = createIconColumn(columnIcons); container.appendChild(column); } updateStats(); }
function filterIcons(searchTerm, selectedGroup = '') { let filtered = [...window.iconComparisonData.allIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { filtered = filtered.filter(icon => icon.group === selectedGroup); } window.iconComparisonData.filteredIcons = filtered; renderFilteredIcons(); }
function renderFilteredIcons() { if (window.iconComparisonData.showGroups) { renderWithGroups(); } else { renderWithoutGroups(); } }
function updateStats() { document.getElementById('iconComparisonTotalCount').textContent = window.iconComparisonData.allIcons.length; document.getElementById('iconComparisonShownCount').textContent = window.iconComparisonData.filteredIcons.length; }
function initGroupSelect(groups) { const groupSelect = document.getElementById('iconComparisonGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</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; filterIcons(searchTerm, this.value); }); }
function renderIcons(images, groups) { window.iconComparisonData.allIcons = images; window.iconComparisonData.filteredIcons = [...window.iconComparisonData.allIcons]; window.iconComparisonData.availableGroups = groups; initGroupSelect(groups); renderFilteredIcons(); }
async function loadIcons() { 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 }; }); renderIcons(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 setupSearch() { const searchInput = document.getElementById('iconComparisonSearch'); let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; filterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconComparisonGroupSelect').value; filterIcons('', selectedGroup); } }); searchInput.addEventListener('keypress', function(e) { e.stopPropagation(); }); searchInput.addEventListener('keydown', function(e) { e.stopPropagation(); }); }
function setupToggle() { const toggle = document.getElementById('iconComparisonShowGroups'); toggle.addEventListener('change', function() { window.iconComparisonData.showGroups = this.checked; renderFilteredIcons(); }); }
// Инициализация document.addEventListener('DOMContentLoaded', function() { // 1. Определяем тему (простая проверка) detectTheme(); // 2. Загружаем иконки loadIcons(); // 3. Настраиваем остальное setupSearch(); setupToggle(); setupResizeHandler(); // 4. Простая проверка темы при загрузке (без сложных наблюдателей) setTimeout(detectTheme, 100); }); </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> :root { /* Светлая тема (по умолчанию) */ --ic-bg-color: #ffffff; --ic-text-color: #333333; --ic-panel-bg: #fafafa; --ic-border-color: #e0e0e0; --ic-header-bg: #f8f9fa; --ic-header-text: #495057; --ic-controls-bg: #f0f0f0; --ic-search-border: #ddd; --ic-search-focus: #007bff; --ic-arrow-color: #999; --ic-loading-color: #666; --ic-row-border: #f0f0f0; --ic-icon-bg: #f5f5f5; } /* Тёмная тема - активируется, когда Hugo устанавливает темную тему */ :root[data-theme="dark"], html.dark-mode, body.dark, .dark-theme { /* Переопределяем переменные для темной темы */ --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; --ic-icon-bg: #2a2a2a; /* Светло-серый фон для иконок */ }
/* Дополнительные проверки для различных способов Hugo включения темной темы */ [data-mode="dark"], [data-theme="dark"], body[class*="dark"] { --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; --ic-icon-bg: #2a2a2a; }
.icon-comparison-container { margin: 0; padding: 0; box-sizing: border-box; }
.icon-comparison-body { font-family: Arial, sans-serif; background-color: var(--ic-bg-color); color: var(--ic-text-color); 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: var(--ic-text-color); 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 var(--ic-search-border); border-radius: 4px; font-size: 14px; background-color: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.icon-comparison-search:focus { outline: none; border-color: var(--ic-search-focus); box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.25); }
.icon-comparison-search::placeholder { color: var(--ic-arrow-color); }
.icon-comparison-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: var(--ic-controls-bg); border-radius: 4px; border: 1px solid var(--ic-border-color); transition: all 0.3s ease; }
.icon-comparison-stats { font-size: 12px; color: var(--ic-text-color); }
.icon-comparison-group-select { padding: 6px 10px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 12px; background: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--ic-text-color); }
.icon-comparison-checkbox { width: 16px; height: 16px; accent-color: var(--ic-search-focus); }
.icon-comparison-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-comparison-group { break-inside: avoid; }
.icon-comparison-group-title { background: var(--ic-header-bg); padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--ic-header-text); font-size: 13px; transition: all 0.3s ease; }
.icon-comparison-panel { border: 1px solid var(--ic-border-color); border-radius: 5px; padding: 6px; background: var(--ic-panel-bg); width: 400px; flex-shrink: 0; transition: all 0.3s ease; }
.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 var(--ic-border-color); font-weight: bold; text-align: center; color: var(--ic-text-color); }
.icon-comparison-header-label { font-size: 11px; color: var(--ic-text-color); 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 var(--ic-row-border); min-height: 36px; transition: border-color 0.3s ease; }
.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: var(--ic-icon-bg); border-radius: 3px; padding: 2px; box-sizing: border-box; }
.icon-comparison-bmp-processed { filter: none; }
.icon-comparison-arrow { text-align: center; color: var(--ic-arrow-color); 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: var(--ic-text-color); 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: var(--ic-loading-color); width: 100%; }
.icon-comparison-no-results { text-align: center; padding: 40px; color: var(--ic-loading-color); 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; }
/* Адаптивность для мобильных устройств */ @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; } }
/* Для очень маленьких экранов */ @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; } }
/* Стили для placeholder в темной теме */ :root[data-theme="dark"] .icon-comparison-image, html.dark-mode .icon-comparison-image, body.dark .icon-comparison-image { background-color: var(--ic-icon-bg); } </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> </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;
// Функция для определения текущей темы Hugo function iconComparisonDetectHugoTheme() { // Проверяем различные способы, которыми Hugo может указывать тему const htmlElement = document.documentElement; const bodyElement = document.body; // Проверяем data-атрибуты if (htmlElement.hasAttribute('data-theme') && htmlElement.getAttribute('data-theme') === 'dark') { return 'dark'; } if (htmlElement.hasAttribute('data-mode') && htmlElement.getAttribute('data-mode') === 'dark') { return 'dark'; } // Проверяем классы на html if (htmlElement.classList.contains('dark')) { return 'dark'; } if (htmlElement.classList.contains('dark-mode')) { return 'dark'; } // Проверяем классы на body if (bodyElement.classList.contains('dark')) { return 'dark'; } if (bodyElement.classList.contains('dark-theme')) { return 'dark'; } if (bodyElement.classList.contains('dark-mode')) { return 'dark'; } // Проверяем наличие класса dark в любом родительском элементе if (document.querySelector('html.dark, body.dark, .dark, [data-theme="dark"], [data-mode="dark"]')) { return 'dark'; } // По умолчанию светлая тема return 'light'; }
// Функция для применения темы к странице function iconComparisonApplyTheme() { const theme = iconComparisonDetectHugoTheme(); const body = document.querySelector('.icon-comparison-body'); const html = document.documentElement; if (theme === 'dark') { // Устанавливаем data-атрибут для CSS-переменных html.setAttribute('data-theme', 'dark'); // Также добавляем класс для дополнительной специфичности body.classList.add('dark-theme'); } else { html.removeAttribute('data-theme'); body.classList.remove('dark-theme'); } console.log('Тема Hugo определена как:', theme); }
// Наблюдатель за изменениями атрибутов (если Hugo динамически меняет тему) function iconComparisonSetupThemeObserver() { const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'attributes' && (mutation.attributeName === 'class' || mutation.attributeName === 'data-theme' || mutation.attributeName === 'data-mode')) { iconComparisonApplyTheme(); } }); }); // Наблюдаем за изменениями в html и body observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'data-theme', 'data-mode'] }); observer.observe(document.body, { attributes: true, attributeFilter: ['class'] }); }
// Функция для разбивки больших групп на части 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,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSJ2YXIoLS1pYy1pY29uLWJnKSIvPgo8cGF0aCBkPSJNMTYgMThDMTYuNTUyMyAxOCAxNyAxNy41NTIzIDE3IDE3VjExQzE3IDEwLjQ0NzcgMTYuNTUyMyAxMCAxNiAxMEMxNS40NDc3IDEwIDE1IDEwLjQ0NzcgMTUgMTFWMTdDMTUgMTcuNTUyMyAxNS40NDc3IDE4IDE2IDE4WiIgZmlsbD0idmFyKC0taWMtYXJyb3ctY29sb3IpIi8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSJ2YXIoLS1pYy1hcnJvdy1jb2xvcikiLz4KPC9zdmc+Cg=='; 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) { 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>'; 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() { // Применяем тему при загрузке iconComparisonApplyTheme(); // Настраиваем наблюдатель за изменениями темы iconComparisonSetupThemeObserver(); // Загружаем остальные компоненты 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> /* Изолированные CSS-переменные с уникальными префиксами */ .icon-compare-isolated-root { /* Светлая тема (по умолчанию) */ --icon-compare-bg-color: #ffffff; --icon-compare-text-color: #333333; --icon-compare-panel-bg: #fafafa; --icon-compare-border-color: #e0e0e0; --icon-compare-header-bg: #f8f9fa; --icon-compare-header-text: #495057; --icon-compare-controls-bg: #f0f0f0; --icon-compare-search-border: #ddd; --icon-compare-search-focus: #007bff; --icon-compare-arrow-color: #999; --icon-compare-loading-color: #666; --icon-compare-row-border: #f0f0f0; --icon-compare-icon-bg: #f5f5f5; --icon-compare-select-bg: #ffffff; --icon-compare-checkbox-color: #007bff; }
/* Темная тема */ .icon-compare-isolated-root[data-icon-compare-theme="dark"], .icon-compare-isolated-root.icon-compare-dark { --icon-compare-bg-color: #1a1a1a; --icon-compare-text-color: #e0e0e0; --icon-compare-panel-bg: #2d2d2d; --icon-compare-border-color: #404040; --icon-compare-header-bg: #3d3d3d; --icon-compare-header-text: #cccccc; --icon-compare-controls-bg: #333333; --icon-compare-search-border: #555; --icon-compare-search-focus: #4dabf7; --icon-compare-arrow-color: #888; --icon-compare-loading-color: #999; --icon-compare-row-border: #3a3a3a; --icon-compare-icon-bg: #2a2a2a; --icon-compare-select-bg: #2d2d2d; --icon-compare-checkbox-color: #4dabf7; }
/* Основной контейнер с уникальным классом */ .icon-compare-isolated-root { margin: 0; padding: 0; box-sizing: border-box; font-family: Arial, sans-serif; background-color: var(--icon-compare-bg-color); color: var(--icon-compare-text-color); padding: 10px; font-size: 11px; min-height: 100vh; transition: background-color 0.3s ease, color 0.3s ease; isolation: isolate; /* Изолируем от внешних стилей */ }
/* Все классы с уникальным префиксом */ .icon-compare-wrapper { max-width: 1200px; margin: 0 auto; }
.icon-compare-title { text-align: center; color: var(--icon-compare-text-color); font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.icon-compare-search-area { margin: 15px 0; text-align: center; }
.icon-compare-search { width: 300px; padding: 8px 12px; border: 1px solid var(--icon-compare-search-border); border-radius: 4px; font-size: 14px; background-color: var(--icon-compare-bg-color); color: var(--icon-compare-text-color); transition: all 0.3s ease; }
.icon-compare-search:focus { outline: none; border-color: var(--icon-compare-search-focus); box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.25); }
.icon-compare-search::placeholder { color: var(--icon-compare-arrow-color); }
.icon-compare-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: var(--icon-compare-controls-bg); border-radius: 4px; border: 1px solid var(--icon-compare-border-color); transition: all 0.3s ease; }
.icon-compare-stats { font-size: 12px; color: var(--icon-compare-text-color); }
.icon-compare-group-select { padding: 6px 10px; border: 1px solid var(--icon-compare-search-border); border-radius: 4px; font-size: 12px; background: var(--icon-compare-select-bg); color: var(--icon-compare-text-color); transition: all 0.3s ease; cursor: pointer; }
.icon-compare-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--icon-compare-text-color); }
.icon-compare-checkbox { width: 16px; height: 16px; accent-color: var(--icon-compare-checkbox-color); cursor: pointer; }
.icon-compare-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.icon-compare-group { break-inside: avoid; }
.icon-compare-group-title { background: var(--icon-compare-header-bg); padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--icon-compare-header-text); font-size: 13px; transition: all 0.3s ease; }
.icon-compare-panel { border: 1px solid var(--icon-compare-border-color); border-radius: 5px; padding: 6px; background: var(--icon-compare-panel-bg); width: 400px; flex-shrink: 0; transition: all 0.3s ease; }
.icon-compare-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 var(--icon-compare-border-color); font-weight: bold; text-align: center; color: var(--icon-compare-text-color); }
.icon-compare-header-label { font-size: 11px; color: var(--icon-compare-text-color); white-space: nowrap; }
.icon-compare-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid var(--icon-compare-row-border); min-height: 36px; transition: border-color 0.3s ease; }
.icon-compare-row:last-child { border-bottom: none; }
.icon-compare-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; background-color: var(--icon-compare-icon-bg); border-radius: 3px; padding: 2px; box-sizing: border-box; }
.icon-compare-bmp-processed { filter: none; }
.icon-compare-arrow { text-align: center; color: var(--icon-compare-arrow-color); font-size: 13px; font-weight: bold; white-space: nowrap; }
.icon-compare-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.icon-compare-name { color: var(--icon-compare-text-color); line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.icon-compare-loading { text-align: center; padding: 20px; color: var(--icon-compare-loading-color); width: 100%; }
.icon-compare-no-results { text-align: center; padding: 40px; color: var(--icon-compare-loading-color); width: 100%; font-size: 14px; }
.icon-compare-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.icon-compare-masonry-column { display: flex; flex-direction: column; gap: 12px; }
/* Стили для оптимизации перерисовки */ .icon-compare-transition-none { transition: none !important; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .icon-compare-wrapper { max-width: 100%; padding: 0 10px; } .icon-compare-panel { width: 100%; max-width: 400px; } .icon-compare-search { width: 100%; max-width: 300px; } .icon-compare-controls { flex-wrap: wrap; gap: 10px; } .icon-compare-panel-header { gap: 4px; padding: 4px 2px; } .icon-compare-header-label { font-size: 10px; } .icon-compare-name { font-size: 11px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .icon-compare-panel { width: 100%; max-width: 100%; } .icon-compare-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .icon-compare-header-label { font-size: 9px; } .icon-compare-arrow { font-size: 12px; } .icon-compare-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .icon-compare-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .icon-compare-wrapper { max-width: 1600px; } }
/* Глобальный сброс только для нашего контейнера */ .icon-compare-isolated-root * { box-sizing: border-box; margin: 0; padding: 0; font-family: inherit; font-size: inherit; } </style> </head> <body class="icon-compare-isolated-root"> <div class="icon-compare-wrapper"> <h1 class="icon-compare-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="icon-compare-search-area"> <input type="text" id="iconCompareSearch" class="icon-compare-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="icon-compare-controls"> <div class="icon-compare-stats"> Всего иконок для сравнения: <span id="iconCompareTotalCount">0</span> | Отображено: <span id="iconCompareShownCount">0</span> </div> <select id="iconCompareGroupSelect" class="icon-compare-group-select" style="display: none;"> <option value="">Все группы</option> </select> <div class="icon-compare-toggle"> <input type="checkbox" id="iconCompareShowGroups" class="icon-compare-checkbox" checked> <label for="iconCompareShowGroups">Показывать группы</label> </div> </div>
<div class="icon-compare-grid" id="iconCompareContainer"> <div class="icon-compare-loading">Загрузка иконок...</div> </div> </div>
<script> // Изолированные переменные с уникальными префиксами let iconCompareAllIcons = []; let iconCompareFilteredIcons = []; let iconCompareAvailableGroups = []; let iconCompareShowGroups = true; let iconCompareThemeObserver = null; let iconCompareThemeCheckInterval = null;
// Оптимизированная функция для определения темы Hugo function iconCompareDetectHugoTheme() { const htmlElement = document.documentElement; const bodyElement = document.body; // Быстрая проверка основных способов Hugo if (htmlElement.getAttribute('data-theme') === 'dark' || htmlElement.getAttribute('data-mode') === 'dark' || htmlElement.classList.contains('dark') || htmlElement.classList.contains('dark-mode') || bodyElement.classList.contains('dark') || bodyElement.classList.contains('dark-theme') || bodyElement.classList.contains('dark-mode')) { return 'dark'; } return 'light'; }
// Оптимизированная функция для применения темы function iconCompareApplyTheme() { try { const theme = iconCompareDetectHugoTheme(); const rootElement = document.querySelector('.icon-compare-isolated-root'); if (!rootElement) return; // Временно отключаем transitions для предотвращения зависания rootElement.classList.add('icon-compare-transition-none'); if (theme === 'dark') { rootElement.setAttribute('data-icon-compare-theme', 'dark'); rootElement.classList.add('icon-compare-dark'); } else { rootElement.removeAttribute('data-icon-compare-theme'); rootElement.classList.remove('icon-compare-dark'); } // Возвращаем transitions через небольшой таймаут setTimeout(() => { rootElement.classList.remove('icon-compare-transition-none'); }, 50); } catch (error) { console.warn('Ошибка применения темы:', error); } }
// Оптимизированный наблюдатель с ограничением частоты проверки function iconCompareSetupThemeObserver() { // Очищаем предыдущие наблюдатели if (iconCompareThemeObserver) { iconCompareThemeObserver.disconnect(); } if (iconCompareThemeCheckInterval) { clearInterval(iconCompareThemeCheckInterval); } let lastTheme = iconCompareDetectHugoTheme(); // Используем легковесный интервал вместо MutationObserver iconCompareThemeCheckInterval = setInterval(() => { const currentTheme = iconCompareDetectHugoTheme(); if (currentTheme !== lastTheme) { lastTheme = currentTheme; iconCompareApplyTheme(); } }, 500); // Проверка каждые 500ms вместо мгновенной реакции // Минимальный MutationObserver только для атрибутов iconCompareThemeObserver = new MutationObserver((mutations) => { let shouldUpdate = false; for (const mutation of mutations) { if (mutation.type === 'attributes' && (mutation.attributeName === 'class' || mutation.attributeName === 'data-theme' || mutation.attributeName === 'data-mode')) { shouldUpdate = true; break; } } if (shouldUpdate) { // Используем requestAnimationFrame для предотвращения зависания requestAnimationFrame(() => { iconCompareApplyTheme(); }); } }); // Наблюдаем только за основными элементами try { iconCompareThemeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'data-theme', 'data-mode'], attributeOldValue: false // Убираем лишнюю нагрузку }); iconCompareThemeObserver.observe(document.body, { attributes: true, attributeFilter: ['class'], attributeOldValue: false }); } catch (e) { console.warn('Не удалось настроить наблюдатель тем:', e); } }
// Функция для разбивки больших групп на части function iconCompareSplitLargeGroup(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 iconCompareCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки function iconCompareCreateMasonryLayout(groupsChunks) { const container = document.getElementById('iconCompareContainer'); if (!container) return []; 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 columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'icon-compare-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 = iconCompareCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой (masonry компоновка) function iconCompareRenderWithGroups() { const container = document.getElementById('iconCompareContainer'); if (!container) return; container.innerHTML = ''; if (iconCompareFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-compare-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconCompareUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = iconCompareGroupIcons(iconCompareFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = iconCompareSplitLargeGroup(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: iconCompareCalculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы if (noGroup.length > 0) { const noGroupChunks = iconCompareSplitLargeGroup(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: iconCompareCalculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = iconCompareCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-compare-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); iconCompareUpdateStats(); }
// Функция для создания секции группы function iconCompareCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'icon-compare-group'; const header = document.createElement('div'); header.className = 'icon-compare-group-title'; header.textContent = groupName; section.appendChild(header); const column = iconCompareCreateIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function iconCompareCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'icon-compare-panel'; const header = document.createElement('div'); header.className = 'icon-compare-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'icon-compare-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'icon-compare-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'icon-compare-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'icon-compare-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(iconCompareCreateIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function iconCompareSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (iconCompareShowGroups && iconCompareFilteredIcons.length > 0) { iconCompareRenderWithGroups(); } }, 250); }); }
// Остальные функции с уникальными префиксами function iconCompareProcessBMP(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 iconCompareLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await iconCompareProcessBMP(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 iconCompareGroupIcons(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 iconCompareCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'icon-compare-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ iconCompareLoadImage(icon.before, `${icon.name} (до)`), iconCompareLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'icon-compare-image' + (beforeResult.processed ? ' icon-compare-bmp-processed' : ''); afterImg.className = 'icon-compare-image' + (afterResult.processed ? ' icon-compare-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'icon-compare-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'icon-compare-info'; const name = document.createElement('div'); name.className = 'icon-compare-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 iconCompareRenderWithoutGroups() { const container = document.getElementById('iconCompareContainer'); if (!container) return; container.innerHTML = ''; if (iconCompareFilteredIcons.length === 0) { container.innerHTML = '<div class="icon-compare-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; iconCompareUpdateStats(); return; } const sortedIcons = [...iconCompareFilteredIcons].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 = iconCompareCreateIconColumn(columnIcons); container.appendChild(column); } iconCompareUpdateStats(); }
function iconCompareFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...iconCompareAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { filtered = filtered.filter(icon => icon.group === selectedGroup); } iconCompareFilteredIcons = filtered; iconCompareRenderFilteredIcons(); }
function iconCompareRenderFilteredIcons() { if (iconCompareShowGroups) { iconCompareRenderWithGroups(); } else { iconCompareRenderWithoutGroups(); } }
function iconCompareUpdateStats() { const totalCount = document.getElementById('iconCompareTotalCount'); const shownCount = document.getElementById('iconCompareShownCount'); if (totalCount) totalCount.textContent = iconCompareAllIcons.length; if (shownCount) shownCount.textContent = iconCompareFilteredIcons.length; }
function iconCompareInitGroupSelect(groups) { const groupSelect = document.getElementById('iconCompareGroupSelect'); if (!groupSelect) return; groupSelect.innerHTML = '<option value="">Все группы</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('iconCompareSearch').value; iconCompareFilterIcons(searchTerm, this.value); }); }
function iconCompareRenderIcons(images, groups) { iconCompareAllIcons = images; iconCompareFilteredIcons = [...iconCompareAllIcons]; iconCompareAvailableGroups = groups; iconCompareInitGroupSelect(groups); iconCompareRenderFilteredIcons(); }
async function iconCompareLoadIcons() { 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 }; }); iconCompareRenderIcons(processedImages, data.availableGroups || []); } else { const container = document.getElementById('iconCompareContainer'); if (container) container.innerHTML = '<div class="icon-compare-loading">Ошибка загрузки данных</div>'; } } catch (error) { const container = document.getElementById('iconCompareContainer'); if (container) container.innerHTML = '<div class="icon-compare-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { const container = document.getElementById('iconCompareContainer'); if (container) container.innerHTML = '<div class="icon-compare-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { const container = document.getElementById('iconCompareContainer'); if (container) container.innerHTML = '<div class="icon-compare-loading">Ошибка загрузки иконок</div>'; } }
function iconCompareSetupSearch() { const searchInput = document.getElementById('iconCompareSearch'); if (!searchInput) return; let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('iconCompareGroupSelect').value; iconCompareFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('iconCompareGroupSelect').value; iconCompareFilterIcons('', selectedGroup); } }); }
function iconCompareSetupToggle() { const toggle = document.getElementById('iconCompareShowGroups'); if (!toggle) return; toggle.addEventListener('change', function() { iconCompareShowGroups = this.checked; iconCompareRenderFilteredIcons(); }); }
// Очистка при разгрузке страницы function iconCompareCleanup() { if (iconCompareThemeObserver) { iconCompareThemeObserver.disconnect(); iconCompareThemeObserver = null; } if (iconCompareThemeCheckInterval) { clearInterval(iconCompareThemeCheckInterval); iconCompareThemeCheckInterval = null; } }
// Основная инициализация document.addEventListener('DOMContentLoaded', function() { try { // Применяем тему при загрузке iconCompareApplyTheme(); // Настраиваем наблюдатель за изменениями темы iconCompareSetupThemeObserver(); // Загружаем остальные компоненты iconCompareLoadIcons(); iconCompareSetupSearch(); iconCompareSetupToggle(); iconCompareSetupResizeHandler(); // Очистка при закрытии страницы window.addEventListener('beforeunload', iconCompareCleanup); window.addEventListener('pagehide', iconCompareCleanup); } catch (error) { console.error('Ошибка инициализации:', error); } }); </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 { margin: 0; padding: 0; width: 100%; height: 100%; } /* Изолированный контейнер для всей страницы */ .ic-isolated-page { /* Светлая тема (по умолчанию) */ --ic-bg-color: #ffffff; --ic-text-color: #333333; --ic-panel-bg: #fafafa; --ic-border-color: #e0e0e0; --ic-header-bg: #f8f9fa; --ic-header-text: #495057; --ic-controls-bg: #f0f0f0; --ic-search-border: #ddd; --ic-search-focus: #007bff; --ic-arrow-color: #999; --ic-loading-color: #666; --ic-row-border: #f0f0f0; --ic-icon-bg: #f5f5f5; --ic-select-bg: #ffffff; --ic-checkbox-color: #007bff; font-family: Arial, sans-serif; background-color: var(--ic-bg-color); color: var(--ic-text-color); padding: 10px; font-size: 11px; min-height: 100vh; box-sizing: border-box; transition: background-color 0.3s ease, color 0.3s ease; /* Изоляция от внешних стилей */ all: initial; font-family: Arial, sans-serif !important; } /* Темная тема - будет активироваться когда Hugo в темном режиме */ .ic-isolated-page[data-ic-theme="dark"] { --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; --ic-icon-bg: #2a2a2a; --ic-select-bg: #2d2d2d; --ic-checkbox-color: #4dabf7; }
/* Восстанавливаем оригинальные стили с уникальными префиксами */ .ic-isolated-page .ic-container { margin: 0; padding: 0; box-sizing: border-box; }
.ic-isolated-page .ic-body { font-family: Arial, sans-serif; background-color: var(--ic-bg-color); padding: 10px; font-size: 11px; min-height: 100vh; }
.ic-isolated-page .ic-wrapper { max-width: 1200px; margin: 0 auto; }
.ic-isolated-page .ic-title { text-align: center; color: var(--ic-text-color); font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.ic-isolated-page .ic-search-area { margin: 15px 0; text-align: center; }
.ic-isolated-page .ic-search { width: 300px; padding: 8px 12px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 14px; background-color: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-search:focus { outline: none; border-color: var(--ic-search-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.ic-isolated-page .ic-search::placeholder { color: var(--ic-arrow-color); }
.ic-isolated-page .ic-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: var(--ic-controls-bg); border-radius: 4px; border: 1px solid var(--ic-border-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-stats { font-size: 12px; color: var(--ic-text-color); }
.ic-isolated-page .ic-group-select { padding: 6px 10px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 12px; background: var(--ic-select-bg); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--ic-text-color); }
.ic-isolated-page .ic-checkbox { width: 16px; height: 16px; accent-color: var(--ic-checkbox-color); }
.ic-isolated-page .ic-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.ic-isolated-page .ic-group { break-inside: avoid; }
.ic-isolated-page .ic-group-title { background: var(--ic-header-bg); padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--ic-header-text); font-size: 13px; transition: all 0.3s ease; }
.ic-isolated-page .ic-panel { border: 1px solid var(--ic-border-color); border-radius: 5px; padding: 6px; background: var(--ic-panel-bg); width: 400px; flex-shrink: 0; transition: all 0.3s ease; }
.ic-isolated-page .ic-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 var(--ic-border-color); font-weight: bold; text-align: center; color: var(--ic-text-color); }
.ic-isolated-page .ic-header-label { font-size: 11px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid var(--ic-row-border); min-height: 36px; transition: border-color 0.3s ease; }
.ic-isolated-page .ic-row:last-child { border-bottom: none; }
.ic-isolated-page .ic-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; background-color: var(--ic-icon-bg); border-radius: 3px; padding: 2px; box-sizing: border-box; }
.ic-isolated-page .ic-bmp-processed { filter: none; }
.ic-isolated-page .ic-arrow { text-align: center; color: var(--ic-arrow-color); font-size: 13px; font-weight: bold; white-space: nowrap; }
.ic-isolated-page .ic-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.ic-isolated-page .ic-name { color: var(--ic-text-color); line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.ic-isolated-page .ic-loading { text-align: center; padding: 20px; color: var(--ic-loading-color); width: 100%; }
.ic-isolated-page .ic-no-results { text-align: center; padding: 40px; color: var(--ic-loading-color); width: 100%; font-size: 14px; }
.ic-isolated-page .ic-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.ic-isolated-page .ic-masonry-column { display: flex; flex-direction: column; gap: 12px; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .ic-isolated-page .ic-wrapper { max-width: 100%; padding: 0 10px; } .ic-isolated-page .ic-panel { width: 100%; max-width: 400px; } .ic-isolated-page .ic-search { width: 100%; max-width: 300px; } .ic-isolated-page .ic-controls { flex-wrap: wrap; gap: 10px; } .ic-isolated-page .ic-panel-header { gap: 4px; padding: 4px 2px; } .ic-isolated-page .ic-header-label { font-size: 10px; } .ic-isolated-page .ic-name { font-size: 11px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .ic-isolated-page .ic-panel { width: 100%; max-width: 100%; } .ic-isolated-page .ic-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .ic-isolated-page .ic-header-label { font-size: 9px; } .ic-isolated-page .ic-arrow { font-size: 12px; } .ic-isolated-page .ic-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1400px) { .ic-isolated-page .ic-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .ic-isolated-page .ic-wrapper { max-width: 1600px; } } </style> </head> <body class="ic-isolated-page"> <div class="ic-container"> <div class="ic-body"> <div class="ic-wrapper"> <h1 class="ic-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="ic-search-area"> <input type="text" id="icSearch" class="ic-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="ic-controls"> <div class="ic-stats"> Всего иконок для сравнения: <span id="icTotalCount">0</span> | Отображено: <span id="icShownCount">0</span> </div> <select id="icGroupSelect" class="ic-group-select" style="display: none;"> <option value="">Все группы</option> </select> <div class="ic-toggle"> <input type="checkbox" id="icShowGroups" class="ic-checkbox" checked> <label for="icShowGroups">Показывать группы</label> </div> </div>
<div class="ic-grid" id="icContainer"> <div class="ic-loading">Загрузка иконок...</div> </div> </div> </div> </div>
<script> // Используем IIFE для полной изоляции (function() { 'use strict'; let icAllIcons = []; let icFilteredIcons = []; let icAvailableGroups = []; let icShowGroups = true; let icThemeCheckInterval = null; let icResizeTimeout = null; let icSearchTimeout = null;
// Функция для определения темы Hugo - легковесная function icDetectHugoTheme() { const html = document.documentElement; const body = document.body; // Быстрая проверка основных способов Hugo if (html.getAttribute('data-theme') === 'dark' || html.getAttribute('data-mode') === 'dark' || html.classList.contains('dark') || html.classList.contains('dark-mode') || body.classList.contains('dark') || body.classList.contains('dark-theme') || body.classList.contains('dark-mode') || document.querySelector('.dark-mode, .dark-theme, [data-theme="dark"]')) { return 'dark'; } return 'light'; }
// Функция для применения темы - без transitions чтобы не зависать function icApplyTheme() { try { const theme = icDetectHugoTheme(); const page = document.querySelector('.ic-isolated-page'); if (!page) return; // Быстрое переключение без анимации if (theme === 'dark') { page.setAttribute('data-ic-theme', 'dark'); } else { page.removeAttribute('data-ic-theme'); } } catch (error) { // Игнорируем ошибки, чтобы не ломать страницу } }
// Простой наблюдатель за темой function icSetupThemeObserver() { // Очищаем предыдущий интервал if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); } let lastTheme = icDetectHugoTheme(); // Проверяем тему раз в секунду - этого достаточно icThemeCheckInterval = setInterval(() => { const currentTheme = icDetectHugoTheme(); if (currentTheme !== lastTheme) { lastTheme = currentTheme; icApplyTheme(); } }, 1000); // Применяем тему сразу icApplyTheme(); }
// Оригинальные функции с короткими именами function icSplitLargeGroup(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 icCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function icCreateMasonryLayout(groupsChunks) { const container = document.getElementById('icContainer'); 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 columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'ic-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 = icCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
function icRenderWithGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = icGroupIcons(icFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы if (noGroup.length > 0) { const noGroupChunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = icCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); icUpdateStats(); }
function icCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'ic-group'; const header = document.createElement('div'); header.className = 'ic-group-title'; header.textContent = groupName; section.appendChild(header); const column = icCreateIconColumn(icons); section.appendChild(column); return section; }
function icCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'ic-panel'; const header = document.createElement('div'); header.className = 'ic-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'ic-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'ic-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'ic-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'ic-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(icCreateIconRow(icon)); }); return column; }
function icSetupResizeHandler() { window.addEventListener('resize', function() { clearTimeout(icResizeTimeout); icResizeTimeout = setTimeout(() => { if (icShowGroups && icFilteredIcons.length > 0) { icRenderWithGroups(); } }, 250); }); }
function icProcessBMP(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) { resolve({ element: img, processed: false }); } }); }
function icLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await icProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { 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 icGroupIcons(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 icCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'ic-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ icLoadImage(icon.before, `${icon.name} (до)`), icLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'ic-image' + (beforeResult.processed ? ' ic-bmp-processed' : ''); afterImg.className = 'ic-image' + (afterResult.processed ? ' ic-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'ic-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'ic-info'; const name = document.createElement('div'); name.className = 'ic-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 icRenderWithoutGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } const sortedIcons = [...icFilteredIcons].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 = icCreateIconColumn(columnIcons); container.appendChild(column); } icUpdateStats(); }
function icFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...icAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { filtered = filtered.filter(icon => icon.group === selectedGroup); } icFilteredIcons = filtered; icRenderFilteredIcons(); }
function icRenderFilteredIcons() { if (icShowGroups) { icRenderWithGroups(); } else { icRenderWithoutGroups(); } }
function icUpdateStats() { document.getElementById('icTotalCount').textContent = icAllIcons.length; document.getElementById('icShownCount').textContent = icFilteredIcons.length; }
function icInitGroupSelect(groups) { const groupSelect = document.getElementById('icGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</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('icSearch').value; icFilterIcons(searchTerm, this.value); }); }
function icRenderIcons(images, groups) { icAllIcons = images; icFilteredIcons = [...icAllIcons]; icAvailableGroups = groups; icInitGroupSelect(groups); icRenderFilteredIcons(); }
function icLoadIcons() { 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 }; }); icRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки иконок</div>'; } }
function icSetupSearch() { const searchInput = document.getElementById('icSearch'); searchInput.addEventListener('input', function(e) { clearTimeout(icSearchTimeout); icSearchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons('', selectedGroup); } }); }
function icSetupToggle() { const toggle = document.getElementById('icShowGroups'); toggle.addEventListener('change', function() { icShowGroups = this.checked; icRenderFilteredIcons(); }); }
// Очистка при разгрузке function icCleanup() { if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); icThemeCheckInterval = null; } clearTimeout(icResizeTimeout); clearTimeout(icSearchTimeout); }
// Инициализация document.addEventListener('DOMContentLoaded', function() { // Настраиваем тему icSetupThemeObserver(); // Загружаем иконки icLoadIcons(); // Настраиваем остальное icSetupSearch(); icSetupToggle(); icSetupResizeHandler(); // Очистка window.addEventListener('beforeunload', icCleanup); window.addEventListener('pagehide', icCleanup); }); })(); </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 { margin: 0; padding: 0; width: 100%; height: 100%; } /* Изолированный контейнер для всей страницы */ .ic-isolated-page { /* Светлая тема (по умолчанию) */ --ic-bg-color: #ffffff; --ic-text-color: #333333; --ic-panel-bg: #fafafa; --ic-border-color: #e0e0e0; --ic-header-bg: #f8f9fa; --ic-header-text: #495057; --ic-controls-bg: #f0f0f0; --ic-search-border: #ddd; --ic-search-focus: #007bff; --ic-arrow-color: #999; --ic-loading-color: #666; --ic-row-border: #f0f0f0; --ic-icon-bg: #f5f5f5; --ic-select-bg: #ffffff; --ic-checkbox-color: #007bff; font-family: Arial, sans-serif; background-color: var(--ic-bg-color); color: var(--ic-text-color); padding: 10px; font-size: 11px; min-height: 100vh; box-sizing: border-box; transition: background-color 0.3s ease, color 0.3s ease; /* Изоляция от внешних стилей */ all: initial; font-family: Arial, sans-serif !important; } /* Темная тема - будет активироваться когда Hugo в темном режиме */ .ic-isolated-page[data-ic-theme="dark"] { --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; --ic-icon-bg: #2a2a2a; --ic-select-bg: #2d2d2d; --ic-checkbox-color: #4dabf7; }
/* Восстанавливаем оригинальные стили с уникальными префиксами */ .ic-isolated-page .ic-container { margin: 0; padding: 0; box-sizing: border-box; }
.ic-isolated-page .ic-body { font-family: Arial, sans-serif; background-color: var(--ic-bg-color); padding: 10px; font-size: 11px; min-height: 100vh; }
.ic-isolated-page .ic-wrapper { max-width: 1200px; margin: 0 auto; }
.ic-isolated-page .ic-title { text-align: center; color: var(--ic-text-color); font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.ic-isolated-page .ic-search-area { margin: 15px 0; text-align: center; }
.ic-isolated-page .ic-search { width: 300px; padding: 8px 12px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 14px; background-color: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-search:focus { outline: none; border-color: var(--ic-search-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.ic-isolated-page .ic-search::placeholder { color: var(--ic-arrow-color); }
.ic-isolated-page .ic-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: var(--ic-controls-bg); border-radius: 4px; border: 1px solid var(--ic-border-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-stats { font-size: 12px; color: var(--ic-text-color); }
.ic-isolated-page .ic-group-select { padding: 6px 10px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 12px; background: var(--ic-select-bg); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--ic-text-color); }
.ic-isolated-page .ic-checkbox { width: 16px; height: 16px; accent-color: var(--ic-checkbox-color); }
.ic-isolated-page .ic-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.ic-isolated-page .ic-group { break-inside: avoid; }
.ic-isolated-page .ic-group-title { background: var(--ic-header-bg); padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--ic-header-text); font-size: 13px; transition: all 0.3s ease; }
.ic-isolated-page .ic-panel { border: 1px solid var(--ic-border-color); border-radius: 5px; padding: 6px; background: var(--ic-panel-bg); width: 400px; flex-shrink: 0; transition: all 0.3s ease; }
.ic-isolated-page .ic-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 var(--ic-border-color); font-weight: bold; text-align: center; color: var(--ic-text-color); }
.ic-isolated-page .ic-header-label { font-size: 11px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid var(--ic-row-border); min-height: 36px; transition: border-color 0.3s ease; }
.ic-isolated-page .ic-row:last-child { border-bottom: none; }
.ic-isolated-page .ic-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; background-color: var(--ic-icon-bg); border-radius: 3px; padding: 2px; box-sizing: border-box; }
.ic-isolated-page .ic-bmp-processed { filter: none; }
.ic-isolated-page .ic-arrow { text-align: center; color: var(--ic-arrow-color); font-size: 13px; font-weight: bold; white-space: nowrap; }
.ic-isolated-page .ic-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.ic-isolated-page .ic-name { color: var(--ic-text-color); line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.ic-isolated-page .ic-loading { text-align: center; padding: 20px; color: var(--ic-loading-color); width: 100%; }
.ic-isolated-page .ic-no-results { text-align: center; padding: 40px; color: var(--ic-loading-color); width: 100%; font-size: 14px; }
.ic-isolated-page .ic-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.ic-isolated-page .ic-masonry-column { display: flex; flex-direction: column; gap: 12px; }
/* Медиа-запросы для адаптивной ширины колонок */ @media (max-width: 831px) { .ic-isolated-page .ic-panel { width: 100%; max-width: 400px; } .ic-isolated-page .ic-wrapper { max-width: 100%; padding: 0 10px; } .ic-isolated-page .ic-search { width: 100%; max-width: 300px; } .ic-isolated-page .ic-controls { flex-wrap: wrap; gap: 10px; } .ic-isolated-page .ic-panel-header { gap: 4px; padding: 4px 2px; } .ic-isolated-page .ic-header-label { font-size: 10px; } .ic-isolated-page .ic-name { font-size: 11px; } }
/* При ширине 832px и выше - две колонки */ @media (min-width: 832px) and (max-width: 1199px) { .ic-isolated-page .ic-panel { width: calc(50% - 6px); max-width: none; min-width: 300px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .ic-isolated-page .ic-panel { width: 100%; max-width: 100%; } .ic-isolated-page .ic-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .ic-isolated-page .ic-header-label { font-size: 9px; } .ic-isolated-page .ic-arrow { font-size: 12px; } .ic-isolated-page .ic-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов - больше колонок */ @media (min-width: 1200px) { .ic-isolated-page .ic-wrapper { max-width: 1200px; } }
@media (min-width: 1400px) { .ic-isolated-page .ic-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .ic-isolated-page .ic-wrapper { max-width: 1600px; } } </style> </head> <body class="ic-isolated-page"> <div class="ic-container"> <div class="ic-body"> <div class="ic-wrapper"> <h1 class="ic-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="ic-search-area"> <input type="text" id="icSearch" class="ic-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="ic-controls"> <div class="ic-stats"> Всего иконок для сравнения: <span id="icTotalCount">0</span> | Отображено: <span id="icShownCount">0</span> </div> <select id="icGroupSelect" class="ic-group-select" style="display: none;"> <option value="">Все группы</option> </select> <div class="ic-toggle"> <input type="checkbox" id="icShowGroups" class="ic-checkbox" checked> <label for="icShowGroups">Показывать группы</label> </div> </div>
<div class="ic-grid" id="icContainer"> <div class="ic-loading">Загрузка иконок...</div> </div> </div> </div> </div>
<script> (function() { 'use strict'; let icAllIcons = []; let icFilteredIcons = []; let icAvailableGroups = []; let icShowGroups = true; let icThemeCheckInterval = null; let icResizeTimeout = null; let icSearchTimeout = null;
// Функция для определения темы Hugo - легковесная function icDetectHugoTheme() { const html = document.documentElement; const body = document.body; // Быстрая проверка основных способов Hugo if (html.getAttribute('data-theme') === 'dark' || html.getAttribute('data-mode') === 'dark' || html.classList.contains('dark') || html.classList.contains('dark-mode') || body.classList.contains('dark') || body.classList.contains('dark-theme') || body.classList.contains('dark-mode') || document.querySelector('.dark-mode, .dark-theme, [data-theme="dark"]')) { return 'dark'; } return 'light'; }
// Функция для применения темы - без transitions чтобы не зависать function icApplyTheme() { try { const theme = icDetectHugoTheme(); const page = document.querySelector('.ic-isolated-page'); if (!page) return; // Быстрое переключение без анимации if (theme === 'dark') { page.setAttribute('data-ic-theme', 'dark'); } else { page.removeAttribute('data-ic-theme'); } } catch (error) { // Игнорируем ошибки, чтобы не ломать страницу } }
// Простой наблюдатель за темой function icSetupThemeObserver() { // Очищаем предыдущий интервал if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); } let lastTheme = icDetectHugoTheme(); // Проверяем тему раз в секунду - этого достаточно icThemeCheckInterval = setInterval(() => { const currentTheme = icDetectHugoTheme(); if (currentTheme !== lastTheme) { lastTheme = currentTheme; icApplyTheme(); } }, 1000); // Применяем тему сразу icApplyTheme(); }
// Оригинальные функции с короткими именами function icSplitLargeGroup(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 icCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
function icCreateMasonryLayout(groupsChunks) { const container = document.getElementById('icContainer'); if (!container) return []; const containerWidth = container.clientWidth; // Определяем количество колонок на основе ширины контейнера let columnCount; if (containerWidth >= 1200) { // Широкие экраны - 3 колонки columnCount = Math.min(3, groupsChunks.length); } else if (containerWidth >= 832) { // Средние экраны (832px и выше) - 2 колонки columnCount = Math.min(2, groupsChunks.length); } else { // Узкие экраны - 1 колонка columnCount = 1; } // Создаем колонки const columns = Array.from({ length: columnCount }, () => { const col = document.createElement('div'); col.className = 'ic-masonry-column'; col.style.flex = '1'; col.style.minWidth = '0'; return col; }); // Массив для отслеживания высоты каждой колонки const columnHeights = Array(columnCount).fill(0); // Распределяем группы по колонкам (алгоритм "наименьшая высота") groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = icCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); // Учитываем высоту группы columnHeights[columnIndex] += group.height; }); return columns; }
function icRenderWithGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = icGroupIcons(icFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы if (noGroup.length > 0) { const noGroupChunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = icCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; columnsContainer.style.display = 'flex'; columnsContainer.style.flexWrap = 'wrap'; columnsContainer.style.gap = '12px'; columnsContainer.style.justifyContent = 'center'; columnsContainer.style.width = '100%'; columns.forEach(col => { if (col.children.length > 0) { // Рассчитываем ширину колонки в зависимости от их количества const containerWidth = container.clientWidth; let columnWidth; if (containerWidth >= 1200) { // 3 колонки columnWidth = `calc(33.333% - 8px)`; } else if (containerWidth >= 832) { // 2 колонки columnWidth = `calc(50% - 6px)`; } else { // 1 колонка columnWidth = '100%'; } col.style.width = columnWidth; col.style.maxWidth = '400px'; col.style.minWidth = '300px'; col.style.flexShrink = '0'; columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); icUpdateStats(); }
function icCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'ic-group'; const header = document.createElement('div'); header.className = 'ic-group-title'; header.textContent = groupName; section.appendChild(header); const column = icCreateIconColumn(icons); section.appendChild(column); return section; }
function icCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'ic-panel'; column.style.width = '100%'; column.style.boxSizing = 'border-box'; const header = document.createElement('div'); header.className = 'ic-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'ic-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'ic-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'ic-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'ic-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(icCreateIconRow(icon)); }); return column; }
function icSetupResizeHandler() { window.addEventListener('resize', function() { clearTimeout(icResizeTimeout); icResizeTimeout = setTimeout(() => { if (icShowGroups && icFilteredIcons.length > 0) { icRenderWithGroups(); } }, 250); }); }
function icProcessBMP(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) { resolve({ element: img, processed: false }); } }); }
function icLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await icProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { 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 icGroupIcons(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 icCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'ic-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ icLoadImage(icon.before, `${icon.name} (до)`), icLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'ic-image' + (beforeResult.processed ? ' ic-bmp-processed' : ''); afterImg.className = 'ic-image' + (afterResult.processed ? ' ic-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'ic-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'ic-info'; const name = document.createElement('div'); name.className = 'ic-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 icRenderWithoutGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } const sortedIcons = [...icFilteredIcons].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); // Создаем контейнер для колонок const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; columnsContainer.style.display = 'flex'; columnsContainer.style.flexWrap = 'wrap'; columnsContainer.style.gap = '12px'; columnsContainer.style.justifyContent = 'center'; columnsContainer.style.width = '100%'; for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = icCreateIconColumn(columnIcons); // Применяем ту же логику ширины что и в режиме с группами const containerWidth = container.clientWidth; let columnWidth; if (containerWidth >= 1200) { columnWidth = '400px'; } else if (containerWidth >= 832) { columnWidth = `calc(50% - 6px)`; } else { columnWidth = '100%'; } column.style.width = columnWidth; column.style.maxWidth = '400px'; column.style.minWidth = '300px'; column.style.flexShrink = '0'; columnsContainer.appendChild(column); } container.appendChild(columnsContainer); icUpdateStats(); }
function icFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...icAllIcons]; if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(icon => icon.name.toLowerCase().includes(term) || (icon.group && icon.group.toLowerCase().includes(term)) ); } if (selectedGroup) { filtered = filtered.filter(icon => icon.group === selectedGroup); } icFilteredIcons = filtered; icRenderFilteredIcons(); }
function icRenderFilteredIcons() { if (icShowGroups) { icRenderWithGroups(); } else { icRenderWithoutGroups(); } }
function icUpdateStats() { document.getElementById('icTotalCount').textContent = icAllIcons.length; document.getElementById('icShownCount').textContent = icFilteredIcons.length; }
function icInitGroupSelect(groups) { const groupSelect = document.getElementById('icGroupSelect'); groupSelect.innerHTML = '<option value="">Все группы</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('icSearch').value; icFilterIcons(searchTerm, this.value); }); }
function icRenderIcons(images, groups) { icAllIcons = images; icFilteredIcons = [...icAllIcons]; icAvailableGroups = groups; icInitGroupSelect(groups); icRenderFilteredIcons(); }
function icLoadIcons() { 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 }; }); icRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки иконок</div>'; } }
function icSetupSearch() { const searchInput = document.getElementById('icSearch'); searchInput.addEventListener('input', function(e) { clearTimeout(icSearchTimeout); icSearchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons('', selectedGroup); } }); }
function icSetupToggle() { const toggle = document.getElementById('icShowGroups'); toggle.addEventListener('change', function() { icShowGroups = this.checked; icRenderFilteredIcons(); }); }
// Очистка при разгрузке function icCleanup() { if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); icThemeCheckInterval = null; } clearTimeout(icResizeTimeout); clearTimeout(icSearchTimeout); }
// Инициализация document.addEventListener('DOMContentLoaded', function() { // Настраиваем тему icSetupThemeObserver(); // Загружаем иконки icLoadIcons(); // Настраиваем остальное icSetupSearch(); icSetupToggle(); icSetupResizeHandler(); // Очистка window.addEventListener('beforeunload', icCleanup); window.addEventListener('pagehide', icCleanup); }); })(); </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 { margin: 0; padding: 0; width: 100%; height: 100%; } /* Изолированный контейнер для всей страницы */ .ic-isolated-page { /* Светлая тема (по умолчанию) */ --ic-bg-color: #ffffff; --ic-text-color: #333333; --ic-panel-bg: #fafafa; --ic-border-color: #e0e0e0; --ic-header-bg: #f8f9fa; --ic-header-text: #495057; --ic-controls-bg: #f0f0f0; --ic-search-border: #ddd; --ic-search-focus: #007bff; --ic-arrow-color: #999; --ic-loading-color: #666; --ic-row-border: #f0f0f0; --ic-icon-bg: #f5f5f5; --ic-select-bg: #ffffff; --ic-checkbox-color: #007bff; font-family: Arial, sans-serif; background-color: var(--ic-bg-color); color: var(--ic-text-color); padding: 10px; font-size: 11px; min-height: 100vh; box-sizing: border-box; transition: background-color 0.3s ease, color 0.3s ease; /* Изоляция от внешних стилей */ all: initial; font-family: Arial, sans-serif !important; } /* Темная тема - будет активироваться когда Hugo в темном режиме */ .ic-isolated-page[data-ic-theme="dark"] { --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; --ic-icon-bg: #2a2a2a; --ic-select-bg: #2d2d2d; --ic-checkbox-color: #4dabf7; }
/* Восстанавливаем оригинальные стили с уникальными префиксами */ .ic-isolated-page .ic-container { margin: 0; padding: 0; box-sizing: border-box; }
.ic-isolated-page .ic-body { font-family: Arial, sans-serif; background-color: var(--ic-bg-color); padding: 10px; font-size: 11px; min-height: 100vh; }
.ic-isolated-page .ic-wrapper { max-width: 1400px; margin: 0 auto; }
.ic-isolated-page .ic-title { text-align: center; color: var(--ic-text-color); font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.ic-isolated-page .ic-search-area { margin: 15px 0; text-align: center; }
.ic-isolated-page .ic-search { width: 300px; padding: 8px 12px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 14px; background-color: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-search:focus { outline: none; border-color: var(--ic-search-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.ic-isolated-page .ic-search::placeholder { color: var(--ic-arrow-color); }
.ic-isolated-page .ic-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: var(--ic-controls-bg); border-radius: 4px; border: 1px solid var(--ic-border-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-stats { font-size: 12px; color: var(--ic-text-color); }
.ic-isolated-page .ic-group-select { padding: 6px 10px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 12px; background: var(--ic-select-bg); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--ic-text-color); }
.ic-isolated-page .ic-checkbox { width: 16px; height: 16px; accent-color: var(--ic-checkbox-color); }
.ic-isolated-page .ic-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.ic-isolated-page .ic-group { break-inside: avoid; width: 100%; }
.ic-isolated-page .ic-group-title { background: var(--ic-header-bg); padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--ic-header-text); font-size: 13px; transition: all 0.3s ease; }
.ic-isolated-page .ic-panel { border: 1px solid var(--ic-border-color); border-radius: 5px; padding: 6px; background: var(--ic-panel-bg); width: 400px; flex-shrink: 0; transition: all 0.3s ease; }
.ic-isolated-page .ic-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 var(--ic-border-color); font-weight: bold; text-align: center; color: var(--ic-text-color); }
.ic-isolated-page .ic-header-label { font-size: 11px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid var(--ic-row-border); min-height: 36px; transition: border-color 0.3s ease; }
.ic-isolated-page .ic-row:last-child { border-bottom: none; }
.ic-isolated-page .ic-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; background-color: var(--ic-icon-bg); border-radius: 3px; padding: 2px; box-sizing: border-box; }
.ic-isolated-page .ic-bmp-processed { filter: none; }
.ic-isolated-page .ic-arrow { text-align: center; color: var(--ic-arrow-color); font-size: 13px; font-weight: bold; white-space: nowrap; }
.ic-isolated-page .ic-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.ic-isolated-page .ic-name { color: var(--ic-text-color); line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.ic-isolated-page .ic-loading { text-align: center; padding: 20px; color: var(--ic-loading-color); width: 100%; }
.ic-isolated-page .ic-no-results { text-align: center; padding: 40px; color: var(--ic-loading-color); width: 100%; font-size: 14px; }
.ic-isolated-page .ic-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.ic-isolated-page .ic-masonry-column { display: flex; flex-direction: column; gap: 12px; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .ic-isolated-page .ic-wrapper { max-width: 100%; padding: 0 10px; } .ic-isolated-page .ic-search { width: 100%; max-width: 300px; } .ic-isolated-page .ic-controls { flex-wrap: wrap; gap: 10px; } .ic-isolated-page .ic-panel-header { gap: 4px; padding: 4px 2px; } .ic-isolated-page .ic-header-label { font-size: 10px; } .ic-isolated-page .ic-name { font-size: 11px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .ic-isolated-page .ic-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .ic-isolated-page .ic-header-label { font-size: 9px; } .ic-isolated-page .ic-arrow { font-size: 12px; } .ic-isolated-page .ic-name { font-size: 11px; max-width: 200px; } }
/* Для широких экранов */ @media (min-width: 1400px) { .ic-isolated-page .ic-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .ic-isolated-page .ic-wrapper { max-width: 1600px; } } </style> </head> <body class="ic-isolated-page"> <div class="ic-container"> <div class="ic-body"> <div class="ic-wrapper"> <h1 class="ic-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="ic-search-area"> <input type="text" id="icSearch" class="ic-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="ic-controls"> <div class="ic-stats"> Всего иконок для сравнения: <span id="icTotalCount">0</span> | Отображено: <span id="icShownCount">0</span> </div> <select id="icGroupSelect" class="ic-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="ic-toggle"> <input type="checkbox" id="icShowGroups" class="ic-checkbox" checked> <label for="icShowGroups">Показывать группы</label> </div> </div>
<div class="ic-grid" id="icContainer"> <div class="ic-loading">Загрузка иконок...</div> </div> </div> </div> </div>
<script> (function() { 'use strict'; let icAllIcons = []; let icFilteredIcons = []; let icAvailableGroups = []; let icShowGroups = true; let icThemeCheckInterval = null; let icResizeTimeout = null; let icSearchTimeout = null;
// Функция для определения темы Hugo - легковесная function icDetectHugoTheme() { const html = document.documentElement; const body = document.body; // Быстрая проверка основных способов Hugo if (html.getAttribute('data-theme') === 'dark' || html.getAttribute('data-mode') === 'dark' || html.classList.contains('dark') || html.classList.contains('dark-mode') || body.classList.contains('dark') || body.classList.contains('dark-theme') || body.classList.contains('dark-mode') || document.querySelector('.dark-mode, .dark-theme, [data-theme="dark"]')) { return 'dark'; } return 'light'; }
// Функция для применения темы - без transitions чтобы не зависать function icApplyTheme() { try { const theme = icDetectHugoTheme(); const page = document.querySelector('.ic-isolated-page'); if (!page) return; // Быстрое переключение без анимации if (theme === 'dark') { page.setAttribute('data-ic-theme', 'dark'); } else { page.removeAttribute('data-ic-theme'); } } catch (error) { // Игнорируем ошибки, чтобы не ломать страницу } }
// Простой наблюдатель за темой function icSetupThemeObserver() { // Очищаем предыдущий интервал if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); } let lastTheme = icDetectHugoTheme(); // Проверяем тему раз в секунду - этого достаточно icThemeCheckInterval = setInterval(() => { const currentTheme = icDetectHugoTheme(); if (currentTheme !== lastTheme) { lastTheme = currentTheme; icApplyTheme(); } }, 1000); // Применяем тему сразу icApplyTheme(); }
// Функция для разбивки больших групп на части function icSplitLargeGroup(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 icCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки function icCreateMasonryLayout(groupsChunks) { const container = document.getElementById('icContainer'); if (!container) return []; const containerWidth = container.clientWidth; const columnWidth = 400 + 12; // Ширина панели + отступ // Динамическое количество колонок let maxColumns; if (containerWidth >= 1400) { maxColumns = 4; } else if (containerWidth >= 1200) { maxColumns = 3; } else if (containerWidth >= 832) { maxColumns = 2; } else { maxColumns = 1; } 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 = 'ic-masonry-column'; col.style.width = '100%'; col.style.maxWidth = '412px'; // 400px + 12px отступ return col; }); // Массив для отслеживания высоты каждой колонки const columnHeights = Array(availableColumns).fill(0); // Распределяем группы по колонкам (алгоритм "наименьшая высота") groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = icCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой (masonry компоновка) function icRenderWithGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = icGroupIcons(icFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы как "Прочие" if (noGroup.length > 0) { const noGroupChunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = icCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); icUpdateStats(); }
// Функция для создания секции группы function icCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'ic-group'; const header = document.createElement('div'); header.className = 'ic-group-title'; header.textContent = groupName; section.appendChild(header); const column = icCreateIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function icCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'ic-panel'; const header = document.createElement('div'); header.className = 'ic-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'ic-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'ic-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'ic-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'ic-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(icCreateIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function icSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (icShowGroups && icFilteredIcons.length > 0) { icRenderWithGroups(); } }, 250); }); }
// Остальные функции с уникальными префиксами function icProcessBMP(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) { resolve({ element: img, processed: false }); } }); }
function icLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await icProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { 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 icGroupIcons(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 icCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'ic-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ icLoadImage(icon.before, `${icon.name} (до)`), icLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'ic-image' + (beforeResult.processed ? ' ic-bmp-processed' : ''); afterImg.className = 'ic-image' + (afterResult.processed ? ' ic-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'ic-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'ic-info'; const name = document.createElement('div'); name.className = 'ic-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 icRenderWithoutGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } const sortedIcons = [...icFilteredIcons].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 = icCreateIconColumn(columnIcons); container.appendChild(column); } icUpdateStats(); }
function icFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...icAllIcons]; 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); } } icFilteredIcons = filtered; icRenderFilteredIcons(); }
function icRenderFilteredIcons() { if (icShowGroups) { icRenderWithGroups(); } else { icRenderWithoutGroups(); } }
function icUpdateStats() { document.getElementById('icTotalCount').textContent = icAllIcons.length; document.getElementById('icShownCount').textContent = icFilteredIcons.length; }
function icInitGroupSelect(groups) { const groupSelect = document.getElementById('icGroupSelect'); 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('icSearch').value; icFilterIcons(searchTerm, this.value); }); }
function icRenderIcons(images, groups) { icAllIcons = images; icFilteredIcons = [...icAllIcons]; icAvailableGroups = groups; icInitGroupSelect(groups); icRenderFilteredIcons(); }
async function icLoadIcons() { 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 }; }); icRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки иконок</div>'; } }
function icSetupSearch() { const searchInput = document.getElementById('icSearch'); searchInput.addEventListener('input', function(e) { clearTimeout(icSearchTimeout); icSearchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons('', selectedGroup); } }); }
function icSetupToggle() { const toggle = document.getElementById('icShowGroups'); toggle.addEventListener('change', function() { icShowGroups = this.checked; icRenderFilteredIcons(); }); }
// Очистка при разгрузке function icCleanup() { if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); icThemeCheckInterval = null; } clearTimeout(icResizeTimeout); clearTimeout(icSearchTimeout); }
// Инициализация document.addEventListener('DOMContentLoaded', function() { // Настраиваем тему icSetupThemeObserver(); // Загружаем иконки icLoadIcons(); // Настраиваем остальное icSetupSearch(); icSetupToggle(); icSetupResizeHandler(); // Очистка window.addEventListener('beforeunload', icCleanup); window.addEventListener('pagehide', icCleanup); }); })(); </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 { margin: 0; padding: 0; width: 100%; height: 100%; } /* Изолированный контейнер для всей страницы */ .ic-isolated-page { /* Светлая тема (по умолчанию) */ --ic-bg-color: #ffffff; --ic-text-color: #333333; --ic-panel-bg: #fafafa; --ic-border-color: #e0e0e0; --ic-header-bg: #f8f9fa; --ic-header-text: #495057; --ic-controls-bg: #f0f0f0; --ic-search-border: #ddd; --ic-search-focus: #007bff; --ic-arrow-color: #999; --ic-loading-color: #666; --ic-row-border: #f0f0f0; --ic-icon-bg: #f5f5f5; --ic-select-bg: #ffffff; --ic-checkbox-color: #007bff; font-family: Arial, sans-serif; background-color: var(--ic-bg-color); color: var(--ic-text-color); padding: 10px; font-size: 11px; min-height: 100vh; box-sizing: border-box; transition: background-color 0.3s ease, color 0.3s ease; /* Изоляция от внешних стилей */ all: initial; font-family: Arial, sans-serif !important; } /* Темная тема - будет активироваться когда Hugo в темном режиме */ .ic-isolated-page[data-ic-theme="dark"] { --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; --ic-icon-bg: #2a2a2a; --ic-select-bg: #2d2d2d; --ic-checkbox-color: #4dabf7; }
/* Восстанавливаем оригинальные стили с уникальными префиксами */ .ic-isolated-page .ic-container { margin: 0; padding: 0; box-sizing: border-box; }
.ic-isolated-page .ic-body { font-family: Arial, sans-serif; background-color: var(--ic-bg-color); padding: 10px; font-size: 11px; min-height: 100vh; }
.ic-isolated-page .ic-wrapper { max-width: 1400px; margin: 0 auto; }
.ic-isolated-page .ic-title { text-align: center; color: var(--ic-text-color); font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.ic-isolated-page .ic-search-area { margin: 15px 0; text-align: center; }
.ic-isolated-page .ic-search { width: 300px; padding: 8px 12px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 14px; background-color: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-search:focus { outline: none; border-color: var(--ic-search-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.ic-isolated-page .ic-search::placeholder { color: var(--ic-arrow-color); }
.ic-isolated-page .ic-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: var(--ic-controls-bg); border-radius: 4px; border: 1px solid var(--ic-border-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-stats { font-size: 12px; color: var(--ic-text-color); }
.ic-isolated-page .ic-group-select { padding: 6px 10px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 12px; background: var(--ic-select-bg); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--ic-text-color); }
.ic-isolated-page .ic-checkbox { width: 16px; height: 16px; accent-color: var(--ic-checkbox-color); }
.ic-isolated-page .ic-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: center; }
.ic-isolated-page .ic-group { break-inside: avoid; width: 100%; }
.ic-isolated-page .ic-group-title { background: var(--ic-header-bg); padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--ic-header-text); font-size: 13px; transition: all 0.3s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ic-isolated-page .ic-panel { border: 1px solid var(--ic-border-color); border-radius: 5px; padding: 6px; background: var(--ic-panel-bg); width: 380px; /* Уменьшено с 400px до 380px */ flex-shrink: 0; transition: all 0.3s ease; }
.ic-isolated-page .ic-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 var(--ic-border-color); font-weight: bold; text-align: center; color: var(--ic-text-color); }
.ic-isolated-page .ic-header-label { font-size: 11px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid var(--ic-row-border); min-height: 36px; transition: border-color 0.3s ease; }
.ic-isolated-page .ic-row:last-child { border-bottom: none; }
.ic-isolated-page .ic-image { width: 32px; height: 32px; object-fit: contain; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated; background-color: var(--ic-icon-bg); border-radius: 3px; padding: 2px; box-sizing: border-box; }
.ic-isolated-page .ic-bmp-processed { filter: none; }
.ic-isolated-page .ic-arrow { text-align: center; color: var(--ic-arrow-color); font-size: 13px; font-weight: bold; white-space: nowrap; }
.ic-isolated-page .ic-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.ic-isolated-page .ic-name { color: var(--ic-text-color); line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 200px; /* Уменьшено с 220px до 200px */ }
.ic-isolated-page .ic-loading { text-align: center; padding: 20px; color: var(--ic-loading-color); width: 100%; }
.ic-isolated-page .ic-no-results { text-align: center; padding: 40px; color: var(--ic-loading-color); width: 100%; font-size: 14px; }
.ic-isolated-page .ic-columns { display: flex; flex-wrap: wrap; gap: 10px; /* Уменьшено с 12px до 10px */ width: 100%; justify-content: center; }
.ic-isolated-page .ic-masonry-column { display: flex; flex-direction: column; gap: 12px; }
/* Адаптивность для мобильных устройств */ @media (max-width: 768px) { .ic-isolated-page .ic-wrapper { max-width: 100%; padding: 0 10px; } .ic-isolated-page .ic-search { width: 100%; max-width: 300px; } .ic-isolated-page .ic-controls { flex-wrap: wrap; gap: 10px; } .ic-isolated-page .ic-panel-header { gap: 4px; padding: 4px 2px; } .ic-isolated-page .ic-header-label { font-size: 10px; } .ic-isolated-page .ic-name { font-size: 11px; max-width: 180px; } .ic-isolated-page .ic-panel { width: 100%; max-width: 380px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .ic-isolated-page .ic-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 3px; } .ic-isolated-page .ic-header-label { font-size: 9px; } .ic-isolated-page .ic-arrow { font-size: 12px; } .ic-isolated-page .ic-name { font-size: 11px; max-width: 150px; } .ic-isolated-page .ic-group-title { font-size: 12px; padding: 4px 8px; } }
/* Для широких экранов */ @media (min-width: 1400px) { .ic-isolated-page .ic-wrapper { max-width: 1400px; } }
@media (min-width: 1600px) { .ic-isolated-page .ic-wrapper { max-width: 1600px; } .ic-isolated-page .ic-panel { width: 380px; } } </style> </head> <body class="ic-isolated-page"> <div class="ic-container"> <div class="ic-body"> <div class="ic-wrapper"> <h1 class="ic-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="ic-search-area"> <input type="text" id="icSearch" class="ic-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="ic-controls"> <div class="ic-stats"> Всего иконок для сравнения: <span id="icTotalCount">0</span> | Отображено: <span id="icShownCount">0</span> </div> <select id="icGroupSelect" class="ic-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="ic-toggle"> <input type="checkbox" id="icShowGroups" class="ic-checkbox" checked> <label for="icShowGroups">Показывать группы</label> </div> </div>
<div class="ic-grid" id="icContainer"> <div class="ic-loading">Загрузка иконок...</div> </div> </div> </div> </div>
<script> (function() { 'use strict'; let icAllIcons = []; let icFilteredIcons = []; let icAvailableGroups = []; let icShowGroups = true; let icThemeCheckInterval = null; let icResizeTimeout = null; let icSearchTimeout = null;
// Функция для определения темы Hugo - легковесная function icDetectHugoTheme() { const html = document.documentElement; const body = document.body; // Быстрая проверка основных способов Hugo if (html.getAttribute('data-theme') === 'dark' || html.getAttribute('data-mode') === 'dark' || html.classList.contains('dark') || html.classList.contains('dark-mode') || body.classList.contains('dark') || body.classList.contains('dark-theme') || body.classList.contains('dark-mode') || document.querySelector('.dark-mode, .dark-theme, [data-theme="dark"]')) { return 'dark'; } return 'light'; }
// Функция для применения темы - без transitions чтобы не зависать function icApplyTheme() { try { const theme = icDetectHugoTheme(); const page = document.querySelector('.ic-isolated-page'); if (!page) return; // Быстрое переключение без анимации if (theme === 'dark') { page.setAttribute('data-ic-theme', 'dark'); } else { page.removeAttribute('data-ic-theme'); } } catch (error) { // Игнорируем ошибки, чтобы не ломать страницу } }
// Простой наблюдатель за темой function icSetupThemeObserver() { // Очищаем предыдущий интервал if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); } let lastTheme = icDetectHugoTheme(); // Проверяем тему раз в секунду - этого достаточно icThemeCheckInterval = setInterval(() => { const currentTheme = icDetectHugoTheme(); if (currentTheme !== lastTheme) { lastTheme = currentTheme; icApplyTheme(); } }, 1000); // Применяем тему сразу icApplyTheme(); }
// Функция для разбивки больших групп на части function icSplitLargeGroup(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 icCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки function icCreateMasonryLayout(groupsChunks) { const container = document.getElementById('icContainer'); if (!container) return []; const containerWidth = container.clientWidth; const panelWidth = 380; // Ширина панели const gap = 10; // Отступ между колонками const totalPanelWidth = panelWidth + gap; // Динамическое количество колонок let maxColumns; if (containerWidth >= 1400) { maxColumns = 4; } else if (containerWidth >= 1200) { maxColumns = 3; } else if (containerWidth >= 800) { // Уменьшено с 832px до 800px maxColumns = 2; } else { maxColumns = 1; } const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / totalPanelWidth))); console.log(`Container width: ${containerWidth}, Panel width: ${panelWidth}, Columns: ${availableColumns}`); // Создаем колонки const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'ic-masonry-column'; col.style.width = `${panelWidth}px`; col.style.flexShrink = '0'; return col; }); // Массив для отслеживания высоты каждой колонки const columnHeights = Array(availableColumns).fill(0); // Распределяем группы по колонкам (алгоритм "наименьшая высота") groupsChunks.forEach(group => { let minHeight = Math.min(...columnHeights); let columnIndex = columnHeights.indexOf(minHeight); const section = icCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой (masonry компоновка) function icRenderWithGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = icGroupIcons(icFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы как "Прочие" if (noGroup.length > 0) { const noGroupChunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = icCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; columnsContainer.style.display = 'flex'; columnsContainer.style.flexWrap = 'wrap'; columnsContainer.style.gap = '10px'; columnsContainer.style.justifyContent = 'center'; columnsContainer.style.width = '100%'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); icUpdateStats(); }
// Функция для создания секции группы function icCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'ic-group'; const header = document.createElement('div'); header.className = 'ic-group-title'; header.textContent = groupName; header.title = groupName; section.appendChild(header); const column = icCreateIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function icCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'ic-panel'; const header = document.createElement('div'); header.className = 'ic-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'ic-header-label'; beforeLabel.textContent = 'ДО'; beforeLabel.style.whiteSpace = 'nowrap'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'ic-arrow'; arrowSpace.textContent = '→'; arrowSpace.style.whiteSpace = 'nowrap'; const afterLabel = document.createElement('div'); afterLabel.className = 'ic-header-label'; afterLabel.textContent = 'ПОСЛЕ'; afterLabel.style.whiteSpace = 'nowrap'; const nameLabel = document.createElement('div'); nameLabel.className = 'ic-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(icCreateIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function icSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (icShowGroups && icFilteredIcons.length > 0) { icRenderWithGroups(); } }, 250); }); }
// Остальные функции с уникальными префиксами function icProcessBMP(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) { resolve({ element: img, processed: false }); } }); }
function icLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await icProcessBMP(img); result.element.alt = alt; resolve(result); } else { img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { 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 icGroupIcons(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 icCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'ic-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ icLoadImage(icon.before, `${icon.name} (до)`), icLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'ic-image' + (beforeResult.processed ? ' ic-bmp-processed' : ''); afterImg.className = 'ic-image' + (afterResult.processed ? ' ic-bmp-processed' : ''); beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'ic-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'ic-info'; const name = document.createElement('div'); name.className = 'ic-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 icRenderWithoutGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } const sortedIcons = [...icFilteredIcons].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); // Создаем контейнер для колонок const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; columnsContainer.style.display = 'flex'; columnsContainer.style.flexWrap = 'wrap'; columnsContainer.style.gap = '10px'; columnsContainer.style.justifyContent = 'center'; columnsContainer.style.width = '100%'; for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = icCreateIconColumn(columnIcons); columnsContainer.appendChild(column); } container.appendChild(columnsContainer); icUpdateStats(); }
function icFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...icAllIcons]; 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); } } icFilteredIcons = filtered; icRenderFilteredIcons(); }
function icRenderFilteredIcons() { if (icShowGroups) { icRenderWithGroups(); } else { icRenderWithoutGroups(); } }
function icUpdateStats() { document.getElementById('icTotalCount').textContent = icAllIcons.length; document.getElementById('icShownCount').textContent = icFilteredIcons.length; }
function icInitGroupSelect(groups) { const groupSelect = document.getElementById('icGroupSelect'); 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('icSearch').value; icFilterIcons(searchTerm, this.value); }); }
function icRenderIcons(images, groups) { icAllIcons = images; icFilteredIcons = [...icAllIcons]; icAvailableGroups = groups; icInitGroupSelect(groups); icRenderFilteredIcons(); }
async function icLoadIcons() { 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 }; }); icRenderIcons(processedImages, data.availableGroups || []); } else { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки данных</div>'; } } catch (error) { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { document.getElementById('icContainer').innerHTML = '<div class="ic-loading">Ошибка загрузки иконок</div>'; } }
function icSetupSearch() { const searchInput = document.getElementById('icSearch'); searchInput.addEventListener('input', function(e) { clearTimeout(icSearchTimeout); icSearchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons('', selectedGroup); } }); }
function icSetupToggle() { const toggle = document.getElementById('icShowGroups'); toggle.addEventListener('change', function() { icShowGroups = this.checked; icRenderFilteredIcons(); }); }
// Очистка при разгрузке function icCleanup() { if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); icThemeCheckInterval = null; } clearTimeout(icResizeTimeout); clearTimeout(icSearchTimeout); }
// Инициализация document.addEventListener('DOMContentLoaded', function() { // Настраиваем тему icSetupThemeObserver(); // Загружаем иконки icLoadIcons(); // Настраиваем остальное icSetupSearch(); icSetupToggle(); icSetupResizeHandler(); // Очистка window.addEventListener('beforeunload', icCleanup); window.addEventListener('pagehide', icCleanup); }); })(); </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; } /* Изолированный контейнер для всей страницы */ .ic-isolated-page { /* Светлая тема (по умолчанию) */ --ic-bg-color: #ffffff; --ic-text-color: #333333; --ic-panel-bg: #fafafa; --ic-border-color: #e0e0e0; --ic-header-bg: #f8f9fa; --ic-header-text: #495057; --ic-controls-bg: #f0f0f0; --ic-search-border: #ddd; --ic-search-focus: #007bff; --ic-arrow-color: #999; --ic-loading-color: #666; --ic-row-border: #f0f0f0; --ic-icon-bg: #f5f5f5; --ic-select-bg: #ffffff; --ic-checkbox-color: #007bff; /* Базовые стили */ font-family: Arial, sans-serif; background-color: var(--ic-bg-color); color: var(--ic-text-color); padding: 10px; font-size: 11px; min-height: 100vh; width: 100%; overflow-x: hidden; } /* Темная тема - ТОЛЬКО для фона и текста, не для иконок */ .ic-isolated-page[data-ic-theme="dark"] { --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; /* Фон для иконок остается светлым для контраста */ --ic-icon-bg: #2a2a2a; --ic-select-bg: #2d2d2d; --ic-checkbox-color: #4dabf7; }
/* Основные стили с уникальными префиксами */ .ic-isolated-page .ic-container { width: 100%; margin: 0; padding: 0; }
.ic-isolated-page .ic-body { width: 100%; min-height: 100vh; }
.ic-isolated-page .ic-wrapper { max-width: 1400px; margin: 0 auto; width: 100%; }
.ic-isolated-page .ic-title { text-align: center; color: var(--ic-text-color); font-size: 16px; margin-bottom: 15px; font-weight: bold; padding: 0 10px; }
.ic-isolated-page .ic-search-area { margin: 15px 0; text-align: center; padding: 0 10px; }
.ic-isolated-page .ic-search { width: 300px; max-width: 100%; padding: 8px 12px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 14px; background-color: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-search:focus { outline: none; border-color: var(--ic-search-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.ic-isolated-page .ic-search::placeholder { color: var(--ic-arrow-color); }
.ic-isolated-page .ic-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 10px; padding: 8px; background: var(--ic-controls-bg); border-radius: 4px; border: 1px solid var(--ic-border-color); transition: all 0.3s ease; flex-wrap: wrap; }
.ic-isolated-page .ic-stats { font-size: 12px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-group-select { padding: 6px 10px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 12px; background: var(--ic-select-bg); color: var(--ic-text-color); transition: all 0.3s ease; min-width: 150px; }
.ic-isolated-page .ic-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--ic-text-color); }
.ic-isolated-page .ic-checkbox { width: 16px; height: 16px; accent-color: var(--ic-checkbox-color); }
.ic-isolated-page .ic-grid { width: 100%; padding: 0 10px; }
.ic-isolated-page .ic-group { margin-bottom: 12px; width: 100%; }
.ic-isolated-page .ic-group-title { background: var(--ic-header-bg); padding: 8px 12px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--ic-header-text); font-size: 13px; transition: all 0.3s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; }
.ic-isolated-page .ic-panel { border: 1px solid var(--ic-border-color); border-radius: 5px; padding: 8px; background: var(--ic-panel-bg); width: 100%; max-width: 380px; min-width: 300px; flex-shrink: 0; transition: all 0.3s ease; }
.ic-isolated-page .ic-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 var(--ic-border-color); font-weight: bold; text-align: center; color: var(--ic-text-color); width: 100%; }
.ic-isolated-page .ic-header-label { font-size: 11px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid var(--ic-row-border); min-height: 36px; transition: border-color 0.3s ease; width: 100%; }
.ic-isolated-page .ic-row:last-child { border-bottom: none; }
.ic-isolated-page .ic-image { width: 32px; height: 32px; object-fit: contain; background-color: var(--ic-icon-bg); border-radius: 3px; padding: 0; display: block; /* Убираем все фильтры для сохранения цветов */ filter: none !important; -webkit-filter: none !important; image-rendering: auto; }
.ic-isolated-page .ic-bmp-processed { /* Убираем все фильтры */ filter: none !important; -webkit-filter: none !important; }
.ic-isolated-page .ic-arrow { text-align: center; color: var(--ic-arrow-color); font-size: 13px; font-weight: bold; white-space: nowrap; }
.ic-isolated-page .ic-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.ic-isolated-page .ic-name { color: var(--ic-text-color); line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 200px; }
.ic-isolated-page .ic-loading { text-align: center; padding: 40px 20px; color: var(--ic-loading-color); width: 100%; }
.ic-isolated-page .ic-no-results { text-align: center; padding: 40px; color: var(--ic-loading-color); width: 100%; font-size: 14px; }
.ic-isolated-page .ic-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.ic-isolated-page .ic-masonry-column { display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 380px; }
/* Адаптивность */ @media (max-width: 768px) { .ic-isolated-page .ic-wrapper { max-width: 100%; } .ic-isolated-page .ic-search { width: 100%; max-width: 300px; } .ic-isolated-page .ic-controls { gap: 10px; margin: 12px 5px; } .ic-isolated-page .ic-group-select { min-width: 120px; } .ic-isolated-page .ic-name { max-width: 180px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .ic-isolated-page .ic-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 4px; } .ic-isolated-page .ic-header-label { font-size: 10px; } .ic-isolated-page .ic-arrow { font-size: 12px; } .ic-isolated-page .ic-name { font-size: 11px; max-width: 150px; } .ic-isolated-page .ic-group-title { font-size: 12px; padding: 6px 10px; } .ic-isolated-page .ic-masonry-column { max-width: 100%; } .ic-isolated-page .ic-panel { max-width: 100%; min-width: 280px; } }
/* Для ширины 800px и выше - 2 колонки */ @media (min-width: 800px) { .ic-isolated-page .ic-masonry-column { max-width: calc(50% - 6px); } }
/* Для ширины 1200px и выше - 3 колонки */ @media (min-width: 1200px) { .ic-isolated-page .ic-masonry-column { max-width: calc(33.333% - 8px); } }
/* Для ширины 1400px и выше - 4 колонки */ @media (min-width: 1400px) { .ic-isolated-page .ic-wrapper { max-width: 1400px; } .ic-isolated-page .ic-masonry-column { max-width: calc(25% - 9px); } } </style> </head> <body class="ic-isolated-page"> <div class="ic-container"> <div class="ic-body"> <div class="ic-wrapper"> <h1 class="ic-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="ic-search-area"> <input type="text" id="icSearch" class="ic-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="ic-controls"> <div class="ic-stats"> Всего иконок для сравнения: <span id="icTotalCount">0</span> | Отображено: <span id="icShownCount">0</span> </div> <select id="icGroupSelect" class="ic-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="ic-toggle"> <input type="checkbox" id="icShowGroups" class="ic-checkbox" checked> <label for="icShowGroups">Показывать группы</label> </div> </div>
<div class="ic-grid" id="icContainer"> <div class="ic-loading">Загрузка иконок...</div> </div> </div> </div> </div>
<script> (function() { 'use strict'; let icAllIcons = []; let icFilteredIcons = []; let icAvailableGroups = []; let icShowGroups = true; let icThemeCheckInterval = null; let icResizeTimeout = null; let icSearchTimeout = null;
// Функция для определения темы Hugo - легковесная function icDetectHugoTheme() { const html = document.documentElement; const body = document.body; // Быстрая проверка основных способов Hugo if (html.getAttribute('data-theme') === 'dark' || html.getAttribute('data-mode') === 'dark' || html.classList.contains('dark') || html.classList.contains('dark-mode') || body.classList.contains('dark') || body.classList.contains('dark-theme') || body.classList.contains('dark-mode') || document.querySelector('.dark-mode, .dark-theme, [data-theme="dark"]')) { return 'dark'; } return 'light'; }
// Функция для применения темы - без transitions чтобы не зависать function icApplyTheme() { try { const theme = icDetectHugoTheme(); const page = document.querySelector('.ic-isolated-page'); if (!page) return; // Быстрое переключение без анимации if (theme === 'dark') { page.setAttribute('data-ic-theme', 'dark'); } else { page.removeAttribute('data-ic-theme'); } } catch (error) { // Игнорируем ошибки, чтобы не ломать страницу } }
// Простой наблюдатель за темой function icSetupThemeObserver() { // Очищаем предыдущий интервал if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); } let lastTheme = icDetectHugoTheme(); // Проверяем тему раз в секунду - этого достаточно icThemeCheckInterval = setInterval(() => { const currentTheme = icDetectHugoTheme(); if (currentTheme !== lastTheme) { lastTheme = currentTheme; icApplyTheme(); } }, 1000); // Применяем тему сразу icApplyTheme(); }
// Функция для разбивки больших групп на части function icSplitLargeGroup(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; }
// Функция для создания адаптивной masonry компоновки function icCreateMasonryLayout(groupsChunks) { const container = document.getElementById('icContainer'); if (!container) return []; 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; } // Ограничиваем количеством групп columnCount = Math.min(columnCount, groupsChunks.length); // Создаем колонки const columns = Array.from({ length: columnCount }, () => { const col = document.createElement('div'); col.className = 'ic-masonry-column'; return col; }); // Распределяем группы по колонкам равномерно groupsChunks.forEach((group, index) => { const columnIndex = index % columnCount; const section = icCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); }); return columns; }
// Функция для отображения иконок с группировкой function icRenderWithGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = icGroupIcons(icFilteredIcons); // Собираем все группы const allGroupChunks = []; // Добавляем группы с иконками Object.entries(groups).forEach(([groupName, icons]) => { const chunks = icSplitLargeGroup(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 }); }); }); // Добавляем иконки без группы как "Прочие" if (noGroup.length > 0) { const noGroupChunks = icSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group' }); }); } // Создаем masonry раскладку const columns = icCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); icUpdateStats(); }
// Функция для создания секции группы function icCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'ic-group'; const header = document.createElement('div'); header.className = 'ic-group-title'; header.textContent = groupName; header.title = groupName; section.appendChild(header); const column = icCreateIconColumn(icons); section.appendChild(column); return section; }
// Функция для создания колонки с иконками function icCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'ic-panel'; const header = document.createElement('div'); header.className = 'ic-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'ic-header-label'; beforeLabel.textContent = 'ДО'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'ic-arrow'; arrowSpace.textContent = '→'; const afterLabel = document.createElement('div'); afterLabel.className = 'ic-header-label'; afterLabel.textContent = 'ПОСЛЕ'; const nameLabel = document.createElement('div'); nameLabel.className = 'ic-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(icCreateIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function icSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (icShowGroups && icFilteredIcons.length > 0) { icRenderWithGroups(); } }, 250); }); }
// Упрощенная функция для обработки BMP (только убираем magenta) function icProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 32; canvas.height = 32; // Рисуем изображение с масштабированием до 32x32 ctx.drawImage(img, 0, 0, 32, 32); // Убираем только magenta (#FF00FF) const imageData = ctx.getImageData(0, 0, 32, 32); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { // Проверяем на чистый magenta 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) { // В случае ошибки возвращаем оригинальное изображение const fallbackImg = new Image(); fallbackImg.onload = () => resolve({ element: fallbackImg, processed: false }); fallbackImg.src = img.src; } }); }
// Упрощенная функция загрузки изображений function icLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { // Только для BMP файлов делаем простую обработку if (src.toLowerCase().endsWith('.bmp')) { const result = await icProcessBMP(img); result.element.alt = alt; resolve(result); } else { // Для остальных форматов - оригинальное изображение img.alt = alt; resolve({ element: img, processed: false }); } } catch (error) { // В случае ошибки возвращаем placeholder img.alt = alt; resolve({ element: img, processed: false }); } }; img.onerror = function() { // Заглушка для ошибок загрузки const placeholder = new Image(); placeholder.width = 32; placeholder.height = 32; placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; img.width = 32; img.height = 32; }); }
function icGroupIcons(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 icCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'ic-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); // Загружаем изображения Promise.all([ icLoadImage(icon.before, `${icon.name} (до)`), icLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; // Применяем классы beforeImg.className = 'ic-image'; afterImg.className = 'ic-image'; // Добавляем класс для обработанных BMP if (beforeResult.processed) { beforeImg.classList.add('ic-bmp-processed'); } if (afterResult.processed) { afterImg.classList.add('ic-bmp-processed'); } // Гарантируем размер 32x32 beforeImg.width = 32; beforeImg.height = 32; afterImg.width = 32; afterImg.height = 32; // Добавляем в контейнеры beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'ic-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'ic-info'; const name = document.createElement('div'); name.className = 'ic-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 icRenderWithoutGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } const sortedIcons = [...icFilteredIcons].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); // Создаем контейнер для колонок const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = icCreateIconColumn(columnIcons); columnsContainer.appendChild(column); } container.appendChild(columnsContainer); icUpdateStats(); }
function icFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...icAllIcons]; 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); } } icFilteredIcons = filtered; icRenderFilteredIcons(); }
function icRenderFilteredIcons() { if (icShowGroups) { icRenderWithGroups(); } else { icRenderWithoutGroups(); } }
function icUpdateStats() { const totalCount = document.getElementById('icTotalCount'); const shownCount = document.getElementById('icShownCount'); if (totalCount) totalCount.textContent = icAllIcons.length; if (shownCount) shownCount.textContent = icFilteredIcons.length; }
function icInitGroupSelect(groups) { const groupSelect = document.getElementById('icGroupSelect'); if (!groupSelect) return; 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('icSearch').value; icFilterIcons(searchTerm, this.value); }); }
function icRenderIcons(images, groups) { icAllIcons = images; icFilteredIcons = [...icAllIcons]; icAvailableGroups = groups; icInitGroupSelect(groups); icRenderFilteredIcons(); }
async function icLoadIcons() { 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 }; }); icRenderIcons(processedImages, data.availableGroups || []); } else { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки данных</div>'; } } catch (error) { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки иконок</div>'; } }
function icSetupSearch() { const searchInput = document.getElementById('icSearch'); if (!searchInput) return; let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons('', selectedGroup); } }); }
function icSetupToggle() { const toggle = document.getElementById('icShowGroups'); if (!toggle) return; toggle.addEventListener('change', function() { icShowGroups = this.checked; icRenderFilteredIcons(); }); }
// Очистка при разгрузке function icCleanup() { if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); icThemeCheckInterval = null; } clearTimeout(icResizeTimeout); clearTimeout(icSearchTimeout); }
// Инициализация document.addEventListener('DOMContentLoaded', function() { try { // Настраиваем тему icSetupThemeObserver(); // Загружаем иконки icLoadIcons(); // Настраиваем остальное icSetupSearch(); icSetupToggle(); icSetupResizeHandler(); // Очистка window.addEventListener('beforeunload', icCleanup); window.addEventListener('pagehide', icCleanup); } catch (error) { console.error('Ошибка инициализации:', error); } }); })(); </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; } /* Изолированный контейнер для всей страницы */ .ic-isolated-page { /* Светлая тема (по умолчанию) */ --ic-bg-color: #ffffff; --ic-text-color: #333333; --ic-panel-bg: #fafafa; --ic-border-color: #e0e0e0; --ic-header-bg: #f8f9fa; --ic-header-text: #495057; --ic-controls-bg: #f0f0f0; --ic-search-border: #ddd; --ic-search-focus: #007bff; --ic-arrow-color: #999; --ic-loading-color: #666; --ic-row-border: #f0f0f0; --ic-select-bg: #ffffff; --ic-checkbox-color: #007bff; /* Иконки НЕ меняются в темной теме */ --ic-icon-bg: #f5f5f5; /* Всегда светло-серый */ /* Базовые стили */ font-family: Arial, sans-serif; background-color: var(--ic-bg-color); color: var(--ic-text-color); padding: 10px; font-size: 11px; min-height: 100vh; width: 100%; overflow-x: hidden; } /* Темная тема - ТОЛЬКО для фона и текста, НЕ для иконок */ .ic-isolated-page[data-ic-theme="dark"] { --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; --ic-select-bg: #2d2d2d; --ic-checkbox-color: #4dabf7; /* Иконки остаются со светлым фоном для контраста */ --ic-icon-bg: #2a2a2a; }
/* Основные стили с уникальными префиксами */ .ic-isolated-page .ic-container { width: 100%; margin: 0; padding: 0; }
.ic-isolated-page .ic-body { width: 100%; min-height: 100vh; }
.ic-isolated-page .ic-wrapper { max-width: 1400px; margin: 0 auto; width: 100%; padding: 0 10px; }
.ic-isolated-page .ic-title { text-align: center; color: var(--ic-text-color); font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.ic-isolated-page .ic-search-area { margin: 15px 0; text-align: center; }
.ic-isolated-page .ic-search { width: 300px; max-width: 100%; padding: 8px 12px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 14px; background-color: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-search:focus { outline: none; border-color: var(--ic-search-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.ic-isolated-page .ic-search::placeholder { color: var(--ic-arrow-color); }
.ic-isolated-page .ic-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: var(--ic-controls-bg); border-radius: 4px; border: 1px solid var(--ic-border-color); transition: all 0.3s ease; flex-wrap: wrap; }
.ic-isolated-page .ic-stats { font-size: 12px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-group-select { padding: 6px 10px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 12px; background: var(--ic-select-bg); color: var(--ic-text-color); transition: all 0.3s ease; min-width: 150px; }
.ic-isolated-page .ic-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--ic-text-color); }
.ic-isolated-page .ic-checkbox { width: 16px; height: 16px; accent-color: var(--ic-checkbox-color); }
.ic-isolated-page .ic-grid { width: 100%; }
.ic-isolated-page .ic-group-container { width: 100%; margin-bottom: 20px; }
.ic-isolated-page .ic-group-title { background: var(--ic-header-bg); padding: 8px 12px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--ic-header-text); font-size: 13px; transition: all 0.3s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; }
.ic-isolated-page .ic-panel { border: 1px solid var(--ic-border-color); border-radius: 5px; padding: 8px; background: var(--ic-panel-bg); width: 400px; flex-shrink: 0; transition: all 0.3s ease; margin-bottom: 12px; }
.ic-isolated-page .ic-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 var(--ic-border-color); font-weight: bold; text-align: center; color: var(--ic-text-color); width: 100%; }
.ic-isolated-page .ic-header-label { font-size: 11px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid var(--ic-row-border); min-height: 36px; transition: border-color 0.3s ease; width: 100%; }
.ic-isolated-page .ic-row:last-child { border-bottom: none; }
.ic-isolated-page .ic-image { width: 32px; height: 32px; object-fit: contain; background-color: var(--ic-icon-bg); border-radius: 3px; padding: 0; display: block; /* Иконки защищены от любых фильтров */ filter: none !important; -webkit-filter: none !important; image-rendering: auto; }
.ic-isolated-page .ic-bmp-processed { /* Только для BMP */ filter: none !important; -webkit-filter: none !important; }
.ic-isolated-page .ic-arrow { text-align: center; color: var(--ic-arrow-color); font-size: 13px; font-weight: bold; white-space: nowrap; }
.ic-isolated-page .ic-info { padding-left: 6px; padding-right: 3px; min-width: 0; width: 100%; }
.ic-isolated-page .ic-name { color: var(--ic-text-color); line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.ic-isolated-page .ic-loading { text-align: center; padding: 40px 20px; color: var(--ic-loading-color); width: 100%; }
.ic-isolated-page .ic-no-results { text-align: center; padding: 40px; color: var(--ic-loading-color); width: 100%; font-size: 14px; }
.ic-isolated-page .ic-columns { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; justify-content: center; }
.ic-isolated-page .ic-column-panels { display: flex; flex-direction: column; gap: 12px; width: 400px; flex-shrink: 0; }
/* Адаптивность */ @media (max-width: 768px) { .ic-isolated-page .ic-wrapper { max-width: 100%; padding: 0 10px; } .ic-isolated-page .ic-search { width: 100%; max-width: 300px; } .ic-isolated-page .ic-controls { gap: 10px; } .ic-isolated-page .ic-group-select { min-width: 120px; } .ic-isolated-page .ic-name { max-width: 180px; } .ic-isolated-page .ic-panel { width: 100%; max-width: 400px; } .ic-isolated-page .ic-column-panels { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .ic-isolated-page .ic-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 4px; } .ic-isolated-page .ic-header-label { font-size: 10px; } .ic-isolated-page .ic-arrow { font-size: 12px; } .ic-isolated-page .ic-name { font-size: 11px; max-width: 150px; } .ic-isolated-page .ic-group-title { font-size: 12px; padding: 6px 10px; } }
/* Для ширины 800px и выше - 2 колонки */ @media (min-width: 800px) { .ic-isolated-page .ic-column-panels { width: calc(50% - 6px); max-width: 400px; } }
/* Для ширины 1200px и выше - 3 колонки */ @media (min-width: 1200px) { .ic-isolated-page .ic-wrapper { max-width: 1200px; } .ic-isolated-page .ic-column-panels { width: calc(33.333% - 8px); max-width: 400px; } }
/* Для ширины 1400px и выше - 4 колонки */ @media (min-width: 1400px) { .ic-isolated-page .ic-wrapper { max-width: 1400px; } .ic-isolated-page .ic-column-panels { width: calc(25% - 9px); max-width: 400px; } } </style> </head> <body class="ic-isolated-page"> <div class="ic-container"> <div class="ic-body"> <div class="ic-wrapper"> <h1 class="ic-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="ic-search-area"> <input type="text" id="icSearch" class="ic-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="ic-controls"> <div class="ic-stats"> Всего иконок для сравнения: <span id="icTotalCount">0</span> | Отображено: <span id="icShownCount">0</span> </div> <select id="icGroupSelect" class="ic-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="ic-toggle"> <input type="checkbox" id="icShowGroups" class="ic-checkbox" checked> <label for="icShowGroups">Показывать группы</label> </div> </div>
<div class="ic-grid" id="icContainer"> <div class="ic-loading">Загрузка иконок...</div> </div> </div> </div> </div>
<script> (function() { 'use strict'; let icAllIcons = []; let icFilteredIcons = []; let icAvailableGroups = []; let icShowGroups = true; let icThemeCheckInterval = null; let icResizeTimeout = null; let icSearchTimeout = null;
// Функция для определения темы Hugo function icDetectHugoTheme() { const html = document.documentElement; const body = document.body; // Быстрая проверка основных способов Hugo if (html.getAttribute('data-theme') === 'dark' || html.getAttribute('data-mode') === 'dark' || html.classList.contains('dark') || html.classList.contains('dark-mode') || body.classList.contains('dark') || body.classList.contains('dark-theme') || body.classList.contains('dark-mode') || document.querySelector('.dark-mode, .dark-theme, [data-theme="dark"]')) { return 'dark'; } return 'light'; }
// Функция для применения темы function icApplyTheme() { try { const theme = icDetectHugoTheme(); const page = document.querySelector('.ic-isolated-page'); if (!page) return; // Быстрое переключение if (theme === 'dark') { page.setAttribute('data-ic-theme', 'dark'); } else { page.removeAttribute('data-ic-theme'); } } catch (error) { console.warn('Ошибка применения темы:', error); } }
// Простой наблюдатель за темой function icSetupThemeObserver() { // Очищаем предыдущий интервал if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); } let lastTheme = icDetectHugoTheme(); // Проверяем тему раз в секунду icThemeCheckInterval = setInterval(() => { const currentTheme = icDetectHugoTheme(); if (currentTheme !== lastTheme) { lastTheme = currentTheme; icApplyTheme(); } }, 1000); // Применяем тему сразу icApplyTheme(); }
// Функция для разбивки больших групп на части function icSplitLargeGroup(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; }
// Функция для создания адаптивной masonry компоновки - УПРОЩЕННАЯ function icCreateMasonryLayout(groupsChunks) { const container = document.getElementById('icContainer'); if (!container) return []; 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; } // Создаем колонки const columns = Array.from({ length: columnCount }, () => { const col = document.createElement('div'); col.className = 'ic-column-panels'; return col; }); // Распределяем группы по колонкам равномерно groupsChunks.forEach((group, index) => { const columnIndex = index % columnCount; const panel = icCreateGroupPanel(group.name, group.icons); columns[columnIndex].appendChild(panel); }); return columns; }
// Функция для отображения иконок с группировкой - НОВАЯ function icRenderWithGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = icGroupIcons(icFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; // Добавляем группы с иконками Object.entries(groups).forEach(([groupName, icons]) => { const chunks = icSplitLargeGroup(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 }); }); }); // Добавляем иконки без группы как "Прочие" if (noGroup.length > 0) { const noGroupChunks = icSplitLargeGroup(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие иконки (${index + 1}/${noGroupChunks.length})` : 'Прочие иконки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group' }); }); } // Создаем masonry раскладку const columns = icCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); icUpdateStats(); }
// Функция для создания панели с группой - НОВАЯ function icCreateGroupPanel(groupName, icons) { const container = document.createElement('div'); container.className = 'ic-group-container'; const header = document.createElement('div'); header.className = 'ic-group-title'; header.textContent = groupName; header.title = groupName; container.appendChild(header); const panel = icCreateIconColumn(icons); container.appendChild(panel); return container; }
// Функция для создания колонки с иконками function icCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'ic-panel'; const header = document.createElement('div'); header.className = 'ic-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'ic-header-label'; beforeLabel.textContent = 'ДО'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'ic-arrow'; arrowSpace.textContent = '→'; const afterLabel = document.createElement('div'); afterLabel.className = 'ic-header-label'; afterLabel.textContent = 'ПОСЛЕ'; const nameLabel = document.createElement('div'); nameLabel.className = 'ic-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(icCreateIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function icSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (icShowGroups && icFilteredIcons.length > 0) { icRenderWithGroups(); } }, 250); }); }
// Упрощенная функция для обработки BMP (только убираем magenta) function icProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth || 32; canvas.height = img.naturalHeight || 32; // Рисуем оригинальное изображение ctx.drawImage(img, 0, 0); // Убираем только magenta (#FF00FF) const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { // Проверяем на чистый magenta 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) { // В случае ошибки возвращаем оригинальное изображение resolve({ element: img, processed: false }); } }); }
// Функция загрузки изображений function icLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { // Только для BMP файлов делаем обработку magenta if (src.toLowerCase().endsWith('.bmp')) { const result = await icProcessBMP(img); result.element.alt = alt; result.element.width = 32; result.element.height = 32; resolve(result); } else { // Для остальных форматов - оригинальное изображение img.alt = alt; img.width = 32; img.height = 32; resolve({ element: img, processed: false }); } } catch (error) { // В случае ошибки возвращаем оригинальное изображение img.alt = alt; img.width = 32; img.height = 32; resolve({ element: img, processed: false }); } }; img.onerror = function() { // Заглушка для ошибок загрузки const placeholder = new Image(); placeholder.width = 32; placeholder.height = 32; placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function icGroupIcons(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 icCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'ic-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); // Загружаем изображения Promise.all([ icLoadImage(icon.before, `${icon.name} (до)`), icLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; // Применяем классы beforeImg.className = 'ic-image'; afterImg.className = 'ic-image'; // Добавляем класс для обработанных BMP if (beforeResult.processed) { beforeImg.classList.add('ic-bmp-processed'); } if (afterResult.processed) { afterImg.classList.add('ic-bmp-processed'); } // Гарантируем размер 32x32 beforeImg.width = 32; beforeImg.height = 32; beforeImg.style.width = '32px'; beforeImg.style.height = '32px'; afterImg.width = 32; afterImg.height = 32; afterImg.style.width = '32px'; afterImg.style.height = '32px'; // Добавляем в контейнеры beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'ic-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'ic-info'; const name = document.createElement('div'); name.className = 'ic-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 icRenderWithoutGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } const sortedIcons = [...icFilteredIcons].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); // Создаем контейнер для колонок const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = icCreateIconColumn(columnIcons); columnsContainer.appendChild(column); } container.appendChild(columnsContainer); icUpdateStats(); }
function icFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...icAllIcons]; 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); } } icFilteredIcons = filtered; icRenderFilteredIcons(); }
function icRenderFilteredIcons() { if (icShowGroups) { icRenderWithGroups(); } else { icRenderWithoutGroups(); } }
function icUpdateStats() { const totalCount = document.getElementById('icTotalCount'); const shownCount = document.getElementById('icShownCount'); if (totalCount) totalCount.textContent = icAllIcons.length; if (shownCount) shownCount.textContent = icFilteredIcons.length; }
function icInitGroupSelect(groups) { const groupSelect = document.getElementById('icGroupSelect'); if (!groupSelect) return; 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('icSearch').value; icFilterIcons(searchTerm, this.value); }); }
function icRenderIcons(images, groups) { icAllIcons = images; icFilteredIcons = [...icAllIcons]; icAvailableGroups = groups; icInitGroupSelect(groups); icRenderFilteredIcons(); }
async function icLoadIcons() { 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 }; }); icRenderIcons(processedImages, data.availableGroups || []); } else { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки данных</div>'; } } catch (error) { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки иконок</div>'; } }
function icSetupSearch() { const searchInput = document.getElementById('icSearch'); if (!searchInput) return; let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons('', selectedGroup); } }); }
function icSetupToggle() { const toggle = document.getElementById('icShowGroups'); if (!toggle) return; toggle.addEventListener('change', function() { icShowGroups = this.checked; icRenderFilteredIcons(); }); }
// Очистка при разгрузке function icCleanup() { if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); icThemeCheckInterval = null; } clearTimeout(icResizeTimeout); clearTimeout(icSearchTimeout); }
// Инициализация document.addEventListener('DOMContentLoaded', function() { try { // Настраиваем тему icSetupThemeObserver(); // Загружаем иконки icLoadIcons(); // Настраиваем остальное icSetupSearch(); icSetupToggle(); icSetupResizeHandler(); // Очистка window.addEventListener('beforeunload', icCleanup); window.addEventListener('pagehide', icCleanup); } catch (error) { console.error('Ошибка инициализации:', error); } }); })(); </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; } /* Изолированный контейнер для всей страницы */ .ic-isolated-page { /* Светлая тема (по умолчанию) */ --ic-bg-color: #ffffff; --ic-text-color: #333333; --ic-panel-bg: #fafafa; --ic-border-color: #e0e0e0; --ic-header-bg: #f8f9fa; --ic-header-text: #495057; --ic-controls-bg: #f0f0f0; --ic-search-border: #ddd; --ic-search-focus: #007bff; --ic-arrow-color: #999; --ic-loading-color: #666; --ic-row-border: #f0f0f0; --ic-select-bg: #ffffff; --ic-checkbox-color: #007bff; /* Иконки НЕ меняются в темной теме */ --ic-icon-bg: #f5f5f5; font-family: Arial, sans-serif; background-color: var(--ic-bg-color); color: var(--ic-text-color); padding: 10px; font-size: 11px; min-height: 100vh; width: 100%; overflow-x: hidden; } /* Темная тема - ТОЛЬКО для фона и текста */ .ic-isolated-page[data-ic-theme="dark"] { --ic-bg-color: #1a1a1a; --ic-text-color: #e0e0e0; --ic-panel-bg: #2d2d2d; --ic-border-color: #404040; --ic-header-bg: #3d3d3d; --ic-header-text: #cccccc; --ic-controls-bg: #333333; --ic-search-border: #555; --ic-search-focus: #4dabf7; --ic-arrow-color: #888; --ic-loading-color: #999; --ic-row-border: #3a3a3a; --ic-select-bg: #2d2d2d; --ic-checkbox-color: #4dabf7; /* Иконки остаются со светлым фоном */ --ic-icon-bg: #2a2a2a; }
/* Основные стили */ .ic-isolated-page .ic-container { width: 100%; }
.ic-isolated-page .ic-body { width: 100%; min-height: 100vh; }
.ic-isolated-page .ic-wrapper { max-width: 1400px; margin: 0 auto; width: 100%; padding: 0 10px; }
.ic-isolated-page .ic-title { text-align: center; color: var(--ic-text-color); font-size: 16px; margin-bottom: 15px; font-weight: bold; }
.ic-isolated-page .ic-search-area { margin: 15px 0; text-align: center; }
.ic-isolated-page .ic-search { width: 300px; max-width: 100%; padding: 8px 12px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 14px; background-color: var(--ic-bg-color); color: var(--ic-text-color); transition: all 0.3s ease; }
.ic-isolated-page .ic-search:focus { outline: none; border-color: var(--ic-search-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); }
.ic-isolated-page .ic-search::placeholder { color: var(--ic-arrow-color); }
.ic-isolated-page .ic-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin: 12px 0; padding: 8px; background: var(--ic-controls-bg); border-radius: 4px; border: 1px solid var(--ic-border-color); transition: all 0.3s ease; flex-wrap: wrap; }
.ic-isolated-page .ic-stats { font-size: 12px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-group-select { padding: 6px 10px; border: 1px solid var(--ic-search-border); border-radius: 4px; font-size: 12px; background: var(--ic-select-bg); color: var(--ic-text-color); transition: all 0.3s ease; min-width: 150px; }
.ic-isolated-page .ic-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; order: 2; color: var(--ic-text-color); }
.ic-isolated-page .ic-checkbox { width: 16px; height: 16px; accent-color: var(--ic-checkbox-color); }
.ic-isolated-page .ic-grid { width: 100%; }
/* Стили для групп с masonry раскладкой */ .ic-isolated-page .ic-group-section { width: 100%; margin-bottom: 15px; }
.ic-isolated-page .ic-group-title { background: var(--ic-header-bg); padding: 8px 12px; border-radius: 4px; margin-bottom: 8px; font-weight: bold; color: var(--ic-header-text); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 400px; /* Фиксированная ширина как у панелей */ max-width: 100%; flex-shrink: 0; }
.ic-isolated-page .ic-group-content { display: flex; flex-direction: column; width: 400px; /* Фиксированная ширина */ max-width: 100%; }
.ic-isolated-page .ic-panel { border: 1px solid var(--ic-border-color); border-radius: 5px; padding: 8px; background: var(--ic-panel-bg); width: 400px; max-width: 100%; flex-shrink: 0; }
.ic-isolated-page .ic-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 var(--ic-border-color); font-weight: bold; text-align: center; color: var(--ic-text-color); }
.ic-isolated-page .ic-header-label { font-size: 11px; color: var(--ic-text-color); white-space: nowrap; }
.ic-isolated-page .ic-row { display: grid; grid-template-columns: 32px 18px 32px 1fr; gap: 6px; align-items: center; padding: 4px 2px; border-bottom: 1px solid var(--ic-row-border); min-height: 36px; }
.ic-isolated-page .ic-row:last-child { border-bottom: none; }
.ic-isolated-page .ic-image { width: 32px; height: 32px; object-fit: contain; background-color: var(--ic-icon-bg); border-radius: 3px; padding: 0; display: block; filter: none !important; -webkit-filter: none !important; }
.ic-isolated-page .ic-bmp-processed { filter: none !important; -webkit-filter: none !important; }
.ic-isolated-page .ic-arrow { text-align: center; color: var(--ic-arrow-color); font-size: 13px; font-weight: bold; white-space: nowrap; }
.ic-isolated-page .ic-info { padding-left: 6px; padding-right: 3px; min-width: 0; }
.ic-isolated-page .ic-name { color: var(--ic-text-color); line-height: 1.3; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; text-align: left; font-size: 12px; max-width: 220px; }
.ic-isolated-page .ic-loading { text-align: center; padding: 40px 20px; color: var(--ic-loading-color); width: 100%; }
.ic-isolated-page .ic-no-results { text-align: center; padding: 40px; color: var(--ic-loading-color); width: 100%; font-size: 14px; }
/* Masonry колонки */ .ic-isolated-page .ic-masonry-columns { display: flex; flex-wrap: wrap; gap: 15px; width: 100%; justify-content: center; }
.ic-isolated-page .ic-masonry-column { display: flex; flex-direction: column; gap: 15px; width: 400px; /* Фиксированная ширина колонки */ max-width: 100%; }
/* Обычные колонки (без групп) */ .ic-isolated-page .ic-columns { display: flex; flex-wrap: wrap; gap: 15px; width: 100%; justify-content: center; }
/* Адаптивность */ @media (max-width: 768px) { .ic-isolated-page .ic-wrapper { max-width: 100%; padding: 0 10px; } .ic-isolated-page .ic-search { width: 100%; max-width: 300px; } .ic-isolated-page .ic-controls { gap: 10px; } .ic-isolated-page .ic-group-select { min-width: 120px; } .ic-isolated-page .ic-name { max-width: 180px; } .ic-isolated-page .ic-group-title, .ic-isolated-page .ic-group-content, .ic-isolated-page .ic-panel, .ic-isolated-page .ic-masonry-column { width: 100%; max-width: 400px; } }
/* Для очень маленьких экранов */ @media (max-width: 480px) { .ic-isolated-page .ic-panel-header { grid-template-columns: 30px 16px 30px 1fr; gap: 4px; } .ic-isolated-page .ic-header-label { font-size: 10px; } .ic-isolated-page .ic-arrow { font-size: 12px; } .ic-isolated-page .ic-name { font-size: 11px; max-width: 150px; } .ic-isolated-page .ic-group-title { font-size: 12px; padding: 6px 10px; } }
/* Для ширины 800px и выше - 2 колонки */ @media (min-width: 800px) { .ic-isolated-page .ic-masonry-column { width: calc(50% - 8px); max-width: 400px; } }
/* Для ширины 1200px и выше - 3 колонки */ @media (min-width: 1200px) { .ic-isolated-page .ic-wrapper { max-width: 1200px; } .ic-isolated-page .ic-masonry-column { width: calc(33.333% - 10px); max-width: 400px; } }
/* Для ширины 1400px и выше - 4 колонки */ @media (min-width: 1400px) { .ic-isolated-page .ic-wrapper { max-width: 1400px; } .ic-isolated-page .ic-masonry-column { width: calc(25% - 12px); max-width: 400px; } } </style> </head> <body class="ic-isolated-page"> <div class="ic-container"> <div class="ic-body"> <div class="ic-wrapper"> <h1 class="ic-title">Сравнение иконок ИСИХОГИ старой и новой версий</h1> <div class="ic-search-area"> <input type="text" id="icSearch" class="ic-search" placeholder="Поиск по названию иконки..." autocomplete="off"> </div> <div class="ic-controls"> <div class="ic-stats"> Всего иконок для сравнения: <span id="icTotalCount">0</span> | Отображено: <span id="icShownCount">0</span> </div> <select id="icGroupSelect" class="ic-group-select" style="display: none;"> <option value="">Все группы</option> <option value="no_group">Прочие</option> </select> <div class="ic-toggle"> <input type="checkbox" id="icShowGroups" class="ic-checkbox" checked> <label for="icShowGroups">Показывать группы</label> </div> </div>
<div class="ic-grid" id="icContainer"> <div class="ic-loading">Загрузка иконок...</div> </div> </div> </div> </div>
<script> (function() { 'use strict'; let icAllIcons = []; let icFilteredIcons = []; let icAvailableGroups = []; let icShowGroups = true; let icThemeCheckInterval = null; let icResizeTimeout = null; let icSearchTimeout = null;
// Функция для определения темы Hugo function icDetectHugoTheme() { const html = document.documentElement; const body = document.body; if (html.getAttribute('data-theme') === 'dark' || html.getAttribute('data-mode') === 'dark' || html.classList.contains('dark') || html.classList.contains('dark-mode') || body.classList.contains('dark') || body.classList.contains('dark-theme') || body.classList.contains('dark-mode') || document.querySelector('.dark-mode, .dark-theme, [data-theme="dark"]')) { return 'dark'; } return 'light'; }
// Функция для применения темы function icApplyTheme() { try { const theme = icDetectHugoTheme(); const page = document.querySelector('.ic-isolated-page'); if (!page) return; if (theme === 'dark') { page.setAttribute('data-ic-theme', 'dark'); } else { page.removeAttribute('data-ic-theme'); } } catch (error) { console.warn('Ошибка применения темы:', error); } }
// Простой наблюдатель за темой function icSetupThemeObserver() { if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); } let lastTheme = icDetectHugoTheme(); icThemeCheckInterval = setInterval(() => { const currentTheme = icDetectHugoTheme(); if (currentTheme !== lastTheme) { lastTheme = currentTheme; icApplyTheme(); } }, 1000); icApplyTheme(); }
// Функция для разбивки больших групп на части function icSplitLargeGroup(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 icCalculateGroupHeight(iconsCount) { const headerHeight = 36; const rowHeight = 30; const padding = 12; return headerHeight + (iconsCount * rowHeight) + padding; }
// Функция для создания адаптивной masonry компоновки - ВОЗВРАЩАЕМ ОРИГИНАЛЬНУЮ ЛОГИКУ function icCreateMasonryLayout(groupsChunks) { const container = document.getElementById('icContainer'); if (!container) return []; const containerWidth = container.clientWidth; const columnWidth = 400 + 15; // Ширина панели + отступ // Динамическое количество колонок let maxColumns; if (containerWidth >= 1400) { maxColumns = 4; } else if (containerWidth >= 1200) { maxColumns = 3; } else if (containerWidth >= 800) { maxColumns = 2; } else { maxColumns = 1; } const availableColumns = Math.min(maxColumns, Math.max(1, Math.floor(containerWidth / columnWidth))); // Создаем колонки const columns = Array.from({ length: availableColumns }, () => { const col = document.createElement('div'); col.className = 'ic-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 = icCreateGroupSection(group.name, group.icons); columns[columnIndex].appendChild(section); columnHeights[columnIndex] += group.height; }); return columns; }
// Функция для отображения иконок с группировкой function icRenderWithGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } // Группируем иконки const { groups, noGroup } = icGroupIcons(icFilteredIcons); // Собираем все группы и разбиваем большие const allGroupChunks = []; Object.entries(groups).forEach(([groupName, icons]) => { const chunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); }); // Добавляем иконки без группы как "Прочие" if (noGroup.length > 0) { const noGroupChunks = icSplitLargeGroup(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: icCalculateGroupHeight(chunk.length) }); }); } // Сортируем по высоте (от высоких к низким для лучшего заполнения) allGroupChunks.sort((a, b) => b.height - a.height); // Создаем адаптивную masonry компоновку const columns = icCreateMasonryLayout(allGroupChunks); // Очищаем контейнер и добавляем колонки container.innerHTML = ''; const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-masonry-columns'; columns.forEach(col => { if (col.children.length > 0) { columnsContainer.appendChild(col); } }); container.appendChild(columnsContainer); icUpdateStats(); }
// Функция для создания секции группы - ВСЕ ЭЛЕМЕНТЫ В ОДНОМ КОНТЕЙНЕРЕ function icCreateGroupSection(groupName, icons) { const section = document.createElement('div'); section.className = 'ic-group-section'; // Заголовок группы с ФИКСИРОВАННОЙ шириной как у панели const header = document.createElement('div'); header.className = 'ic-group-title'; header.textContent = groupName; header.title = groupName; section.appendChild(header); // Контейнер для панели const content = document.createElement('div'); content.className = 'ic-group-content'; const panel = icCreateIconColumn(icons); content.appendChild(panel); section.appendChild(content); return section; }
// Функция для создания колонки с иконками function icCreateIconColumn(icons) { const column = document.createElement('div'); column.className = 'ic-panel'; const header = document.createElement('div'); header.className = 'ic-panel-header'; const beforeLabel = document.createElement('div'); beforeLabel.className = 'ic-header-label'; beforeLabel.textContent = 'ДО'; const arrowSpace = document.createElement('div'); arrowSpace.className = 'ic-arrow'; arrowSpace.textContent = '→'; const afterLabel = document.createElement('div'); afterLabel.className = 'ic-header-label'; afterLabel.textContent = 'ПОСЛЕ'; const nameLabel = document.createElement('div'); nameLabel.className = 'ic-header-label'; nameLabel.textContent = 'Название'; nameLabel.style.textAlign = 'center'; header.appendChild(beforeLabel); header.appendChild(arrowSpace); header.appendChild(afterLabel); header.appendChild(nameLabel); column.appendChild(header); icons.forEach(icon => { column.appendChild(icCreateIconRow(icon)); }); return column; }
// Обработчик изменения размера окна function icSetupResizeHandler() { let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { if (icShowGroups && icFilteredIcons.length > 0) { icRenderWithGroups(); } }, 250); }); }
// Упрощенная функция для обработки BMP function icProcessBMP(img) { return new Promise((resolve) => { try { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth || 32; canvas.height = img.naturalHeight || 32; ctx.drawImage(img, 0, 0); // Убираем только magenta (#FF00FF) 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) { resolve({ element: img, processed: false }); } }); }
// Функция загрузки изображений function icLoadImage(src, alt) { return new Promise((resolve) => { const img = new Image(); img.onload = async function() { try { if (src.toLowerCase().endsWith('.bmp')) { const result = await icProcessBMP(img); result.element.alt = alt; result.element.width = 32; result.element.height = 32; resolve(result); } else { img.alt = alt; img.width = 32; img.height = 32; resolve({ element: img, processed: false }); } } catch (error) { img.alt = alt; img.width = 32; img.height = 32; resolve({ element: img, processed: false }); } }; img.onerror = function() { const placeholder = new Image(); placeholder.width = 32; placeholder.height = 32; placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo='; placeholder.alt = alt; resolve({ element: placeholder, processed: false }); }; img.src = src; }); }
function icGroupIcons(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 icCreateIconRow(icon) { const row = document.createElement('div'); row.className = 'ic-row'; const beforeContainer = document.createElement('div'); const afterContainer = document.createElement('div'); Promise.all([ icLoadImage(icon.before, `${icon.name} (до)`), icLoadImage(icon.after, `${icon.name} (после)`) ]).then(([beforeResult, afterResult]) => { const beforeImg = beforeResult.element; const afterImg = afterResult.element; beforeImg.className = 'ic-image'; afterImg.className = 'ic-image'; if (beforeResult.processed) beforeImg.classList.add('ic-bmp-processed'); if (afterResult.processed) afterImg.classList.add('ic-bmp-processed'); beforeImg.width = 32; beforeImg.height = 32; beforeImg.style.width = '32px'; beforeImg.style.height = '32px'; afterImg.width = 32; afterImg.height = 32; afterImg.style.width = '32px'; afterImg.style.height = '32px'; beforeContainer.appendChild(beforeImg); afterContainer.appendChild(afterImg); }); const arrow = document.createElement('div'); arrow.className = 'ic-arrow'; arrow.textContent = '→'; const infoDiv = document.createElement('div'); infoDiv.className = 'ic-info'; const name = document.createElement('div'); name.className = 'ic-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 icRenderWithoutGroups() { const container = document.getElementById('icContainer'); container.innerHTML = ''; if (icFilteredIcons.length === 0) { container.innerHTML = '<div class="ic-no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>'; icUpdateStats(); return; } const sortedIcons = [...icFilteredIcons].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); const columnsContainer = document.createElement('div'); columnsContainer.className = 'ic-columns'; for (let i = 0; i < columnCount; i++) { const startIndex = i * itemsPerColumn; const endIndex = startIndex + itemsPerColumn; const columnIcons = sortedIcons.slice(startIndex, endIndex); const column = icCreateIconColumn(columnIcons); columnsContainer.appendChild(column); } container.appendChild(columnsContainer); icUpdateStats(); }
function icFilterIcons(searchTerm, selectedGroup = '') { let filtered = [...icAllIcons]; 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); } } icFilteredIcons = filtered; icRenderFilteredIcons(); }
function icRenderFilteredIcons() { if (icShowGroups) { icRenderWithGroups(); } else { icRenderWithoutGroups(); } }
function icUpdateStats() { const totalCount = document.getElementById('icTotalCount'); const shownCount = document.getElementById('icShownCount'); if (totalCount) totalCount.textContent = icAllIcons.length; if (shownCount) shownCount.textContent = icFilteredIcons.length; }
function icInitGroupSelect(groups) { const groupSelect = document.getElementById('icGroupSelect'); if (!groupSelect) return; 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('icSearch').value; icFilterIcons(searchTerm, this.value); }); }
function icRenderIcons(images, groups) { icAllIcons = images; icFilteredIcons = [...icAllIcons]; icAvailableGroups = groups; icInitGroupSelect(groups); icRenderFilteredIcons(); }
async function icLoadIcons() { 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 }; }); icRenderIcons(processedImages, data.availableGroups || []); } else { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки данных</div>'; } } catch (error) { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка обработки данных</div>'; } }; script.onerror = function() { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки файла image_list.js</div>'; }; } catch (error) { const container = document.getElementById('icContainer'); if (container) container.innerHTML = '<div class="ic-loading">Ошибка загрузки иконок</div>'; } }
function icSetupSearch() { const searchInput = document.getElementById('icSearch'); if (!searchInput) return; let searchTimeout; searchInput.addEventListener('input', function(e) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons(e.target.value, selectedGroup); }, 300); }); searchInput.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; const selectedGroup = document.getElementById('icGroupSelect').value; icFilterIcons('', selectedGroup); } }); }
function icSetupToggle() { const toggle = document.getElementById('icShowGroups'); if (!toggle) return; toggle.addEventListener('change', function() { icShowGroups = this.checked; icRenderFilteredIcons(); }); }
// Очистка при разгрузке function icCleanup() { if (icThemeCheckInterval) { clearInterval(icThemeCheckInterval); icThemeCheckInterval = null; } clearTimeout(icResizeTimeout); clearTimeout(icSearchTimeout); }
// Инициализация document.addEventListener('DOMContentLoaded', function() { try { icSetupThemeObserver(); icLoadIcons(); icSetupSearch(); icSetupToggle(); icSetupResizeHandler(); window.addEventListener('beforeunload', icCleanup); window.addEventListener('pagehide', icCleanup); } catch (error) { console.error('Ошибка инициализации:', error); } }); })(); </script> </body> </html>
|