Отзывы и предложения к софту от AleXStam
  • Страница 25 из 25
  • «
  • 1
  • 2
  • 23
  • 24
  • 25
Поговорим о...
<!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: 100%;
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;
}

.search-input {
width: 300px;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}

.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;
}

.group-toggle {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px;
}

.group-toggle input {
margin: 0;
}

.comparison-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-start;
}

.group-section {
width: 100%;
margin-bottom: 20px;
}

.group-header {
background: #f8f9fa;
padding: 6px 10px;
border-radius: 4px;
margin-bottom: 8px;
font-weight: bold;
color: #495057;
font-size: 13px;
width: 350px;
}

.group-columns-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
}

.icon-column {
border: 1px solid #e0e0e0;
border-radius: 5px;
padding: 8px;
background: #fafafa;
width: 350px;
}

.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;
}

.icon-row {
display: grid;
grid-template-columns: 32px 20px 32px 1fr;
gap: 8px;
align-items: center;
padding: 6px 3px;
border-bottom: 1px solid #f0f0f0;
}

.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 {
/* Стиль для обработанных BMP с прозрачностью */
filter: none;
}

.arrow {
text-align: center;
color: #999;
font-size: 14px;
font-weight: bold;
}

.icon-info {
padding-left: 8px;
padding-right: 5px;
min-width: 0;
}

.icon-name {
color: #333;
line-height: 1.3;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
text-align: left;
}

.loading {
text-align: center;
padding: 20px;
color: #666;
width: 100%;
}

.no-results {
text-align: center;
padding: 40px;
color: #666;
width: 100%;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>Сравнение иконок ИСИХОГИ старой и новой версий</h1>

<div class="search-container">
<input type="text"
id="searchInput"
class="search-input"
placeholder="Поиск по названию иконки...">
</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="group-toggle" id="groupToggle" style="display: none;">
<input type="checkbox" id="showGroups" checked>
<label for="showGroups">Отображать группы</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;

// Улучшенная функция для обработки BMP с хромакеем
function processBMPWithChromaKey(img) {
return new Promise((resolve) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;

// Рисуем изображение на canvas
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) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];

// Точное определение ярко-розового хромакея (255, 0, 255)
const isChromaKey = r === 255 && g === 0 && b === 255;

// Также обрабатываем близкие оттенки (допуск ±5)
const isNearChromaKey =
r >= 250 && r <= 255 &&
g >= 0 && g <= 5 &&
b >= 250 && b <= 255;

if (isChromaKey || isNearChromaKey) {
data[i + 3] = 0; // Устанавливаем полную прозрачность
}
}

// Записываем обработанные данные обратно
ctx.putImageData(imageData, 0, 0);

const processedImg = new Image();
processedImg.onload = () => {
console.log('BMP успешно обработан, прозрачность применена');
resolve({
element: processedImg,
processed: true
});
};
processedImg.src = canvas.toDataURL('image/png');
} catch (error) {
console.warn('Ошибка обработки BMP:', error);
resolve({
element: img,
processed: false
});
}
});
}

// Функция для загрузки изображения с обработкой BMP
function loadImageWithBMPProcessing(src, alt) {
return new Promise((resolve) => {
const img = new Image();

img.onload = async function() {
// Обрабатываем только BMP файлы
if (src.toLowerCase().endsWith('.bmp')) {
try {
console.log('Обработка BMP:', src);
const result = await processBMPWithChromaKey(img);
resolve(result);
} catch (error) {
console.warn('Ошибка при обработке BMP:', error);
resolve({ element: img, processed: false });
}
} else {
// Для других форматов просто возвращаем изображение
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=';
resolve({ element: placeholder, processed: false });
};

img.src = src;
img.alt = alt;
});
}

// Функция для группировки иконок
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 splitIconsIntoColumns(icons, maxIconsPerColumn = 18) {
const columns = [];
const totalIcons = icons.length;

if (totalIcons <= maxIconsPerColumn) {
columns.push(icons);
} else {
const numberOfColumns = Math.ceil(totalIcons / maxIconsPerColumn);
const iconsPerColumn = Math.ceil(totalIcons / numberOfColumns);

for (let i = 0; i < totalIcons; i += iconsPerColumn) {
columns.push(icons.slice(i, i + iconsPerColumn));
}
}

return columns;
}

// Функция для создания колонки с иконками
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 = 'ДО';

const arrowSpace = document.createElement('div');
arrowSpace.className = 'arrow';
arrowSpace.textContent = '→';

const afterLabel = document.createElement('div');
afterLabel.className = 'header-label';
afterLabel.textContent = 'ПОСЛЕ';

const nameLabel = document.createElement('div');
nameLabel.className = '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(createIconRow(icon));
});

return column;
}

// Функция для создания строки с иконками
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(icons) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (icons.length === 0) return;

// Сортируем иконки по алфавиту
const sortedIcons = [...icons].sort((a, b) => a.name.localeCompare(b.name));

// Разбиваем на колонки
const columns = splitIconsIntoColumns(sortedIcons);

columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
container.appendChild(column);
});
}

// Функция для отображения иконок с группами
function renderIconsWithGroups() {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (filteredIcons.length === 0) return;

// Группируем иконки
const { groups, noGroup } = groupIcons(filteredIcons);

// Создаем все колонки для отображения
const allColumns = [];

// Добавляем колонки групп
Object.keys(groups).sort().forEach(groupName => {
const groupIcons = groups[groupName];
const columns = splitIconsIntoColumns(groupIcons);

columns.forEach((columnIcons, index) => {
const column = createIconColumn(columnIcons);
// Для первой колонки группы добавляем заголовок
if (index === 0) {
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = groupName;
allColumns.push({ element: header, isHeader: true });
}
allColumns.push({ element: column, isHeader: false });
});
});

// Добавляем прочие иконки
if (noGroup.length > 0 && Object.keys(groups).length > 0) {
const columns = splitIconsIntoColumns(noGroup);

// Добавляем заголовок для прочих иконок
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = 'Прочие иконки';
allColumns.push({ element: header, isHeader: true });

columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allColumns.push({ element: column, isHeader: false });
});
} else if (noGroup.length > 0) {
// Если групп нет, просто отображаем прочие иконки
const columns = splitIconsIntoColumns(noGroup);
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allColumns.push({ element: column, isHeader: false });
});
}

// Добавляем все элементы в контейнер
allColumns.forEach(item => {
container.appendChild(item.element);
});
}

// Функция для фильтрации иконок по поисковому запросу и группе
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 (filteredIcons.length === 0) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>';
updateStats();
return;
}

if (showGroups) {
renderIconsWithGroups();
} else {
renderIconsWithoutGroups(filteredIcons);
}

updateStats();
}

// Обновление статистики
function updateStats() {
document.getElementById('totalCount').textContent = allIcons.length;
document.getElementById('shownCount').textContent = filteredIcons.length;
}

// Инициализация комбобокса групп и переключателя
function initGroupControls(groups) {
const groupSelect = document.getElementById('groupSelect');
const groupToggle = document.getElementById('groupToggle');

groupSelect.innerHTML = '<option value="">Все группы</option>';

groups.forEach(group => {
const option = document.createElement('option');
option.value = group;
option.textContent = group;
groupSelect.appendChild(option);
});

const hasGroups = groups.length > 0;
groupSelect.style.display = hasGroups ? 'block' : 'none';
groupToggle.style.display = hasGroups ? 'flex' : 'none';

groupSelect.addEventListener('change', function() {
const searchTerm = document.getElementById('searchInput').value;
filterIcons(searchTerm, this.value);
});

document.getElementById('showGroups').addEventListener('change', function() {
showGroups = this.checked;
renderFilteredIcons();
});
}

// Функция для отображения всех иконок
function renderIcons(images, groups) {
allIcons = images;
filteredIcons = [...allIcons];
availableGroups = groups;

initGroupControls(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('searchInput');
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);
}
});
}

// Загружаем иконки при загрузке страницы
window.addEventListener('DOMContentLoaded', function() {
loadIcons();
setupSearch();
});
</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: 100%;
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;
}

.search-input {
width: 300px;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}

.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;
}

.group-toggle {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px;
}

