| const COL_ORDER = [ |
| "Model Name", |
| "WorldScore-Static", |
| "WorldScore-Dynamic", |
| "Camera Control", |
| "Object Control", |
| "Content Alignment", |
| "3D Consistency", |
| "Photometric Consistency", |
| "Style Consistency", |
| "Subjective Quality", |
| "Motion Accuracy", |
| "Motion Magnitude", |
| "Motion Smoothness", |
| "Model Type", |
| "Ability", |
| "Sampled by", |
| "Evaluated by", |
| "Accessibility", |
| "Date", |
| ]; |
|
|
| |
| function parseCsv(text) { |
| const lines = text.trim().split(/\r?\n/); |
| const header = lines[0].split(","); |
| const rows = lines |
| .slice(1) |
| .map((line) => { |
| if (!line.trim()) return null; |
| const parts = line.split(","); |
| const obj = {}; |
| header.forEach((h, i) => { |
| obj[h.trim()] = (parts[i] || "").trim(); |
| }); |
| return obj; |
| }) |
| .filter(Boolean); |
| return { header, rows }; |
| } |
|
|
| function parseMarkdownLink(s) { |
| const m = s.match(/^\[(.*?)\]\((.*?)\)$/); |
| if (m) { |
| return { text: m[1], url: m[2] }; |
| } |
| return { text: s, url: null }; |
| } |
|
|
| function isNumericColumn(colName) { |
| const numericCols = new Set([ |
| "WorldScore-Static", |
| "WorldScore-Dynamic", |
| "Camera Control", |
| "Object Control", |
| "Content Alignment", |
| "3D Consistency", |
| "Photometric Consistency", |
| "Style Consistency", |
| "Subjective Quality", |
| "Motion Accuracy", |
| "Motion Magnitude", |
| "Motion Smoothness", |
| ]); |
| return numericCols.has(colName); |
| } |
|
|
| function buildTable(rows) { |
| const container = document.getElementById("table-container"); |
| container.innerHTML = ""; |
|
|
| const table = document.createElement("table"); |
| table.className = "ws-table"; |
|
|
| const thead = document.createElement("thead"); |
| const headRow = document.createElement("tr"); |
|
|
| const firstRow = rows[0] || {}; |
| const cols = COL_ORDER.filter((c) => c in firstRow); |
|
|
| |
| cols.forEach((col) => { |
| const th = document.createElement("th"); |
| th.textContent = col; |
| th.classList.add("sortable"); |
| th.dataset.col = col; |
|
|
| |
| if (col === "WorldScore-Static") { |
| th.classList.add("desc"); |
| } |
| headRow.appendChild(th); |
| }); |
|
|
| thead.appendChild(headRow); |
| table.appendChild(thead); |
|
|
| const tbody = document.createElement("tbody"); |
| table.appendChild(tbody); |
| container.appendChild(table); |
|
|
| |
| function computeTopInfo(data) { |
| const info = {}; |
| cols.forEach((col) => { |
| if (!isNumericColumn(col)) return; |
|
|
| const vals = []; |
| data.forEach((row) => { |
| const v = parseFloat(row[col] ?? ""); |
| if (!isNaN(v)) vals.push(v); |
| }); |
|
|
| if (vals.length === 0) { |
| info[col] = { max: null, second: null }; |
| return; |
| } |
|
|
| vals.sort((a, b) => b - a); |
|
|
| const max = vals[0]; |
| let second = null; |
| for (let i = 1; i < vals.length; i++) { |
| if (vals[i] !== max) { |
| second = vals[i]; |
| break; |
| } |
| } |
| info[col] = { max, second }; |
| }); |
| return info; |
| } |
|
|
| |
| function renderBody(data, topInfo) { |
| tbody.innerHTML = ""; |
| data.forEach((row) => { |
| const tr = document.createElement("tr"); |
| cols.forEach((col) => { |
| const td = document.createElement("td"); |
| const val = row[col] ?? ""; |
|
|
| if (col === "Model Name") { |
| const { text, url } = parseMarkdownLink(val); |
| if (url) { |
| const a = document.createElement("a"); |
| a.href = url; |
| a.target = "_blank"; |
| a.rel = "noopener noreferrer"; |
| a.textContent = text; |
| td.appendChild(a); |
| } else { |
| td.textContent = text; |
| } |
| } else { |
| td.textContent = val; |
|
|
| if (isNumericColumn(col)) { |
| const num = parseFloat(val); |
| const info = topInfo && topInfo[col]; |
|
|
| if (!isNaN(num) && info) { |
| if (info.max != null && num === info.max) { |
| |
| td.style.fontWeight = "700"; |
| } else if (info.second != null && num === info.second) { |
| |
| td.style.textDecoration = "underline"; |
| } |
| } |
| } |
| } |
|
|
| tr.appendChild(td); |
| }); |
| tbody.appendChild(tr); |
| }); |
| } |
|
|
| |
| let currentSortCol = "WorldScore-Static"; |
| let currentAsc = false; |
|
|
| |
| function sortAndRender(col, asc) { |
| const sorted = [...rows].sort((a, b) => { |
| const va = a[col] ?? ""; |
| const vb = b[col] ?? ""; |
|
|
| if (isNumericColumn(col)) { |
| const na = parseFloat(va); |
| const nb = parseFloat(vb); |
| if (isNaN(na) && isNaN(nb)) return 0; |
| if (isNaN(na)) return asc ? -1 : 1; |
| if (isNaN(nb)) return asc ? 1 : -1; |
| return asc ? na - nb : nb - na; |
| } else { |
| return asc |
| ? String(va).localeCompare(String(vb)) |
| : String(vb).localeCompare(String(va)); |
| } |
| }); |
|
|
| const topInfo = computeTopInfo(sorted); |
| renderBody(sorted, topInfo); |
|
|
| |
| const headers = Array.from(thead.querySelectorAll("th.sortable")); |
| headers.forEach((h) => { |
| h.classList.remove("asc", "desc"); |
| }); |
| const target = headers.find((h) => h.dataset.col === col); |
| if (target) { |
| target.classList.add(asc ? "asc" : "desc"); |
| } |
| } |
|
|
| |
| sortAndRender(currentSortCol, currentAsc); |
|
|
| |
| const headers = Array.from(thead.querySelectorAll("th.sortable")); |
| headers.forEach((th) => { |
| th.addEventListener("click", () => { |
| const col = th.dataset.col; |
| if (col === currentSortCol) { |
| currentAsc = !currentAsc; |
| } else { |
| currentSortCol = col; |
| currentAsc = true; |
| } |
| sortAndRender(currentSortCol, currentAsc); |
| }); |
| }); |
| } |
|
|
| |
| fetch("leaderboard.csv") |
| .then((res) => { |
| if (!res.ok) { |
| throw new Error("HTTP " + res.status); |
| } |
| return res.text(); |
| }) |
| .then((text) => { |
| const parsed = parseCsv(text); |
| if (!parsed.rows || parsed.rows.length === 0) { |
| document.getElementById("table-container").innerHTML = |
| "<p>No data rows in leaderboard.csv</p>"; |
| return; |
| } |
|
|
| |
| const rows = parsed.rows.slice(); |
| buildTable(rows); |
| }) |
| .catch((err) => { |
| console.error("Failed to load CSV:", err); |
| document.getElementById("table-container").innerHTML = |
| "<p style='color:#f97316'>Failed to load leaderboard.csv: " + String(err) + "</p>"; |
| }); |
|
|