cyhuang-tw commited on
Commit
6ad9359
·
verified ·
1 Parent(s): 364f28f

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1000 -18
index.html CHANGED
@@ -1,19 +1,1001 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Dynamic-SUPERB Leaderboard</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
+ margin: 0;
11
+ padding: 20px;
12
+ background-color: #f8f9fa;
13
+ }
14
+
15
+ /* Menu Styles */
16
+ .menu-container {
17
+ position: fixed;
18
+ top: 15px;
19
+ left: 15px;
20
+ z-index: 1000;
21
+ }
22
+
23
+ .menu-icon {
24
+ font-size: 28px;
25
+ cursor: pointer;
26
+ user-select: none;
27
+ color: #667eea;
28
+ background: white;
29
+ padding: 8px 12px;
30
+ border-radius: 8px;
31
+ box-shadow: 0 2px 8px rgba(0,0,0,0.15);
32
+ transition: all 0.2s ease;
33
+ }
34
+
35
+ .menu-icon:hover {
36
+ background: #667eea;
37
+ color: white;
38
+ transform: scale(1.05);
39
+ }
40
+
41
+ .menu-list {
42
+ display: none;
43
+ position: absolute;
44
+ top: 50px;
45
+ left: 0;
46
+ background: white;
47
+ border: 1px solid #dee2e6;
48
+ border-radius: 8px;
49
+ box-shadow: 0 4px 16px rgba(0,0,0,0.15);
50
+ min-width: 220px;
51
+ font-size: 14px;
52
+ overflow: hidden;
53
+ }
54
+
55
+ .menu-list.show {
56
+ display: block;
57
+ animation: slideDown 0.2s ease;
58
+ }
59
+
60
+ @keyframes slideDown {
61
+ from { opacity: 0; transform: translateY(-10px); }
62
+ to { opacity: 1; transform: translateY(0); }
63
+ }
64
+
65
+ .menu-list a {
66
+ display: block;
67
+ padding: 12px 16px;
68
+ color: #495057;
69
+ text-decoration: none;
70
+ border-bottom: 1px solid #f8f9fa;
71
+ transition: background-color 0.2s ease;
72
+ }
73
+
74
+ .menu-list a:last-child {
75
+ border-bottom: none;
76
+ }
77
+
78
+ .menu-list a:hover {
79
+ background-color: #667eea;
80
+ color: white;
81
+ }
82
+
83
+ .menu-list a::before {
84
+ content: "→ ";
85
+ margin-right: 8px;
86
+ opacity: 0;
87
+ transition: opacity 0.2s ease;
88
+ }
89
+
90
+ .menu-list a:hover::before {
91
+ opacity: 1;
92
+ }
93
+
94
+ .container {
95
+ max-width: 1400px;
96
+ margin: 0 auto;
97
+ background: white;
98
+ border-radius: 8px;
99
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
100
+ overflow: hidden;
101
+ }
102
+
103
+ .header {
104
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
105
+ color: white;
106
+ padding: 30px;
107
+ text-align: center;
108
+ }
109
+
110
+ .header h1 {
111
+ margin: 0;
112
+ font-size: 2.5em;
113
+ font-weight: 300;
114
+ }
115
+
116
+ .controls {
117
+ padding: 20px 30px;
118
+ border-bottom: 1px solid #dee2e6;
119
+ background: #f8f9fa;
120
+ }
121
+
122
+ .filter-row {
123
+ display: flex;
124
+ gap: 20px;
125
+ margin-bottom: 15px;
126
+ flex-wrap: wrap;
127
+ }
128
+
129
+ .form-group {
130
+ flex: 1;
131
+ min-width: 200px;
132
+ }
133
+
134
+ label {
135
+ display: block;
136
+ margin-bottom: 5px;
137
+ font-weight: 600;
138
+ color: #495057;
139
+ }
140
+
141
+ select {
142
+ width: 100%;
143
+ padding: 10px 15px;
144
+ border: 2px solid #dee2e6;
145
+ border-radius: 6px;
146
+ background: white;
147
+ font-size: 14px;
148
+ transition: border-color 0.3s;
149
+ }
150
+
151
+ select:focus {
152
+ outline: none;
153
+ border-color: #667eea;
154
+ }
155
+
156
+ select:disabled {
157
+ background-color: #f8f9fa;
158
+ color: #6c757d;
159
+ cursor: not-allowed;
160
+ }
161
+
162
+ .clear-filters {
163
+ padding: 8px 16px;
164
+ background: #dc3545;
165
+ color: white;
166
+ border: none;
167
+ border-radius: 4px;
168
+ cursor: pointer;
169
+ font-size: 14px;
170
+ transition: background-color 0.3s;
171
+ }
172
+
173
+ .clear-filters:hover {
174
+ background: #c82333;
175
+ }
176
+
177
+ .table-container {
178
+ overflow-x: auto;
179
+ max-height: 70vh;
180
+ overflow-y: auto;
181
+ }
182
+
183
+ table {
184
+ width: 100%;
185
+ border-collapse: collapse;
186
+ font-size: 12px;
187
+ }
188
+
189
+ th {
190
+ background: #343a40;
191
+ color: white;
192
+ padding: 8px 6px;
193
+ text-align: center;
194
+ position: sticky;
195
+ top: 0;
196
+ z-index: 10;
197
+ border-right: 1px solid #495057;
198
+ writing-mode: horizontal-tb;
199
+ text-orientation: initial;
200
+ min-width: 80px;
201
+ max-width: 120px;
202
+ white-space: normal;
203
+ word-wrap: break-word;
204
+ word-break: break-word;
205
+ vertical-align: bottom;
206
+ height: auto;
207
+ line-height: 1.2;
208
+ }
209
+
210
+ th:first-child {
211
+ min-width: 150px;
212
+ text-align: left;
213
+ padding: 12px 8px;
214
+ white-space: nowrap;
215
+ }
216
+
217
+ .task-header {
218
+ font-size: 11px;
219
+ line-height: 1.2;
220
+ white-space: normal;
221
+ word-wrap: break-word;
222
+ word-break: break-word;
223
+ text-align: center;
224
+ vertical-align: bottom;
225
+ padding: 8px 4px;
226
+ background: #495057;
227
+ cursor: default;
228
+ }
229
+
230
+ .metric-header {
231
+ font-size: 10px;
232
+ line-height: 1.1;
233
+ text-align: center;
234
+ vertical-align: bottom;
235
+ padding: 6px 3px;
236
+ background: #6c757d;
237
+ position: sticky;
238
+ top: 40px;
239
+ z-index: 9;
240
+ cursor: pointer;
241
+ transition: background-color 0.2s;
242
+ user-select: none;
243
+ }
244
+
245
+ .metric-header:hover {
246
+ background: #5a6268;
247
+ }
248
+
249
+ .metric-header.sorted {
250
+ background: #007bff;
251
+ font-weight: bold;
252
+ }
253
+
254
+ .nar-metric {
255
+ background: #856404 !important;
256
+ color: #fff3cd;
257
+ }
258
+
259
+ .nar-metric:hover {
260
+ background: #975a16 !important;
261
+ }
262
+
263
+ .nar-metric.sorted {
264
+ background: #6c5100 !important;
265
+ }
266
+
267
+ .sort-indicator {
268
+ font-size: 12px;
269
+ margin-left: 3px;
270
+ color: #28a745;
271
+ }
272
+
273
+ .sort-arrow {
274
+ font-size: 10px;
275
+ margin-left: 2px;
276
+ color: #ffc107;
277
+ }
278
+
279
+ td {
280
+ padding: 6px;
281
+ border-bottom: 1px solid #dee2e6;
282
+ border-right: 1px solid #dee2e6;
283
+ text-align: center;
284
+ font-family: 'Monaco', 'Menlo', monospace;
285
+ font-size: 11px;
286
+ }
287
+
288
+ td:first-child {
289
+ text-align: left;
290
+ font-family: inherit;
291
+ font-weight: 600;
292
+ background: #f8f9fa;
293
+ position: sticky;
294
+ left: 0;
295
+ z-index: 5;
296
+ padding: 8px 12px;
297
+ font-size: 13px;
298
+ white-space: nowrap;
299
+ }
300
+
301
+ .score.na {
302
+ color: #6c757d;
303
+ font-style: italic;
304
+ }
305
+
306
+ .stats {
307
+ padding: 15px 30px;
308
+ background: #e9ecef;
309
+ font-size: 14px;
310
+ color: #495057;
311
+ }
312
+
313
+ .task-name {
314
+ display: block;
315
+ margin-bottom: 4px;
316
+ font-weight: 600;
317
+ }
318
+
319
+ .metric-indicator {
320
+ font-size: 9px;
321
+ color: #ffc107;
322
+ display: block;
323
+ margin-top: 2px;
324
+ font-weight: normal;
325
+ }
326
+
327
+ .nar-indicator {
328
+ color: #fff3cd;
329
+ font-weight: bold;
330
+ }
331
+ </style>
332
+ </head>
333
+ <body>
334
+ <!-- Menu Container -->
335
+ <div class="menu-container">
336
+ <div class="menu-icon" id="menuIcon">☰</div>
337
+ <div class="menu-list" id="menuList">
338
+ <a href="checker.html">Checker</a>
339
+ <a href="https://github.com/dynamic-superb/dynamic-superb" target="_blank">GitHub Repository</a>
340
+ <a href="https://arxiv.org/abs/2411.05361" target="_blank">Paper on arXiv</a>
341
+ </div>
342
+ </div>
343
+
344
+ <div class="container">
345
+ <div class="header">
346
+ <h1>Dynamic-SUPERB Leaderboard V3</h1>
347
+ <p>Transposed View with Sorting - Click metric headers to sort</p>
348
+ </div>
349
+
350
+ <div class="controls">
351
+ <div class="filter-row">
352
+ <div class="form-group">
353
+ <label for="level1Filter">Level 1 (Top-level):</label>
354
+ <select id="level1Filter">
355
+ <option value="">All Categories</option>
356
+ </select>
357
+ </div>
358
+ <div class="form-group">
359
+ <label for="level2Filter">Level 2:</label>
360
+ <select id="level2Filter" disabled>
361
+ <option value="">Select Level 1 first</option>
362
+ </select>
363
+ </div>
364
+ <div class="form-group">
365
+ <label for="level3Filter">Level 3:</label>
366
+ <select id="level3Filter" disabled>
367
+ <option value="">Select Level 2 first</option>
368
+ </select>
369
+ </div>
370
+ <div class="form-group">
371
+ <label for="level4Filter">Level 4:</label>
372
+ <select id="level4Filter" disabled>
373
+ <option value="">Select Level 3 first</option>
374
+ </select>
375
+ </div>
376
+ <div class="form-group">
377
+ <button class="clear-filters" onclick="clearAllFilters()">Clear All</button>
378
+ </div>
379
+ </div>
380
+ </div>
381
+
382
+ <div class="stats">
383
+ <span id="taskCount">Loading...</span>
384
+ </div>
385
+
386
+ <div class="table-container">
387
+ <table id="leaderboard">
388
+ <thead id="tableHead">
389
+ <!-- Headers will be populated dynamically -->
390
+ </thead>
391
+ <tbody id="tableBody">
392
+ </tbody>
393
+ </table>
394
+ </div>
395
+ </div>
396
+
397
+ <script>
398
+ // Menu functionality
399
+ const menuIcon = document.getElementById('menuIcon');
400
+ const menuList = document.getElementById('menuList');
401
+
402
+ menuIcon.addEventListener('click', (e) => {
403
+ e.stopPropagation();
404
+ menuList.classList.toggle('show');
405
+ });
406
+
407
+ // Close menu if clicked outside
408
+ document.addEventListener('click', (e) => {
409
+ if (!menuIcon.contains(e.target) && !menuList.contains(e.target)) {
410
+ menuList.classList.remove('show');
411
+ }
412
+ });
413
+
414
+ // Utility functions
415
+ function cleanTaxonomy(tax) {
416
+ return tax.trim().replace(/^"|"$/g, '');
417
+ }
418
+
419
+ function parseCSVLine(line) {
420
+ const result = [];
421
+ let current = '';
422
+ let inQuotes = false;
423
+
424
+ for (let i = 0; i < line.length; i++) {
425
+ const char = line[i];
426
+
427
+ if (char === '"') {
428
+ inQuotes = !inQuotes;
429
+ } else if (char === ',' && !inQuotes) {
430
+ result.push(current.trim());
431
+ current = '';
432
+ } else {
433
+ current += char;
434
+ }
435
+ }
436
+
437
+ result.push(current.trim());
438
+ return result;
439
+ }
440
+
441
+ function parseTaxonomyPath(taxonomyPath) {
442
+ const cleaned = cleanTaxonomy(taxonomyPath);
443
+ const parts = cleaned.split('/').map(part => part.trim());
444
+ return {
445
+ level1: parts[0] || '',
446
+ level2: parts[1] || '',
447
+ level3: parts[2] || '',
448
+ level4: parts[3] || '',
449
+ fullPath: cleaned,
450
+ levels: parts.length
451
+ };
452
+ }
453
+
454
+ function parseScore(scoreStr) {
455
+ if (!scoreStr || scoreStr === '-' || scoreStr === '') return null;
456
+ const cleaned = scoreStr.replace(/[%$,]/g, '');
457
+ const parsed = parseFloat(cleaned);
458
+ return isNaN(parsed) ? null : parsed;
459
+ }
460
+
461
+ function clearAllFilters() {
462
+ document.getElementById('level1Filter').value = '';
463
+ document.getElementById('level2Filter').value = '';
464
+ document.getElementById('level3Filter').value = '';
465
+ document.getElementById('level4Filter').value = '';
466
+ document.getElementById('level2Filter').disabled = true;
467
+ document.getElementById('level3Filter').disabled = true;
468
+ document.getElementById('level4Filter').disabled = true;
469
+
470
+ document.getElementById('level2Filter').innerHTML = '<option value="">Select Level 1 first</option>';
471
+ document.getElementById('level3Filter').innerHTML = '<option value="">Select Level 2 first</option>';
472
+ document.getElementById('level4Filter').innerHTML = '<option value="">Select Level 3 first</option>';
473
+
474
+ // FIXED: Reset the internal filter state
475
+ if (leaderboard) {
476
+ leaderboard.currentFilters = { level1: '', level2: '', level3: '', level4: '' };
477
+ }
478
+
479
+ const headers = document.querySelectorAll('[data-taxonomy]');
480
+ headers.forEach(header => header.style.display = '');
481
+ const cells = document.querySelectorAll('td[data-taxonomy]');
482
+ cells.forEach(cell => cell.style.display = '');
483
+
484
+ // FIXED: Re-render table body to ensure consistency
485
+ if (leaderboard) {
486
+ leaderboard.renderTableBody();
487
+ leaderboard.updateStats();
488
+ }
489
+ }
490
+
491
+ class SortableDynamicSUPERBLeaderboard {
492
+ constructor() {
493
+ this.data = [];
494
+ this.models = [];
495
+ this.tasks = {};
496
+ this.taskMetrics = {};
497
+ this.higherBetterMap = {};
498
+ this.currentSort = { taskName: null, metric: null, direction: 'desc' };
499
+ this.currentFilters = { level1: '', level2: '', level3: '', level4: '' };
500
+ this.taxonomyHierarchy = {
501
+ level1: new Set(),
502
+ level2: new Map(),
503
+ level3: new Map(),
504
+ level4: new Map()
505
+ };
506
+ this.init();
507
+ }
508
+
509
+ async init() {
510
+ try {
511
+ await this.loadData();
512
+ this.processData();
513
+ this.buildTaxonomyHierarchy();
514
+ this.buildTransposedTable();
515
+ this.setupFilters();
516
+ this.setupSorting();
517
+ } catch (error) {
518
+ console.error('Error initializing leaderboard:', error);
519
+ document.getElementById('tableBody').innerHTML = '<tr><td colspan="100%">Error loading data. Please check the CSV file.</td></tr>';
520
+ }
521
+ }
522
+
523
+ async loadData() {
524
+ const response = await fetch('data.csv');
525
+ const csvText = await response.text();
526
+ const lines = csvText.trim().split('\n');
527
+ const headers = parseCSVLine(lines[0]);
528
+
529
+ // Find HigherBetter and Taxonomy column indices
530
+ const higherBetterIndex = headers.findIndex(h => h.trim().toLowerCase() === 'higherbetter');
531
+ const taxonomyIndex = headers.findIndex(h => h.trim().toLowerCase() === 'taxonomy');
532
+
533
+ if (higherBetterIndex === -1) {
534
+ console.error('HigherBetter column not found');
535
+ return;
536
+ }
537
+
538
+ // Models are between HigherBetter and Taxonomy
539
+ const startIndex = higherBetterIndex + 1;
540
+ const endIndex = taxonomyIndex !== -1 ? taxonomyIndex : headers.length - 1;
541
+ this.models = headers.slice(startIndex, endIndex);
542
+
543
+ for (let i = 1; i < lines.length; i++) {
544
+ const row = parseCSVLine(lines[i]);
545
+ if (row.length < headers.length) continue;
546
+
547
+ const taskName = row[0];
548
+ const metric = row[1];
549
+ const taxonomy = cleanTaxonomy(row[row.length - 1]);
550
+
551
+ if (metric === 'X') continue;
552
+
553
+ const scores = {};
554
+ for (let j = 0; j < this.models.length; j++) {
555
+ scores[this.models[j]] = row[startIndex + j];
556
+ }
557
+
558
+ // Parse HigherBetter as string
559
+ const higherBetterValue = row[higherBetterIndex].trim().toUpperCase();
560
+ const higherBetter = higherBetterValue === 'TRUE';
561
+
562
+ const parsedTaxonomy = parseTaxonomyPath(taxonomy);
563
+
564
+ this.data.push({
565
+ taskName,
566
+ metric,
567
+ scores,
568
+ taxonomy,
569
+ parsedTaxonomy,
570
+ higherBetter
571
+ });
572
+
573
+ this.higherBetterMap[`${taskName}|${metric}`] = higherBetter;
574
+
575
+ if (!this.tasks[taskName]) {
576
+ this.tasks[taskName] = {
577
+ taxonomy,
578
+ parsedTaxonomy,
579
+ metrics: []
580
+ };
581
+ this.taskMetrics[taskName] = [];
582
+ }
583
+ this.tasks[taskName].metrics.push({ metric, scores, higherBetter });
584
+ this.taskMetrics[taskName].push(metric);
585
+ }
586
+ }
587
+
588
+ processData() {
589
+ Object.values(this.tasks).forEach(task => {
590
+ task.metrics.sort((a, b) => {
591
+ if (a.metric.includes('NAR')) return -1;
592
+ if (b.metric.includes('NAR')) return 1;
593
+ return a.metric.localeCompare(b.metric);
594
+ });
595
+ });
596
+
597
+ Object.keys(this.taskMetrics).forEach(taskName => {
598
+ this.taskMetrics[taskName].sort((a, b) => {
599
+ if (a.includes('NAR')) return -1;
600
+ if (b.includes('NAR')) return 1;
601
+ return a.localeCompare(b);
602
+ });
603
+ });
604
+ }
605
+
606
+ buildTaxonomyHierarchy() {
607
+ Object.values(this.tasks).forEach(task => {
608
+ const parsed = task.parsedTaxonomy;
609
+ if (parsed.level1) {
610
+ this.taxonomyHierarchy.level1.add(parsed.level1);
611
+ if (parsed.level2) {
612
+ if (!this.taxonomyHierarchy.level2.has(parsed.level1)) {
613
+ this.taxonomyHierarchy.level2.set(parsed.level1, new Set());
614
+ }
615
+ this.taxonomyHierarchy.level2.get(parsed.level1).add(parsed.level2);
616
+ if (parsed.level3) {
617
+ const key3 = parsed.level1 + '/' + parsed.level2;
618
+ if (!this.taxonomyHierarchy.level3.has(key3)) {
619
+ this.taxonomyHierarchy.level3.set(key3, new Set());
620
+ }
621
+ this.taxonomyHierarchy.level3.get(key3).add(parsed.level3);
622
+ if (parsed.level4) {
623
+ const key4 = key3 + '/' + parsed.level3;
624
+ if (!this.taxonomyHierarchy.level4.has(key4)) {
625
+ this.taxonomyHierarchy.level4.set(key4, new Set());
626
+ }
627
+ this.taxonomyHierarchy.level4.get(key4).add(parsed.level4);
628
+ }
629
+ }
630
+ }
631
+ }
632
+ });
633
+ }
634
+
635
+ // Check if a task should be visible based on current filters
636
+ isTaskVisible(taskData) {
637
+ const { level1, level2, level3, level4 } = this.currentFilters;
638
+ const parsed = taskData.parsedTaxonomy;
639
+
640
+ if (level1 && parsed.level1 !== level1) return false;
641
+ if (level2 && parsed.level2 !== level2) return false;
642
+ if (level3 && parsed.level3 !== level3) return false;
643
+ if (level4 && parsed.level4 !== level4) return false;
644
+
645
+ return true;
646
+ }
647
+
648
+ buildTransposedTable() {
649
+ const thead = document.getElementById('tableHead');
650
+
651
+ let taskHeaderHTML = '<th rowspan="2">Model</th>';
652
+ let metricHeaderHTML = '';
653
+
654
+ Object.entries(this.tasks).forEach(([taskName, taskData]) => {
655
+ const taxonomyAttrs = this.buildTaxonomyDataAttributes(taskData.parsedTaxonomy);
656
+ const metrics = this.taskMetrics[taskName];
657
+ const hasNAR = metrics.some(m => m.includes('NAR'));
658
+
659
+ taskHeaderHTML += `
660
+ <th class="task-header" colspan="${metrics.length}" ${taxonomyAttrs}>
661
+ <span class="task-name">${taskName}</span>
662
+ <span class="metric-indicator ${hasNAR ? 'nar-indicator' : ''}">
663
+ ${metrics.length} metrics${hasNAR ? ' (NAR)' : ''}
664
+ </span>
665
+ </th>
666
+ `;
667
+
668
+ metrics.forEach(metric => {
669
+ const metricClass = metric.includes('NAR') ? 'nar-metric' : '';
670
+ const higherBetter = this.higherBetterMap[`${taskName}|${metric}`];
671
+ const sortIndicator = higherBetter ? '↑' : '↓';
672
+
673
+ metricHeaderHTML += `
674
+ <th class="metric-header ${metricClass}"
675
+ data-task="${taskName}"
676
+ data-metric="${metric}"
677
+ ${taxonomyAttrs}>
678
+ ${metric}
679
+ <span class="sort-indicator">${sortIndicator}</span>
680
+ <span class="sort-arrow" id="arrow-${taskName}-${metric}"></span>
681
+ </th>
682
+ `;
683
+ });
684
+ });
685
+
686
+ thead.innerHTML = `
687
+ <tr>${taskHeaderHTML}</tr>
688
+ <tr>${metricHeaderHTML}</tr>
689
+ `;
690
+
691
+ this.renderTableBody();
692
+ this.updateStats();
693
+ }
694
+
695
+ renderTableBody() {
696
+ const tbody = document.getElementById('tableBody');
697
+
698
+ let sortedModels = [...this.models];
699
+ if (this.currentSort.taskName && this.currentSort.metric) {
700
+ sortedModels = this.sortModels(this.currentSort.taskName, this.currentSort.metric, this.currentSort.direction);
701
+ }
702
+
703
+ let tableHTML = '';
704
+ sortedModels.forEach(modelName => {
705
+ tableHTML += `<tr><td>${modelName}</td>`;
706
+
707
+ Object.entries(this.tasks).forEach(([taskName, taskData]) => {
708
+ const taxonomyAttrs = this.buildTaxonomyDataAttributes(taskData.parsedTaxonomy);
709
+ const metrics = this.taskMetrics[taskName];
710
+
711
+ metrics.forEach(metric => {
712
+ const metricData = taskData.metrics.find(m => m.metric === metric);
713
+ let score = '-';
714
+ let cellClass = 'na';
715
+
716
+ if (metricData) {
717
+ score = metricData.scores[modelName];
718
+ cellClass = this.getScoreClass(score);
719
+ }
720
+
721
+ // Apply the same visibility logic as the headers
722
+ const isVisible = this.isTaskVisible(taskData);
723
+ const displayStyle = isVisible ? '' : 'none';
724
+
725
+ tableHTML += `
726
+ <td class="score ${cellClass}" ${taxonomyAttrs} style="display: ${displayStyle};">
727
+ ${this.formatScore(score)}
728
+ </td>
729
+ `;
730
+ });
731
+ });
732
+
733
+ tableHTML += '</tr>';
734
+ });
735
+
736
+ tbody.innerHTML = tableHTML;
737
+ }
738
+
739
+ sortModels(taskName, metric, direction) {
740
+ const taskData = this.tasks[taskName];
741
+ const metricData = taskData.metrics.find(m => m.metric === metric);
742
+
743
+ if (!metricData) return [...this.models];
744
+
745
+ const higherBetter = this.higherBetterMap[`${taskName}|${metric}`];
746
+
747
+ return [...this.models].sort((modelA, modelB) => {
748
+ const scoreA = parseScore(metricData.scores[modelA]);
749
+ const scoreB = parseScore(metricData.scores[modelB]);
750
+
751
+ if (scoreA === null && scoreB === null) return 0;
752
+ if (scoreA === null) return 1;
753
+ if (scoreB === null) return -1;
754
+
755
+ let comparison = scoreA - scoreB;
756
+
757
+ // Adjust for higherBetter preference
758
+ if (!higherBetter) {
759
+ comparison = -comparison;
760
+ }
761
+
762
+ // Apply sort direction
763
+ if (direction === 'asc') {
764
+ comparison = -comparison;
765
+ }
766
+
767
+ return comparison;
768
+ });
769
+ }
770
+
771
+ setupSorting() {
772
+ document.addEventListener('click', (e) => {
773
+ if (e.target.classList.contains('metric-header') || e.target.closest('.metric-header')) {
774
+ const header = e.target.classList.contains('metric-header') ? e.target : e.target.closest('.metric-header');
775
+ const taskName = header.dataset.task;
776
+ const metric = header.dataset.metric;
777
+
778
+ if (!taskName || !metric) return;
779
+
780
+ // Toggle sort direction if clicking the same column
781
+ let direction = 'desc';
782
+ if (this.currentSort.taskName === taskName && this.currentSort.metric === metric) {
783
+ direction = this.currentSort.direction === 'desc' ? 'asc' : 'desc';
784
+ }
785
+
786
+ this.currentSort = { taskName, metric, direction };
787
+
788
+ // Update visual indicators
789
+ document.querySelectorAll('.metric-header').forEach(h => {
790
+ h.classList.remove('sorted');
791
+ });
792
+ document.querySelectorAll('.sort-arrow').forEach(arrow => {
793
+ arrow.textContent = '';
794
+ });
795
+
796
+ header.classList.add('sorted');
797
+ const arrowElement = document.getElementById(`arrow-${taskName}-${metric}`);
798
+ if (arrowElement) {
799
+ arrowElement.textContent = direction === 'desc' ? '▼' : '▲';
800
+ }
801
+
802
+ this.renderTableBody();
803
+ }
804
+ });
805
+ }
806
+
807
+ buildTaxonomyDataAttributes(parsed) {
808
+ return `
809
+ data-taxonomy="${parsed.fullPath}"
810
+ data-level1="${parsed.level1}"
811
+ data-level2="${parsed.level2}"
812
+ data-level3="${parsed.level3}"
813
+ data-level4="${parsed.level4}"
814
+ `;
815
+ }
816
+
817
+ getScoreClass(score) {
818
+ if (!score || score === '-' || score === '') return 'na';
819
+ return '';
820
+ }
821
+
822
+ formatScore(score) {
823
+ if (!score || score === '-' || score === '') return '-';
824
+ return score;
825
+ }
826
+
827
+ setupFilters() {
828
+ const level1Filter = document.getElementById('level1Filter');
829
+ const level2Filter = document.getElementById('level2Filter');
830
+ const level3Filter = document.getElementById('level3Filter');
831
+ const level4Filter = document.getElementById('level4Filter');
832
+
833
+ const sortedLevel1 = Array.from(this.taxonomyHierarchy.level1).sort();
834
+ sortedLevel1.forEach(item => {
835
+ const option = document.createElement('option');
836
+ option.value = item;
837
+ option.textContent = item;
838
+ level1Filter.appendChild(option);
839
+ });
840
+
841
+ level1Filter.addEventListener('change', (e) => {
842
+ const selected = e.target.value;
843
+ this.currentFilters.level1 = selected;
844
+ level2Filter.innerHTML = '<option value="">All Sub-categories</option>';
845
+ level3Filter.innerHTML = '<option value="">Select Level 2 first</option>';
846
+ level4Filter.innerHTML = '<option value="">Select Level 3 first</option>';
847
+ level2Filter.disabled = !selected;
848
+ level3Filter.disabled = true;
849
+ level4Filter.disabled = true;
850
+ level2Filter.value = '';
851
+ level3Filter.value = '';
852
+ level4Filter.value = '';
853
+ this.currentFilters.level2 = '';
854
+ this.currentFilters.level3 = '';
855
+ this.currentFilters.level4 = '';
856
+
857
+ if (selected && this.taxonomyHierarchy.level2.has(selected)) {
858
+ const level2Options = Array.from(this.taxonomyHierarchy.level2.get(selected)).sort();
859
+ level2Options.forEach(item => {
860
+ const option = document.createElement('option');
861
+ option.value = item;
862
+ option.textContent = item;
863
+ level2Filter.appendChild(option);
864
+ });
865
+ }
866
+ this.applyFilters();
867
+ });
868
+
869
+ level2Filter.addEventListener('change', (e) => {
870
+ const level1Value = level1Filter.value;
871
+ const level2Value = e.target.value;
872
+ this.currentFilters.level2 = level2Value;
873
+ level3Filter.innerHTML = '<option value="">All Sub-categories</option>';
874
+ level4Filter.innerHTML = '<option value="">Select Level 3 first</option>';
875
+ level3Filter.disabled = !level2Value;
876
+ level4Filter.disabled = true;
877
+ level3Filter.value = '';
878
+ level4Filter.value = '';
879
+ this.currentFilters.level3 = '';
880
+ this.currentFilters.level4 = '';
881
+
882
+ if (level1Value && level2Value) {
883
+ const level2Key = `${level1Value}/${level2Value}`;
884
+ if (this.taxonomyHierarchy.level3.has(level2Key)) {
885
+ const level3Options = Array.from(this.taxonomyHierarchy.level3.get(level2Key)).sort();
886
+ level3Options.forEach(item => {
887
+ const option = document.createElement('option');
888
+ option.value = item;
889
+ option.textContent = item;
890
+ level3Filter.appendChild(option);
891
+ });
892
+ }
893
+ }
894
+ this.applyFilters();
895
+ });
896
+
897
+ level3Filter.addEventListener('change', (e) => {
898
+ const level1Value = level1Filter.value;
899
+ const level2Value = level2Filter.value;
900
+ const level3Value = e.target.value;
901
+ this.currentFilters.level3 = level3Value;
902
+ level4Filter.innerHTML = '<option value="">All Sub-categories</option>';
903
+ level4Filter.disabled = !level3Value;
904
+ level4Filter.value = '';
905
+ this.currentFilters.level4 = '';
906
+
907
+ if (level1Value && level2Value && level3Value) {
908
+ const level3Key = `${level1Value}/${level2Value}/${level3Value}`;
909
+ if (this.taxonomyHierarchy.level4.has(level3Key)) {
910
+ const level4Options = Array.from(this.taxonomyHierarchy.level4.get(level3Key)).sort();
911
+ level4Options.forEach(item => {
912
+ const option = document.createElement('option');
913
+ option.value = item;
914
+ option.textContent = item;
915
+ level4Filter.appendChild(option);
916
+ });
917
+ }
918
+ }
919
+ this.applyFilters();
920
+ });
921
+
922
+ level4Filter.addEventListener('change', (e) => {
923
+ this.currentFilters.level4 = e.target.value;
924
+ this.applyFilters();
925
+ });
926
+ }
927
+
928
+ applyFilters() {
929
+ const level1Value = document.getElementById('level1Filter').value;
930
+ const level2Value = document.getElementById('level2Filter').value;
931
+ const level3Value = document.getElementById('level3Filter').value;
932
+ const level4Value = document.getElementById('level4Filter').value;
933
+
934
+ // Update current filters
935
+ this.currentFilters = { level1: level1Value, level2: level2Value, level3: level3Value, level4: level4Value };
936
+
937
+ const taskHeaders = document.querySelectorAll('.task-header[data-taxonomy]');
938
+ const metricHeaders = document.querySelectorAll('.metric-header[data-taxonomy]');
939
+ let visibleTasks = 0;
940
+
941
+ taskHeaders.forEach((header, index) => {
942
+ const headerLevel1 = header.dataset.level1;
943
+ const headerLevel2 = header.dataset.level2;
944
+ const headerLevel3 = header.dataset.level3;
945
+ const headerLevel4 = header.dataset.level4;
946
+
947
+ let show = true;
948
+ if (level1Value && headerLevel1 !== level1Value) show = false;
949
+ if (level2Value && headerLevel2 !== level2Value) show = false;
950
+ if (level3Value && headerLevel3 !== level3Value) show = false;
951
+ if (level4Value && headerLevel4 !== level4Value) show = false;
952
+
953
+ header.style.display = show ? '' : 'none';
954
+ if (show) visibleTasks++;
955
+ });
956
+
957
+ metricHeaders.forEach((header, index) => {
958
+ const headerLevel1 = header.dataset.level1;
959
+ const headerLevel2 = header.dataset.level2;
960
+ const headerLevel3 = header.dataset.level3;
961
+ const headerLevel4 = header.dataset.level4;
962
+
963
+ let show = true;
964
+ if (level1Value && headerLevel1 !== level1Value) show = false;
965
+ if (level2Value && headerLevel2 !== level2Value) show = false;
966
+ if (level3Value && headerLevel3 !== level3Value) show = false;
967
+ if (level4Value && headerLevel4 !== level4Value) show = false;
968
+
969
+ header.style.display = show ? '' : 'none';
970
+
971
+ const columnIndex = index + 2;
972
+ const cells = document.querySelectorAll(`td:nth-child(${columnIndex})`);
973
+ cells.forEach(cell => {
974
+ cell.style.display = show ? '' : 'none';
975
+ });
976
+ });
977
+
978
+ // Re-render table body to respect filter state in data cells
979
+ this.renderTableBody();
980
+
981
+ this.updateStats(visibleTasks);
982
+ }
983
+
984
+ updateStats(visibleTasks = null) {
985
+ const totalTasks = Object.keys(this.tasks).length;
986
+ const totalMetrics = this.data.length;
987
+ const visible = visibleTasks !== null ? visibleTasks : totalTasks;
988
+
989
+ document.getElementById('taskCount').textContent =
990
+ `Showing ${visible} of ${totalTasks} tasks (${totalMetrics} total metrics) across ${this.models.length} models`;
991
+ }
992
+ }
993
+
994
+ let leaderboard;
995
+
996
+ document.addEventListener('DOMContentLoaded', () => {
997
+ leaderboard = new SortableDynamicSUPERBLeaderboard();
998
+ });
999
+ </script>
1000
+ </body>
1001
  </html>