.group-toggle input {
margin: 0;
}

.comparison-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-start;
}

.group-section {
display: contents; /* Убираем обертку группы, чтобы элементы располагались свободно */
}

.group-header {
background: #f8f9fa;
padding: 6px 10px;
border-radius: 4px;
margin-bottom: 8px;
font-weight: bold;
color: #495057;
font-size: 13px;
width: 350px;
height: fit-content;
}

.icon-column {
border: 1px solid #e0e0e0;
border-radius: 5px;
padding: 8px;
background: #fafafa;
width: 350px;
}

.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;
}

.icon-row {
display: grid;
grid-template-columns: 32px 20px 32px 1fr;
gap: 8px;
align-items: center;
padding: 6px 3px;
border-bottom: 1px solid #f0f0f0;
}

.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 {
/* Стиль для обработанных BMP с прозрачностью */
filter: none;
}

.arrow {
text-align: center;
color: #999;
font-size: 14px;
font-weight: bold;
}

.icon-info {
padding-left: 8px;
padding-right: 5px;
min-width: 0;
}

.icon-name {
color: #333;
line-height: 1.3;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
text-align: left;
}

.loading {
text-align: center;
padding: 20px;
color: #666;
width: 100%;
}

.no-results {
text-align: center;
padding: 40px;
color: #666;
width: 100%;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>Сравнение иконок ИСИХОГИ старой и новой версий</h1>

<div class="search-container">
<input type="text"
id="searchInput"
class="search-input"
placeholder="Поиск по названию иконки...">
</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="group-toggle" id="groupToggle" style="display: none;">
<input type="checkbox" id="showGroups" checked>
<label for="showGroups">Отображать группы</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;

// Улучшенная функция для обработки BMP с хромакеем
function processBMPWithChromaKey(img) {
return new Promise((resolve) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;

// Рисуем изображение на canvas
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) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];

// Точное определение ярко-розового хромакея (255, 0, 255)
const isChromaKey = r === 255 && g === 0 && b === 255;

// Также обрабатываем близкие оттенки (допуск ±5)
const isNearChromaKey =
r >= 250 && r <= 255 &&
g >= 0 && g <= 5 &&
b >= 250 && b <= 255;

if (isChromaKey || isNearChromaKey) {
data[i + 3] = 0; // Устанавливаем полную прозрачность
}
}

// Записываем обработанные данные обратно
ctx.putImageData(imageData, 0, 0);

const processedImg = new Image();
processedImg.onload = () => {
console.log('BMP успешно обработан, прозрачность применена');
resolve({
element: processedImg,
processed: true
});
};
processedImg.src = canvas.toDataURL('image/png');
} catch (error) {
console.warn('Ошибка обработки BMP:', error);
resolve({
element: img,
processed: false
});
}
});
}

// Функция для загрузки изображения с обработкой BMP
function loadImageWithBMPProcessing(src, alt) {
return new Promise((resolve) => {
const img = new Image();

img.onload = async function() {
// Обрабатываем только BMP файлы
if (src.toLowerCase().endsWith('.bmp')) {
try {
console.log('Обработка BMP:', src);
const result = await processBMPWithChromaKey(img);
resolve(result);
} catch (error) {
console.warn('Ошибка при обработке BMP:', error);
resolve({ element: img, processed: false });
}
} else {
// Для других форматов просто возвращаем изображение
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=';
resolve({ element: placeholder, processed: false });
};

img.src = src;
img.alt = alt;
});
}

// Функция для группировки иконок
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 splitIconsIntoColumns(icons, maxIconsPerColumn = 18) {
const columns = [];
const totalIcons = icons.length;

if (totalIcons <= maxIconsPerColumn) {
columns.push(icons);
} else {
const numberOfColumns = Math.ceil(totalIcons / maxIconsPerColumn);
const iconsPerColumn = Math.ceil(totalIcons / numberOfColumns);

for (let i = 0; i < totalIcons; i += iconsPerColumn) {
columns.push(icons.slice(i, i + iconsPerColumn));
}
}

return columns;
}

// Функция для создания колонки с иконками
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 = 'ДО';

const arrowSpace = document.createElement('div');
arrowSpace.className = 'arrow';
arrowSpace.textContent = '→';

const afterLabel = document.createElement('div');
afterLabel.className = 'header-label';
afterLabel.textContent = 'ПОСЛЕ';

const nameLabel = document.createElement('div');
nameLabel.className = '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(createIconRow(icon));
});

return column;
}

// Функция для создания строки с иконками
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(icons) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (icons.length === 0) return;

// Сортируем иконки по алфавиту
const sortedIcons = [...icons].sort((a, b) => a.name.localeCompare(b.name));

// Разбиваем на колонки
const columns = splitIconsIntoColumns(sortedIcons);

columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
container.appendChild(column);
});
}

// Функция для отображения иконок с группами
function renderIconsWithGroups() {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (filteredIcons.length === 0) return;

// Группируем иконки
const { groups, noGroup } = groupIcons(filteredIcons);

// Создаем все элементы для отображения
const allElements = [];

// Добавляем группы
Object.keys(groups).sort().forEach(groupName => {
const groupIcons = groups[groupName];
const columns = splitIconsIntoColumns(groupIcons);

// Добавляем заголовок группы
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = groupName;
allElements.push(header);

// Добавляем колонки группы
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allElements.push(column);
});
});

// Добавляем прочие иконки
if (noGroup.length > 0) {
const columns = splitIconsIntoColumns(noGroup);

// Добавляем заголовок для прочих иконок только если есть другие группы
if (Object.keys(groups).length > 0) {
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = 'Прочие иконки';
allElements.push(header);
}

// Добавляем колонки прочих иконок
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allElements.push(column);
});
}

// Добавляем все элементы в контейнер
allElements.forEach(element => {
container.appendChild(element);
});
}

// Функция для фильтрации иконок по поисковому запросу и группе
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 (filteredIcons.length === 0) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>';
updateStats();
return;
}

if (showGroups) {
renderIconsWithGroups();
} else {
renderIconsWithoutGroups(filteredIcons);
}

updateStats();
}

// Обновление статистики
function updateStats() {
document.getElementById('totalCount').textContent = allIcons.length;
document.getElementById('shownCount').textContent = filteredIcons.length;
}

// Инициализация комбобокса групп и переключателя
function initGroupControls(groups) {
const groupSelect = document.getElementById('groupSelect');
const groupToggle = document.getElementById('groupToggle');

groupSelect.innerHTML = '<option value="">Все группы</option>';

groups.forEach(group => {
const option = document.createElement('option');
option.value = group;
option.textContent = group;
groupSelect.appendChild(option);
});

const hasGroups = groups.length > 0;
groupSelect.style.display = hasGroups ? 'block' : 'none';
groupToggle.style.display = hasGroups ? 'flex' : 'none';

groupSelect.addEventListener('change', function() {
const searchTerm = document.getElementById('searchInput').value;
filterIcons(searchTerm, this.value);
});

document.getElementById('showGroups').addEventListener('change', function() {
showGroups = this.checked;
renderFilteredIcons();
});
}

// Функция для отображения всех иконок
function renderIcons(images, groups) {
allIcons = images;
filteredIcons = [...allIcons];
availableGroups = groups;

initGroupControls(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('searchInput');
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);
}
});
}

// Загружаем иконки при загрузке страницы
window.addEventListener('DOMContentLoaded', function() {
loadIcons();
setupSearch();
});
</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: 100%;
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;
}

.search-input {
width: 300px;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}

.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;
}

.group-toggle {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px;
}

.group-toggle input {
margin: 0;
}

.comparison-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-start;
}

.group-block {
display: contents; /* Разрешаем элементам группы участвовать в общем потоке */
}

.group-header {
background: #f8f9fa;
padding: 6px 10px;
border-radius: 4px;
margin-bottom: 8px;
font-weight: bold;
color: #495057;
font-size: 13px;
width: 350px;
flex-shrink: 0;
}

.icon-column {
border: 1px solid #e0e0e0;
border-radius: 5px;
padding: 8px;
background: #fafafa;
width: 350px;
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;
}

