Howieeeee commited on
Commit
94c6762
·
verified ·
1 Parent(s): 4d849c0

Update script.js

Browse files
Files changed (1) hide show
  1. script.js +233 -186
script.js CHANGED
@@ -1,5 +1,54 @@
1
  const COL_ORDER = [
2
- "Model Name",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  "WorldScore-Static",
4
  "WorldScore-Dynamic",
5
  "Camera Control",
@@ -12,203 +61,201 @@ const COL_ORDER = [
12
  "Motion Accuracy",
13
  "Motion Magnitude",
14
  "Motion Smoothness",
15
- "Model Type",
16
- "Ability",
17
- "Sampled by",
18
- "Evaluated by",
19
- "Accessibility",
20
- "Date",
21
- ];
22
-
23
- // 简单 CSV 解析(你的数据没有带逗号的字段,这样已经够用)
24
- function parseCsv(text) {
25
- const lines = text.trim().split(/\r?\n/);
26
- const header = lines[0].split(",");
27
- const rows = lines.slice(1).map((line) => {
28
- if (!line.trim()) return null;
29
- const parts = line.split(",");
30
- const obj = {};
31
- header.forEach((h, i) => {
32
- obj[h.trim()] = (parts[i] || "").trim();
33
- });
34
- return obj;
35
- }).filter(Boolean);
36
- return { header, rows };
37
- }
38
-
39
- function parseMarkdownLink(s) {
40
- const m = s.match(/^\[(.*?)\]\((.*?)\)$/);
41
- if (m) {
42
- return { text: m[1], url: m[2] };
43
  }
44
- return { text: s, url: null };
45
- }
46
-
47
- function isNumericColumn(colName) {
48
- const numericCols = new Set([
49
- "WorldScore-Static",
50
- "WorldScore-Dynamic",
51
- "Camera Control",
52
- "Object Control",
53
- "Content Alignment",
54
- "3D Consistency",
55
- "Photometric Consistency",
56
- "Style Consistency",
57
- "Subjective Quality",
58
- "Motion Accuracy",
59
- "Motion Magnitude",
60
- "Motion Smoothness",
61
- ]);
62
- return numericCols.has(colName);
63
- }
64
-
65
- function buildTable(rows) {
66
- const container = document.getElementById("table-container");
67
- container.innerHTML = "";
68
-
69
- const table = document.createElement("table");
70
- table.className = "ws-table";
71
-
72
- const thead = document.createElement("thead");
73
- const headRow = document.createElement("tr");
74
-
75
- const firstRow = rows[0] || {};
76
- const cols = COL_ORDER.filter((c) => c in firstRow);
77
-
78
- // 表头:全部 sortable
79
  cols.forEach((col) => {
80
- const th = document.createElement("th");
81
- th.textContent = col;
82
- th.classList.add("sortable");
83
- th.dataset.col = col;
84
-
85
- // 默认排序列:WorldScore-Static,标记为 desc
86
- if (col === "WorldScore-Static") {
87
- th.classList.add("desc");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
- headRow.appendChild(th);
90
  });
91
-
92
- thead.appendChild(headRow);
93
- table.appendChild(thead);
94
-
95
- const tbody = document.createElement("tbody");
96
- table.appendChild(tbody);
97
- container.appendChild(table);
98
-
99
- // 内部函数:根据当前 rows 渲染 body
100
- function renderBody(data) {
101
- tbody.innerHTML = "";
102
- data.forEach((row) => {
103
- const tr = document.createElement("tr");
104
- cols.forEach((col) => {
105
- const td = document.createElement("td");
106
- const val = row[col] ?? "";
107
-
108
- if (col === "Model Name") {
109
- const { text, url } = parseMarkdownLink(val);
110
- if (url) {
111
- const a = document.createElement("a");
112
- a.href = url;
113
- a.target = "_blank";
114
- a.rel = "noopener noreferrer";
115
- a.textContent = text;
116
- td.appendChild(a);
117
- } else {
118
- td.textContent = text;
119
- }
120
  } else {
121
- td.textContent = val;
122
  }
123
-
124
- tr.appendChild(td);
125
- });
126
- tbody.appendChild(tr);
127
- });
128
- }
129
-
130
- // 当前排序状态
131
- let currentSortCol = "WorldScore-Static";
132
- let currentAsc = false; // 默认降序
133
-
134
- // 根据列名和升降序排序,然后渲染
135
- function sortAndRender(col, asc) {
136
- const sorted = [...rows].sort((a, b) => {
137
- const va = a[col] ?? "";
138
- const vb = b[col] ?? "";
139
-
140
- if (isNumericColumn(col)) {
141
- const na = parseFloat(va);
142
- const nb = parseFloat(vb);
143
- if (isNaN(na) && isNaN(nb)) return 0;
144
- if (isNaN(na)) return asc ? -1 : 1;
145
- if (isNaN(nb)) return asc ? 1 : -1;
146
- return asc ? na - nb : nb - na;
147
  } else {
148
- return asc
149
- ? String(va).localeCompare(String(vb))
150
- : String(vb).localeCompare(String(va));
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  }
 
 
152
  });
153
-
154
- renderBody(sorted);
155
-
156
- // 更新箭头 class
157
- const headers = Array.from(thead.querySelectorAll("th.sortable"));
158
- headers.forEach((h) => {
159
- h.classList.remove("asc", "desc");
160
- });
161
- const target = headers.find((h) => h.dataset.col === col);
162
- if (target) {
163
- target.classList.add(asc ? "asc" : "desc");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  }
165
- }
166
-
167
- // 初始渲染:WorldScore-Static 降序
168
- sortAndRender(currentSortCol, currentAsc);
169
-
170
- // 给所有表头绑定 click 排序
171
  const headers = Array.from(thead.querySelectorAll("th.sortable"));
172
- headers.forEach((th) => {
173
- th.addEventListener("click", () => {
174
- const col = th.dataset.col;
175
- if (col === currentSortCol) {
176
- currentAsc = !currentAsc; // 同一列就翻转方向
177
- } else {
178
- currentSortCol = col;
179
- currentAsc = true; // 新列默认升序
180
- }
181
- sortAndRender(currentSortCol, currentAsc);
182
- });
183
  });
 
 
 
 
184
  }
185
-
186
- // 入口:加载 CSV,默认在 JS 里先按 WorldScore-Static 降序排一次,然后 buildTable
187
- fetch("leaderboard.csv")
188
- .then((res) => {
189
- if (!res.ok) {
190
- throw new Error("HTTP " + res.status);
191
- }
192
- return res.text();
193
- })
194
- .then((text) => {
195
- const parsed = parseCsv(text);
196
- if (!parsed.rows || parsed.rows.length === 0) {
197
- document.getElementById("table-container").innerHTML =
198
- "<p>No data rows in leaderboard.csv</p>";
199
- return;
200
  }
201
-
202
- // 这里的 rows 保留原始字符串形式(包括 [Name](url))
203
- const rows = parsed.rows.slice();
204
-
205
- // 默认排序逻辑放在 buildTable 内部 sortAndRender 已经做了,
206
- // 这里不需要再排序一次,直接丢过去
207
- buildTable(rows);
208
- })
209
- .catch((err) => {
210
- console.error("Failed to load CSV:", err);
211
- document.getElementById("table-container").innerHTML =
212
- "<p style='color:#f97316'>Failed to load leaderboard.csv: " + String(err) + "</p>";
213
  });
214
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  const COL_ORDER = [
2
+ "Model Name",
3
+ "WorldScore-Static",
4
+ "WorldScore-Dynamic",
5
+ "Camera Control",
6
+ "Object Control",
7
+ "Content Alignment",
8
+ "3D Consistency",
9
+ "Photometric Consistency",
10
+ "Style Consistency",
11
+ "Subjective Quality",
12
+ "Motion Accuracy",
13
+ "Motion Magnitude",
14
+ "Motion Smoothness",
15
+ "Model Type",
16
+ "Ability",
17
+ "Sampled by",
18
+ "Evaluated by",
19
+ "Accessibility",
20
+ "Date",
21
+ ];
22
+
23
+ // 简单 CSV 解析(你的数据没有带逗号的字段,这样已经够用)
24
+ function parseCsv(text) {
25
+ const lines = text.trim().split(/\r?\n/);
26
+ const header = lines[0].split(",");
27
+ const rows = lines
28
+ .slice(1)
29
+ .map((line) => {
30
+ if (!line.trim()) return null;
31
+ const parts = line.split(",");
32
+ const obj = {};
33
+ header.forEach((h, i) => {
34
+ obj[h.trim()] = (parts[i] || "").trim();
35
+ });
36
+ return obj;
37
+ })
38
+ .filter(Boolean);
39
+ return { header, rows };
40
+ }
41
+
42
+ function parseMarkdownLink(s) {
43
+ const m = s.match(/^\[(.*?)\]\((.*?)\)$/);
44
+ if (m) {
45
+ return { text: m[1], url: m[2] };
46
+ }
47
+ return { text: s, url: null };
48
+ }
49
+
50
+ function isNumericColumn(colName) {
51
+ const numericCols = new Set([
52
  "WorldScore-Static",
53
  "WorldScore-Dynamic",
54
  "Camera Control",
 
61
  "Motion Accuracy",
62
  "Motion Magnitude",
63
  "Motion Smoothness",
64
+ ]);
65
+ return numericCols.has(colName);
66
+ }
67
+
68
+ function buildTable(rows) {
69
+ const container = document.getElementById("table-container");
70
+ container.innerHTML = "";
71
+
72
+ const table = document.createElement("table");
73
+ table.className = "ws-table";
74
+
75
+ const thead = document.createElement("thead");
76
+ const headRow = document.createElement("tr");
77
+
78
+ const firstRow = rows[0] || {};
79
+ const cols = COL_ORDER.filter((c) => c in firstRow);
80
+
81
+ // 表头:全部 sortable
82
+ cols.forEach((col) => {
83
+ const th = document.createElement("th");
84
+ th.textContent = col;
85
+ th.classList.add("sortable");
86
+ th.dataset.col = col;
87
+
88
+ // 默认排序列:WorldScore-Static,标记为 desc
89
+ if (col === "WorldScore-Static") {
90
+ th.classList.add("desc");
 
91
  }
92
+ headRow.appendChild(th);
93
+ });
94
+
95
+ thead.appendChild(headRow);
96
+ table.appendChild(thead);
97
+
98
+ const tbody = document.createElement("tbody");
99
+ table.appendChild(tbody);
100
+ container.appendChild(table);
101
+
102
+ // 计算每个数值列的 最大值 / 第二大值
103
+ function computeTopInfo(data) {
104
+ const info = {};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  cols.forEach((col) => {
106
+ if (!isNumericColumn(col)) return;
107
+
108
+ const vals = [];
109
+ data.forEach((row) => {
110
+ const v = parseFloat(row[col] ?? "");
111
+ if (!isNaN(v)) vals.push(v);
112
+ });
113
+
114
+ if (vals.length === 0) {
115
+ info[col] = { max: null, second: null };
116
+ return;
117
+ }
118
+
119
+ vals.sort((a, b) => b - a); // 降序
120
+
121
+ const max = vals[0];
122
+ let second = null;
123
+ for (let i = 1; i < vals.length; i++) {
124
+ if (vals[i] !== max) {
125
+ second = vals[i];
126
+ break;
127
+ }
128
  }
129
+ info[col] = { max, second };
130
  });
131
+ return info;
132
+ }
133
+
134
+ // 根据当前 rows 渲染 body(使用 topInfo 做加粗/下划线)
135
+ function renderBody(data, topInfo) {
136
+ tbody.innerHTML = "";
137
+ data.forEach((row) => {
138
+ const tr = document.createElement("tr");
139
+ cols.forEach((col) => {
140
+ const td = document.createElement("td");
141
+ const val = row[col] ?? "";
142
+
143
+ if (col === "Model Name") {
144
+ const { text, url } = parseMarkdownLink(val);
145
+ if (url) {
146
+ const a = document.createElement("a");
147
+ a.href = url;
148
+ a.target = "_blank";
149
+ a.rel = "noopener noreferrer";
150
+ a.textContent = text;
151
+ td.appendChild(a);
 
 
 
 
 
 
 
 
152
  } else {
153
+ td.textContent = text;
154
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  } else {
156
+ td.textContent = val;
157
+
158
+ if (isNumericColumn(col)) {
159
+ const num = parseFloat(val);
160
+ const info = topInfo && topInfo[col];
161
+
162
+ if (!isNaN(num) && info) {
163
+ if (info.max != null && num === info.max) {
164
+ // 最大值:加粗
165
+ td.style.fontWeight = "700";
166
+ } else if (info.second != null && num === info.second) {
167
+ // 第二大值:下划线
168
+ td.style.textDecoration = "underline";
169
+ }
170
+ }
171
+ }
172
  }
173
+
174
+ tr.appendChild(td);
175
  });
176
+ tbody.appendChild(tr);
177
+ });
178
+ }
179
+
180
+ // 当前排序状态
181
+ let currentSortCol = "WorldScore-Static";
182
+ let currentAsc = false; // 默认降序
183
+
184
+ // 根据列名和升降序排序,然后渲染
185
+ function sortAndRender(col, asc) {
186
+ const sorted = [...rows].sort((a, b) => {
187
+ const va = a[col] ?? "";
188
+ const vb = b[col] ?? "";
189
+
190
+ if (isNumericColumn(col)) {
191
+ const na = parseFloat(va);
192
+ const nb = parseFloat(vb);
193
+ if (isNaN(na) && isNaN(nb)) return 0;
194
+ if (isNaN(na)) return asc ? -1 : 1;
195
+ if (isNaN(nb)) return asc ? 1 : -1;
196
+ return asc ? na - nb : nb - na;
197
+ } else {
198
+ return asc
199
+ ? String(va).localeCompare(String(vb))
200
+ : String(vb).localeCompare(String(va));
201
  }
202
+ });
203
+
204
+ const topInfo = computeTopInfo(sorted);
205
+ renderBody(sorted, topInfo);
206
+
207
+ // 更新箭头 class
208
  const headers = Array.from(thead.querySelectorAll("th.sortable"));
209
+ headers.forEach((h) => {
210
+ h.classList.remove("asc", "desc");
 
 
 
 
 
 
 
 
 
211
  });
212
+ const target = headers.find((h) => h.dataset.col === col);
213
+ if (target) {
214
+ target.classList.add(asc ? "asc" : "desc");
215
+ }
216
  }
217
+
218
+ // 初始渲染:WorldScore-Static 降序
219
+ sortAndRender(currentSortCol, currentAsc);
220
+
221
+ // 给所有表头绑定 click 排序
222
+ const headers = Array.from(thead.querySelectorAll("th.sortable"));
223
+ headers.forEach((th) => {
224
+ th.addEventListener("click", () => {
225
+ const col = th.dataset.col;
226
+ if (col === currentSortCol) {
227
+ currentAsc = !currentAsc; // 同一列就翻转方向
228
+ } else {
229
+ currentSortCol = col;
230
+ currentAsc = true; // 新列默认升序
 
231
  }
232
+ sortAndRender(currentSortCol, currentAsc);
 
 
 
 
 
 
 
 
 
 
 
233
  });
234
+ });
235
+ }
236
+
237
+ // 入口:加载 CSV,默认在 JS 里先按 WorldScore-Static 降序排一次,然后 buildTable
238
+ fetch("leaderboard.csv")
239
+ .then((res) => {
240
+ if (!res.ok) {
241
+ throw new Error("HTTP " + res.status);
242
+ }
243
+ return res.text();
244
+ })
245
+ .then((text) => {
246
+ const parsed = parseCsv(text);
247
+ if (!parsed.rows || parsed.rows.length === 0) {
248
+ document.getElementById("table-container").innerHTML =
249
+ "<p>No data rows in leaderboard.csv</p>";
250
+ return;
251
+ }
252
+
253
+ // 这里的 rows 保留原始字符串形式(包括 [Name](url))
254
+ const rows = parsed.rows.slice();
255
+ buildTable(rows);
256
+ })
257
+ .catch((err) => {
258
+ console.error("Failed to load CSV:", err);
259
+ document.getElementById("table-container").innerHTML =
260
+ "<p style='color:#f97316'>Failed to load leaderboard.csv: " + String(err) + "</p>";
261
+ });