NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name (VIP) PEFL Features: Ненужные // @namespace http://tampermonkey.net/ // @version 1.11 // @description Добавляет расширенный функционал для страницы ненужных // @author Вы // @match *://*/nenuj.php?t=scout* // @grant GM_xmlhttpRequest // @license MIT // @downloadURL https://update.greasyfork.org/scripts/533465/%28VIP%29%20PEFL%20Features%3A%20%D0%9D%D0%B5%D0%BD%D1%83%D0%B6%D0%BD%D1%8B%D0%B5.user.js // @updateURL https://update.greasyfork.org/scripts/533465/%28VIP%29%20PEFL%20Features%3A%20%D0%9D%D0%B5%D0%BD%D1%83%D0%B6%D0%BD%D1%8B%D0%B5.meta.js // ==/UserScript== (function() { 'use strict'; // Функция для логирования function log(message, data) { console.log(`[ХодТоргов] ${message}`, data || ''); } log('Скрипт запущен'); // Создаем контейнер для времени с флекс-разметкой const timeContainer = document.createElement('div'); timeContainer.style.display = 'flex'; timeContainer.style.justifyContent = 'space-between'; timeContainer.style.margin = '10px 0'; timeContainer.style.padding = '5px'; timeContainer.style.backgroundColor = '#f0f0f0'; timeContainer.style.border = '1px solid #ccc'; timeContainer.style.borderRadius = '3px'; // Создаем элемент для отображения текущего времени const currentTimeDisplay = document.createElement('div'); currentTimeDisplay.style.fontSize = '14px'; currentTimeDisplay.style.fontWeight = 'bold'; // Создаем элемент для заголовка const titleDisplay = document.createElement('div'); titleDisplay.textContent = 'PF-Ненужные'; titleDisplay.style.fontSize = '16px'; titleDisplay.style.fontWeight = 'bold'; titleDisplay.style.color = '#666'; // Создаем элемент для отображения времени обновления const updatedTimeDisplay = document.createElement('div'); updatedTimeDisplay.style.fontSize = '14px'; updatedTimeDisplay.style.fontWeight = 'bold'; // Функция форматирования времени function formatTime(date) { // Получаем текущее время в UTC const now = new Date(date); // Форматируем время в московском часовом поясе (UTC+3) const options = { hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'Europe/Moscow' }; // Возвращаем отформатированное московское время return new Intl.DateTimeFormat('ru-RU', options).format(now) + ' (МСК)'; } // Функция обновления текущего времени function updateCurrentTime() { const now = new Date(); currentTimeDisplay.innerHTML = '<span style="color:#666;">Текущее время:</span> ' + formatTime(now); } // Устанавливаем время обновления один раз const loadTime = new Date(); updatedTimeDisplay.innerHTML = '<span style="color:#666;">Обновлено:</span> ' + formatTime(loadTime); // Обновляем текущее время сразу и затем каждую секунду updateCurrentTime(); setInterval(updateCurrentTime, 1000); // Добавляем элементы в контейнер timeContainer.appendChild(currentTimeDisplay); timeContainer.appendChild(titleDisplay); timeContainer.appendChild(updatedTimeDisplay); // Находим таблицу на странице const tables = document.querySelectorAll('table[width="100%"][border="0"]'); log('Найдено таблиц:', tables.length); if (tables.length > 0) { // Вставляем элемент с временем перед таблицей tables[0].parentNode.insertBefore(timeContainer, tables[0]); } // Задержка для запросов function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Счетчик запросов (для отслеживания и добавления задержки) let requestCounter = 0; // Хранилище данных профилей игроков let playerProfiles = JSON.parse(localStorage.getItem('playerProfiles') || '{}'); log('Загружено профилей из localStorage:', Object.keys(playerProfiles).length); // Функция для получения дедлайна по позиции function getDeadlineByPosition(position) { // Приводим к нижнему регистру для упрощения проверки position = position.toLowerCase(); // Проверяем наличие разных позиций по приоритету if (position.includes('fw')) { return '14:00'; } else if (position.includes('dm') || position.includes('mf') || position.includes('am')) { return '14:30'; } else if (position.includes('sw') || position.includes('df')) { return '15:00'; } else if (position.includes('gk')) { return '15:30'; } else { return 'Н/Д'; // Если позиция не определена } } // Функция для обработки строки контракта function processContractString(contractString) { // Если контракт содержит пробел, берем только вторую часть (сумму) const parts = contractString.split(' '); if (parts.length > 1) { return parts.slice(1).join(' '); } return contractString; } // Функция для получения данных профиля игрока async function getPlayerProfile(playerName, profileUrl) { // Проверяем, есть ли данные в кэше if (playerProfiles[playerName]) { log(`Данные профиля для ${playerName} загружены из кэша`); return playerProfiles[playerName]; } log(`Получение данных профиля для ${playerName} из ${profileUrl}`); // Добавляем задержку перед каждым запросом await delay(500 * requestCounter); requestCounter++; // Формируем полный URL, если это относительный путь let fullUrl = profileUrl; if (profileUrl.startsWith('/')) { fullUrl = window.location.origin + profileUrl; } else if (!profileUrl.startsWith('http')) { fullUrl = window.location.origin + '/' + profileUrl; } return new Promise((resolve, reject) => { try { GM_xmlhttpRequest({ method: 'GET', url: fullUrl, onload: function(response) { if (response.status === 200) { // Создаем временный элемент для парсинга HTML const tempDiv = document.createElement('div'); tempDiv.innerHTML = response.responseText; // Извлекаем возраст (только число) let age = extractTextAfterLabel(tempDiv, 'Возраст:'); // Оставляем только число, убираем буквы if (age) { const ageMatch = age.match(/\d+/); age = ageMatch ? ageMatch[0] : 'Н/Д'; } // Получаем позицию const position = extractTextAfterLabel(tempDiv, 'Позиция:'); // Получаем контракт и обрабатываем его const contractRaw = extractTextAfterLabel(tempDiv, 'Контракт:'); const contract = processContractString(contractRaw); // Извлекаем данные профиля const playerData = { // Возраст (только число) age: age, // Позиция position: position, // Дедлайн deadline: getDeadlineByPosition(position || ''), // Номинал value: extractValueFromProfile(tempDiv), // Контракт (только сумма) contract: contract, // ID флага flagId: extractFlagId(tempDiv) }; log(`Данные профиля для ${playerName}:`, playerData); // Сохраняем данные в кэш playerProfiles[playerName] = playerData; localStorage.setItem('playerProfiles', JSON.stringify(playerProfiles)); resolve(playerData); } else { log(`Ошибка запроса профиля:`, response.status); resolve(null); } }, onerror: function(error) { log(`Ошибка запроса профиля:`, error); resolve(null); } }); } catch (e) { log('Исключение при выполнении запроса профиля:', e); resolve(null); } }); } // Функция для извлечения текста после метки function extractTextAfterLabel(element, label) { // Ищем все текстовые узлы const textNodes = []; const walkNodes = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false); let node; while (node = walkNodes.nextNode()) { textNodes.push(node); } // Ищем текстовый узел с нашей меткой for (let i = 0; i < textNodes.length; i++) { if (textNodes[i].textContent.trim() === label) { // Если нашли узел с меткой, берем следующий узел if (i + 1 < textNodes.length) { return textNodes[i + 1].textContent.trim(); } } } // Альтернативный метод для поиска: через ячейки таблицы const cells = element.querySelectorAll('td'); for (let i = 0; i < cells.length; i++) { if (cells[i].textContent.trim() === label && i + 1 < cells.length) { return cells[i + 1].textContent.trim(); } } return 'Н/Д'; // Не найдено } // Функция для извлечения номинала function extractValueFromProfile(element) { // Ищем ячейку с ID value0 const valueCell = element.querySelector('td#value0'); if (valueCell && valueCell.nextElementSibling) { return valueCell.nextElementSibling.textContent.trim(); } // Альтернативный поиск по тексту "Номинал:" const cells = element.querySelectorAll('td'); for (let i = 0; i < cells.length; i++) { if (cells[i].textContent.trim() === 'Номинал:' && i + 1 < cells.length) { return cells[i + 1].textContent.trim(); } } return 'Н/Д'; // Не найдено } // Функция для извлечения ID флага function extractFlagId(element) { // Ищем ячейку с ID nation_img const nationCell = element.querySelector('td#nation_img'); if (nationCell) { const flagImg = nationCell.querySelector('img'); if (flagImg && flagImg.src) { // Извлекаем ID из URL флага const match = flagImg.src.match(/flags\/mod\/(\d+)\.gif/); if (match && match[1]) { return match[1]; } } } return null; // Не найдено } tables.forEach(table => { log('Обработка таблицы'); // Добавляем бордер к таблице table.style.border = '1px solid #000'; table.style.borderCollapse = 'collapse'; // Добавляем бордер к ячейкам таблицы const cells = table.querySelectorAll('td'); cells.forEach(cell => { cell.style.border = '1px solid #ccc'; cell.style.padding = '3px'; }); // Находим ссылки с именами игроков и меняем их поведение const playerLinks = table.querySelectorAll('td:first-child a'); playerLinks.forEach(link => { // Добавляем target="_blank" для открытия в новом окне link.setAttribute('target', '_blank'); // Дополнительно, можно добавить подсветку при наведении link.style.textDecoration = 'none'; link.addEventListener('mouseover', function() { this.style.backgroundColor = '#f0f0f0'; }); link.addEventListener('mouseout', function() { this.style.backgroundColor = ''; }); }); // Функция для преобразования текстовой ставки в числовое значение function parseBidValue(bidText) { bidText = bidText.trim(); // Удаляем все теги script, если они есть bidText = bidText.replace(/<script[\s\S]*?<\/script>/gi, ''); // Удаляем все HTML-теги, если они есть bidText = bidText.replace(/<[^>]*>/g, ''); let value = 0; if (bidText.includes('тыс.')) { // Если ставка в тысячах, умножаем на 1000 value = parseFloat(bidText) * 1000; } else if (bidText.includes('М')) { // Если ставка в миллионах, умножаем на 1000000 value = parseFloat(bidText) * 1000000; } else { // Попытка просто извлечь число const match = bidText.match(/\d+(\.\d+)?/); if (match) { value = parseFloat(match[0]); } } return value; } // Функция для подсчета количества "Отправлено" на странице и проверки наличия определенных фраз async function analyzeBidPage(url, index, callback) { // Добавляем задержку перед каждым запросом await delay(500 * requestCounter); requestCounter++; // Формируем полный URL, если это относительный путь let fullUrl = url; if (url.startsWith('nenuj.php')) { fullUrl = window.location.origin + '/' + url; } log(`Выполняем запрос #${requestCounter} к URL:`, fullUrl); try { GM_xmlhttpRequest({ method: 'GET', url: fullUrl, onload: function(response) { log(`Ответ для запроса #${requestCounter}:`, { status: response.status, responseLength: response.responseText ? response.responseText.length : 0 }); if (response.status === 200) { // Создаем временный элемент для парсинга HTML const tempDiv = document.createElement('div'); tempDiv.innerHTML = response.responseText; // Проверяем, что получили в ответе log('HTML-содержимое получено, длина:', tempDiv.innerHTML.length); // Ищем все вхождения "Отправлено" в тексте const text = tempDiv.textContent || tempDiv.innerText; log('Текстовое содержимое, начало:', text.substring(0, 100)); // Ищем просто "Отправлено" без звездочек const regex = /Отправлено/g; const matches = text.match(regex); const count = matches ? matches.length : 0; // Проверяем наличие определенных фраз const hasThinkingPhrase = text.includes('Условия контракта ухудшены покупателем, игрок думает'); const hasContractOfferedPhrase = text.includes('Контракт предложен'); const hasSpecialPhrase = hasThinkingPhrase || hasContractOfferedPhrase; // Анализируем текст над вторым <hr> let textBeforeSecondHr = ''; const hrElements = tempDiv.querySelectorAll('hr'); if (hrElements.length >= 2) { // Получаем родительский элемент второго <hr> const secondHr = hrElements[1]; const parent = secondHr.parentNode; // Ищем все узлы текста до второго <hr> let foundHr = 0; for (let i = 0; i < parent.childNodes.length; i++) { const node = parent.childNodes[i]; if (node === secondHr) { // Это второй <hr>, прекращаем искать break; } if (node.nodeName === 'HR') { // Это первый <hr>, начинаем собирать текст foundHr = 1; continue; } if (foundHr === 1 && node.nodeType === Node.TEXT_NODE) { // Собираем текст между первым и вторым <hr> textBeforeSecondHr += node.textContent; } else if (foundHr === 1 && node.nodeType === Node.ELEMENT_NODE) { // Для элементов берём их текстовое содержимое textBeforeSecondHr += node.textContent; } } textBeforeSecondHr = textBeforeSecondHr.trim(); log(`Текст перед вторым HR: "${textBeforeSecondHr}"`); } else { log('Не найдено двух тегов HR на странице'); } // Определяем цвет для окраски суммы let textColor = '#b09a40'; // Более тёмный желтый if (textBeforeSecondHr.includes('Условия контракта ухудшены покупателем, игрок думает') || textBeforeSecondHr.includes('Контракт предложен')) { textColor = '#445577'; // Более тёмный синий } log(`Найдено "Отправлено" для строки ${index}:`, count); log(`Найдены специальные фразы для строки ${index}:`, hasSpecialPhrase); log(`Цвет текста для строки ${index}:`, textColor); callback(count, hasSpecialPhrase, textColor); } else { log(`Ошибка запроса #${requestCounter}:`, response.status); callback(0, false, 'none'); // В случае ошибки возвращаем 0, false и 'none' } }, onerror: function(error) { log(`Ошибка запроса #${requestCounter}:`, error); callback(0, false, 'none'); // В случае ошибки возвращаем 0, false и 'none' } }); } catch (e) { log('Исключение при выполнении запроса:', e); callback(0, false, 'none'); } } // Сортировка строк таблицы // Сначала получаем заголовок таблицы (первую строку) const headerRow = table.querySelector('tr'); // Стилизуем заголовок таблицы headerRow.style.backgroundColor = '#e0e0e0'; // Светло-серый фон для заголовка headerRow.style.fontWeight = 'bold'; // Добавляем новую ячейку в заголовок для нумерации const numHeaderCell = document.createElement('td'); numHeaderCell.innerHTML = '<b>№</b>'; numHeaderCell.style.border = '1px solid #ccc'; numHeaderCell.style.padding = '3px'; headerRow.insertBefore(numHeaderCell, headerRow.firstChild); // Добавляем новые ячейки в заголовок для данных профиля const deadlineHeaderCell = document.createElement('td'); deadlineHeaderCell.innerHTML = '<b>Дедлайн</b>'; deadlineHeaderCell.style.border = '1px solid #ccc'; deadlineHeaderCell.style.padding = '3px'; headerRow.insertBefore(deadlineHeaderCell, headerRow.children[2]); // После имени игрока const ageHeaderCell = document.createElement('td'); ageHeaderCell.innerHTML = '<b>Возраст</b>'; ageHeaderCell.style.border = '1px solid #ccc'; ageHeaderCell.style.padding = '3px'; headerRow.insertBefore(ageHeaderCell, headerRow.children[3]); // После дедлайна const positionHeaderCell = document.createElement('td'); positionHeaderCell.innerHTML = '<b>Позиция</b>'; positionHeaderCell.style.border = '1px solid #ccc'; positionHeaderCell.style.padding = '3px'; headerRow.insertBefore(positionHeaderCell, headerRow.children[4]); // После возраста const valueHeaderCell = document.createElement('td'); valueHeaderCell.innerHTML = '<b>Номинал</b>'; valueHeaderCell.style.border = '1px solid #ccc'; valueHeaderCell.style.padding = '3px'; headerRow.insertBefore(valueHeaderCell, headerRow.children[5]); // После позиции const contractHeaderCell = document.createElement('td'); contractHeaderCell.innerHTML = '<b>Контракт</b>'; contractHeaderCell.style.border = '1px solid #ccc'; contractHeaderCell.style.padding = '3px'; headerRow.insertBefore(contractHeaderCell, headerRow.children[6]); // После номинала // Добавляем новую ячейку в заголовок для количества "Отправлено" const sentHeaderCell = document.createElement('td'); sentHeaderCell.innerHTML = '<b>Кол-во ставок</b>'; sentHeaderCell.style.border = '1px solid #ccc'; sentHeaderCell.style.padding = '3px'; // Добавляем новую ячейку в заголовок для статуса хорошего контракта const bidContractHeaderCell = document.createElement('td'); bidContractHeaderCell.innerHTML = '<b>Хор. контракт</b>'; bidContractHeaderCell.style.border = '1px solid #ccc'; bidContractHeaderCell.style.padding = '3px'; // Создаем пустую ячейку для заголовка колонки с "Ход торгов" const bidLinkHeaderCell = document.createElement('td'); bidLinkHeaderCell.style.border = '1px solid #ccc'; bidLinkHeaderCell.style.padding = '3px'; // Добавляем в правильном порядке на один столбец влево // Сначала находим ячейку, перед которой нужно вставить const beforeCell = headerRow.children[headerRow.children.length - 1]; // Очищаем заголовок и добавляем все ячейки заново в правильном порядке // Сохраняем первую ячейку (номер) и удаляем остальные const firstHeaderCell = headerRow.firstChild; while (headerRow.childNodes.length > 1) { headerRow.removeChild(headerRow.lastChild); } // Добавляем все заголовки по порядку headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Игрок</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Дедлайн</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Возраст</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Позиция</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Номинал</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Контракт</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = ''; // Для "2 ИД" headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Макс. ставка</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Кол-во ставок</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = '<b>Хор. контракт</b>'; headerRow.appendChild(document.createElement('td')).innerHTML = ''; // Для "Ход торгов" // Стилизуем все заголовки headerRow.querySelectorAll('td').forEach(cell => { cell.style.border = '1px solid #ccc'; cell.style.padding = '3px'; }); // Получаем все строки с данными (кроме заголовка) const rows = Array.from(table.querySelectorAll('tr')).slice(1); log('Найдено строк в таблице:', rows.length); // Сортируем строки по критериям rows.sort((a, b) => { const hasIdA = a.querySelector('td:nth-child(2)').textContent.trim() === '2 ИД'; const hasIdB = b.querySelector('td:nth-child(2)').textContent.trim() === '2 ИД'; // Если один имеет 2 ИД, а другой нет if (hasIdA && !hasIdB) return -1; if (!hasIdA && hasIdB) return 1; // Если оба имеют или оба не имеют 2 ИД, сортируем по максимальной ставке const bidA = parseBidValue(a.querySelector('td:nth-child(3)').textContent); const bidB = parseBidValue(b.querySelector('td:nth-child(3)').textContent); if (bidA > bidB) return -1; if (bidA < bidB) return 1; // Если ставки равны, сортируем по имени const nameA = a.querySelector('td:first-child a').textContent.trim(); const nameB = b.querySelector('td:first-child a').textContent.trim(); return nameA.localeCompare(nameB, 'ru'); }); // Находим ячейки с "Ход торгов" для перемещения их в конец таблицы rows.forEach(row => { // Находим все ячейки строки const rowCells = row.querySelectorAll('td'); // Ищем ячейку с ссылкой "Ход торгов" let bidLinkCell = null; for (let i = 0; i < rowCells.length; i++) { const links = rowCells[i].querySelectorAll('a'); for (let j = 0; j < links.length; j++) { const linkText = links[j].textContent.trim(); if (linkText === 'Ход торгов' || linkText === 'ход торгов' || linkText === 'Хoд тoргoв' || linkText === 'хoд тoргoв') { bidLinkCell = rowCells[i]; break; } } if (bidLinkCell) break; } // Если нашли ячейку с "Ход торгов", помечаем её атрибутом для последующего перемещения if (bidLinkCell) { bidLinkCell.setAttribute('data-bid-link-cell', 'true'); } }); // Удаляем все строки из таблицы while (table.firstChild) { table.removeChild(table.firstChild); } // Добавляем заголовок обратно table.appendChild(headerRow); // Добавляем отсортированные строки с нумерацией let idCounter = 1; // Счетчик для строк с "2 ИД" let noIdCounter = 1; // Счетчик для строк без "2 ИД" let lastWasWithId = true; // Флаг для определения перехода между группами let isFirstRow = true; // Флаг для первой строки // Обрабатываем каждую строку rows.forEach((row, rowIndex) => { // Удаляем ячейку "Ход торгов" из её текущего положения, чтобы добавить в конец let bidLinkCell = null; const bidLinkCellElement = row.querySelector('td[data-bid-link-cell="true"]'); if (bidLinkCellElement) { bidLinkCell = bidLinkCellElement.cloneNode(true); row.removeChild(bidLinkCellElement); } // Добавляем ячейку с номером const numCell = document.createElement('td'); // Определяем, есть ли у строки "2 ИД" const hasId = row.querySelector('td:nth-child(2)').textContent.trim() === '2 ИД'; // Проверяем переход между группами if (!isFirstRow && hasId !== lastWasWithId) { // Если переход между группами, добавляем стилизованный разделитель const separatorRow = document.createElement('tr'); // Создаем ячейку, которая растягивается на всю ширину таблицы const separatorCell = document.createElement('td'); separatorCell.colSpan = headerRow.cells.length; separatorCell.style.height = '3px'; separatorCell.style.backgroundColor = '#aabbcc'; // Более мягкий сине-серый цвет separatorCell.style.padding = '0'; separatorCell.style.border = 'none'; separatorRow.appendChild(separatorCell); table.appendChild(separatorRow); lastWasWithId = hasId; } if (isFirstRow) { isFirstRow = false; lastWasWithId = hasId; } // Устанавливаем цвет фона в зависимости от группы if (hasId) { row.style.backgroundColor = '#f8f8f8'; // Светло-серый фон для строк с "2 ИД" numCell.textContent = idCounter++; } else { row.style.backgroundColor = '#e8f4ff'; // Светло-голубой фон для строк без "2 ИД" numCell.textContent = noIdCounter++; } numCell.style.border = '1px solid #ccc'; numCell.style.padding = '3px'; numCell.style.textAlign = 'center'; // Делаем номер строки кликабельным numCell.style.cursor = 'pointer'; // Получаем ссылку на игрока и его имя const playerLink = row.querySelector('td:first-child a'); const playerName = playerLink.textContent.trim(); const profileUrl = playerLink.getAttribute('href'); // Проверяем, отмечен ли уже этот игрок в localStorage const markedPlayers = JSON.parse(localStorage.getItem('markedPlayers') || '{}'); const isMarked = markedPlayers[playerName] === true; // Применяем стиль круга, если игрок отмечен if (isMarked) { numCell.style.backgroundColor = '#ffeecc'; } // Добавляем обработчик события клика numCell.addEventListener('click', function(event) { // Получаем текущих отмеченных игроков const currentMarkedPlayers = JSON.parse(localStorage.getItem('markedPlayers') || '{}'); // Переключаем состояние отметки if (currentMarkedPlayers[playerName]) { // Убираем отметку delete currentMarkedPlayers[playerName]; this.style.borderRadius = ''; this.style.border = '1px solid #ccc'; this.style.backgroundColor = ''; } else { // Добавляем отметку currentMarkedPlayers[playerName] = true; this.style.backgroundColor = '#ffeecc'; } // Сохраняем обновленный список отмеченных игроков в localStorage localStorage.setItem('markedPlayers', JSON.stringify(currentMarkedPlayers)); // Предотвращаем распространение события event.stopPropagation(); }); // Вставляем ячейку в начало строки row.insertBefore(numCell, row.firstChild); // Получаем текущую ячейку с именем игрока const nameCell = row.querySelector('td:nth-child(2)'); // Добавляем ячейки для данных профиля const deadlineCell = document.createElement('td'); deadlineCell.style.border = '1px solid #ccc'; deadlineCell.style.padding = '3px'; deadlineCell.style.textAlign = 'center'; deadlineCell.textContent = 'Загрузка...'; const ageCell = document.createElement('td'); ageCell.style.border = '1px solid #ccc'; ageCell.style.padding = '3px'; ageCell.style.textAlign = 'center'; ageCell.textContent = 'Загрузка...'; const positionCell = document.createElement('td'); positionCell.style.border = '1px solid #ccc'; positionCell.style.padding = '3px'; positionCell.style.textAlign = 'center'; positionCell.textContent = 'Загрузка...'; const valueCell = document.createElement('td'); valueCell.style.border = '1px solid #ccc'; valueCell.style.padding = '3px'; valueCell.style.textAlign = 'right'; valueCell.textContent = 'Загрузка...'; const contractPlayerCell = document.createElement('td'); contractPlayerCell.style.border = '1px solid #ccc'; contractPlayerCell.style.padding = '3px'; contractPlayerCell.style.textAlign = 'center'; contractPlayerCell.textContent = 'Загрузка...'; // Добавляем ячейку для количества "Отправлено" const sentCell = document.createElement('td'); sentCell.style.border = '1px solid #ccc'; sentCell.style.padding = '3px'; sentCell.style.textAlign = 'center'; // Добавляем ячейку для статуса хорошего контракта const bidContractCell = document.createElement('td'); bidContractCell.style.border = '1px solid #ccc'; bidContractCell.style.padding = '3px'; bidContractCell.style.textAlign = 'center'; // Вставляем новые ячейки после ячейки с именем row.insertBefore(deadlineCell, nameCell.nextSibling); row.insertBefore(ageCell, deadlineCell.nextSibling); row.insertBefore(positionCell, ageCell.nextSibling); row.insertBefore(valueCell, positionCell.nextSibling); row.insertBefore(contractPlayerCell, valueCell.nextSibling); // Получаем данные профиля getPlayerProfile(playerName, profileUrl).then(profileData => { if (profileData) { // Обновляем ячейку с именем - добавляем флаг if (profileData.flagId) { const flagUrl = `system/img/flags/mod/${profileData.flagId}.gif`; const flagImg = document.createElement('img'); flagImg.src = flagUrl; flagImg.style.height = '16px'; flagImg.style.marginRight = '5px'; flagImg.style.verticalAlign = 'middle'; flagImg.style.border = '0'; // Вставляем флаг перед текстом имени nameCell.insertBefore(flagImg, nameCell.firstChild); } // Заполняем ячейки данными deadlineCell.textContent = profileData.deadline; ageCell.textContent = profileData.age; positionCell.textContent = profileData.position; valueCell.textContent = profileData.value; contractPlayerCell.textContent = profileData.contract; } }); // Проверяем, имеет ли строка "2 ИД" - только для них делаем запросы для хода торгов if (hasId) { sentCell.textContent = 'Загрузка...'; bidContractCell.textContent = '...'; // Находим ячейку с максимальной ставкой (теперь она будет дальше из-за добавленных ячеек) const maxBidCellIndex = 8; // Индекс ячейки с максимальной ставкой после добавления новых ячеек const maxBidCell = row.querySelector(`td:nth-child(${maxBidCellIndex + 1})`); // +1 из-за добавленной ячейки нумерации // Ищем ссылку "Ход торгов" в строке let bidLink = null; // Если у нас есть сохраненная ячейка с "Ход торгов" if (bidLinkCell) { const links = bidLinkCell.querySelectorAll('a'); for (let i = 0; i < links.length; i++) { const linkText = links[i].textContent.trim(); const linkHref = links[i].getAttribute('href'); // Проверяем текст ссылки (разные варианты написания) if (linkText === 'Ход торгов') { bidLink = linkHref; log(`Строка ${rowIndex}: найдена ссылка "Ход торгов":`, bidLink); break; } // Если не нашли по тексту, проверяем URL на наличие параметра t=list if (!bidLink && linkHref && linkHref.includes('t=list')) { bidLink = linkHref; log(`Строка ${rowIndex}: найдена ссылка по URL (t=list):`, bidLink); break; } } } // Если нашли ссылку, получаем количество "Отправлено" и проверяем наличие фраз if (bidLink) { analyzeBidPage(bidLink, rowIndex, function(count, hasSpecialPhrase, textColor) { // Устанавливаем количество "Отправлено" sentCell.textContent = count; // Создаем кружок для отображения статуса контракта const circle = document.createElement('div'); circle.style.width = '12px'; circle.style.height = '12px'; circle.style.borderRadius = '50%'; circle.style.margin = '0 auto'; // Центрируем кружок в ячейке if (hasSpecialPhrase) { // Закрашенный кружок мягкого цвета circle.style.backgroundColor = '#aabbcc'; // Такой же цвет, как у разделителя групп } else { // Полый кружок circle.style.border = '2px solid #aabbcc'; circle.style.width = '8px'; // Компенсируем ширину бордера circle.style.height = '8px'; // Компенсируем ширину бордера } // Добавляем кружок в ячейку контракта bidContractCell.innerHTML = ''; bidContractCell.appendChild(circle); // Добавляем цветовую индикацию в зависимости от количества (обратный порядок) if (count > 10) { sentCell.style.backgroundColor = '#ffd6d6'; // Светло-красный для больших значений } else if (count > 5) { sentCell.style.backgroundColor = '#ffffd6'; // Светло-желтый для средних значений } else if (count > 0) { sentCell.style.backgroundColor = '#d6ffd6'; // Светло-зеленый для малых значений } // Окрашиваем сумму максимальной ставки в соответствующий цвет if (textColor !== 'none' && maxBidCell) { maxBidCell.style.color = textColor; maxBidCell.style.fontWeight = 'bold'; // Добавляем всплывающую подсказку в зависимости от цвета if (textColor === '#b09a40') { // Желтый цвет maxBidCell.title = 'Плохой контракт'; } else if (textColor === '#445577') { // Синий цвет maxBidCell.title = 'Хороший контракт'; } } }); } else { log(`Строка ${rowIndex}: ссылка "Ход торгов" не найдена`); sentCell.textContent = 'Н/Д'; bidContractCell.textContent = '—'; } } else { // Для строк без "2 ИД" просто выводим тире sentCell.textContent = '—'; sentCell.style.color = '#999'; // Серый цвет для символа тире bidContractCell.textContent = '—'; bidContractCell.style.color = '#999'; // Серый цвет для символа тире } // Добавляем ячейки в конец строки row.appendChild(sentCell); row.appendChild(bidContractCell); // Добавляем ячейку "Ход торгов" в конец строки, если она была найдена if (bidLinkCell) { row.appendChild(bidLinkCell); } else { // Создаем пустую ячейку, если у строки нет ссылки "Ход торгов" const emptyCell = document.createElement('td'); emptyCell.style.border = '1px solid #ccc'; emptyCell.style.padding = '3px'; row.appendChild(emptyCell); } // Добавляем строку в таблицу table.appendChild(row); }); // Добавляем кнопку для очистки кэша профилей const clearCacheButton = document.createElement('button'); clearCacheButton.textContent = 'Очистить кэш профилей'; clearCacheButton.style.marginTop = '10px'; clearCacheButton.style.padding = '5px 10px'; clearCacheButton.style.backgroundColor = '#f0f0f0'; clearCacheButton.style.border = '1px solid #ccc'; clearCacheButton.style.borderRadius = '3px'; clearCacheButton.style.cursor = 'pointer'; clearCacheButton.addEventListener('click', function() { if (confirm('Вы уверены, что хотите очистить кэш профилей игроков? Данные будут загружены заново при следующем обновлении страницы.')) { localStorage.removeItem('playerProfiles'); alert('Кэш профилей очищен. Обновите страницу для загрузки данных заново.'); location.reload(); } }); // Добавляем кнопку после таблицы table.parentNode.insertBefore(clearCacheButton, table.nextSibling); }); // Добавляем ссылку на форум справа под hr const forumLinkContainer = document.createElement('div'); forumLinkContainer.style.textAlign = 'right'; forumLinkContainer.style.margin = '10px 0'; const forumLink = document.createElement('a'); forumLink.href = '/forums.php?m=posts&p=16393540&arch=#16393540'; forumLink.textContent = 'Топ скрипта на форуме'; forumLink.target = '_blank'; // Открывать в новом окне forumLink.style.textDecoration = 'none'; forumLink.style.color = '#445577'; forumLink.style.fontWeight = 'bold'; forumLinkContainer.appendChild(forumLink); // Находим hr и вставляем ссылку после него const hrElements = document.querySelectorAll('hr'); if (hrElements.length > 0) { const lastHr = hrElements[hrElements.length - 1]; lastHr.parentNode.insertBefore(forumLinkContainer, lastHr.nextSibling); } })();