.icon-row {
display: grid;
grid-template-columns: 32px 20px 32px 1fr;
gap: 8px;
align-items: center;
padding: 6px 3px;
border-bottom: 1px solid #f0f0f0;
}

.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 {
/* Стиль для обработанных BMP с прозрачностью */
filter: none;
}

.arrow {
text-align: center;
color: #999;
font-size: 14px;
font-weight: bold;
}

.icon-info {
padding-left: 8px;
padding-right: 5px;
min-width: 0;
}

.icon-name {
color: #333;
line-height: 1.3;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
text-align: left;
}

.loading {
text-align: center;
padding: 20px;
color: #666;
width: 100%;
}

.no-results {
text-align: center;
padding: 40px;
color: #666;
width: 100%;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>Сравнение иконок ИСИХОГИ старой и новой версий</h1>

<div class="search-container">
<input type="text"
id="searchInput"
class="search-input"
placeholder="Поиск по названию иконки...">
</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="group-toggle" id="groupToggle" style="display: none;">
<input type="checkbox" id="showGroups" checked>
<label for="showGroups">Отображать группы</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;

// Улучшенная функция для обработки BMP с хромакеем
function processBMPWithChromaKey(img) {
return new Promise((resolve) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;

// Рисуем изображение на canvas
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) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];

// Точное определение ярко-розового хромакея (255, 0, 255)
const isChromaKey = r === 255 && g === 0 && b === 255;

// Также обрабатываем близкие оттенки (допуск ±5)
const isNearChromaKey =
r >= 250 && r <= 255 &&
g >= 0 && g <= 5 &&
b >= 250 && b <= 255;

if (isChromaKey || isNearChromaKey) {
data[i + 3] = 0; // Устанавливаем полную прозрачность
}
}

// Записываем обработанные данные обратно
ctx.putImageData(imageData, 0, 0);

const processedImg = new Image();
processedImg.onload = () => {
console.log('BMP успешно обработан, прозрачность применена');
resolve({
element: processedImg,
processed: true
});
};
processedImg.src = canvas.toDataURL('image/png');
} catch (error) {
console.warn('Ошибка обработки BMP:', error);
resolve({
element: img,
processed: false
});
}
});
}

// Функция для загрузки изображения с обработкой BMP
function loadImageWithBMPProcessing(src, alt) {
return new Promise((resolve) => {
const img = new Image();

img.onload = async function() {
// Обрабатываем только BMP файлы
if (src.toLowerCase().endsWith('.bmp')) {
try {
console.log('Обработка BMP:', src);
const result = await processBMPWithChromaKey(img);
resolve(result);
} catch (error) {
console.warn('Ошибка при обработке BMP:', error);
resolve({ element: img, processed: false });
}
} else {
// Для других форматов просто возвращаем изображение
resolve({ element: img, processed: false });
}
};

img.onerror = function() {
console.warn('Ошибка загрузки изображения:', src);
// Заглушка для отсутствующего изображения
const placeholder = new Image();
placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIz IDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo=';
resolve({ element: placeholder, processed: false });
};

img.src = src;
img.alt = alt;
});
}

// Функция для группировки иконок
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 splitIconsIntoColumns(icons, maxIconsPerColumn = 18) {
const columns = [];
const totalIcons = icons.length;

if (totalIcons <= maxIconsPerColumn) {
columns.push(icons);
} else {
const numberOfColumns = Math.ceil(totalIcons / maxIconsPerColumn);
const iconsPerColumn = Math.ceil(totalIcons / numberOfColumns);

for (let i = 0; i < totalIcons; i += iconsPerColumn) {
columns.push(icons.slice(i, i + iconsPerColumn));
}
}

return columns;
}

// Функция для создания колонки с иконками
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 = 'ДО';

const arrowSpace = document.createElement('div');
arrowSpace.className = 'arrow';
arrowSpace.textContent = '→';

const afterLabel = document.createElement('div');
afterLabel.className = 'header-label';
afterLabel.textContent = 'ПОСЛЕ';

const nameLabel = document.createElement('div');
nameLabel.className = '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(createIconRow(icon));
});

return column;
}

// Функция для создания строки с иконками
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(icons) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (icons.length === 0) return;

// Сортируем иконки по алфавиту
const sortedIcons = [...icons].sort((a, b) => a.name.localeCompare(b.name));

// Разбиваем на колонки
const columns = splitIconsIntoColumns(sortedIcons);

columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
container.appendChild(column);
});
}

// Функция для отображения иконок с группами
function renderIconsWithGroups() {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (filteredIcons.length === 0) return;

// Группируем иконки
const { groups, noGroup } = groupIcons(filteredIcons);

// Создаем все элементы для отображения
const allElements = [];

// Добавляем группы
Object.keys(groups).sort().forEach(groupName => {
const groupIcons = groups[groupName];
const columns = splitIconsIntoColumns(groupIcons);

// Создаем контейнер для группы
const groupBlock = document.createElement('div');
groupBlock.className = 'group-block';

// Добавляем заголовок группы
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = groupName;
allElements.push(header);

// Добавляем колонки группы
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allElements.push(column);
});
});

// Добавляем прочие иконки
if (noGroup.length > 0) {
const columns = splitIconsIntoColumns(noGroup);

// Если есть другие группы, добавляем заголовок для прочих иконок
if (Object.keys(groups).length > 0) {
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = 'Прочие иконки';
allElements.push(header);
}

// Добавляем колонки прочих иконок
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allElements.push(column);
});
}

// Добавляем все элементы в контейнер
allElements.forEach(element => {
container.appendChild(element);
});
}

// Функция для фильтрации иконок по поисковому запросу и группе
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 (filteredIcons.length === 0) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>';
updateStats();
return;
}

if (showGroups) {
renderIconsWithGroups();
} else {
renderIconsWithoutGroups(filteredIcons);
}

updateStats();
}

// Обновление статистики
function updateStats() {
document.getElementById('totalCount').textContent = allIcons.length;
document.getElementById('shownCount').textContent = filteredIcons.length;
}

// Инициализация комбобокса групп и переключателя
function initGroupControls(groups) {
const groupSelect = document.getElementById('groupSelect');
const groupToggle = document.getElementById('groupToggle');

groupSelect.innerHTML = '<option value="">Все группы</option>';

groups.forEach(group => {
const option = document.createElement('option');
option.value = group;
option.textContent = group;
groupSelect.appendChild(option);
});

const hasGroups = groups.length > 0;
groupSelect.style.display = hasGroups ? 'block' : 'none';
groupToggle.style.display = hasGroups ? 'flex' : 'none';

groupSelect.addEventListener('change', function() {
const searchTerm = document.getElementById('searchInput').value;
filterIcons(searchTerm, this.value);
});

document.getElementById('showGroups').addEventListener('change', function() {
showGroups = this.checked;
renderFilteredIcons();
});
}

// Функция для отображения всех иконок
function renderIcons(images, groups) {
allIcons = images;
filteredIcons = [...allIcons];
availableGroups = groups;

initGroupControls(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('searchInput');
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);
}
});
}

// Загружаем иконки при загрузке страницы
window.addEventListener('DOMContentLoaded', function() {
loadIcons();
setupSearch();
});
</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: 100%;
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;
}

.search-input {
width: 300px;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}

.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;
}

.group-toggle {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px;
}

.group-toggle input {
margin: 0;
}

.comparison-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-start;
}

.group-block {
display: contents; /* Разрешаем элементам группы участвовать в общем потоке */
}

.group-header {
background: #f8f9fa;
padding: 6px 10px;
border-radius: 4px;
margin-bottom: 8px;
font-weight: bold;
color: #495057;
font-size: 13px;
width: 350px;
flex-shrink: 0;
}

.icon-column {
border: 1px solid #e0e0e0;
border-radius: 5px;
padding: 8px;
background: #fafafa;
width: 350px;
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;
}

.icon-row {
display: grid;
grid-template-columns: 32px 20px 32px 1fr;
gap: 8px;
align-items: center;
padding: 6px 3px;
border-bottom: 1px solid #f0f0f0;
}

.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 {
/* Стиль для обработанных BMP с прозрачностью */
filter: none;
}

.arrow {
text-align: center;
color: #999;
font-size: 14px;
font-weight: bold;
}

