// assets/js/category.js const CSV_PATH = "cleaned_data.csv"; /* >>> DATASET VIDEO BASE <<< */ const VIDEO_DATASET_BASE = "https://huggingface.co/datasets/therarelab/codebook-videos/resolve/main/videos/"; const params = new URLSearchParams(window.location.search); const cat = (params.get("cat") || "").trim(); const titleEl = document.getElementById("catTitle"); const defEl = document.getElementById("catDef"); const repMetaEl = document.getElementById("repMeta"); const repGrid = document.getElementById("repGrid"); const repEmpty = document.getElementById("repEmpty"); const instCountEl = document.getElementById("instCount"); const instVideoCountEl = document.getElementById("instVideoCount"); const tbody = document.querySelector("#catTable tbody"); function parseCSV(text) { const lines = text.trim().split("\n"); const header = lines[0].split(",").map(s => s.trim()); const rows = []; for (let i = 1; i < lines.length; i++) { const cols = lines[i].split(","); const obj = {}; for (let j = 0; j < header.length; j++) { obj[header[j]] = (cols[j] ?? "").trim(); } rows.push(obj); } return rows; } function setHeader(failureInfo) { if (!cat) { titleEl.textContent = "No category specified"; defEl.textContent = "Return to Home and select a category."; return; } titleEl.textContent = failureInfo?.name || cat; defEl.textContent = failureInfo?.definition || ""; document.title = `${cat} — Failure Category`; } const failureInfo = window.FAILURE_MAP?.[cat] || window.FAILURES?.find(f => f.name === cat); setHeader(failureInfo); /* ---------------- Representative Video ---------------- */ function renderRep(instance) { repGrid.innerHTML = ""; if (!instance) { repEmpty.hidden = false; repMetaEl.textContent = ""; return; } repEmpty.hidden = true; const filename = instance.filename; const label = instance.label; const start = instance.start; const end = instance.end; const video_url = VIDEO_DATASET_BASE + encodeURIComponent(filename); repMetaEl.textContent = `Example: ${filename} (frames ${start}-${end})`; const wrap = document.createElement("div"); wrap.className = "video-item"; wrap.innerHTML = `
${label}
`; const videoEl = wrap.querySelector("video"); attachLabeledLoop(videoEl, { label, startFrame: start, endFrame: end, fps: DEFAULT_FPS }); repGrid.appendChild(wrap); } /* ---------------- Table ---------------- */ function renderTable(instances) { tbody.innerHTML = ""; const uniqueVideos = new Set(); for (const r of instances) { uniqueVideos.add(r.filename); const video_url = VIDEO_DATASET_BASE + encodeURIComponent(r.filename); const tr = document.createElement("tr"); tr.innerHTML = ` ${r.filename} ${r.start}-${r.end} `; const videoEl = tr.querySelector("video"); attachLabeledLoop(videoEl, { label: r.label, startFrame: r.start, endFrame: r.end, fps: DEFAULT_FPS }); tbody.appendChild(tr); } instCountEl.textContent = `${instances.length} segments`; instVideoCountEl.textContent = `${uniqueVideos.size} videos`; } /* ---------------- Load CSV ---------------- */ fetch(CSV_PATH) .then(r => r.text()) .then(text => { const rows = parseCSV(text); const instances = rows .filter(r => String(r.label || "").trim() === cat) .filter(r => r.filename && r.start !== "" && r.end !== ""); instances.sort((a, b) => { if (a.filename !== b.filename) return a.filename.localeCompare(b.filename); return Number(a.start) - Number(b.start); }); renderRep(instances[0]); renderTable(instances); }) .catch(err => { console.error(err); repEmpty.hidden = false; repEmpty.textContent = "Could not load cleaned_data.csv."; tbody.innerHTML = `Could not load cleaned_data.csv`; });