Spaces:
Running
Running
Download app.js from Metric-AI/ArmBench-ASR: direct link, hf CLI and curl.
- Browser
- Download file 31 kB
-
https://huggingface.co/spaces/Metric-AI/ArmBench-ASR/resolve/main/app.js
- Command line
-
hf download hf://spaces/Metric-AI/ArmBench-ASR/app.js
-
curl -L -o app.js https://huggingface.co/spaces/Metric-AI/ArmBench-ASR/resolve/main/app.js
31 kB
| const DATASET_LABELS = { | |
| combined: "Combined", | |
| mcv26: "Common Voice 26", | |
| fleurs: "FLEURS", | |
| poems: "Poems", | |
| movies: "Movies", | |
| infocom_may_26: "News", | |
| }; | |
| const DEFAULT_METRICS = ["WER-NN", "CER-NN", "WER-norm-NN", "CER-norm-NN"]; | |
| const METRIC_PATTERN = /^(?:WER|CER)(?:-norm)?(?:-(?:NN|LLM))?$/; | |
| const METRIC_LABELS = { | |
| "WER-NN": "WER", | |
| "CER-NN": "CER", | |
| "WER-norm-NN": "WER-norm", | |
| "CER-norm-NN": "CER-norm", | |
| "WER-LLM": "WER-LLM", | |
| "CER-LLM": "CER-LLM", | |
| }; | |
| const DEFAULT_VERSION = "v1.0"; | |
| const state = { | |
| records: [], | |
| datasets: [], | |
| metrics: DEFAULT_METRICS, | |
| columns: new Map(), | |
| view: "matrix", | |
| sortKey: "combined|WER-NN", | |
| sortDirection: "asc", | |
| colorScores: false, | |
| availability: new Set(["Open", "Closed"]), | |
| availabilityMenuOpen: false, | |
| chartDataset: "combined", | |
| chartMetric: DEFAULT_METRICS[0], | |
| chartAvailability: "All", | |
| version: DEFAULT_VERSION, | |
| versionedResults: {}, | |
| }; | |
| function setTheme(theme, persist = true) { | |
| document.documentElement.dataset.theme = theme; | |
| const toggle = document.querySelector("#theme-toggle"); | |
| if (toggle) { | |
| const nextTheme = theme === "dark" ? "light" : "dark"; | |
| toggle.setAttribute("aria-label", `Switch to ${nextTheme} mode`); | |
| toggle.title = `Switch to ${nextTheme} mode`; | |
| } | |
| if (persist) { | |
| try { | |
| localStorage.setItem("arm-asr-theme", theme); | |
| } catch (_) {} | |
| } | |
| } | |
| function metricLabel(metric) { | |
| return METRIC_LABELS[metric] || metric; | |
| } | |
| function parseCsv(text) { | |
| const rows = []; | |
| let row = []; | |
| let field = ""; | |
| let quoted = false; | |
| for (let index = 0; index < text.length; index += 1) { | |
| const char = text[index]; | |
| if (quoted) { | |
| if (char === '"' && text[index + 1] === '"') { | |
| field += '"'; | |
| index += 1; | |
| } else if (char === '"') { | |
| quoted = false; | |
| } else { | |
| field += char; | |
| } | |
| } else if (char === '"') { | |
| quoted = true; | |
| } else if (char === ",") { | |
| row.push(field); | |
| field = ""; | |
| } else if (char === "\n") { | |
| row.push(field.replace(/\r$/, "")); | |
| if (row.some((value) => value !== "")) { | |
| rows.push(row); | |
| } | |
| row = []; | |
| field = ""; | |
| } else { | |
| field += char; | |
| } | |
| } | |
| if (field || row.length) { | |
| row.push(field.replace(/\r$/, "")); | |
| rows.push(row); | |
| } | |
| return rows; | |
| } | |
| function ingest(rows) { | |
| const [sections, metrics] = rows.slice(0, 2); | |
| const modelIndex = sections.indexOf("model"); | |
| const urlIndex = sections.indexOf("model_url"); | |
| const availabilityIndex = urlIndex + 1; | |
| const originIndex = sections.indexOf("origin"); | |
| let currentDataset = ""; | |
| state.records = []; | |
| state.datasets = []; | |
| state.columns = new Map(); | |
| state.metrics = []; | |
| for (let index = availabilityIndex + 1; index < sections.length; index += 1) { | |
| if (sections[index]) { | |
| currentDataset = sections[index]; | |
| } | |
| if (currentDataset && METRIC_PATTERN.test(metrics[index])) { | |
| state.columns.set( | |
| `${currentDataset}|${metrics[index]}`, | |
| index | |
| ); | |
| if (!state.datasets.includes(currentDataset)) { | |
| state.datasets.push(currentDataset); | |
| } | |
| if (!state.metrics.includes(metrics[index])) { | |
| state.metrics.push(metrics[index]); | |
| } | |
| } | |
| } | |
| state.records = rows.slice(2).map((row) => { | |
| const values = {}; | |
| for (const [key, index] of state.columns) { | |
| values[key] = | |
| row[index] === "" || row[index] === undefined | |
| ? null | |
| : Number(row[index]); | |
| } | |
| return { | |
| model: row[modelIndex], | |
| url: row[urlIndex], | |
| availability: row[availabilityIndex], | |
| origin: originIndex >= 0 ? row[originIndex] : "", | |
| values, | |
| }; | |
| }); | |
| } | |
| function currentDataset() { | |
| return ( | |
| document.querySelector('input[name="dataset"]:checked')?.value || | |
| state.datasets[0] | |
| ); | |
| } | |
| function currentMatrixMetric() { | |
| return ( | |
| document.querySelector('input[name="matrix-metric"]:checked')?.value || | |
| state.metrics[0] | |
| ); | |
| } | |
| function rankRecords(records, dataset, metric = state.metrics[0]) { | |
| const rankKey = `${dataset}|${metric}`; | |
| const ranked = [...records].sort((left, right) => { | |
| const a = left.values[rankKey]; | |
| const b = right.values[rankKey]; | |
| if (a === null) return 1; | |
| if (b === null) return -1; | |
| return a - b || left.model.localeCompare(right.model); | |
| }); | |
| return new Map( | |
| ranked.map((record, index) => [ | |
| record.model, | |
| index + 1, | |
| ]) | |
| ); | |
| } | |
| function makeCell(tag, text, className = "") { | |
| const cell = document.createElement(tag); | |
| cell.textContent = text; | |
| if (className) { | |
| cell.className = className; | |
| } | |
| return cell; | |
| } | |
| function matrixRanges(columns) { | |
| const familyValues = { | |
| wer: [], | |
| cer: [], | |
| }; | |
| columns.forEach((column) => { | |
| const metric = column.key.split("|")[1]; | |
| const family = metric.startsWith("CER") ? "cer" : "wer"; | |
| state.records.forEach((record) => { | |
| const value = record.values[column.key]; | |
| if (value !== null) { | |
| familyValues[family].push(value); | |
| } | |
| }); | |
| }); | |
| const familyRanges = Object.fromEntries( | |
| Object.entries(familyValues).map(([family, values]) => [ | |
| family, | |
| values.length | |
| ? { | |
| min: Math.min(...values), | |
| max: Math.max(...values), | |
| } | |
| : null, | |
| ]) | |
| ); | |
| return new Map( | |
| columns.map((column) => { | |
| const metric = column.key.split("|")[1]; | |
| const family = metric.startsWith("CER") ? "cer" : "wer"; | |
| return [ | |
| column.key, | |
| familyRanges[family], | |
| ]; | |
| }) | |
| ); | |
| } | |
| function heatClass(value, range) { | |
| if ( | |
| value === null || | |
| !range || | |
| range.max === range.min | |
| ) { | |
| return ""; | |
| } | |
| const ratio = | |
| (value - range.min) / | |
| (range.max - range.min); | |
| return ` heat-score-${Math.min( | |
| 5, | |
| Math.floor(ratio * 5) + 1 | |
| )}`; | |
| } | |
| function renderComparisonChart() { | |
| const container = | |
| document.querySelector("#comparison-bars"); | |
| const empty = | |
| document.querySelector("#comparison-chart-empty"); | |
| if (!container || !empty) { | |
| return; | |
| } | |
| const key = | |
| `${state.chartDataset}|${state.chartMetric}`; | |
| const records = state.records | |
| .filter((record) => { | |
| return ( | |
| state.chartAvailability === "All" || | |
| record.availability === state.chartAvailability | |
| ); | |
| }) | |
| .map((record) => ({ | |
| model: record.model, | |
| url: record.url, | |
| availability: record.availability, | |
| value: record.values[key], | |
| })) | |
| .filter((record) => { | |
| return ( | |
| record.value !== null && | |
| Number.isFinite(record.value) | |
| ); | |
| }) | |
| .sort((left, right) => { | |
| return ( | |
| left.value - right.value || | |
| left.model.localeCompare(right.model) | |
| ); | |
| }); | |
| empty.hidden = records.length !== 0; | |
| if (!records.length) { | |
| container.replaceChildren(); | |
| return; | |
| } | |
| const maxValue = Math.max( | |
| ...records.map((record) => record.value) | |
| ); | |
| const rows = records.map((record) => { | |
| const row = | |
| document.createElement("div"); | |
| row.className = | |
| "comparison-bar-row"; | |
| const modelWrap = | |
| document.createElement("div"); | |
| modelWrap.className = | |
| "comparison-model"; | |
| const availabilityDot = | |
| document.createElement("span"); | |
| availabilityDot.className = | |
| `comparison-availability-dot ${record.availability.toLowerCase()}`; | |
| availabilityDot.title = | |
| record.availability === "Open" | |
| ? "Open model" | |
| : "Closed model"; | |
| availabilityDot.setAttribute( | |
| "aria-label", | |
| record.availability === "Open" | |
| ? "Open model" | |
| : "Closed model" | |
| ); | |
| modelWrap.append( | |
| availabilityDot | |
| ); | |
| if (record.url) { | |
| const modelLink = | |
| document.createElement("a"); | |
| modelLink.className = | |
| "comparison-model-link"; | |
| modelLink.href = | |
| record.url; | |
| modelLink.target = | |
| "_blank"; | |
| modelLink.rel = | |
| "noreferrer"; | |
| modelLink.title = | |
| `Open ${record.model}`; | |
| modelLink.setAttribute( | |
| "aria-label", | |
| `Open ${record.model} model page` | |
| ); | |
| const modelName = | |
| document.createElement("span"); | |
| modelName.textContent = | |
| record.model; | |
| const icon = | |
| document.createElement("svg"); | |
| icon.setAttribute( | |
| "viewBox", | |
| "0 0 24 24" | |
| ); | |
| icon.setAttribute( | |
| "aria-hidden", | |
| "true" | |
| ); | |
| icon.innerHTML = | |
| '<path d="M14 5h5v5m0-5-8 8M19 14v4a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h4"/>'; | |
| modelLink.append( | |
| modelName, | |
| icon | |
| ); | |
| modelWrap.append( | |
| modelLink | |
| ); | |
| } else { | |
| const modelName = | |
| document.createElement("span"); | |
| modelName.className = | |
| "comparison-model-name"; | |
| modelName.textContent = | |
| record.model; | |
| modelName.title = | |
| record.model; | |
| modelWrap.append( | |
| modelName | |
| ); | |
| } | |
| const track = | |
| document.createElement("div"); | |
| track.className = | |
| "comparison-track"; | |
| track.setAttribute( | |
| "aria-hidden", | |
| "true" | |
| ); | |
| const fill = | |
| document.createElement("div"); | |
| fill.className = | |
| "comparison-fill"; | |
| fill.style.width = `${ | |
| maxValue | |
| ? (record.value / maxValue) * 100 | |
| : 0 | |
| }%`; | |
| track.append(fill); | |
| const value = | |
| document.createElement("div"); | |
| value.className = | |
| "comparison-value"; | |
| value.textContent = | |
| `${record.value.toFixed(2)}%`; | |
| row.setAttribute( | |
| "aria-label", | |
| `${record.model}: ${record.value.toFixed( | |
| 2 | |
| )} percent ${metricLabel(state.chartMetric)}, ${record.availability}` | |
| ); | |
| row.append( | |
| modelWrap, | |
| track, | |
| value | |
| ); | |
| return row; | |
| }); | |
| container.replaceChildren( | |
| ...rows | |
| ); | |
| } | |
| function buildChartControls() { | |
| const datasetSelect = | |
| document.querySelector("#chart-dataset"); | |
| const metricSelect = | |
| document.querySelector("#chart-metric"); | |
| const availabilitySelect = | |
| document.querySelector("#chart-availability"); | |
| if ( | |
| !datasetSelect || | |
| !metricSelect || | |
| !availabilitySelect | |
| ) { | |
| return; | |
| } | |
| state.datasets.forEach((dataset) => { | |
| const option = | |
| document.createElement("option"); | |
| option.value = | |
| dataset; | |
| option.textContent = | |
| DATASET_LABELS[dataset] || | |
| dataset; | |
| datasetSelect.append( | |
| option | |
| ); | |
| }); | |
| state.metrics.forEach((metric) => { | |
| const option = | |
| document.createElement("option"); | |
| option.value = | |
| metric; | |
| option.textContent = | |
| metricLabel(metric); | |
| metricSelect.append( | |
| option | |
| ); | |
| }); | |
| state.chartDataset = | |
| state.datasets.includes("combined") | |
| ? "combined" | |
| : state.datasets[0]; | |
| state.chartMetric = | |
| state.metrics[0]; | |
| state.chartAvailability = | |
| "All"; | |
| datasetSelect.value = | |
| state.chartDataset; | |
| metricSelect.value = | |
| state.chartMetric; | |
| availabilitySelect.value = | |
| state.chartAvailability; | |
| datasetSelect.addEventListener( | |
| "change", | |
| () => { | |
| state.chartDataset = | |
| datasetSelect.value; | |
| renderComparisonChart(); | |
| } | |
| ); | |
| metricSelect.addEventListener( | |
| "change", | |
| () => { | |
| state.chartMetric = | |
| metricSelect.value; | |
| renderComparisonChart(); | |
| } | |
| ); | |
| availabilitySelect.addEventListener( | |
| "change", | |
| () => { | |
| state.chartAvailability = | |
| availabilitySelect.value; | |
| renderComparisonChart(); | |
| } | |
| ); | |
| } | |
| function render() { | |
| const dataset = | |
| currentDataset(); | |
| const matrixMetric = | |
| currentMatrixMetric(); | |
| const isMatrix = | |
| state.view === "matrix"; | |
| const columns = isMatrix | |
| ? state.datasets.map((item) => ({ | |
| label: | |
| DATASET_LABELS[item] || | |
| item, | |
| key: | |
| `${item}|${matrixMetric}`, | |
| })) | |
| : state.metrics.map((metric) => ({ | |
| label: | |
| metricLabel(metric), | |
| key: | |
| `${dataset}|${metric}`, | |
| })); | |
| const table = | |
| document.querySelector( | |
| "#leaderboard-table" | |
| ); | |
| const head = | |
| table.querySelector("thead"); | |
| const body = | |
| table.querySelector("tbody"); | |
| const rankDataset = | |
| isMatrix | |
| ? "combined" | |
| : dataset; | |
| const rankMetric = | |
| isMatrix | |
| ? matrixMetric | |
| : state.metrics[0]; | |
| const ranks = | |
| rankRecords( | |
| state.records, | |
| rankDataset, | |
| rankMetric | |
| ); | |
| const ranges = | |
| state.colorScores | |
| ? matrixRanges(columns) | |
| : new Map(); | |
| let records = | |
| state.records.filter( | |
| (record) => | |
| state.availability.has( | |
| record.availability | |
| ) | |
| ); | |
| if ( | |
| columns.some( | |
| (column) => | |
| column.key === state.sortKey | |
| ) | |
| ) { | |
| const direction = | |
| state.sortDirection === "asc" | |
| ? 1 | |
| : -1; | |
| records = | |
| [...records].sort( | |
| (left, right) => { | |
| const a = | |
| left.values[ | |
| state.sortKey | |
| ]; | |
| const b = | |
| right.values[ | |
| state.sortKey | |
| ]; | |
| if (a === null) return 1; | |
| if (b === null) return -1; | |
| return ( | |
| (a - b) * direction || | |
| left.model.localeCompare( | |
| right.model | |
| ) | |
| ); | |
| } | |
| ); | |
| } | |
| const headerRow = | |
| document.createElement("tr"); | |
| const rankHeader = | |
| makeCell("th", "Rank"); | |
| rankHeader.title = | |
| `Ranked by ${metricLabel(rankMetric)} for ${ | |
| DATASET_LABELS[rankDataset] || | |
| rankDataset | |
| }`; | |
| const availabilityHeader = | |
| document.createElement("th"); | |
| const availabilityControl = | |
| document.createElement("span"); | |
| availabilityControl.className = | |
| "availability-control"; | |
| const availabilityLabel = | |
| document.createElement("button"); | |
| availabilityLabel.type = | |
| "button"; | |
| availabilityLabel.className = | |
| "availability-label"; | |
| availabilityLabel.setAttribute( | |
| "aria-expanded", | |
| String( | |
| state.availabilityMenuOpen | |
| ) | |
| ); | |
| availabilityLabel.setAttribute( | |
| "aria-label", | |
| "Filter models by availability" | |
| ); | |
| const availabilitySummary = | |
| state.availability.size === 2 | |
| ? "All" | |
| : [...state.availability][0]; | |
| availabilityLabel.innerHTML = | |
| `Availability <span>${availabilitySummary}</span><b aria-hidden="true">⌄</b>`; | |
| availabilityLabel.addEventListener( | |
| "click", | |
| (event) => { | |
| event.stopPropagation(); | |
| state.availabilityMenuOpen = | |
| !state.availabilityMenuOpen; | |
| render(); | |
| } | |
| ); | |
| const availabilityFilters = | |
| document.createElement("span"); | |
| availabilityFilters.className = | |
| "availability-filters"; | |
| availabilityFilters.hidden = | |
| !state.availabilityMenuOpen; | |
| availabilityFilters.addEventListener( | |
| "click", | |
| (event) => | |
| event.stopPropagation() | |
| ); | |
| ["Open", "Closed"].forEach( | |
| (value) => { | |
| const button = | |
| document.createElement( | |
| "button" | |
| ); | |
| button.type = | |
| "button"; | |
| button.className = | |
| `availability-filter ${value.toLowerCase()}`; | |
| button.classList.toggle( | |
| "is-active", | |
| state.availability.has( | |
| value | |
| ) | |
| ); | |
| button.setAttribute( | |
| "aria-pressed", | |
| String( | |
| state.availability.has( | |
| value | |
| ) | |
| ) | |
| ); | |
| button.setAttribute( | |
| "aria-label", | |
| `Toggle ${value.toLowerCase()} models` | |
| ); | |
| button.textContent = | |
| value; | |
| button.addEventListener( | |
| "click", | |
| () => { | |
| if ( | |
| state.availability.has( | |
| value | |
| ) && | |
| state.availability.size > 1 | |
| ) { | |
| state.availability.delete( | |
| value | |
| ); | |
| } else { | |
| state.availability.add( | |
| value | |
| ); | |
| } | |
| render(); | |
| } | |
| ); | |
| availabilityFilters.append( | |
| button | |
| ); | |
| } | |
| ); | |
| availabilityControl.append( | |
| availabilityLabel, | |
| availabilityFilters | |
| ); | |
| availabilityHeader.append( | |
| availabilityControl | |
| ); | |
| headerRow.append( | |
| rankHeader, | |
| makeCell("th", "Model"), | |
| availabilityHeader | |
| ); | |
| columns.forEach((column) => { | |
| const header = | |
| document.createElement("th"); | |
| if ( | |
| isMatrix && | |
| column.key.startsWith( | |
| "combined|" | |
| ) | |
| ) { | |
| header.classList.add( | |
| "combined-score" | |
| ); | |
| } | |
| const button = | |
| document.createElement( | |
| "button" | |
| ); | |
| button.type = | |
| "button"; | |
| button.className = | |
| "sort-button"; | |
| if ( | |
| state.sortKey === | |
| column.key | |
| ) { | |
| button.classList.add( | |
| `is-${state.sortDirection}` | |
| ); | |
| } | |
| button.textContent = | |
| column.label; | |
| button.setAttribute( | |
| "aria-label", | |
| `Sort by ${column.label}` | |
| ); | |
| button.addEventListener( | |
| "click", | |
| () => { | |
| if ( | |
| state.sortKey === | |
| column.key | |
| ) { | |
| state.sortDirection = | |
| state.sortDirection === | |
| "asc" | |
| ? "desc" | |
| : "asc"; | |
| } else { | |
| state.sortKey = | |
| column.key; | |
| state.sortDirection = | |
| "asc"; | |
| } | |
| render(); | |
| } | |
| ); | |
| header.append(button); | |
| headerRow.append(header); | |
| }); | |
| head.replaceChildren( | |
| headerRow | |
| ); | |
| const rows = | |
| records.map((record) => { | |
| const row = | |
| document.createElement("tr"); | |
| const modelCell = | |
| document.createElement( | |
| "td" | |
| ); | |
| const modelWrap = | |
| document.createElement( | |
| "div" | |
| ); | |
| modelWrap.className = | |
| "model-cell"; | |
| const model = | |
| document.createElement( | |
| "span" | |
| ); | |
| model.className = | |
| "model-name"; | |
| model.textContent = | |
| record.model; | |
| modelWrap.append( | |
| model | |
| ); | |
| if (record.origin) { | |
| const origin = document.createElement("span"); | |
| origin.className = "model-origin-flag"; | |
| origin.title = "Armenian-origin model"; | |
| origin.setAttribute("aria-label", "Armenian-origin model"); | |
| modelWrap.append(origin); | |
| } | |
| if (record.url) { | |
| const link = | |
| document.createElement( | |
| "a" | |
| ); | |
| link.className = | |
| "model-link"; | |
| link.href = | |
| record.url; | |
| link.target = | |
| "_blank"; | |
| link.rel = | |
| "noreferrer"; | |
| link.title = | |
| `Open ${record.model}`; | |
| link.setAttribute( | |
| "aria-label", | |
| `Open ${record.model} model page` | |
| ); | |
| link.innerHTML = | |
| '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14 5h5v5m0-5-8 8M19 14v4a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h4"/></svg>'; | |
| modelWrap.append( | |
| link | |
| ); | |
| } | |
| modelCell.append( | |
| modelWrap | |
| ); | |
| const rank = | |
| ranks.get( | |
| record.model | |
| ); | |
| const rankCell = | |
| makeCell( | |
| "td", | |
| rank | |
| ? `#${rank}` | |
| : "—", | |
| `rank${ | |
| rank <= 3 | |
| ? " rank-top" | |
| : "" | |
| }` | |
| ); | |
| const availabilityCell = | |
| document.createElement( | |
| "td" | |
| ); | |
| const badge = | |
| document.createElement( | |
| "span" | |
| ); | |
| badge.className = | |
| `availability-badge ${record.availability.toLowerCase()}`; | |
| badge.textContent = | |
| record.availability; | |
| availabilityCell.append( | |
| badge | |
| ); | |
| row.append( | |
| rankCell, | |
| modelCell, | |
| availabilityCell | |
| ); | |
| columns.forEach( | |
| (column) => { | |
| const value = | |
| record.values[ | |
| column.key | |
| ]; | |
| const heat = | |
| state.colorScores | |
| ? `heat-cell${heatClass( | |
| value, | |
| ranges.get( | |
| column.key | |
| ) | |
| )}` | |
| : ""; | |
| const cell = | |
| makeCell( | |
| "td", | |
| value === null | |
| ? "—" | |
| : value.toFixed(2), | |
| heat | |
| ); | |
| if ( | |
| isMatrix && | |
| column.key.startsWith( | |
| "combined|" | |
| ) | |
| ) { | |
| cell.classList.add( | |
| "combined-score" | |
| ); | |
| } | |
| row.append( | |
| cell | |
| ); | |
| } | |
| ); | |
| return row; | |
| }); | |
| body.replaceChildren( | |
| ...rows | |
| ); | |
| document.querySelector( | |
| "#table-title" | |
| ).textContent = | |
| isMatrix | |
| ? `${metricLabel(matrixMetric)} across all datasets` | |
| : `${ | |
| DATASET_LABELS[dataset] || | |
| dataset | |
| } leaderboard`; | |
| const colorNote = | |
| state.colorScores | |
| ? " Colors are scaled separately within the WER and CER metric families: green is better, red is worse." | |
| : ""; | |
| document.querySelector( | |
| "#table-description" | |
| ).textContent = | |
| ( | |
| isMatrix | |
| ? `Rank is based on Overall ${metricLabel(matrixMetric)}. Scores are percentages; lower is better.` | |
| : `Rank is based on ${metricLabel(state.metrics[0])}. Scores are percentages; lower is better.` | |
| ) + colorNote; | |
| document.querySelector( | |
| "#result-count" | |
| ).textContent = | |
| `${records.length} ${ | |
| records.length === 1 | |
| ? "model" | |
| : "models" | |
| }`; | |
| document.querySelector( | |
| "#empty-state" | |
| ).hidden = | |
| records.length !== 0; | |
| table.hidden = | |
| records.length === 0; | |
| } | |
| function setView(view) { | |
| state.view = | |
| view; | |
| const isMatrix = | |
| view === "matrix"; | |
| document | |
| .querySelector( | |
| "#leaderboards" | |
| ) | |
| .classList.toggle( | |
| "matrix-view", | |
| isMatrix | |
| ); | |
| document.querySelector( | |
| ".matrix-metric-field" | |
| ).hidden = | |
| !isMatrix; | |
| document | |
| .querySelectorAll( | |
| ".view-button" | |
| ) | |
| .forEach((button) => { | |
| const active = | |
| button.dataset.view === | |
| view; | |
| button.classList.toggle( | |
| "is-active", | |
| active | |
| ); | |
| button.setAttribute( | |
| "aria-pressed", | |
| String(active) | |
| ); | |
| }); | |
| state.sortKey = | |
| isMatrix | |
| ? `combined|${currentMatrixMetric()}` | |
| : `${currentDataset()}|${state.metrics[0]}`; | |
| state.sortDirection = | |
| "asc"; | |
| render(); | |
| } | |
| function syncMetricControls() { | |
| const matrixOptions = document.querySelector( | |
| "#matrix-metric-options" | |
| ); | |
| const chartMetric = document.querySelector("#chart-metric"); | |
| matrixOptions.replaceChildren(); | |
| chartMetric.replaceChildren(); | |
| state.metrics.forEach((metric, index) => { | |
| const label = document.createElement("label"); | |
| label.className = "choice-pill"; | |
| const input = document.createElement("input"); | |
| input.type = "radio"; | |
| input.name = "matrix-metric"; | |
| input.value = metric; | |
| input.checked = index === 0; | |
| input.addEventListener("change", () => { | |
| state.sortKey = `combined|${input.value}`; | |
| state.sortDirection = "asc"; | |
| render(); | |
| }); | |
| label.append(input, document.createTextNode(metricLabel(metric))); | |
| matrixOptions.append(label); | |
| const option = document.createElement("option"); | |
| option.value = metric; | |
| option.textContent = metricLabel(metric); | |
| chartMetric.append(option); | |
| }); | |
| } | |
| function setVersion(version) { | |
| const csv = state.versionedResults[version]; | |
| if (!csv || version === state.version) { | |
| return; | |
| } | |
| ingest(parseCsv(csv)); | |
| state.version = version; | |
| state.sortKey = `combined|${state.metrics[0]}`; | |
| state.sortDirection = "asc"; | |
| state.availability = new Set(["Open", "Closed"]); | |
| state.availabilityMenuOpen = false; | |
| state.chartDataset = state.datasets.includes("combined") ? "combined" : state.datasets[0]; | |
| state.chartMetric = state.metrics[0]; | |
| syncMetricControls(); | |
| const viewingOldVersion = version === "v0.1"; | |
| document.querySelector("#version-select").value = version; | |
| document.querySelector("#old-version-notice").hidden = !viewingOldVersion; | |
| document.querySelector("#chart-dataset").value = state.chartDataset; | |
| document.querySelector("#chart-metric").value = state.chartMetric; | |
| render(); | |
| renderComparisonChart(); | |
| } | |
| function activateTab( | |
| name, | |
| updateHash = true | |
| ) { | |
| const validName = | |
| [ | |
| "leaderboards", | |
| "datasets", | |
| "about", | |
| "changelog", | |
| ].includes(name) | |
| ? name | |
| : "leaderboards"; | |
| document | |
| .querySelectorAll( | |
| ".tab" | |
| ) | |
| .forEach((tab) => { | |
| const active = | |
| tab.dataset.tab === | |
| validName; | |
| tab.classList.toggle( | |
| "is-active", | |
| active | |
| ); | |
| tab.setAttribute( | |
| "aria-selected", | |
| String(active) | |
| ); | |
| }); | |
| document | |
| .querySelectorAll( | |
| ".panel" | |
| ) | |
| .forEach((panel) => { | |
| const active = | |
| panel.id === | |
| validName; | |
| panel.classList.toggle( | |
| "is-active", | |
| active | |
| ); | |
| panel.hidden = | |
| !active; | |
| }); | |
| if (updateHash) { | |
| history.replaceState( | |
| null, | |
| "", | |
| `#${validName}` | |
| ); | |
| } | |
| } | |
| function buildControls() { | |
| const datasetOptions = | |
| document.querySelector( | |
| "#dataset-options" | |
| ); | |
| state.datasets.forEach( | |
| (dataset, index) => { | |
| const label = | |
| document.createElement( | |
| "label" | |
| ); | |
| label.className = | |
| "choice-pill"; | |
| if ( | |
| dataset === "combined" | |
| ) { | |
| label.classList.add( | |
| "combined-choice" | |
| ); | |
| } | |
| const input = | |
| document.createElement( | |
| "input" | |
| ); | |
| input.type = | |
| "radio"; | |
| input.name = | |
| "dataset"; | |
| input.value = | |
| dataset; | |
| input.checked = | |
| index === 0; | |
| input.addEventListener( | |
| "change", | |
| () => { | |
| state.sortKey = | |
| `${dataset}|${state.metrics[0]}`; | |
| state.sortDirection = | |
| "asc"; | |
| render(); | |
| } | |
| ); | |
| label.append( | |
| input, | |
| document.createTextNode( | |
| DATASET_LABELS[ | |
| dataset | |
| ] || dataset | |
| ) | |
| ); | |
| datasetOptions.append( | |
| label | |
| ); | |
| } | |
| ); | |
| const matrixOptions = | |
| document.querySelector( | |
| "#matrix-metric-options" | |
| ); | |
| state.metrics.forEach( | |
| (metric, index) => { | |
| const matrixLabel = | |
| document.createElement( | |
| "label" | |
| ); | |
| matrixLabel.className = | |
| "choice-pill"; | |
| const matrixInput = | |
| document.createElement( | |
| "input" | |
| ); | |
| matrixInput.type = | |
| "radio"; | |
| matrixInput.name = | |
| "matrix-metric"; | |
| matrixInput.value = | |
| metric; | |
| matrixInput.checked = | |
| index === 0; | |
| matrixInput.addEventListener( | |
| "change", | |
| () => { | |
| state.sortKey = | |
| `combined|${matrixInput.value}`; | |
| state.sortDirection = | |
| "asc"; | |
| render(); | |
| } | |
| ); | |
| matrixLabel.append( | |
| matrixInput, | |
| document.createTextNode( | |
| metricLabel(metric) | |
| ) | |
| ); | |
| matrixOptions.append( | |
| matrixLabel | |
| ); | |
| } | |
| ); | |
| document | |
| .querySelector( | |
| "#color-toggle" | |
| ) | |
| .addEventListener( | |
| "click", | |
| (event) => { | |
| state.colorScores = | |
| !state.colorScores; | |
| event.currentTarget.classList.toggle( | |
| "is-active", | |
| state.colorScores | |
| ); | |
| event.currentTarget.setAttribute( | |
| "aria-pressed", | |
| String( | |
| state.colorScores | |
| ) | |
| ); | |
| render(); | |
| } | |
| ); | |
| document | |
| .querySelectorAll( | |
| ".view-button" | |
| ) | |
| .forEach((button) => { | |
| button.addEventListener( | |
| "click", | |
| () => { | |
| setView( | |
| button.dataset.view | |
| ); | |
| } | |
| ); | |
| }); | |
| document | |
| .querySelectorAll( | |
| ".tab" | |
| ) | |
| .forEach((tab) => { | |
| tab.addEventListener( | |
| "click", | |
| () => { | |
| activateTab( | |
| tab.dataset.tab | |
| ); | |
| } | |
| ); | |
| }); | |
| document | |
| .querySelector( | |
| "#theme-toggle" | |
| ) | |
| .addEventListener( | |
| "click", | |
| () => { | |
| setTheme( | |
| document.documentElement | |
| .dataset.theme === | |
| "dark" | |
| ? "light" | |
| : "dark" | |
| ); | |
| } | |
| ); | |
| document | |
| .querySelector("#version-select") | |
| .addEventListener("change", (event) => { | |
| setVersion(event.currentTarget.value); | |
| }); | |
| document | |
| .querySelector("#new-version-link") | |
| .addEventListener("click", () => { | |
| setVersion(DEFAULT_VERSION); | |
| }); | |
| document.addEventListener( | |
| "click", | |
| () => { | |
| if ( | |
| !state.availabilityMenuOpen | |
| ) { | |
| return; | |
| } | |
| state.availabilityMenuOpen = | |
| false; | |
| render(); | |
| } | |
| ); | |
| document.addEventListener( | |
| "keydown", | |
| (event) => { | |
| if ( | |
| event.key !== "Escape" || | |
| !state.availabilityMenuOpen | |
| ) { | |
| return; | |
| } | |
| state.availabilityMenuOpen = | |
| false; | |
| render(); | |
| } | |
| ); | |
| setTheme( | |
| document.documentElement | |
| .dataset.theme, | |
| false | |
| ); | |
| } | |
| async function start() { | |
| try { | |
| const embeddedResults = | |
| document.querySelector( | |
| "#results-data" | |
| ); | |
| if (!embeddedResults) { | |
| throw new Error( | |
| "Could not find the embedded results. Run build_site.py." | |
| ); | |
| } | |
| state.versionedResults = JSON.parse(embeddedResults.textContent); | |
| if (!state.versionedResults[DEFAULT_VERSION]) { | |
| throw new Error(`Could not find ${DEFAULT_VERSION} results.`); | |
| } | |
| ingest(parseCsv(state.versionedResults[DEFAULT_VERSION])); | |
| buildControls(); | |
| buildChartControls(); | |
| activateTab( | |
| location.hash.slice(1), | |
| false | |
| ); | |
| render(); | |
| renderComparisonChart(); | |
| } catch (error) { | |
| document.querySelector( | |
| "#table-title" | |
| ).textContent = | |
| "Leaderboard unavailable"; | |
| document.querySelector( | |
| "#leaderboard-table" | |
| ).hidden = | |
| true; | |
| const empty = | |
| document.querySelector( | |
| "#empty-state" | |
| ); | |
| empty.hidden = | |
| false; | |
| empty.querySelector( | |
| "h3" | |
| ).textContent = | |
| "Could not load results"; | |
| empty.querySelector( | |
| "p" | |
| ).textContent = | |
| error.message; | |
| } | |
| } | |
| if ( | |
| typeof module !== | |
| "undefined" | |
| ) { | |
| module.exports = { | |
| heatClass, | |
| ingest, | |
| matrixRanges, | |
| parseCsv, | |
| rankRecords, | |
| setTheme, | |
| state, | |
| }; | |
| } | |
| if ( | |
| typeof document !== | |
| "undefined" | |
| ) { | |
| start(); | |
| } | |