.icon-info {
padding-left: 8px;
padding-right: 5px;
min-width: 0;
}

.icon-name {
color: #333;
line-height: 1.3;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
text-align: left;
}

.loading {
text-align: center;
padding: 20px;
color: #666;
width: 100%;
}

.no-results {
text-align: center;
padding: 40px;
color: #666;
width: 100%;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>Сравнение иконок ИСИХОГИ старой и новой версий</h1>

<div class="search-container">
<input type="text"
id="searchInput"
class="search-input"
placeholder="Поиск по названию иконки...">
</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="group-toggle" id="groupToggle" style="display: none;">
<input type="checkbox" id="showGroups" checked>
<label for="showGroups">Отображать группы</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;

// Улучшенная функция для обработки BMP с хромакеем
function processBMPWithChromaKey(img) {
return new Promise((resolve) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;

// Рисуем изображение на canvas
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) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];

// Точное определение ярко-розового хромакея (255, 0, 255)
const isChromaKey = r === 255 && g === 0 && b === 255;

// Также обрабатываем близкие оттенки (допуск ±5)
const isNearChromaKey =
r >= 250 && r <= 255 &&
g >= 0 && g <= 5 &&
b >= 250 && b <= 255;

if (isChromaKey || isNearChromaKey) {
data[i + 3] = 0; // Устанавливаем полную прозрачность
}
}

// Записываем обработанные данные обратно
ctx.putImageData(imageData, 0, 0);

const processedImg = new Image();
processedImg.onload = () => {
console.log('BMP успешно обработан, прозрачность применена');
resolve({
element: processedImg,
processed: true
});
};
processedImg.src = canvas.toDataURL('image/png');
} catch (error) {
console.warn('Ошибка обработки BMP:', error);
resolve({
element: img,
processed: false
});
}
});
}

// Функция для загрузки изображения с обработкой BMP
function loadImageWithBMPProcessing(src, alt) {
return new Promise((resolve) => {
const img = new Image();

img.onload = async function() {
// Обрабатываем только BMP файлы
if (src.toLowerCase().endsWith('.bmp')) {
try {
console.log('Обработка BMP:', src);
const result = await processBMPWithChromaKey(img);
resolve(result);
} catch (error) {
console.warn('Ошибка при обработке BMP:', error);
resolve({ element: img, processed: false });
}
} else {
// Для других форматов просто возвращаем изображение
resolve({ element: img, processed: false });
}
};

img.onerror = function() {
console.warn('Ошибка загрузки изображения:', src);
// Заглушка для отсутствующего изображения
const placeholder = new Image();
placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIz IDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo=';
resolve({ element: placeholder, processed: false });
};

img.src = src;
img.alt = alt;
});
}

// Функция для группировки иконок
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 splitIconsIntoColumns(icons, maxIconsPerColumn = 18) {
const columns = [];
const totalIcons = icons.length;

if (totalIcons <= maxIconsPerColumn) {
columns.push(icons);
} else {
const numberOfColumns = Math.ceil(totalIcons / maxIconsPerColumn);
const iconsPerColumn = Math.ceil(totalIcons / numberOfColumns);

for (let i = 0; i < totalIcons; i += iconsPerColumn) {
columns.push(icons.slice(i, i + iconsPerColumn));
}
}

return columns;
}

// Функция для создания колонки с иконками
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 = 'ДО';

const arrowSpace = document.createElement('div');
arrowSpace.className = 'arrow';
arrowSpace.textContent = '→';

const afterLabel = document.createElement('div');
afterLabel.className = 'header-label';
afterLabel.textContent = 'ПОСЛЕ';

const nameLabel = document.createElement('div');
nameLabel.className = '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(createIconRow(icon));
});

return column;
}

// Функция для создания строки с иконками
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(icons) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (icons.length === 0) return;

// Сортируем иконки по алфавиту
const sortedIcons = [...icons].sort((a, b) => a.name.localeCompare(b.name));

// Разбиваем на колонки
const columns = splitIconsIntoColumns(sortedIcons);

columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
container.appendChild(column);
});
}

// Функция для отображения иконок с группами
function renderIconsWithGroups() {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (filteredIcons.length === 0) return;

// Группируем иконки
const { groups, noGroup } = groupIcons(filteredIcons);

// Создаем все элементы для отображения
const allElements = [];

// Добавляем группы
Object.keys(groups).sort().forEach(groupName => {
const groupIcons = groups[groupName];
const columns = splitIconsIntoColumns(groupIcons);

// Создаем контейнер для группы
const groupBlock = document.createElement('div');
groupBlock.className = 'group-block';

// Добавляем заголовок группы
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = groupName;
allElements.push(header);

// Добавляем колонки группы
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allElements.push(column);
});
});

// Добавляем прочие иконки
if (noGroup.length > 0) {
const columns = splitIconsIntoColumns(noGroup);

// Если есть другие группы, добавляем заголовок для прочих иконок
if (Object.keys(groups).length > 0) {
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = 'Прочие иконки';
allElements.push(header);
}

// Добавляем колонки прочих иконок
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allElements.push(column);
});
}

// Добавляем все элементы в контейнер
allElements.forEach(element => {
container.appendChild(element);
});
}

// Функция для фильтрации иконок по поисковому запросу и группе
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 (filteredIcons.length === 0) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>';
updateStats();
return;
}

if (showGroups) {
renderIconsWithGroups();
} else {
renderIconsWithoutGroups(filteredIcons);
}

updateStats();
}

// Обновление статистики
function updateStats() {
document.getElementById('totalCount').textContent = allIcons.length;
document.getElementById('shownCount').textContent = filteredIcons.length;
}

// Инициализация комбобокса групп и переключателя
function initGroupControls(groups) {
const groupSelect = document.getElementById('groupSelect');
const groupToggle = document.getElementById('groupToggle');

groupSelect.innerHTML = '<option value="">Все группы</option>';

groups.forEach(group => {
const option = document.createElement('option');
option.value = group;
option.textContent = group;
groupSelect.appendChild(option);
});

const hasGroups = groups.length > 0;
groupSelect.style.display = hasGroups ? 'block' : 'none';
groupToggle.style.display = hasGroups ? 'flex' : 'none';

groupSelect.addEventListener('change', function() {
const searchTerm = document.getElementById('searchInput').value;
filterIcons(searchTerm, this.value);
});

document.getElementById('showGroups').addEventListener('change', function() {
showGroups = this.checked;
renderFilteredIcons();
});
}

// Функция для отображения всех иконок
function renderIcons(images, groups) {
allIcons = images;
filteredIcons = [...allIcons];
availableGroups = groups;

initGroupControls(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('searchInput');
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);
}
});
}

// Загружаем иконки при загрузке страницы
window.addEventListener('DOMContentLoaded', function() {
loadIcons();
setupSearch();
});
</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: 100%;
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;
}

.search-input {
width: 300px;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}

.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;
}

.group-toggle {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px;
}

.group-toggle input {
margin: 0;
}

.comparison-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-start;
}

.group-block {
display: contents; /* Элементы группы участвуют в общем потоке */
}

.group-header-container {
width: 100%;
flex-basis: 100%;
margin-bottom: 8px;
}

.group-header {
background: #f8f9fa;
padding: 6px 10px;
border-radius: 4px;
font-weight: bold;
color: #495057;
font-size: 13px;
display: inline-block;
}

.icon-column {
border: 1px solid #e0e0e0;
border-radius: 5px;
padding: 8px;
background: #fafafa;
width: 350px;
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;
}

.icon-row {
display: grid;
grid-template-columns: 32px 20px 32px 1fr;
gap: 8px;
align-items: center;
padding: 6px 3px;
border-bottom: 1px solid #f0f0f0;
}

.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 {
/* Стиль для обработанных BMP с прозрачностью */
filter: none;
}

.arrow {
text-align: center;
color: #999;
font-size: 14px;
font-weight: bold;
}

.icon-info {
padding-left: 8px;
padding-right: 5px;
min-width: 0;
}

.icon-name {
color: #333;
line-height: 1.3;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
text-align: left;
}

.loading {
text-align: center;
padding: 20px;
color: #666;
width: 100%;
}

