// Player Details Modal - Quick-peek popup for player stats (function () { let modalInjected = false; let currentTab = 'overview'; function T(key) { return (window.__t && window.__t(key)) || key; } function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function formatNumber(n) { return (n || 0).toLocaleString(); } function getWinRateColor(wr) { if (wr >= 70) return '#90EE90'; if (wr >= 60) return '#A8E6CF'; if (wr >= 50) return '#FFD700'; if (wr >= 40) return '#FFA500'; return '#FF6B6B'; } function getKDRColor(kdr) { if (kdr >= 3) return '#90EE90'; if (kdr >= 2) return '#A8E6CF'; if (kdr >= 1.5) return '#FFD700'; if (kdr >= 1) return '#FFA500'; return '#FF6B6B'; } function injectModal() { if (modalInjected) return; modalInjected = true; const style = document.createElement('style'); style.textContent = ` .pdm-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); backdrop-filter: blur(4px); z-index: 9999; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.2s ease; } .pdm-overlay.pdm-visible { opacity: 1; } .pdm-modal { background: #1e1e1e; border: 1px solid rgba(144,238,144,0.15); border-radius: 12px; max-width: 700px; width: 90%; max-height: 85vh; overflow-y: auto; box-shadow: 0 20px 60px rgba(0,0,0,0.5); transform: scale(0.95); transition: transform 0.2s ease; } .pdm-overlay.pdm-visible .pdm-modal { transform: scale(1); } .pdm-header { display: flex; align-items: center; justify-content: space-between; padding: 1rem 1.25rem; border-bottom: 1px solid rgba(144,238,144,0.1); position: sticky; top: 0; background: #1e1e1e; z-index: 1; border-radius: 12px 12px 0 0; } .pdm-header-left { display: flex; align-items: center; gap: 0.75rem; min-width: 0; } .pdm-player-name { font-size: 1.1rem; font-weight: 700; color: #F5F5DC; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pdm-profile-link { font-size: 0.75rem; color: #90EE90; text-decoration: none; white-space: nowrap; opacity: 0.8; transition: opacity 0.2s; } .pdm-profile-link:hover { opacity: 1; text-decoration: underline; } .pdm-close { background: none; border: none; color: rgba(255,255,255,0.5); font-size: 1.4rem; cursor: pointer; padding: 0.25rem 0.5rem; border-radius: 6px; transition: color 0.2s, background 0.2s; flex-shrink: 0; } .pdm-close:hover { color: #fff; background: rgba(255,255,255,0.1); } .pdm-tabs { display: flex; justify-content: center; padding: 0.75rem 1.25rem 0; } .pdm-tab-group { position: relative; display: inline-flex; background: rgba(255,255,255,0.05); border-radius: 2rem; padding: 3px; border: 1px solid rgba(144,238,144,0.2); } .pdm-tab-slider { position: absolute; top: 3px; left: 3px; height: calc(100% - 6px); background: #90EE90; border-radius: calc(2rem - 2px); transition: left 0.25s cubic-bezier(0.4,0,0.2,1), width 0.25s cubic-bezier(0.4,0,0.2,1); pointer-events: none; } .pdm-tab { position: relative; z-index: 1; background: transparent; border: none; color: rgba(255,255,255,0.45); padding: 0.4rem 1.3rem; border-radius: calc(2rem - 2px); cursor: pointer; font-size: 0.82rem; font-weight: 600; transition: color 0.25s ease; white-space: nowrap; } .pdm-tab.active { color: #1b1b1b; } .pdm-tab:hover:not(.active) { color: rgba(255,255,255,0.8); } .pdm-body { padding: 1rem 1.25rem 1.25rem; } .pdm-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 3rem; color: rgba(255,255,255,0.5); gap: 0.75rem; } .pdm-spinner { width: 32px; height: 32px; border: 3px solid rgba(144,238,144,0.2); border-top-color: #90EE90; border-radius: 50%; animation: pdm-spin 0.8s linear infinite; } @keyframes pdm-spin { to { transform: rotate(360deg); } } .pdm-error { text-align: center; padding: 2rem; color: #ff6b6b; } .pdm-error i { font-size: 2rem; margin-bottom: 0.5rem; } /* Overview tab */ .pdm-stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.6rem; } .pdm-stats-grid-last-row { display: flex; justify-content: center; gap: 0.6rem; margin-top: 0.6rem; } .pdm-stats-grid-last-row .pdm-stat-card { flex: 0 1 calc(33.333% - 0.4rem); } .pdm-stat-card { background: rgba(255,255,255,0.03); border: 1px solid rgba(144,238,144,0.08); border-radius: 8px; padding: 0.6rem 0.75rem; text-align: center; } .pdm-stat-value { font-size: 1.1rem; font-weight: 700; color: #90EE90; } .pdm-stat-toggle { display: flex; justify-content: center; gap: 0.3rem; margin-top: 0.15rem; } .pdm-stat-toggle span { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.5px; cursor: pointer; transition: color 0.2s; } .pdm-stat-toggle span.pdm-toggle-active { color: #90EE90; font-weight: 700; } .pdm-stat-toggle span.pdm-toggle-inactive { color: rgba(255,255,255,0.3); } .pdm-stat-toggle span.pdm-toggle-inactive:hover { color: rgba(255,255,255,0.55); } .pdm-stat-toggle .pdm-toggle-sep { color: rgba(255,255,255,0.15); cursor: default; } .pdm-stat-label { font-size: 0.7rem; color: rgba(255,255,255,0.5); margin-top: 0.15rem; text-transform: uppercase; letter-spacing: 0.5px; } /* Mini chart card */ .pdm-chart-card { background: rgba(255,255,255,0.03); border: 1px solid rgba(144,238,144,0.08); border-radius: 8px; padding: 0.5rem 0.6rem; margin-top: 0.6rem; cursor: pointer; position: relative; overflow: hidden; transition: border-color 0.2s; } .pdm-chart-card:hover { border-color: rgba(144,238,144,0.25); } .pdm-chart-label { font-size: 0.65rem; color: rgba(255,255,255,0.5); text-transform: uppercase; letter-spacing: 0.5px; text-align: center; margin-bottom: 0.2rem; } .pdm-chart-hint { font-size: 0.55rem; color: rgba(255,255,255,0.25); text-align: center; margin-top: 0.15rem; } .pdm-mini-canvas-wrap { position: relative; width: 100%; height: 60px; } .pdm-mini-canvas { width: 100%; height: 100%; display: block; } /* Vehicles tab */ .pdm-vehicles-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.5rem; max-height: 55vh; overflow-y: auto; } .pdm-vehicle-card { background: rgba(255,255,255,0.03); border: 1px solid rgba(144,238,144,0.08); border-radius: 8px; padding: 0.6rem 0.75rem; } .pdm-vehicle-card.pdm-crown-card { border-color: rgba(255,215,0,0.3); background: rgba(255,215,0,0.03); } .pdm-vehicle-name { font-weight: 600; font-size: 0.85rem; color: #F5F5DC; margin-bottom: 0.4rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pdm-crown { color: #FFD700; margin-right: 0.3rem; font-size: 0.75rem; } .pdm-vehicle-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.15rem 0.5rem; font-size: 0.72rem; color: rgba(255,255,255,0.6); } .pdm-vehicle-stats span { white-space: nowrap; } .pdm-vs-val { font-weight: 600; } /* Sessions tab */ .pdm-sessions-wrap { max-height: 55vh; overflow-y: auto; } .pdm-sessions-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; } .pdm-sessions-table th { text-align: left; padding: 0.55rem 0.6rem; color: rgba(255,255,255,0.5); font-weight: 600; border-bottom: 1px solid rgba(144,238,144,0.1); white-space: nowrap; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.3px; } .pdm-sessions-table td { padding: 0.55rem 0.6rem; color: rgba(255,255,255,0.75); border-bottom: 1px solid rgba(255,255,255,0.03); white-space: nowrap; } .pdm-sessions-table tr:hover td { background: rgba(255,255,255,0.03); } .pdm-result-badge { display: inline-block; padding: 0.2rem 0.55rem; border-radius: 4px; font-size: 0.72rem; font-weight: 700; text-transform: uppercase; } .pdm-result-win { background: rgba(144,238,144,0.15); color: #90EE90; } .pdm-result-loss { background: rgba(255,107,107,0.15); color: #ff6b6b; } .pdm-no-data { text-align: center; padding: 2rem; color: rgba(255,255,255,0.4); } /* Details button */ .pdm-details-btn { display: inline-flex; align-items: center; justify-content: center; background: transparent; border: 1px solid rgba(144,238,144,0.15); color: rgba(144,238,144,0.5); width: 22px; height: 22px; border-radius: 4px; cursor: pointer; font-size: 0.65rem; margin-left: 0.4rem; vertical-align: middle; transition: all 0.2s; padding: 0; line-height: 1; flex-shrink: 0; } .pdm-details-btn:hover { background: rgba(144,238,144,0.12); color: #90EE90; border-color: rgba(144,238,144,0.4); } /* Scrollbar */ .pdm-modal::-webkit-scrollbar, .pdm-vehicles-grid::-webkit-scrollbar, .pdm-sessions-wrap::-webkit-scrollbar { width: 6px; } .pdm-modal::-webkit-scrollbar-track, .pdm-vehicles-grid::-webkit-scrollbar-track, .pdm-sessions-wrap::-webkit-scrollbar-track { background: transparent; } .pdm-modal::-webkit-scrollbar-thumb, .pdm-vehicles-grid::-webkit-scrollbar-thumb, .pdm-sessions-wrap::-webkit-scrollbar-thumb { background: rgba(144,238,144,0.15); border-radius: 3px; } @media (max-width: 640px) { .pdm-stats-grid { grid-template-columns: repeat(2, 1fr); } .pdm-vehicles-grid { grid-template-columns: 1fr; } .pdm-sessions-table { font-size: 0.75rem; } .pdm-sessions-table th, .pdm-sessions-table td { padding: 0.45rem 0.4rem; } } `; document.head.appendChild(style); const overlay = document.createElement('div'); overlay.id = 'pdm-overlay'; overlay.className = 'pdm-overlay'; overlay.style.display = 'none'; overlay.innerHTML = `
`; document.body.appendChild(overlay); // Events overlay.addEventListener('click', function (e) { if (e.target === overlay) closeModal(); }); document.getElementById('pdm-close').addEventListener('click', closeModal); document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && overlay.style.display !== 'none') closeModal(); }); // Tab switching document.getElementById('pdm-tab-group').addEventListener('click', function (e) { const btn = e.target.closest('.pdm-tab'); if (!btn || btn.classList.contains('active')) return; document.querySelectorAll('.pdm-tab').forEach(b => b.classList.remove('active')); btn.classList.add('active'); currentTab = btn.dataset.tab; updateSlider(); renderTab(); }); // Prevent modal scroll from propagating document.getElementById('pdm-modal').addEventListener('click', function (e) { e.stopPropagation(); }); } function updateSlider() { const group = document.getElementById('pdm-tab-group'); const slider = document.getElementById('pdm-tab-slider'); const active = group.querySelector('.pdm-tab.active'); if (!active || !slider) return; slider.style.left = active.offsetLeft + 'px'; slider.style.width = active.offsetWidth + 'px'; } let playerDataCache = null; let gamesDataCache = null; let historyDataCache = null; let miniChart = null; let miniChartMetric = 'kdr'; let currentKdrKps = 'kdr'; const miniChartMetrics = ['kdr', 'win_rate', 'battles']; const miniChartConfig = { kdr: { label: T('playerModal.kdr'), color: '#64b5f6', suffix: '' }, win_rate: { label: T('playerModal.winRate'), color: '#90EE90', suffix: '%' }, battles: { label: T('playerModal.battles'), color: '#ffb74d', suffix: '' } }; function closeModal() { const overlay = document.getElementById('pdm-overlay'); overlay.classList.remove('pdm-visible'); setTimeout(() => { overlay.style.display = 'none'; document.body.style.overflow = ''; }, 200); // Destroy mini chart to prevent canvas reuse issues if (miniChart) { miniChart.destroy(); miniChart = null; } } function renderTab() { const body = document.getElementById('pdm-body'); if (!playerDataCache) return; // Destroy old mini chart before re-rendering if (miniChart) { miniChart.destroy(); miniChart = null; } if (currentTab === 'overview') { body.innerHTML = renderOverview(playerDataCache); initMiniChart(); } else if (currentTab === 'vehicles') { body.innerHTML = renderVehicles(playerDataCache); initVehicleKdrToggle(); } else if (currentTab === 'sessions') { body.innerHTML = renderSessions(gamesDataCache); } } function renderOverview(data) { const vehicles = (data.vehicles || []).filter(v => v.vehicle !== 'DISCONNECTED'); let totalBattles = 0, wins = 0, groundKills = 0, airKills = 0, assists = 0, deaths = 0, captures = 0; vehicles.forEach(v => { const s = v.stats; totalBattles += s.total_battles || 0; wins += s.wins || 0; groundKills += s.ground_kills || 0; airKills += s.air_kills || 0; assists += s.assists || 0; deaths += s.deaths || 0; captures += s.captures || 0; }); const totalKills = groundKills + airKills; const winRate = totalBattles > 0 ? ((wins / totalBattles) * 100).toFixed(1) + '%' : '0.0%'; const kdr = deaths > 0 ? (totalKills / deaths).toFixed(2) : totalKills.toFixed(2); const kps = totalBattles > 0 ? (totalKills / totalBattles).toFixed(2) : '0.00'; const stats = [ [T('playerModal.totalBattles'), formatNumber(totalBattles)], [T('playerModal.wins'), formatNumber(wins)], [T('playerModal.winRate'), winRate], [T('playerModal.totalKills'), formatNumber(totalKills)], [T('playerModal.kdr'), kdr], [T('playerModal.kps'), kps], [T('playerModal.airKills'), formatNumber(airKills)], [T('playerModal.groundKills'), formatNumber(groundKills)], [T('playerModal.assists'), formatNumber(assists)], [T('playerModal.deaths'), formatNumber(deaths)], [T('playerModal.captures'), formatNumber(captures)], ]; const cfg = miniChartConfig[miniChartMetric]; const chartCard = `| ${T('playerModal.date')} | ${T('playerModal.vehicle')} | ${T('playerModal.ground')} | ${T('playerModal.air')} | ${T('playerModal.assists')} | ${T('playerModal.deaths')} | ${T('playerModal.result')} |
|---|