1document.addEventListener('DOMContentLoaded', function() { 2 // Navbar active link highlighting 3 const navLinks = document.querySelectorAll('.nav-link'); 4 navLinks.forEach(link => { 5 link.addEventListener('click', function() { 6 navLinks.forEach(l => l.classList.remove('active')); 7 this.classList.add('active'); 8 }); 9 }); 10 11 // Smooth scrolling for anchor links 12 document.querySelectorAll('a[href^="#"]').forEach(anchor => { 13 anchor.addEventListener('click', function(e) { 14 e.preventDefault(); 15 const targetId = this.getAttribute('href'); 16 if (targetId === '#') return; 17 18 const targetElement = document.querySelector(targetId); 19 if (targetElement) { 20 window.scrollTo({ 21 top: targetElement.offsetTop - 80, 22 behavior: 'smooth' 23 }); 24 } 25 }); 26 }); 27 28 // Model filtering functionality 29 const modelTypeFilter = document.querySelector('.filter-group select:first-of-type'); 30 const providerFilter = document.querySelector('.filter-group select:last-of-type'); 31 const modelCards = document.querySelectorAll('.model-card'); 32 33 if (modelTypeFilter && providerFilter) { 34 const filterModels = () => { 35 const typeValue = modelTypeFilter.value; 36 const providerValue = providerFilter.value; 37 38 modelCards.forEach(card => { 39 const modelType = card.querySelector('.badge:first-of-type').textContent; 40 const provider = card.querySelector('.badge:last-of-type').textContent; 41 42 const typeMatch = typeValue === 'All Types' || modelType === typeValue; 43 const providerMatch = providerValue === 'All Providers' || provider.includes(providerValue); 44 45 card.closest('.col-md-4').style.display = typeMatch && providerMatch ? 'block' : 'none'; 46 }); 47 }; 48 49 modelTypeFilter.addEventListener('change', filterModels); 50 providerFilter.addEventListener('change', filterModels); 51 } 52 53 // Leaderboard sorting functionality for all ranking tables 54 document.querySelectorAll('.leaderboard-table').forEach(leaderboardTable => { 55 const headers = leaderboardTable.querySelectorAll('thead th'); 56 let currentSortCol = 0; // Default sort by rank 57 let sortDirection = 'asc'; 58 59 headers.forEach((header, index) => { 60 // Skip first column (rank) as it's already sorted 61 if (index === 0) return; 62 63 header.style.cursor = 'pointer'; 64 header.addEventListener('click', () => { 65 sortTable(leaderboardTable, index); 66 }); 67 68 // Add sort indicator 69 const sortIndicator = document.createElement('span'); 70 sortIndicator.classList.add('sort-indicator'); 71 sortIndicator.style.marginLeft = '5px'; 72 sortIndicator.innerHTML = 'â'; 73 header.appendChild(sortIndicator); 74 }); 75 76 function sortTable(table, columnIndex) { 77 const tbody = table.querySelector('tbody'); 78 const rows = Array.from(tbody.querySelectorAll('tr')); 79 80 // Reset all indicators 81 headers.forEach(h => { 82 const indicator = h.querySelector('.sort-indicator'); 83 if (indicator) indicator.innerHTML = 'â'; 84 }); 85 86 // Set new indicator 87 const indicator = headers[columnIndex].querySelector('.sort-indicator'); 88 89 // Toggle sort direction if clicking on same column 90 if (currentSortCol === columnIndex) { 91 sortDirection = sortDirection === 'asc' ? 'desc' : 'asc'; 92 } else { 93 sortDirection = 'asc'; 94 currentSortCol = columnIndex; 95 } 96 97 // Update indicator 98 indicator.innerHTML = sortDirection === 'asc' ? 'â' : 'â'; 99 100 // Sort the rows 101 rows.sort((a, b) => { 102 let aValue = a.cells[columnIndex].textContent; 103 let bValue = b.cells[columnIndex].textContent; 104 105 // Handle numeric values 106 if (!isNaN(parseFloat(aValue))) { 107 aValue = parseFloat(aValue);
108 bValue = parseFloat(bValue); 109 } 110 111 if (aValue < bValue) return sortDirection === 'asc' ? -1 : 1; 112 if (aValue > bValue) return sortDirection === 'asc' ? 1 : -1; 113 return 0; 114 }); 115 116 // Re-append rows in new order 117 rows.forEach(row => tbody.appendChild(row)); 118 } 119 }); 120 121 // Card hover effects enhancement 122 const cards = document.querySelectorAll('.model-card'); 123 cards.forEach(card => { 124 card.addEventListener('mouseenter', function() { 125 this.style.transform = 'translateY(-8px)'; 126 this.style.boxShadow = '0 12px 20px rgba(0, 0, 0, 0.15)'; 127 }); 128 129 card.addEventListener('mouseleave', function() { 130 this.style.transform = 'translateY(0)'; 131 this.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)'; 132 }); 133 }); 134 135 // Add a small animation to hero section 136 const heroContent = document.querySelector('.hero-section .container'); 137 if (heroContent) { 138 heroContent.style.opacity = '0'; 139 heroContent.style.transform = 'translateY(20px)'; 140 heroContent.style.transition = 'opacity 0.8s ease, transform 0.8s ease'; 141 142 setTimeout(() => { 143 heroContent.style.opacity = '1'; 144 heroContent.style.transform = 'translateY(0)'; 145 }, 300); 146 } 147 148 // Add scroll reveal animation to sections 149 const sections = document.querySelectorAll('section:not(.hero-section)'); 150 const revealSection = (entries, observer) => { 151 entries.forEach(entry => { 152 if (entry.isIntersecting) { 153 entry.target.classList.add('revealed'); 154 observer.unobserve(entry.target); 155 } 156 }); 157 }; 158 159 const sectionObserver = new IntersectionObserver(revealSection, { 160 root: null, 161 threshold: 0.15, 162 }); 163 164 sections.forEach(section => { 165 section.style.opacity = '0'; 166 section.style.transform = 'translateY(30px)'; 167 section.style.transition = 'opacity 0.8s ease, transform 0.8s ease'; 168 169 sectionObserver.observe(section); 170 }); 171 172 document.addEventListener('scroll', () => { 173 sections.forEach(section => { 174 const sectionTop = section.getBoundingClientRect().top; 175 const windowHeight = window.innerHeight; 176 177 if (sectionTop < windowHeight * 0.85) { 178 section.style.opacity = '1'; 179 section.style.transform = 'translateY(0)'; 180 } 181 }); 182 }); 183 184 // CSV Data Loading functionality 185 const csvDownloadButtons = document.querySelectorAll('.csv-download .btn'); 186 csvDownloadButtons.forEach(button => { 187 button.addEventListener('click', function(e) { 188 e.preventDefault(); 189 const section = this.closest('section'); 190 const sectionId = section.id; 191 192 // Define CSV file paths based on section ID 193 let csvPath; 194 switch(sectionId) { 195 case 'point-bench': 196 csvPath = 'data/point-bench.csv'; 197 break; 198 case 'point-battle': 199 csvPath = 'data/point-battle.csv'; 200 break; 201 case 'point-act': 202 csvPath = 'data/point-act.csv'; 203 break; 204 default: 205 console.error('Unknown section ID'); 206 return; 207 } 208 209 // Download the CSV file 210 downloadCSV(csvPath); 211 }); 212 }); 213 214 function downloadCSV(filePath) { 215 fetch(filePath) 216 .then(response => { 217 if (!response.ok) { 218 throw new Error(`HTTP error! Status: ${response.status}`); 219 } 220 return response.blob(); 221 }) 222 .then(blob => { 223 // Create a temporary URL for the blob 224 const url = window.URL.createObjectURL(blob); 225 226 // Create a temporary anchor element and trigger download 227 const a = document.createElement('a'); 228 a.style.display = 'none'; 229 a.href = url; 230 a.download = filePath.split('/').pop(); 231 document.body.appendChild(a); 232 a.click(); 233 234 // Clean up 235 window.URL.revokeObjectURL(url); 236 a.remove(); 237 }) 238 .catch(error => { 239 console.error('Error downloading CSV:', error); 240 alert('Failed to download CSV file. Please try again later.'); 241 }); 242 } 243 244 // Function to create directory structure for CSV files if implementing 245 function createDataDirectories() { 246 // This would typically be done on the server side 247 // For GitHub Pages, you would manually create these directories and files 248 console.log('Data directories should be created manually for GitHub Pages'); 249 } 250 251 // Function to parse CSV data 252 function parseCSV(csv) { 253 const lines = csv.trim().split('\n'); 254 const headers = lines[0].split(','); 255 256 return lines.slice(1).map(line => { 257 const values = line.split(','); 258 const entry = {}; 259
260 headers.forEach((header, index) => { 261 entry[header.trim()] = values[index]?.trim() || ''; 262 }); 263 264 return entry; 265 }); 266 } 267 268 // Function to load CSV data 269 async function loadCSV(url) { 270 const response = await fetch(url); 271 const text = await response.text(); 272 return parseCSV(text); 273 } 274 275 // Function to safely parse numeric values 276 function safeParseFloat(value, fallback = 0) { 277 if (value === undefined || value === null || value === '') { 278 return fallback; 279 } 280 281 // Remove any % character if present 282 if (typeof value === 'string' && value.includes('%')) { 283 value = value.replace('%', ''); 284 } 285 286 const parsed = parseFloat(value); 287 return isNaN(parsed) ? fallback : parsed; 288 } 289 290 // Function to update Point-Bench leaderboard 291 async function updatePointBenchTable() { 292 try { 293 const data = await loadCSV('data/point-bench.csv'); 294 const tableBody = document.querySelector('#point-bench .leaderboard-table tbody'); 295 tableBody.innerHTML = ''; 296 297 // Sort by Average score (descending) 298 data.sort((a, b) => safeParseFloat(b.Average) - safeParseFloat(a.Average)); 299 300 // Model URL mapping 301 const modelUrls = { 302 "gemini-2.0-Flash": "https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-0-flash", 303 "gemini-2.5-flash-preview-04-17": "https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-5-flash", 304 "GPT-4o": "https://openai.com/api/", 305 "GPT-4.1": "https://openai.com/api/", 306 "o3": "https://openai.com/api/", 307 "Qwen2.5-VL-7B-Instruct": "https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct", 308 "Qwen2.5-VL-32B-Instruct": "https://huggingface.co/Qwen/Qwen2.5-VL-32B-Instruct", 309 "Qwen2.5-VL-72B-Instruct": "https://huggingface.co/Qwen/Qwen2.5-VL-72B-Instruct", 310 "llava-onevision-qwen2-7b-ov-hf": "https://huggingface.co/llava-hf/llava-onevision-qwen2-7b-ov-hf", 311 "llava-onevision-qwen2-72b-ov-hf": "https://huggingface.co/llava-hf/llava-onevision-qwen2-72b-ov-hf", 312 "Molmo-7B-D": "https://huggingface.co/allenai/Molmo-7B-D-0924", 313 "Molmo-7B-O": "https://huggingface.co/allenai/Molmo-7B-O-0924", 314 "Molmo-72B": "https://huggingface.co/allenai/Molmo-72B-0924", 315 "gemini-2.5-pro-preview-05-06": "https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-5-pro", 316 "grok-2-vision-latest": "https://docs.x.ai/docs/models", 317 "claude-3-7-sonnet-20250219": "https://docs.anthropic.com/en/docs/about-claude/models/all-models", 318 "Gemini-Robotics-ER 1.5": "https://deepmind.google/", 319 "Human": "#" 320 }; 321 322 // Display all results (not just top 5) 323 data.forEach((row, index) => { 324 const tr = document.createElement('tr'); 325 const modelName = row[''] || row.Model || ''; 326 const modelUrl = modelUrls[modelName] || '#'; 327 328 tr.innerHTML = ` 329 <td>${index + 1}</td> 330 <td><a href="${modelUrl}" target="_blank" rel="noopener noreferrer">${modelName}</a></td> 331 <td class="score">${safeParseFloat(row.Affordance).toFixed(2)}</td> 332 <td class="score">${safeParseFloat(row.Spatial).toFixed(2)}</td> 333 <td class="score">${safeParseFloat(row.Reasoning).toFixed(2)}</td> 334 <td class="score">${safeParseFloat(row.Steerability).toFixed(2)}</td> 335 <td class="score">${safeParseFloat(row.Counting).toFixed(2)}</td> 336 <td class="score">${safeParseFloat(row.Average).toFixed(3)}</td> 337 `; 338 339 tableBody.appendChild(tr); 340 }); 341 342 // Hide loading indicator 343 document.querySelector('#point-bench .loading-indicator').style.display = 'none'; 344 } catch (error) { 345 console.error('Error loading point-bench.csv:', error); 346 document.querySelector('#point-bench .loading-indicator').innerHTML = 347 '<p class="text-danger">Error loading data. Please try again later.</p>'; 348 } 349 } 350 351 // Function to update Point-Battle leaderboard 352 async function updatePointBattleTable() { 353 try { 354 const data = await loadCSV('data/point-battle.csv'); 355 const tableBody = document.querySelector('#point-battle .leaderboard-table tbody'); 356 tableBody.innerHTML = ''; 357 358 // Sort by Elo Rating (descending) just to be sure 359 data.sort((a, b) => safeParseFloat(b['Elo Rating']) - safeParseFloat(a['Elo Rating'])); 360 361 // Display all available results 362 data.forEach((row, index) => { 363 const tr = document.createElement('tr'); 364 365 tr.innerHTML = ` 366 <td>${index + 1}</td> 367 <td>${row.Model || ''}</td> 368 <td class="score">${safeParseFloat(row['Elo Rating']).toFixed(1)}</td> 369 <td class="score">${safeParseFloat(row.Wins, 0)}</td> 370 <td class="score">${safeParseFloat(row.Losses, 0)}</td> 371 <td class="score">${safeParseFloat(row.Games, 0)}</td> 372 <td class="score">${row['Win Rate'] || '0%'}</td> 373 <td class="score">${safeParseFloat(row['Lower CI']).toFixed(1)}</td> 374 <td class="score">${safeParseFloat(row['Upper CI']).toFixed(1)}</td> 375 `; 376 377 tableBody.appendChild(tr); 378 }); 379 380 // Hide loading indicator 381 document.querySelector('#point-battle .loading-indicator').style.display = 'none'; 382 } catch (error) { 383 console.error('Error loading point-battle.csv:', error); 384 document.querySelector('#point-battle .loading-indicator').innerHTML = 385 '<p class="text-danger">Error loading data. Please try again later.</p>'; 386 } 387 } 388 389 // Function to update Point-Act leaderboard 390 async function updatePointActTable() { 391 try { 392 const data = await loadCSV('data/point-act.csv'); 393 const tableBody = document.querySelector('#point-act .leaderboard-table tbody'); 394 tableBody.innerHTML = ''; 395 396 // Sort by Success Rate (descending) 397 data.sort((a, b) => safeParseFloat(b['Success Rate']) - safeParseFloat(a['Success Rate'])); 398 399 data.forEach((row) => { 400 const tr = document.createElement('tr'); 401 402 tr.innerHTML = ` 403 <td>${row.Challenge || ''}</td> 404 <td class="score">${safeParseFloat(row['Success Rate'])}</td> 405 <td class="score">${safeParseFloat(row['SUS Score'])}</td> 406 `; 407 408 tableBody.appendChild(tr); 409 }); 410 411 // Hide loading indicator 412 document.querySelector('#point-act .loading-indicator').style.display = 'none'; 413 } catch (error) { 414 console.error('Error loading point-act.csv:', error); 415 document.querySelector('#point-act .loading-indicator').innerHTML = 416 '<p class="text-danger">Error loading data. Please try again later.</p>'; 417 } 418 } 419 420 // Gallery functionality 421 let galleryData = [];
422 let currentExampleIndex = 0; 423 let filteredGalleryData = []; 424 425 async function loadGalleryData() { 426 try { 427 const response = await fetch('data/gallery/data.json'); 428 if (!response.ok) { 429 throw new Error(`Failed to load gallery data: ${response.status}`); 430 } 431 galleryData = await response.json(); 432 433 // Use the data directly without checking all files first 434 // This avoids multiple HEAD requests that can lead to broken pipes 435 filteredGalleryData = [...galleryData]; 436 437 console.log(`Loaded ${filteredGalleryData.length} gallery examples`); 438 return filteredGalleryData; 439 } catch (error) { 440 console.error('Error loading gallery data:', error); 441 return []; 442 } 443 } 444 445 function displayGalleryExample(index) { 446 if (!filteredGalleryData || !filteredGalleryData.length) { 447 // Handle case where no valid examples exist 448 const carousel = document.querySelector('#exampleCarousel .carousel-inner'); 449 if (carousel) { 450 carousel.innerHTML = '<div class="carousel-item active"><div class="text-center p-5">No valid examples found. Please check the image files.</div></div>'; 451 } 452 453 const queryText = document.getElementById('queryText'); 454 const categoryBadge = document.getElementById('categoryBadge'); 455 const countInfo = document.getElementById('countInfo'); 456 457 if (queryText) queryText.textContent = ''; 458 if (categoryBadge) categoryBadge.textContent = ''; 459 if (countInfo) countInfo.style.display = 'none'; 460 return; 461 } 462 463 // Get example data 464 const example = filteredGalleryData[index]; 465 if (!example) return; 466 467 // Get DOM elements 468 const carousel = document.querySelector('#exampleCarousel .carousel-inner'); 469 const queryText = document.getElementById('queryText'); 470 const categoryBadge = document.getElementById('categoryBadge'); 471 const countInfo = document.getElementById('countInfo'); 472 473 if (!carousel || !queryText || !categoryBadge || !countInfo) { 474 console.error('Missing required DOM elements for gallery display'); 475 return; 476 } 477 478 // Clear existing items 479 carousel.innerHTML = ''; 480 481 // Create carousel item 482 const carouselItem = document.createElement('div'); 483 carouselItem.className = 'carousel-item active'; 484 485 // Create image container with both original and mask 486 const imageContainer = document.createElement('div'); 487 imageContainer.className = 'gallery-image-container position-relative'; 488 489 // Original image with error handling 490 const originalImg = document.createElement('img'); 491 originalImg.className = 'gallery-image d-block w-100 original-image'; 492 originalImg.alt = 'Original image'; 493 originalImg.src = `data/gallery/${example.image_filename}`; 494 originalImg.dataset.src = `data/gallery/${example.image_filename}`; 495 originalImg.onerror = function() { 496 this.onerror = null; 497 this.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAwIiBoZWlnaHQ9IjQwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iODAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iI2YxZjFmMSIvPjx0ZXh0IHg9IjQwMCIgeT0iMjAwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMzZweCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzk5OTk5OSI+SW1hZ2Ugbm90IGF2YWlsYWJsZTwvdGV4dD48L3N2Zz4='; 498 }; 499 500 // Mask image with error handling 501 const maskImg = document.createElement('img'); 502 maskImg.className = 'gallery-image d-block w-100 mask-image'; 503 maskImg.alt = 'Mask'; 504 maskImg.src = `data/gallery/${example.mask_filename}`; 505 maskImg.dataset.src = `data/gallery/${example.mask_filename}`; 506 maskImg.style.display = 'none'; 507 maskImg.onerror = function() { 508 this.onerror = null; 509 this.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAwIiBoZWlnaHQ9IjQwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iODAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iI2YxZjFmMSIvPjx0ZXh0IHg9IjQwMCIgeT0iMjAwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMzZweCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzk5OTk5OSI+TWFzayBpbWFnZSBub3QgYXZhaWxhYmxlPC90ZXh0Pjwvc3ZnPg=='; 510 }; 511 512 // Add images to container 513 imageContainer.appendChild(originalImg); 514 imageContainer.appendChild(maskImg); 515 516 // Add image container to carousel item 517 carouselItem.appendChild(imageContainer); 518 519 // Add carousel item to carousel
520 carousel.appendChild(carouselItem); 521 522 // Update metadata 523 queryText.textContent = example.user_input || 'No query provided'; 524 categoryBadge.textContent = example.category || 'Unknown'; 525 526 // Update count info if available 527 if (example.count) { 528 countInfo.textContent = `Count: ${example.count}`; 529 countInfo.style.display = 'inline-block'; 530 } else { 531 countInfo.style.display = 'none'; 532 } 533 534 // Set color based on category 535 const categoryColors = { 536 'spatial': 'bg-primary', 537 'reasoning': 'bg-success', 538 'affordable': 'bg-warning', 539 'steerable': 'bg-info', 540 'counting': 'bg-danger' 541 }; 542 543 // Reset all classes and add the appropriate one 544 categoryBadge.className = 'badge'; 545 categoryBadge.classList.add(categoryColors[example.category] || 'bg-secondary'); 546 547 currentExampleIndex = index; 548 } 549 550 function filterGalleryByCategory(category) { 551 if (category === 'all') { 552 filteredGalleryData = [...galleryData]; 553 } else { 554 filteredGalleryData = galleryData.filter(item => item.category === category); 555 } 556 557 if (filteredGalleryData.length > 0) { 558 displayGalleryExample(0); 559 } else { 560 // No examples for this category 561 const carousel = document.querySelector('#exampleCarousel .carousel-inner'); 562 carousel.innerHTML = '<div class="carousel-item active"><div class="text-center p-5">No examples found for this category</div></div>'; 563 564 const queryText = document.getElementById('queryText'); 565 const categoryBadge = document.getElementById('categoryBadge'); 566 const countInfo = document.getElementById('countInfo'); 567 568 queryText.textContent = ''; 569 categoryBadge.textContent = ''; 570 countInfo.style.display = 'none'; 571 } 572 } 573 574 async function initGallery() { 575 await loadGalleryData(); 576 577 if (galleryData.length) { 578 displayGalleryExample(0); 579 580 // Initialize view mode buttons 581 const showOriginalBtn = document.getElementById('showOriginal'); 582 const showMaskBtn = document.getElementById('showMask'); 583 const showOverlayBtn = document.getElementById('showOverlay'); 584 585 if (showOriginalBtn && showMaskBtn && showOverlayBtn) { 586 // Show original image 587 showOriginalBtn.addEventListener('click', () => { 588 // Load original images if they're still showing placeholders 589 document.querySelectorAll('.original-image').forEach(img => { 590 if (img.src !== img.dataset.src && img.dataset.src) { 591 img.src = img.dataset.src; 592 } 593 img.style.display = 'block'; 594 }); 595 document.querySelectorAll('.mask-image').forEach(img => img.style.display = 'none'); 596 document.querySelectorAll('.gallery-image-container').forEach(container => container.classList.remove('overlay-mode')); 597 598 // Update button states 599 showOriginalBtn.classList.add('active'); 600 showMaskBtn.classList.remove('active'); 601 showOverlayBtn.classList.remove('active'); 602 }); 603 604 // Show mask only 605 showMaskBtn.addEventListener('click', () => { 606 document.querySelectorAll('.original-image').forEach(img => img.style.display = 'none'); 607 608 // Load mask images if they're still showing placeholders 609 document.querySelectorAll('.mask-image').forEach(img => { 610 if (img.src !== img.dataset.src && img.dataset.src) { 611 img.src = img.dataset.src; 612 } 613 img.style.display = 'block'; 614 }); 615 616 document.querySelectorAll('.gallery-image-container').forEach(container => container.classList.remove('overlay-mode')); 617 618 // Update button states 619 showOriginalBtn.classList.remove('active'); 620 showMaskBtn.classList.add('active'); 621 showOverlayBtn.classList.remove('active'); 622 }); 623 624 // Show overlay (both original and mask) 625 showOverlayBtn.addEventListener('click', () => { 626 // Load original images if they're still showing placeholders 627 document.querySelectorAll('.original-image').forEach(img => { 628 if (img.src !== img.dataset.src && img.dataset.src) { 629 img.src = img.dataset.src; 630 } 631 img.style.display = 'block'; 632 }); 633 634 // Load mask images if they're still showing placeholders 635 document.querySelectorAll('.mask-image').forEach(img => { 636 if (img.src !== img.dataset.src && img.dataset.src) { 637 img.src = img.dataset.src; 638 } 639 img.style.display = 'block'; 640 img.style.opacity = '0.7'; 641 img.style.position = 'absolute'; 642 img.style.top = '0'; 643 img.style.left = '0'; 644 }); 645 646 document.querySelectorAll('.gallery-image-container').forEach(container => container.classList.add('overlay-mode')); 647 648 // Update button states 649 showOriginalBtn.classList.remove('active'); 650 showMaskBtn.classList.remove('active'); 651 showOverlayBtn.classList.add('active'); 652 }); 653 } 654 655 // Initialize category filter 656 const categoryFilter = document.getElementById('categoryFilter'); 657 if (categoryFilter) { 658 categoryFilter.addEventListener('change', function() { 659 filterGalleryByCategory(this.value); 660 }); 661 } 662 663 // Initialize carousel navigation 664 const carousel = document.getElementById('exampleCarousel'); 665 if (carousel) { 666 const prevButton = carousel.querySelector('.carousel-control-prev'); 667 const nextButton = carousel.querySelector('.carousel-control-next'); 668 669 prevButton.addEventListener('click', () => { 670 currentExampleIndex = (currentExampleIndex - 1 + filteredGalleryData.length) % filteredGalleryData.length; 671 displayGalleryExample(currentExampleIndex); 672 }); 673 674 nextButton.addEventListener('click', () => { 675 currentExampleIndex = (currentExampleIndex + 1) % filteredGalleryData.length; 676 displayGalleryExample(currentExampleIndex); 677 }); 678 } 679 } 680 } 681 682 // Initialize tables and features 683 async function initializeTables() { 684 try { 685 await Promise.all([ 686 updatePointBenchTable(), 687 updatePointBattleTable(), 688 updatePointActTable(), 689 initGallery() 690 ]); 691 692 // Hide all loading indicators once data is loaded 693 document.querySelectorAll('.loading-indicator').forEach(indicator => { 694 indicator.style.display = 'none'; 695 }); 696 } catch (error) { 697 console.error('Error initializing tables:', error); 698 document.querySelectorAll('.loading-indicator').forEach(indicator => { 699 indicator.innerHTML = '<p class="text-danger">Error loading data. Please try again later.</p>'; 700 }); 701 } 702 } 703 704 // Initialize everything 705 initializeTables(); 706 707 // Function to copy BibTeX citation 708 function copyBibtex() { 709 const bibtexText = document.querySelector('.bibtex-container pre').textContent; 710 navigator.clipboard.writeText(bibtexText) 711 .then(() => { 712 const copyBtn = document.querySelector('.copy-button'); 713 const originalText = copyBtn.textContent; 714 copyBtn.textContent = 'Copied!'; 715 setTimeout(() => { 716 copyBtn.textContent = originalText; 717 }, 2000); 718 }) 719 .catch(err => { 720 console.error('Failed to copy text: ', err); 721 }); 722 } 723});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.