.no-results {
text-align: center;
padding: 40px;
color: #666;
width: 100%;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>Сравнение иконок ИСИХОГИ старой и новой версий</h1>

<div class="search-container">
<input type="text"
id="searchInput"
class="search-input"
placeholder="Поиск по названию иконки...">
</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="group-toggle" id="groupToggle" style="display: none;">
<input type="checkbox" id="showGroups" checked>
<label for="showGroups">Отображать группы</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;

// Улучшенная функция для обработки BMP с хромакеем
function processBMPWithChromaKey(img) {
return new Promise((resolve) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getImageData('2d');
canvas.width = img.width;
canvas.height = img.height;

// Рисуем изображение на canvas
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) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];

// Точное определение ярко-розового хромакея (255, 0, 255)
const isChromaKey = r === 255 && g === 0 && b === 255;

// Также обрабатываем близкие оттенки (допуск ±5)
const isNearChromaKey =
r >= 250 && r <= 255 &&
g >= 0 && g <= 5 &&
b >= 250 && b <= 255;

if (isChromaKey || isNearChromaKey) {
data[i + 3] = 0; // Устанавливаем полную прозрачность
}
}

// Записываем обработанные данные обратно
ctx.putImageData(imageData, 0, 0);

const processedImg = new Image();
processedImg.onload = () => {
console.log('BMP успешно обработан, прозрачность применена');
resolve({
element: processedImg,
processed: true
});
};
processedImg.src = canvas.toDataURL('image/png');
} catch (error) {
console.warn('Ошибка обработки BMP:', error);
resolve({
element: img,
processed: false
});
}
});
}

// Функция для загрузки изображения с обработкой BMP
function loadImageWithBMPProcessing(src, alt) {
return new Promise((resolve) => {
const img = new Image();

img.onload = async function() {
// Обрабатываем только BMP файлы
if (src.toLowerCase().endsWith('.bmp')) {
try {
console.log('Обработка BMP:', src);
const result = await processBMPWithChromaKey(img);
resolve(result);
} catch (error) {
console.warn('Ошибка при обработке BMP:', error);
resolve({ element: img, processed: false });
}
} else {
// Для других форматов просто возвращаем изображение
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=';
resolve({ element: placeholder, processed: false });
};

img.src = src;
img.alt = alt;
});
}

// Функция для группировки иконок
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 splitIconsIntoColumns(icons, maxIconsPerColumn = 18) {
const columns = [];
const totalIcons = icons.length;

if (totalIcons <= maxIconsPerColumn) {
columns.push(icons);
} else {
const numberOfColumns = Math.ceil(totalIcons / maxIconsPerColumn);
const iconsPerColumn = Math.ceil(totalIcons / numberOfColumns);

for (let i = 0; i < totalIcons; i += iconsPerColumn) {
columns.push(icons.slice(i, i + iconsPerColumn));
}
}

return columns;
}

// Функция для создания колонки с иконками
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 = 'ДО';

const arrowSpace = document.createElement('div');
arrowSpace.className = 'arrow';
arrowSpace.textContent = '→';

const afterLabel = document.createElement('div');
afterLabel.className = 'header-label';
afterLabel.textContent = 'ПОСЛЕ';

const nameLabel = document.createElement('div');
nameLabel.className = '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(createIconRow(icon));
});

return column;
}

// Функция для создания строки с иконками
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(icons) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (icons.length === 0) return;

// Сортируем иконки по алфавиту
const sortedIcons = [...icons].sort((a, b) => a.name.localeCompare(b.name));

// Разбиваем на колонки
const columns = splitIconsIntoColumns(sortedIcons);

columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
container.appendChild(column);
});
}

// Функция для отображения иконок с группами
function renderIconsWithGroups() {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (filteredIcons.length === 0) return;

// Группируем иконки
const { groups, noGroup } = groupIcons(filteredIcons);

// Создаем все элементы для отображения
const allElements = [];

// Добавляем группы
Object.keys(groups).sort().forEach(groupName => {
const groupIcons = groups[groupName];
const columns = splitIconsIntoColumns(groupIcons);

// Добавляем заголовок группы (занимает всю ширину)
const headerContainer = document.createElement('div');
headerContainer.className = 'group-header-container';

const header = document.createElement('div');
header.className = 'group-header';
header.textContent = groupName;
headerContainer.appendChild(header);

allElements.push(headerContainer);

// Добавляем колонки группы
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allElements.push(column);
});
});

// Добавляем прочие иконки
if (noGroup.length > 0) {
const columns = splitIconsIntoColumns(noGroup);

// Если есть другие группы, добавляем заголовок для прочих иконок
if (Object.keys(groups).length > 0) {
const headerContainer = document.createElement('div');
headerContainer.className = 'group-header-container';

const header = document.createElement('div');
header.className = 'group-header';
header.textContent = 'Прочие иконки';
headerContainer.appendChild(header);

allElements.push(headerContainer);
}

// Добавляем колонки прочих иконок
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
allElements.push(column);
});
}

// Добавляем все элементы в контейнер
allElements.forEach(element => {
container.appendChild(element);
});
}

// Функция для фильтрации иконок по поисковому запросу и группе
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 (filteredIcons.length === 0) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>';
updateStats();
return;
}

if (showGroups) {
renderIconsWithGroups();
} else {
renderIconsWithoutGroups(filteredIcons);
}

updateStats();
}

// Обновление статистики
function updateStats() {
document.getElementById('totalCount').textContent = allIcons.length;
document.getElementById('shownCount').textContent = filteredIcons.length;
}

// Инициализация комбобокса групп и переключателя
function initGroupControls(groups) {
const groupSelect = document.getElementById('groupSelect');
const groupToggle = document.getElementById('groupToggle');

groupSelect.innerHTML = '<option value="">Все группы</option>';

groups.forEach(group => {
const option = document.createElement('option');
option.value = group;
option.textContent = group;
groupSelect.appendChild(option);
});

const hasGroups = groups.length > 0;
groupSelect.style.display = hasGroups ? 'block' : 'none';
groupToggle.style.display = hasGroups ? 'flex' : 'none';

groupSelect.addEventListener('change', function() {
const searchTerm = document.getElementById('searchInput').value;
filterIcons(searchTerm, this.value);
});

document.getElementById('showGroups').addEventListener('change', function() {
showGroups = this.checked;
renderFilteredIcons();
});
}

// Функция для отображения всех иконок
function renderIcons(images, groups) {
allIcons = images;
filteredIcons = [...allIcons];
availableGroups = groups;

initGroupControls(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('searchInput');
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);
}
});
}

// Загружаем иконки при загрузке страницы
window.addEventListener('DOMContentLoaded', function() {
loadIcons();
setupSearch();
});
</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: 100%;
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;
}

.search-input {
width: 300px;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}

.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;
}

.group-toggle {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px;
}

.group-toggle input {
margin: 0;
}

.groups-container {
display: flex;
flex-direction: column;
gap: 20px;
}

.group-section {
display: flex;
flex-direction: column;
gap: 8px;
}

.group-header {
background: #f8f9fa;
padding: 6px 10px;
border-radius: 4px;
font-weight: bold;
color: #495057;
font-size: 13px;
width: fit-content;
}

.columns-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-start;
}

.icon-column {
border: 1px solid #e0e0e0;
border-radius: 5px;
padding: 8px;
background: #fafafa;
width: 350px;
}

.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;
}

.icon-row {
display: grid;
grid-template-columns: 32px 20px 32px 1fr;
gap: 8px;
align-items: center;
padding: 6px 3px;
border-bottom: 1px solid #f0f0f0;
}

.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;
}

.icon-info {
padding-left: 8px;
padding-right: 5px;
min-width: 0;
}

.icon-name {
color: #333;
line-height: 1.3;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
text-align: left;
}

.loading {
text-align: center;
padding: 20px;
color: #666;
}

