Howieeeee commited on
Commit
e284ad1
·
verified ·
1 Parent(s): b955c3e

Update script.js

Browse files
Files changed (1) hide show
  1. script.js +248 -187
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,215 @@ 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
+ // ==== 给数值单元格加“百分比条”背景 ====
69
+ function styleScoreCell(td, valueStr) {
70
+ const v = parseFloat(valueStr);
71
+ if (isNaN(v)) return;
72
+
73
+ const pct = Math.max(0, Math.min(100, v));
74
+ const ratio = pct / 100;
75
+ const hue = 120 * ratio; // 0=red, 120=green
76
+ const bgColor = `hsl(${hue}, 80%, 90%)`;
77
+
78
+ td.style.backgroundImage = `linear-gradient(to right, ${bgColor} ${pct}%, transparent ${pct}%)`;
79
+ td.style.backgroundRepeat = "no-repeat";
80
+ td.style.backgroundOrigin = "content-box";
81
+ }
82
+
83
+ // ==== 构建表格 + 排序 + top1/top2 标记 ====
84
+ function buildTable(rows) {
85
+ const container = document.getElementById("table-container");
86
+ container.innerHTML = "";
87
+
88
+ const table = document.createElement("table");
89
+ table.className = "ws-table";
90
+
91
+ const thead = document.createElement("thead");
92
+ const headRow = document.createElement("tr");
93
+
94
+ const firstRow = rows[0] || {};
95
+ const cols = COL_ORDER.filter((c) => c in firstRow);
96
+
97
+ // 表头
98
+ cols.forEach((col) => {
99
+ const th = document.createElement("th");
100
+ th.textContent = col;
101
+ th.classList.add("sortable");
102
+ th.dataset.col = col;
103
+ if (col === "WorldScore-Static") {
104
+ th.classList.add("desc"); // 默认降序
105
  }
106
+ headRow.appendChild(th);
107
+ });
108
+
109
+ thead.appendChild(headRow);
110
+ table.appendChild(thead);
111
+
112
+ const tbody = document.createElement("tbody");
113
+ table.appendChild(tbody);
114
+ container.appendChild(table);
115
+
116
+ /**
117
+ * 渲染表体,带每列 top1 / top2 信息
118
+ * topInfo: { [colName]: { max: number|null, second: number|null } }
119
+ */
120
+ function renderBody(data, topInfo) {
121
+ tbody.innerHTML = "";
122
+ data.forEach((row) => {
123
+ const tr = document.createElement("tr");
124
+ cols.forEach((col) => {
125
+ const td = document.createElement("td");
126
+ const val = row[col] ?? "";
127
+
128
+ if (col === "Model Name") {
129
+ const { text, url } = parseMarkdownLink(val);
130
+ if (url) {
131
+ const a = document.createElement("a");
132
+ a.href = url;
133
+ a.target = "_blank";
134
+ a.rel = "noopener noreferrer";
135
+ a.textContent = text;
136
+ td.appendChild(a);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  } else {
138
+ td.textContent = text;
139
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  } else {
141
+ td.textContent = val;
142
+
143
+ if (isNumericColumn(col)) {
144
+ styleScoreCell(td, val);
145
+
146
+ const num = parseFloat(val);
147
+ const info = topInfo[col];
148
+ if (!isNaN(num) && info) {
149
+ if (info.max != null && num === info.max) {
150
+ // 最大值:加粗
151
+ td.style.fontWeight = "700";
152
+ } else if (info.second != null && num === info.second) {
153
+ // 第二大值:下划线
154
+ td.style.textDecoration = "underline";
155
+ }
156
+ }
157
+ }
158
  }
159
+
160
+ tr.appendChild(td);
161
  });
162
+ tbody.appendChild(tr);
163
+ });
164
+ }
165
+
166
+ // 当前排序状态
167
+ let currentSortCol = "WorldScore-Static";
168
+ let currentAsc = false; // 默认降序
169
+
170
+ function computeTopInfo(data) {
171
+ const info = {};
172
+ cols.forEach((col) => {
173
+ if (!isNumericColumn(col)) return;
174
+
175
+ const values = [];
176
+ data.forEach((row) => {
177
+ const v = parseFloat(row[col] ?? "");
178
+ if (!isNaN(v)) values.push(v);
179
  });
180
+
181
+ if (values.length === 0) {
182
+ info[col] = { max: null, second: null };
183
+ return;
184
  }
185
+
186
+ // 从大到小排序
187
+ values.sort((a, b) => b - a);
188
+
189
+ // 取 unique 前两名
190
+ const max = values[0];
191
+ let second = null;
192
+ for (let i = 1; i < values.length; i++) {
193
+ if (values[i] !== max) {
194
+ second = values[i];
195
+ break;
 
 
 
 
196
  }
197
+ }
198
+ info[col] = { max, second };
199
  });
200
+ return info;
201
  }
202
+
203
+ function sortAndRender(col, asc) {
204
+ const sorted = [...rows].sort((a, b) => {
205
+ const va = a[col] ?? "";
206
+ const vb = b[col] ?? "";
207
+
208
+ if (isNumericColumn(col)) {
209
+ const na = parseFloat(va);
210
+ const nb = parseFloat(vb);
211
+ if (isNaN(na) && isNaN(nb)) return 0;
212
+ if (isNaN(na)) return asc ? -1 : 1;
213
+ if (isNaN(nb)) return asc ? 1 : -1;
214
+ return asc ? na - nb : nb - na;
215
+ } else {
216
+ return asc
217
+ ? String(va).localeCompare(String(vb))
218
+ : String(vb).localeCompare(String(va));
219
  }
220
+ });
221
+
222
+ const topInfo = computeTopInfo(sorted);
223
+ renderBody(sorted, topInfo);
224
+
225
+ // 更新箭头状态
226
+ const headers = Array.from(thead.querySelectorAll("th.sortable"));
227
+ headers.forEach((h) => h.classList.remove("asc", "desc"));
228
+ const target = headers.find((h) => h.dataset.col === col);
229
+ if (target) {
230
+ target.classList.add(asc ? "asc" : "desc");
231
+ }
232
+ }
233
+
234
+ // 初始渲染:WorldScore-Static 降序
235
+ sortAndRender(currentSortCol, currentAsc);
236
+
237
+ // 绑定点击排序
238
+ const headers = Array.from(thead.querySelectorAll("th.sortable"));
239
+ headers.forEach((th) => {
240
+ th.addEventListener("click", () => {
241
+ const col = th.dataset.col;
242
+ if (col === currentSortCol) {
243
+ currentAsc = !currentAsc;
244
+ } else {
245
+ currentSortCol = col;
246
+ currentAsc = true; // 新列默认升序
247
  }
248
+ sortAndRender(currentSortCol, currentAsc);
 
 
 
 
 
 
 
 
 
 
 
249
  });
250
+ });
251
+ }
252
+
253
+ // ==== 入口 ====
254
+ fetch("leaderboard.csv")
255
+ .then((res) => {
256
+ if (!res.ok) {
257
+ throw new Error("HTTP " + res.status);
258
+ }
259
+ return res.text();
260
+ })
261
+ .then((text) => {
262
+ const parsed = parseCsv(text);
263
+ if (!parsed.rows || parsed.rows.length === 0) {
264
+ document.getElementById("table-container").innerHTML =
265
+ "<p>No data rows in leaderboard.csv</p>";
266
+ return;
267
+ }
268
+ const rows = parsed.rows.slice();
269
+ buildTable(rows);
270
+ })
271
+ .catch((err) => {
272
+ console.error("Failed to load CSV:", err);
273
+ document.getElementById("table-container").innerHTML =
274
+ "<p style='color:#f97316'>Failed to load leaderboard.csv: " + String(err) + "</p>";
275
+ });