// =========================================================
// ГЕНЕРАЦИЯ ПОРЯДКА ЗАГРУЗКИ
// =========================================================
let modsData = [];
let currentVersion = '1.60';
let currentSubVersion = '1.60-1.0.0.0';
function gotoaltlink() {
window.open('combo/altlink.html', '_blank');
}
// ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ==========
function cleanModFileName(filename) {
if (!filename) return 'default';
let cleanName = filename.replace(/\.(scs|zip)$/i, '');
cleanName = cleanName.replace(/\.(scs|zip)\./i, '.');
return cleanName;
}
function escapeHtml(text) {
if (!text && text !== 0) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function escapeAttr(text) {
if (!text && text !== 0) return '';
return String(text).replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(//g, '>');
}
function getIconPath(modFile) {
if (!modFile) return 'combo/icons/default.webp';
const cleanName = cleanModFileName(modFile);
const currentVersion = localStorage.getItem('comboVersion') || '1.60';
if (currentVersion === '1.59') {
return `combo/prevVer/1.59/icons/${cleanName}.webp`;
}
return `combo/icons/${cleanName}.webp`;
}
function getIconHTML(modFile, className = 'mod-icon') {
const iconPath = getIconPath(modFile);
return `
`;
}
function getGroupItemHTML(modFile, modName, showFilename = true) {
const iconHTML = getIconHTML(modFile, 'group-item-icon');
const escapedFileName = escapeHtml(modFile);
const escapedModName = escapeHtml(modName);
return `
${iconHTML}
${escapedModName}
${showFilename ? `
${escapedFileName}
` : ''}
`;
}
function getTooltipText(tag) {
const currentLang = localStorage.getItem('lang') || 'ru';
const translations = {
ru: {
'📝': 'У мода есть примечание',
'🆕': 'Недавно добавленный мод',
'🔄': 'Недавно обновлённый мод',
'⚠️': 'Мод имеет критические ошибки',
'🔧': 'Опциональный мод',
'💰': 'Мод платный',
'📎': 'Мод имеет альтернативные ссылки',
'📍': 'Недавно изменено положение'
},
en: {
'📝': 'The mod has a note',
'🆕': 'Recently added mod',
'🔄': 'Recently updated mod',
'⚠️': 'The mod has critical errors',
'🔧': 'Optional mod',
'💰': 'Paid mod',
'📎': 'The mod has alternative links',
'📍': 'Recently changed position'
}
};
return translations[currentLang]?.[tag] || '';
}
// ========== ФУНКЦИИ ДЛЯ ТЭГОВ (вызывают функции из animations.js) ==========
function addTagToMod(modName, tag) {
if (!modName || !tag) return false;
const priorityTags = ['🆕', '🔄', '📍'];
let found = false;
const cleanName = modName.trim();
function addTagWithPriority(item) {
if (!item.tags) item.tags = [];
if (item.tags.includes(tag)) return false;
item.tags.push(tag);
item.tags.sort((a, b) => {
const indexA = priorityTags.indexOf(a);
const indexB = priorityTags.indexOf(b);
if (indexA !== -1 && indexB !== -1) return indexA - indexB;
if (indexA !== -1) return -1;
if (indexB !== -1) return 1;
return 0;
});
return true;
}
for (let i = 0; i < modsData.length; i++) {
const item = modsData[i];
if (item.group) {
if (item.group === cleanName || item.name === cleanName) {
found = addTagWithPriority(item);
break;
}
if (item.items) {
for (let j = 0; j < item.items.length; j++) {
if (item.items[j].name === cleanName || item.items[j].file === cleanName) {
found = addTagWithPriority(item.items[j]);
break;
}
}
}
} else {
if (item.name === cleanName || item.file === cleanName) {
found = addTagWithPriority(item);
break;
}
}
if (found) break;
}
if (!found) {
for (let i = 0; i < modsData.length; i++) {
const item = modsData[i];
if (item.group) {
if (item.group.toLowerCase().includes(cleanName.toLowerCase()) ||
(item.name && item.name.toLowerCase().includes(cleanName.toLowerCase()))) {
found = addTagWithPriority(item);
break;
}
if (item.items) {
for (let j = 0; j < item.items.length; j++) {
if (item.items[j].name.toLowerCase().includes(cleanName.toLowerCase()) ||
item.items[j].file.toLowerCase().includes(cleanName.toLowerCase())) {
found = addTagWithPriority(item.items[j]);
break;
}
}
}
} else {
if (item.name && item.name.toLowerCase().includes(cleanName.toLowerCase())) {
found = addTagWithPriority(item);
break;
}
}
if (found) break;
}
}
return found;
}
function removeTagFromMod(modName, tag) {
if (!modName || !tag) return false;
let found = false;
const cleanName = modName.trim();
function removeTag(item) {
if (!item.tags) return false;
const index = item.tags.indexOf(tag);
if (index !== -1) {
item.tags.splice(index, 1);
return true;
}
return false;
}
for (let i = 0; i < modsData.length; i++) {
const item = modsData[i];
if (item.group) {
if (item.group === cleanName || item.name === cleanName) {
found = removeTag(item);
break;
}
if (item.items) {
for (let j = 0; j < item.items.length; j++) {
if (item.items[j].name === cleanName || item.items[j].file === cleanName) {
found = removeTag(item.items[j]);
break;
}
}
}
} else {
if (item.name === cleanName || item.file === cleanName) {
found = removeTag(item);
break;
}
}
if (found) break;
}
if (!found) {
for (let i = 0; i < modsData.length; i++) {
const item = modsData[i];
if (item.group) {
if (item.group.toLowerCase().includes(cleanName.toLowerCase()) ||
(item.name && item.name.toLowerCase().includes(cleanName.toLowerCase()))) {
found = removeTag(item);
break;
}
if (item.items) {
for (let j = 0; j < item.items.length; j++) {
if (item.items[j].name.toLowerCase().includes(cleanName.toLowerCase()) ||
item.items[j].file.toLowerCase().includes(cleanName.toLowerCase())) {
found = removeTag(item.items[j]);
break;
}
}
}
} else {
if (item.name && item.name.toLowerCase().includes(cleanName.toLowerCase())) {
found = removeTag(item);
break;
}
}
if (found) break;
}
}
return found;
}
function removeAllPriorityTags() {
const priorityTags = ['🆕', '🔄', '📍'];
function removePriorityTagsFromItem(item) {
if (!item.tags) return;
priorityTags.forEach(tag => {
const index = item.tags.indexOf(tag);
if (index !== -1) {
item.tags.splice(index, 1);
}
});
}
modsData.forEach(item => {
if (item.group) {
removePriorityTagsFromItem(item);
if (item.items) {
item.items.forEach(subItem => {
removePriorityTagsFromItem(subItem);
});
}
} else {
removePriorityTagsFromItem(item);
}
});
}
// ========== РЕНДЕРИНГ ==========
function renderLoadOrder() {
const container = document.getElementById('loadOrderList');
if (!container) {
console.error('Контейнер loadOrderList не найден!');
return;
}
container.innerHTML = '';
if (!modsData || modsData.length === 0) {
container.innerHTML = '📦 Нет данных для отображения
';
return;
}
const currentLang = localStorage.getItem('lang') || 'ru';
const t = translations[currentLang];
const currentVersion = localStorage.getItem('comboVersion') || '1.60';
const iconsPath = currentVersion === '1.59' ? 'combo/prevVer/1.59/icons/' : 'combo/icons/';
modsData.forEach((item) => {
const isGroup = !!item.group;
const getLocalizedText = (item, fieldName) => {
if (currentLang === 'ru' && item[`${fieldName}_ru`]) {
return item[`${fieldName}_ru`];
}
return item[fieldName] || '';
};
const dateLabel = typeof getModDateLabel === 'function' ? getModDateLabel(item.name || item.group) : '';
if (isGroup) {
let groupTagsHtml = '';
let groupNoteIcon = '';
let groupAltIcon = '';
let hasPaidTag = false;
let tagMarkers = '';
(item.tags || []).forEach(tag => {
if (tag === '📝') {
const noteText = (getLocalizedText(item, 'note') || (currentLang === 'ru' ? 'Нет примечания' : 'No note')).replace(/'/g, "\\'");
groupNoteIcon = `📝`;
} else if (tag === '📎') {
groupAltIcon = `📎`;
} else if (tag === '💰') {
hasPaidTag = true;
tagMarkers += '-paid';
groupTagsHtml += `${tag}`;
} else {
let tagClass = '';
if (tag === '🆕') { tagClass = 'tag-new'; tagMarkers += '-new'; }
else if (tag === '🔄') { tagClass = 'tag-updated'; tagMarkers += '-updated'; }
else if (tag === '⚠️') { tagClass = 'tag-critical'; tagMarkers += '-critical'; }
else if (tag === '🔧') { tagClass = 'tag-optional'; tagMarkers += '-optional'; }
else if (tag === '📍') { tagClass = 'tag-moved'; tagMarkers += '-moved'; }
groupTagsHtml += `${tag}`;
}
});
let modlink = '';
if (hasPaidTag) {
modlink = `${t.buymod}`;
} else {
modlink = `${t.download}`;
}
let leftColumnHtml = `
${item.group}
`;
(item.items || []).forEach(subItem => {
let subItemName = subItem.name;
if (currentLang === 'ru' && subItem.name_ru) {
subItemName = subItem.name_ru;
}
const cleanFile = cleanModFileName(subItem.file);
leftColumnHtml += `
${subItemName}
${subItem.file}
`;
});
leftColumnHtml += `
`;
const groupDescription = getLocalizedText(item, 'description');
let groupAuthorHtml = '';
if (item.author) {
groupAuthorHtml = `${t.author || 'Author:'} ${item.author}
`;
}
const rightColumnHtml = `
${groupDescription || ''}
${t.version} ${item.version}
${dateLabel ? `${dateLabel}` : ''}
${groupNoteIcon}
${groupAltIcon}
${groupTagsHtml}
${modlink}
${groupAuthorHtml}
`;
let groupDiv = document.createElement('div');
if (tagMarkers) {
const wrapper = document.createElement('div');
wrapper.className = `load-order-row-tags tag-marker${tagMarkers}`;
groupDiv.className = 'load-order-group';
wrapper.appendChild(groupDiv);
groupDiv.innerHTML = leftColumnHtml + rightColumnHtml;
container.appendChild(wrapper);
} else {
groupDiv.className = 'load-order-group';
groupDiv.innerHTML = leftColumnHtml + rightColumnHtml;
container.appendChild(groupDiv);
}
} else {
const cleanFile = cleanModFileName(item.file);
const imageHtml = ``;
let tagsHtml = '';
let noteIcon = '';
let altIcon = '';
let modlink = '';
let hasPaidTag = false;
let tagMarkers = '';
const modNote = getLocalizedText(item, 'note');
const modDesc = getLocalizedText(item, 'desc');
(item.tags || []).forEach(tag => {
if (tag === '📝') {
const noteText = (modNote || (currentLang === 'ru' ? 'Нет примечания' : 'No note')).replace(/'/g, "\\'");
noteIcon = `📝`;
} else if (tag === '📎') {
altIcon = `📎`;
} else if (tag === '💰') {
hasPaidTag = true;
tagMarkers += '-paid';
tagsHtml += `${tag}`;
} else {
let tagClass = '';
if (tag === '🆕') { tagClass = 'tag-new'; tagMarkers += '-new'; }
else if (tag === '🔄') { tagClass = 'tag-updated'; tagMarkers += '-updated'; }
else if (tag === '⚠️') { tagClass = 'tag-critical'; tagMarkers += '-critical'; }
else if (tag === '🔧') { tagClass = 'tag-optional'; tagMarkers += '-optional'; }
else if (tag === '📍') { tagClass = 'tag-moved'; tagMarkers += '-moved'; }
tagsHtml += `${tag}`;
}
});
if (hasPaidTag) {
modlink = `${t.buymod}`;
} else {
modlink = `${t.download}`;
}
let authorHtml = '';
let fileHtml = '';
if (item.author) {
authorHtml = `${t.author || 'Author:'} ${item.author}
`;
}
if (item.file) {
fileHtml = `${t.file || 'File:'} ${item.file}
`;
}
const rowHtml = `
${imageHtml}
${item.name}
${modDesc || ''}
${t.version} ${item.version}
${dateLabel ? `${dateLabel}` : ''}
${noteIcon}
${altIcon}
${authorHtml}
${fileHtml}
${tagsHtml}
${modlink}
`;
const row = document.createElement('div');
row.className = 'load-order-row';
row.innerHTML = rowHtml;
if (tagMarkers) {
const wrapper = document.createElement('div');
wrapper.className = `load-order-row-tags tag-marker${tagMarkers}`;
wrapper.appendChild(row);
container.appendChild(wrapper);
} else {
container.appendChild(row);
}
}
});
// Вызываем функцию из animations.js
if (typeof generateTagMarkerStyles === 'function') {
generateTagMarkerStyles();
}
}
// ========== ВЕРСИИ ДЛЯ ПРОФИЛЕЙ ==========
const versionData = {
v1_60: {
name: 'Версия 1.60',
buttons: [
{ text: '1.60-1.0.0.2', url: 'https://modsfire.com/V9D7vM08N3xd0VN' },
{ text: '1.60-1.0.0.1', url: 'https://modsfire.com/f10K35A82F39OSk' },
{ text: '1.60-1.0.0.0', url: 'https://modsfire.com/mp23Zy29E8QJPn6' }
]
},
v1_59: {
name: 'Версия 1.59',
buttons: [
{ text: '1.59-1.0.0.2', url: 'https://modsfire.com/IKo7Ts92YWrtn31' },
{ text: '1.59-1.0.0.1', url: 'https://modsfire.com/6DZY9n7jH9olnO2' },
{ text: '1.59-1.0.0.0', url: 'https://modsfire.com/75g5XgOIEgEA7Oi' }
]
},
v1_58: {
name: 'Версия 1.58',
buttons: [
{ text: '1.58-1.3.1.0', url: 'https://modsfire.com/Hd8HA4Fm6T73Kt9' },
{ text: '1.58-1.3.0.0', url: 'https://modsfire.com/792ki398g8UXE8F' },
{ text: '1.58-1.2.0.0', url: 'https://modsfire.com/X29Uvg2qS0lTyEl' },
{ text: '1.58-1.1.0.0', url: 'https://modsfire.com/D1wsZ4GL3O8P7QV' },
{ text: '1.58-1.0.0.0', url: 'https://modsfire.com/x8X2Qr036RMu7X6' }
]
},
v1_57: {
name: 'Версия 1.57',
buttons: [
{ text: '1.57-2.1.0.0', url: 'https://modsfire.com/hOR4VgW97Mm352n' },
{ text: '1.57-2.0.0.1', url: 'https://modsfire.com/fO6tCNjDzzl704l' },
{ text: '1.57-2.0.0.0', url: 'https://modsfire.com/9tE225t74B27z3o' },
{ text: '1.57-1.0.2.0', url: 'https://modsfire.com/Dlx75WA2C5k49Mt' },
{ text: '1.57-1.0.1.0', url: 'https://modsfire.com/knJy5qg0V92oTHf' },
{ text: '1.57-1.0.0.1', url: 'https://modsfire.com/5FKioFXjKNBLqxe' },
{ text: '1.57-1.0.0.0', url: 'https://modsfire.com/ii8CrD01nDB92RN' }
]
},
v1_56: {
name: 'Версия 1.56',
buttons: [
{ text: '1.56-1.2.2.1', url: 'https://modsfire.com/m19I48w6S71oClU' },
{ text: '1.56-1.2.2.0', url: 'https://modsfire.com/kr41PZv8b50gC2M' },
{ text: '1.56-1.2.1.0', url: 'https://modsfire.com/Kq25WL3BazP9470' },
{ text: '1.56-1.2.0.1', url: 'https://modsfire.com/9v2jrF3j7Anzk3n' },
{ text: '1.56-1.2.0.0', url: 'https://modsfire.com/43H05ik08b01m3y' },
{ text: '1.56-1.1.2.0', url: 'https://modsfire.com/5oNH3U5G5a620qH' },
{ text: '1.56-1.1.1.0', url: 'https://modsfire.com/3nVG66RWb9fkIo4' },
{ text: '1.56-1.1.0.1', url: 'https://modsfire.com/tBm5su7uG5M2N4t' },
{ text: '1.56-1.1.0.0', url: 'https://modsfire.com/6x4ev53MJn3Zn9x' },
{ text: '1.56-1.0.2.0', url: 'https://modsfire.com/2697EwvMhI6YMeh' },
{ text: '1.56-1.0.1.0', url: 'https://modsfire.com/rUi97hNOpKybw9d' },
{ text: '1.56-1.0.0.0', url: 'https://modsfire.com/Ge4X61le4nbXfB8' }
]
}
};
function renderVersionButtons() {
const container = document.getElementById('versionButtons');
if (!container) return;
container.innerHTML = '';
const currentLang = localStorage.getItem('lang') || 'ru';
const t = translations[currentLang];
for (const [key, data] of Object.entries(versionData)) {
const versionBlock = document.createElement('div');
versionBlock.className = 'version-block';
let versionName = data.name;
const versionKey = key.replace('v', 'version_');
if (t[versionKey]) {
versionName = t[versionKey];
}
versionBlock.innerHTML = `
${data.buttons.map(btn => `
`).join('')}
`;
container.appendChild(versionBlock);
}
}
function renderVersionList() {
const container = document.getElementById('versionList');
if (!container) return;
const versions = [
{ id: 'v1_60', nameKey: 'version_1_60' },
{ id: 'v1_59', nameKey: 'version_1_59' },
{ id: 'v1_58', nameKey: 'version_1_58' },
{ id: 'v1_57', nameKey: 'version_1_57' },
{ id: 'v1_56', nameKey: 'version_1_56' }
];
container.innerHTML = '';
const currentLang = localStorage.getItem('lang') || 'ru';
const t = translations[currentLang];
versions.forEach(version => {
const versionBlock = document.createElement('div');
versionBlock.className = 'version-block';
versionBlock.innerHTML = `
`;
container.appendChild(versionBlock);
});
}
function copyLink(inputId) {
const input = document.getElementById(inputId);
if (input && input.value) {
navigator.clipboard.writeText(input.value);
const btn = input.nextElementSibling;
const originalText = btn.textContent;
const currentLang = localStorage.getItem('lang') || 'ru';
btn.textContent = translations[currentLang].copiedMsg;
setTimeout(() => {
btn.textContent = originalText;
}, 2000);
}
}
function addLinkToVersion(versionId) {
const container = document.getElementById(versionId);
const linkDiv = document.createElement('div');
linkDiv.className = 'version-link-item';
linkDiv.innerHTML = `
`;
container.appendChild(linkDiv);
}