.no-results {
text-align: center;
padding: 40px;
color: #666;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>Сравнение иконок ИСИХОГИ старой и новой версий</h1>

<div class="search-container">
<input type="text"
id="searchInput"
class="search-input"
placeholder="Поиск по названию иконки...">
</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="group-toggle" id="groupToggle" style="display: none;">
<input type="checkbox" id="showGroups" checked>
<label for="showGroups">Отображать группы</label>
</div>
</div>

<div id="iconsContainer">
<div class="loading">Загрузка иконок...</div>
</div>
</div>

<script>
let allIcons = [];
let filteredIcons = [];
let availableGroups = [];
let showGroups = true;

// Функция для обработки BMP с хромакеем
function processBMPWithChromaKey(img) {
return new Promise((resolve) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;

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) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];

const isChromaKey = r === 255 && g === 0 && b === 255;
const isNearChromaKey =
r >= 250 && r <= 255 &&
g >= 0 && g <= 5 &&
b >= 250 && b <= 255;

if (isChromaKey || isNearChromaKey) {
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 });
}
});
}

// Функция для загрузки изображения с обработкой BMP
function loadImageWithBMPProcessing(src, alt) {
return new Promise((resolve) => {
const img = new Image();

img.onload = async function() {
if (src.toLowerCase().endsWith('.bmp')) {
try {
const result = await processBMPWithChromaKey(img);
resolve(result);
} catch (error) {
resolve({ element: img, processed: false });
}
} else {
resolve({ element: img, processed: false });
}
};

img.onerror = function() {
const placeholder = new Image();
placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSIjRjVGNUY1Ii8+CjxwYXRoIGQ9Ik0xNiAxOEMxNi41NTIzIDE4IDE3IDE3LjU1MjMgMTcgMTdWMTFDMTcgMTAuNDQ3NyAxNi41NTIzIDEwIDE2IDEwQzE1LjQ0NzcgMTAgMTUgMTAuNDQ3NyAxNSAxMVYxN0MxNSAxNy41NTIzIDE1LjQ0NzcgMTggMTYgMThaIiBmaWxsPSIjOTk5OTk5Ii8+CjxwYXRoIGQ9Ik0xNiAyMkMxNi41NTIzIDIyIDE3IDIxLjU1MjMgMTcgMjFDMTcgMjAuNDQ3NyAxNi41NTIzIDIwIDE2IDIwQzE1LjQ0NzcgMjAgMTUgMjAuNDQ3NyAxNSAyMUMxNSAyMS41NTIzIDE1LjQ0NzcgMjIgMTYgMjJaIiBmaWxsPSIjOTk5OTk5Ii8+Cjwvc3ZnPgo=';
resolve({ element: placeholder, processed: false });
};

img.src = src;
img.alt = alt;
});
}

// Функция для группировки иконок
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 splitIconsIntoColumns(icons, maxIconsPerColumn = 18) {
const columns = [];
const totalIcons = icons.length;

if (totalIcons <= maxIconsPerColumn) {
columns.push(icons);
} else {
const numberOfColumns = Math.ceil(totalIcons / maxIconsPerColumn);
const iconsPerColumn = Math.ceil(totalIcons / numberOfColumns);

for (let i = 0; i < totalIcons; i += iconsPerColumn) {
columns.push(icons.slice(i, i + iconsPerColumn));
}
}

return columns;
}

// Функция для создания колонки с иконками
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 = 'ДО';

const arrowSpace = document.createElement('div');
arrowSpace.className = 'arrow';
arrowSpace.textContent = '→';

const afterLabel = document.createElement('div');
afterLabel.className = 'header-label';
afterLabel.textContent = 'ПОСЛЕ';

const nameLabel = document.createElement('div');
nameLabel.className = '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(createIconRow(icon));
});

return column;
}

// Функция для создания строки с иконками
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(icons) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (icons.length === 0) return;

const sortedIcons = [...icons].sort((a, b) => a.name.localeCompare(b.name));
const columns = splitIconsIntoColumns(sortedIcons);
const columnsContainer = document.createElement('div');
columnsContainer.className = 'columns-container';

columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
columnsContainer.appendChild(column);
});

container.appendChild(columnsContainer);
}

// Функция для отображения иконок с группами
function renderIconsWithGroups() {
const container = document.getElementById('iconsContainer');
container.innerHTML = '';

if (filteredIcons.length === 0) return;

const { groups, noGroup } = groupIcons(filteredIcons);
const groupsContainer = document.createElement('div');
groupsContainer.className = 'groups-container';

// Отображаем группы
Object.keys(groups).sort().forEach(groupName => {
const groupSection = document.createElement('div');
groupSection.className = 'group-section';

const header = document.createElement('div');
header.className = 'group-header';
header.textContent = groupName;
groupSection.appendChild(header);

const columnsContainer = document.createElement('div');
columnsContainer.className = 'columns-container';

const columns = splitIconsIntoColumns(groups[groupName]);
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
columnsContainer.appendChild(column);
});

groupSection.appendChild(columnsContainer);
groupsContainer.appendChild(groupSection);
});

// Отображаем прочие иконки
if (noGroup.length > 0) {
const groupSection = document.createElement('div');
groupSection.className = 'group-section';

if (Object.keys(groups).length > 0) {
const header = document.createElement('div');
header.className = 'group-header';
header.textContent = 'Прочие иконки';
groupSection.appendChild(header);
}

const columnsContainer = document.createElement('div');
columnsContainer.className = 'columns-container';

const columns = splitIconsIntoColumns(noGroup);
columns.forEach(columnIcons => {
const column = createIconColumn(columnIcons);
columnsContainer.appendChild(column);
});

groupSection.appendChild(columnsContainer);
groupsContainer.appendChild(groupSection);
}

container.appendChild(groupsContainer);
}

// Функция для фильтрации иконок
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 (filteredIcons.length === 0) {
const container = document.getElementById('iconsContainer');
container.innerHTML = '<div class="no-results">Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другую группу.</div>';
updateStats();
return;
}

if (showGroups) {
renderIconsWithGroups();
} else {
renderIconsWithoutGroups(filteredIcons);
}

updateStats();
}

// Обновление статистики
function updateStats() {
document.getElementById('totalCount').textContent = allIcons.length;
document.getElementById('shownCount').textContent = filteredIcons.length;
}

// Инициализация комбобокса групп и переключателя
function initGroupControls(groups) {
const groupSelect = document.getElementById('groupSelect');
const groupToggle = document.getElementById('groupToggle');

groupSelect.innerHTML = '<option value="">Все группы</option>';

groups.forEach(group => {
const option = document.createElement('option');
option.value = group;
option.textContent = group;
groupSelect.appendChild(option);
});

const hasGroups = groups.length > 0;
groupSelect.style.display = hasGroups ? 'block' : 'none';
groupToggle.style.display = hasGroups ? 'flex' : 'none';

groupSelect.addEventListener('change', function() {
const searchTerm = document.getElementById('searchInput').value;
filterIcons(searchTerm, this.value);
});

document.getElementById('showGroups').addEventListener('change', function() {
showGroups = this.checked;
renderFilteredIcons();
});
}

// Функция для отображения всех иконок
function renderIcons(images, groups) {
allIcons = images;
filteredIcons = [...allIcons];
availableGroups = groups;

initGroupControls(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('searchInput');
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);
}
});
}

// Загружаем иконки при загрузке страницы
window.addEventListener('DOMContentLoaded', function() {
loadIcons();
setupSearch();
});
</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>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}

.container {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
}

.upload-area {
border: 2px dashed #ccc;
border-radius: 8px;
padding: 30px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
}

.upload-area p {
margin: 0;
color: #666;
}

#fileInput {
display: none;
}

.preview-container {
display: flex;
justify-content: space-around;
margin-top: 20px;
}

.preview-box {
text-align: center;
}

.preview-box h3 {
margin-bottom: 10px;
color: #444;
}

.preview-image {
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f9f9f9;
padding: 10px;
width: 160px;
height: 160px;
image-rendering: pixelated;
object-fit: contain;
}

.transparent-bg {
background-image:
linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}

.error {
color: #d32f2f;
text-align: center;
margin-top: 10px;
padding: 10px;
background-color: #ffebee;
border-radius: 4px;
}

.info {
color: #666;
font-size: 14px;
margin-top: 20px;
text-align: center;
}

.file-info {
background-color: #e8f5e9;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
text-align: center;
}

.warning {
color: #ff9800;
text-align: center;
margin-top: 10px;
padding: 10px;
background-color: #fff3e0;
border-radius: 4px;
}

