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 += '' + data.details.map(d => `- ${d}
`).join('') + '
';
}
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();