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

Update script.js

Browse files
Files changed (1) hide show
  1. script.js +187 -248
script.js CHANGED
@@ -1,54 +1,5 @@
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,215 +12,203 @@ function isNumericColumn(colName) {
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
- });
 
1
  const COL_ORDER = [
2
+ "Model Name",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  "WorldScore-Static",
4
  "WorldScore-Dynamic",
5
  "Camera Control",
 
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
+