.debug-info {
background-color: #e3f2fd;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
font-size: 12px;
font-family: monospace;
max-height: 100px;
overflow-y: auto;
}

.controls {
text-align: center;
margin: 20px 0;
}

.download-btn {
background-color: #4CAF50;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}

.download-btn:hover {
background-color: #45a049;
}

.download-btn:disabled {
background-color: #cccccc;
cursor: not-allowed;
}

.filename-info {
margin-top: 10px;
font-size: 14px;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<h1>Удаление розового фона с изображений</h1>

<div class="upload-area" id="uploadArea">
<p>Нажмите здесь или перетащите файл изображения</p>
<input type="file" id="fileInput" accept=".bmp,.png,.jpg,.jpeg,.gif,.ico">
</div>

<div id="fileInfo" class="file-info" style="display: none;"></div>
<div id="debugInfo" class="debug-info" style="display: none;"></div>

<div class="preview-container" id="previewContainer" style="display: none;">
<div class="preview-box">
<h3>Исходное изображение</h3>
<img id="originalImage" class="preview-image">
</div>
<div class="preview-box">
<h3>Без фона</h3>
<canvas id="processedCanvas" class="preview-image transparent-bg"></canvas>
</div>
</div>

<div class="controls" id="controls" style="display: none;">
<button id="downloadBtn" class="download-btn">
💾 Скачать без фона
</button>
<div id="filenameInfo" class="filename-info"></div>
</div>

<div id="errorMessage" class="error" style="display: none;"></div>
<div id="warningMessage" class="warning" style="display: none;"></div>

<div class="info">
<p>Ярко-розовый фон (RGB: 255, 0, 255) будет заменён на прозрачный.</p>
<p>Поддерживаются BMP, PNG, JPG, GIF и другие форматы.</p>
</div>
</div>

<script>
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const previewContainer = document.getElementById('previewContainer');
const originalImage = document.getElementById('originalImage');
const processedCanvas = document.getElementById('processedCanvas');
const errorMessage = document.getElementById('errorMessage');
const warningMessage = document.getElementById('warningMessage');
const fileInfo = document.getElementById('fileInfo');
const debugInfo = document.getElementById('debugInfo');
const controls = document.getElementById('controls');
const downloadBtn = document.getElementById('downloadBtn');
const filenameInfo = document.getElementById('filenameInfo');

let currentFile = null;
let processedImageData = null;

// Обработчик клика по области загрузки
uploadArea.addEventListener('click', () => {
fileInput.click();
});

// Обработчик перетаскивания файлов
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
});

uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
if (e.dataTransfer.files.length) {
handleFile(e.dataTransfer.files[0]);
}
});

// Обработчик выбора файла через input
fileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
handleFile(e.target.files[0]);
}
});

// Обработчик кнопки скачивания
downloadBtn.addEventListener('click', downloadProcessedImage);

function handleFile(file) {
// Скрываем сообщения
errorMessage.style.display = 'none';
warningMessage.style.display = 'none';
debugInfo.style.display = 'none';
controls.style.display = 'none';

currentFile = file;

// Показываем информацию о файле
fileInfo.textContent = `Файл: ${file.name} (${formatFileSize(file.size)})`;
fileInfo.style.display = 'block';

const reader = new FileReader();

reader.onload = function(e) {
try {
// Сначала пробуем открыть как обычное изображение
openAsStandardImage(file, e.target.result);
} catch (error) {
console.warn('Не удалось открыть как стандартное изображение:', error);
// Если не получилось, пробуем как BMP
try {
openAsBMP(file, e.target.result);
} catch (bmpError) {
showError('Не удалось открыть файл: ' + bmpError.message);
console.error('Ошибка открытия BMP:', bmpError);
}
}
};

reader.onerror = function() {
showError('Ошибка при чтении файла');
};

reader.readAsArrayBuffer(file);
}

function openAsStandardImage(file, arrayBuffer) {
// Создаем Blob и URL для стандартного отображения
const blob = new Blob([arrayBuffer], { type: file.type });
const url = URL.createObjectURL(blob);

originalImage.onload = function() {
URL.revokeObjectURL(url);

// Отображаем исходное изображение
originalImage.style.display = 'block';

// Создаем canvas для обработки
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = originalImage.naturalWidth;
tempCanvas.height = originalImage.naturalHeight;

tempCtx.drawImage(originalImage, 0, 0);
const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);

// Обрабатываем изображение
processedImageData = removePinkBackgroundFromImageData(imageData);
displayProcessedImage(processedImageData);

// Показываем контейнер с превью
previewContainer.style.display = 'flex';
controls.style.display = 'block';

// Показываем информацию о имени файла для скачивания
const exportFilename = getExportFilename(currentFile.name);
filenameInfo.textContent = `Будет сохранено как: ${exportFilename}`;

// Добавляем отладочную информацию
debugInfo.innerHTML = `
Формат: Стандартное изображение<br>
Размер: ${tempCanvas.width}×${tempCanvas.height}<br>
Пикселей: ${imageData.data.length / 4}
`;
debugInfo.style.display = 'block';
};

originalImage.onerror = function() {
URL.revokeObjectURL(url);
throw new Error('Не удалось загрузить изображение');
};

originalImage.src = url;
}

function openAsBMP(file, arrayBuffer) {
try {
const bmpData = parseBMP(arrayBuffer);

// Отображаем исходное BMP
displayBMPImage(originalImage, bmpData);
originalImage.style.display = 'block';

// Обрабатываем BMP
processedImageData = removeStrictPinkBackground(bmpData);
displayProcessedImage(processedImageData);

// Показываем контейнер с превью
previewContainer.style.display = 'flex';
controls.style.display = 'block';

// Показываем информацию о имени файла для скачивания
const exportFilename = getExportFilename(currentFile.name);
filenameInfo.textContent = `Будет сохранено как: ${exportFilename}`;

// Добавляем отладочную информацию
debugInfo.innerHTML = `
Формат: BMP<br>
Размер: ${bmpData.width}×${bmpData.height}<br>
Битность: ${bmpData.bitsPerPixel} бит<br>
Пикселей: ${bmpData.pixels.length}
`;
debugInfo.style.display = 'block';

} catch (error) {
throw new Error('Неверный формат BMP: ' + error.message);
}
}

function parseBMP(arrayBuffer) {
const dataView = new DataView(arrayBuffer);

// Проверяем сигнатуру BMP
if (dataView.getUint16(0, true) !== 0x4D42) {
throw new Error('Неверная сигнатура BMP');
}

const pixelDataOffset = dataView.getUint32(10, true);
const headerSize = dataView.getUint32(14, true);

let width, height, bitsPerPixel;

if (headerSize === 12) {
width = dataView.getUint16(18, true);
height = dataView.getUint16(20, true);
bitsPerPixel = dataView.getUint16(24, true);
} else {
width = dataView.getInt32(18, true);
height = dataView.getInt32(22, true);
bitsPerPixel = dataView.getUint16(28, true);
}

const absHeight = Math.abs(height);
const isTopDown = height < 0;

let pixels = [];

if (bitsPerPixel === 32) {
pixels = parse32BppBMP(dataView, width, absHeight, pixelDataOffset, isTopDown);
} else if (bitsPerPixel === 24) {
pixels = parse24BppBMP(dataView, width, absHeight, pixelDataOffset, isTopDown);
} else if (bitsPerPixel === 8) {
pixels = parse8BppBMP(dataView, width, absHeight, pixelDataOffset, headerSize, isTopDown);
} else if (bitsPerPixel === 4) {
pixels = parse4BppBMP(dataView, width, absHeight, pixelDataOffset, headerSize, isTopDown);
} else {
throw new Error(`Неподдерживаемая битность: ${bitsPerPixel}`);
}

return { width, height: absHeight, bitsPerPixel, pixels };
}

