const rawData = window.SERVER_DATA || []; // --- State Management --- let state = { championTrack: "Standard", sortKey: "overall_em", sortDir: "desc", filters: { track: "all", agent: "", backbone: "", retriever: "" // New retriever filter } }; // List of score column keys (used to determine which columns need highlighting) const SCORE_KEYS = ["overall_em", "overall_f1", "intra_em", "intra_f1", "inter_em", "inter_f1"]; // --- Main Tab Switching --- function switchMainTab(tabName) { document.querySelectorAll('.nav-btn').forEach(btn => btn.classList.remove('active')); const map = {'leaderboard': 0, 'full-metrics': 1, 'submit': 2}; document.querySelectorAll('.nav-btn')[map[tabName]].classList.add('active'); document.querySelectorAll('.view-section').forEach(el => el.style.display = 'none'); document.getElementById(`view-${tabName}`).style.display = 'block'; if (tabName === 'leaderboard') renderChampionTable(); if (tabName === 'full-metrics') renderFullTable(); } // --- Update active-sort state for all table headers --- function updateSortHeaders() { document.querySelectorAll('.sortable').forEach(th => { const key = th.dataset.key; // Remove arrows from text let text = th.textContent.replace(/ ↓/g, '').replace(/ ↑/g, '').trim(); if (key === state.sortKey) { th.classList.add('active-sort'); text += state.sortDir === 'desc' ? ' ↓' : ' ↑'; } else { th.classList.remove('active-sort'); } th.textContent = text; }); } // --- Logic: Champion Table --- function renderChampionTable() { const tbody = document.querySelector('#champion-table tbody'); let data = rawData.filter(d => d.track === state.championTrack); // For each method name, keep only the one with the highest overall_em const methodBestMap = new Map(); data.forEach(row => { const method = row.method; if (!methodBestMap.has(method) || row.overall_em > methodBestMap.get(method).overall_em) { methodBestMap.set(method, row); } }); // Convert to array data = Array.from(methodBestMap.values()); data = sortData(data); tbody.innerHTML = data.map((row, idx) => { return buildRowHTML(row, idx + 1, false); }).join(''); updateSortHeaders(); } // --- Logic: Full Table --- function renderFullTable() { const tbody = document.querySelector('#full-table tbody'); let data = rawData.filter(d => { const f = state.filters; if (f.track !== 'all' && d.track !== f.track) return false; if (f.agent && !d.agent.toLowerCase().includes(f.agent.toLowerCase())) return false; if (f.backbone && !d.backbone.toLowerCase().includes(f.backbone.toLowerCase())) return false; if (f.retriever && !d.retriever.toLowerCase().includes(f.retriever.toLowerCase())) return false; return true; }); data = sortData(data); if (data.length === 0) { tbody.innerHTML = `No matching results.`; return; } tbody.innerHTML = data.map((row, idx) => { return buildRowHTML(row, idx + 1, true); }).join(''); updateSortHeaders(); } // --- Helper: Sort --- function sortData(data) { return data.sort((a, b) => { let valA = a[state.sortKey]; let valB = b[state.sortKey]; if (typeof valA === 'number') { return state.sortDir === 'desc' ? valB - valA : valA - valB; } return 0; }); } // --- Helper: Row HTML Builder --- function buildRowHTML(row, rank, showTrack) { const medal = rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : rank; const trackTd = showTrack ? `${row.track}` : ''; // Generate score cell, add active-col class to current sort column function scoreCell(key, value) { const isActive = (key === state.sortKey) ? ' active-col' : ''; return `${value.toFixed(1)}`; } return ` ${medal} ${row.method}
${row.date}
${trackTd} ${row.agent} ${row.backbone} ${row.retriever} ${scoreCell('overall_em', row.overall_em)} ${scoreCell('overall_f1', row.overall_f1)} ${scoreCell('intra_em', row.intra_em)} ${scoreCell('intra_f1', row.intra_f1)} ${scoreCell('inter_em', row.inter_em)} ${scoreCell('inter_f1', row.inter_f1)} `; } // --- Event Listeners --- // 1. Champion Sub-Tabs (Standard/Open) document.querySelectorAll('.sub-tab-btn').forEach(btn => { btn.addEventListener('click', (e) => { document.querySelectorAll('.sub-tab-btn').forEach(b => b.classList.remove('active')); e.target.classList.add('active'); state.championTrack = e.target.dataset.track; renderChampionTable(); }); }); // 2. Sorting document.querySelectorAll('.sortable').forEach(th => { th.addEventListener('click', (e) => { const key = e.currentTarget.dataset.key; if (state.sortKey === key) { state.sortDir = state.sortDir === 'desc' ? 'asc' : 'desc'; } else { state.sortKey = key; state.sortDir = 'desc'; } // Re-render the currently visible table if (document.getElementById('view-leaderboard').style.display !== 'none') { renderChampionTable(); } else { renderFullTable(); } }); }); // 3. Full Metrics Filters document.getElementById('filter-track').addEventListener('change', (e) => { state.filters.track = e.target.value; renderFullTable(); }); document.getElementById('filter-agent').addEventListener('input', (e) => { state.filters.agent = e.target.value; renderFullTable(); }); document.getElementById('filter-backbone').addEventListener('input', (e) => { state.filters.backbone = e.target.value; renderFullTable(); }); document.getElementById('filter-retriever').addEventListener('input', (e) => { state.filters.retriever = e.target.value; renderFullTable(); }); // 4. Submit Form (AJAX, for displaying PR link feedback) document.getElementById('submit-form').addEventListener('submit', async (e) => { e.preventDefault(); const fileInput = document.getElementById('submit-file'); const submitBtn = document.getElementById('submit-btn'); const resultDiv = document.getElementById('submit-result'); if (!fileInput.files.length) return; // Disable button, show loading submitBtn.disabled = true; submitBtn.innerHTML = ' Submitting...'; resultDiv.style.display = 'none'; const formData = new FormData(); formData.append('file', fileInput.files[0]); try { const response = await fetch('/upload', { method: 'POST', body: formData, }); const data = await response.json(); resultDiv.style.display = 'block'; if (data.success) { let html = `✅ ${data.message}`; if (data.pr_url) { html += `

🔗 View your Pull Request →`; } html += `

After maintainers review and merge your PR, scores will be computed and published on the leaderboard.`; resultDiv.className = 'success'; resultDiv.innerHTML = html; } else { let html = `❌ ${data.error || 'Submission failed.'}`; if (data.details) { html += ''; } resultDiv.className = 'error'; resultDiv.innerHTML = html; } } catch (err) { resultDiv.style.display = 'block'; resultDiv.className = 'error'; resultDiv.innerHTML = `❌ Network error: ${err.message}`; } finally { submitBtn.disabled = false; submitBtn.innerHTML = ' Submit & Create PR'; } }); // --- Init --- renderChampionTable();