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", ]; // 简单 CSV 解析(你的数据没有带逗号的字段,这样已经够用) 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); // 表头:全部 sortable cols.forEach((col) => { const th = document.createElement("th"); th.textContent = col; th.classList.add("sortable"); th.dataset.col = col; // 默认排序列:WorldScore-Static,标记为 desc 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; } // 根据当前 rows 渲染 body(使用 topInfo 做加粗/下划线) 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); // 更新箭头 class 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"); } } // 初始渲染:WorldScore-Static 降序 sortAndRender(currentSortCol, currentAsc); // 给所有表头绑定 click 排序 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); }); }); } // 入口:加载 CSV,默认在 JS 里先按 WorldScore-Static 降序排一次,然后 buildTable 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 = "

No data rows in leaderboard.csv

"; return; } // 这里的 rows 保留原始字符串形式(包括 [Name](url)) const rows = parsed.rows.slice(); buildTable(rows); }) .catch((err) => { console.error("Failed to load CSV:", err); document.getElementById("table-container").innerHTML = "

Failed to load leaderboard.csv: " + String(err) + "

"; });