function parse32BppBMP(dataView, width, height, dataOffset, isTopDown) {
const pixels = [];
const rowSize = width * 4;

for (let y = 0; y < height; y++) {
const rowY = isTopDown ? y : height - 1 - y;
const rowOffset = dataOffset + rowY * rowSize;

for (let x = 0; x < width; x++) {
const pixelOffset = rowOffset + x * 4;
const b = dataView.getUint8(pixelOffset);
const g = dataView.getUint8(pixelOffset + 1);
const r = dataView.getUint8(pixelOffset + 2);
let a = dataView.getUint8(pixelOffset + 3);

if (a === 0 && (r !== 0 || g !== 0 || b !== 0)) a = 255;
pixels.push({ r, g, b, a });
}
}
return pixels;
}

function parse24BppBMP(dataView, width, height, dataOffset, isTopDown) {
const pixels = [];
const rowSize = Math.floor((width * 3 + 3) / 4) * 4;

for (let y = 0; y < height; y++) {
const rowY = isTopDown ? y : height - 1 - y;
const rowOffset = dataOffset + rowY * rowSize;

for (let x = 0; x < width; x++) {
const pixelOffset = rowOffset + x * 3;
const b = dataView.getUint8(pixelOffset);
const g = dataView.getUint8(pixelOffset + 1);
const r = dataView.getUint8(pixelOffset + 2);
pixels.push({ r, g, b, a: 255 });
}
}
return pixels;
}

function parse8BppBMP(dataView, width, height, dataOffset, headerSize, isTopDown) {
const pixels = [];
const paletteOffset = 14 + headerSize;
const palette = readPalette(dataView, 256, paletteOffset);
const rowSize = Math.floor((width + 3) / 4) * 4;

for (let y = 0; y < height; y++) {
const rowY = isTopDown ? y : height - 1 - y;
const rowOffset = dataOffset + rowY * rowSize;

for (let x = 0; x < width; x++) {
const index = dataView.getUint8(rowOffset + x);
pixels.push({ ...palette[index], a: 255 });
}
}
return pixels;
}

function parse4BppBMP(dataView, width, height, dataOffset, headerSize, isTopDown) {
const pixels = [];
const paletteOffset = 14 + headerSize;
const palette = readPalette(dataView, 16, paletteOffset);
const rowSize = Math.floor((width + 7) / 8) * 4;

for (let y = 0; y < height; y++) {
const rowY = isTopDown ? y : height - 1 - y;
const rowOffset = dataOffset + rowY * rowSize;

for (let x = 0; x < width; x += 2) {
const byteIndex = Math.floor(x / 2);
const byte = dataView.getUint8(rowOffset + byteIndex);
const index1 = (byte >> 4) & 0x0F;
const index2 = byte & 0x0F;

pixels.push({ ...palette[index1], a: 255 });
if (x + 1 < width) pixels.push({ ...palette[index2], a: 255 });
}
}
return pixels;
}

function readPalette(dataView, colorCount, paletteOffset) {
const palette = [];
for (let i = 0; i < colorCount; i++) {
const offset = paletteOffset + i * 4;
const b = dataView.getUint8(offset);
const g = dataView.getUint8(offset + 1);
const r = dataView.getUint8(offset + 2);
palette.push({ r, g, b });
}
return palette;
}

function displayBMPImage(imgElement, bmpData) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = bmpData.width;
canvas.height = bmpData.height;

const imgData = ctx.createImageData(bmpData.width, bmpData.height);

for (let i = 0; i < bmpData.pixels.length; i++) {
const pixel = bmpData.pixels[i];
const dataIndex = i * 4;
imgData.data[dataIndex] = pixel.r;
imgData.data[dataIndex + 1] = pixel.g;
imgData.data[dataIndex + 2] = pixel.b;
imgData.data[dataIndex + 3] = pixel.a;
}

ctx.putImageData(imgData, 0, 0);
imgElement.src = canvas.toDataURL();
}

function removePinkBackgroundFromImageData(imageData) {
const data = imageData.data;

for (let i = 0; i < data.length; i += 4) {
// Удаляем только чистый розовый (255,0,255)
if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) {
data[i + 3] = 0; // Устанавливаем прозрачность
}
}

return imageData;
}

function removeStrictPinkBackground(bmpData) {
const processedPixels = [];

for (let i = 0; i < bmpData.pixels.length; i++) {
const pixel = bmpData.pixels[i];

// УДАЛЯЕМ ТОЛЬКО ЧИСТЫЙ РОЗОВЫЙ (255,0,255)
if (pixel.r === 255 && pixel.g === 0 && pixel.b === 255) {
processedPixels.push({ r: 0, g: 0, b: 0, a: 0 });
} else {
processedPixels.push({ ...pixel });
}
}

return {
width: bmpData.width,
height: bmpData.height,
pixels: processedPixels
};
}

function displayProcessedImage(imageData) {
const ctx = processedCanvas.getContext('2d');
const scale = 10;

processedCanvas.width = imageData.width * scale;
processedCanvas.height = imageData.height * scale;

ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height);

// Создаем временный canvas для масштабирования
const tempCanvas = document.createElement('canvas');
tempCanvas.width = imageData.width;
tempCanvas.height = imageData.height;
const tempCtx = tempCanvas.getContext('2d');

if (imageData.data) {
// Это ImageData из стандартного изображения
tempCtx.putImageData(imageData, 0, 0);
} else {
// Это данные BMP
const imgData = tempCtx.createImageData(imageData.width, imageData.height);
for (let i = 0; i < imageData.pixels.length; i++) {
const pixel = imageData.pixels[i];
const dataIndex = i * 4;
imgData.data[dataIndex] = pixel.r;
imgData.data[dataIndex + 1] = pixel.g;
imgData.data[dataIndex + 2] = pixel.b;
imgData.data[dataIndex + 3] = pixel.a;
}
tempCtx.putImageData(imgData, 0, 0);
}

ctx.imageSmoothingEnabled = false;
ctx.drawImage(tempCanvas, 0, 0, imageData.width, imageData.height, 0, 0, processedCanvas.width, processedCanvas.height);
}

function getExportFilename(originalFilename) {
// Убираем расширение файла
const nameWithoutExt = originalFilename.replace(/\.[^/.]+$/, "");
// Добавляем суффикс и расширение PNG
return `${nameWithoutExt}_no_bg.png`;
}

function downloadProcessedImage() {
if (!processedImageData || !currentFile) return;

try {
const exportFilename = getExportFilename(currentFile.name);

// Создаем временный canvas для экспорта
const exportCanvas = document.createElement('canvas');
const exportCtx = exportCanvas.getContext('2d');

if (processedImageData.data) {
// Это ImageData из стандартного изображения
exportCanvas.width = processedImageData.width;
exportCanvas.height = processedImageData.height;
exportCtx.putImageData(processedImageData, 0, 0);
} else {
// Это данные BMP
exportCanvas.width = processedImageData.width;
exportCanvas.height = processedImageData.height;
const imgData = exportCtx.createImageData(processedImageData.width, processedImageData.height);
for (let i = 0; i < processedImageData.pixels.length; i++) {
const pixel = processedImageData.pixels[i];
const dataIndex = i * 4;
imgData.data[dataIndex] = pixel.r;
imgData.data[dataIndex + 1] = pixel.g;
imgData.data[dataIndex + 2] = pixel.b;
imgData.data[dataIndex + 3] = pixel.a;
}
exportCtx.putImageData(imgData, 0, 0);
}

// Создаем ссылку для скачивания
const link = document.createElement('a');
link.download = exportFilename;
link.href = exportCanvas.toDataURL('image/png');
link.click();

// Показываем сообщение об успешном скачивании
showWarning(`Изображение сохранено как: ${exportFilename}`);

} catch (error) {
showError('Ошибка при сохранении файла: ' + error.message);
}
}

function formatFileSize(bytes) {
if (bytes < 1024) return bytes + ' байт';
else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' КБ';
else return (bytes / 1048576).toFixed(1) + ' МБ';
}

function showError(message) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
previewContainer.style.display = 'none';
fileInfo.style.display = 'none';
controls.style.display = 'none';
}

function showWarning(message) {
warningMessage.textContent = message;
warningMessage.style.display = 'block';
}
</script>
</body>
</html>
Проор
Прикрепления:
shhgnmioshh.noext (64.0 Kb)
  • Страница 25 из 25
  • «
  • 1
  • 2
  • 23
  • 24
  • 25
Поиск:
Новый ответ
Имя:
Текст сообщения: