Download script.js from stat2025/psrc: direct link, hf CLI and curl.
- Browser
- Download file 5.16 kB
-
https://huggingface.co/spaces/stat2025/psrc/resolve/main/script.js
- Command line
-
hf download hf://spaces/stat2025/psrc/script.js
-
curl -L -o script.js https://huggingface.co/spaces/stat2025/psrc/resolve/main/script.js
5.16 kB
| (() => { | |
| const $ = (s, root = document) => root.querySelector(s); | |
| const $$ = (s, root = document) => [...root.querySelectorAll(s)]; | |
| const header = $('#siteHeader'); | |
| const progress = $('#scrollProgress'); | |
| const bottomCta = $('#bottomCta'); | |
| const toTop = $('#toTop'); | |
| const menu = $('#mobileMenu'); | |
| const menuToggle = $('#menuToggle'); | |
| const accessPanel = $('#accessPanel'); | |
| const root = document.documentElement; | |
| const setLock = (locked) => document.body.classList.toggle('menu-lock', locked); | |
| function onScroll(){ | |
| const y = window.scrollY; | |
| const h = Math.max(1, document.documentElement.scrollHeight - window.innerHeight); | |
| header?.classList.toggle('scrolled', y > 18); | |
| if(progress) progress.style.width = `${Math.min(100, (y / h) * 100)}%`; | |
| const ctaSection = $('#cta'); | |
| const footer = $('#contact'); | |
| const ctaTop = ctaSection ? ctaSection.getBoundingClientRect().top + y : Infinity; | |
| const footerTop = footer ? footer.getBoundingClientRect().top + y : Infinity; | |
| const shouldShow = y > window.innerHeight * .78 && y + window.innerHeight < Math.min(ctaTop + 120, footerTop); | |
| bottomCta?.classList.toggle('show', shouldShow); | |
| toTop?.classList.toggle('show', y > 650); | |
| } | |
| addEventListener('scroll', onScroll, {passive:true}); | |
| addEventListener('resize', onScroll, {passive:true}); | |
| onScroll(); | |
| if('IntersectionObserver' in window){ | |
| const revealObserver = new IntersectionObserver(entries => { | |
| entries.forEach(entry => { | |
| if(entry.isIntersecting){ | |
| entry.target.classList.add('in'); | |
| revealObserver.unobserve(entry.target); | |
| } | |
| }); | |
| }, {threshold:.12, rootMargin:'0px 0px -30px 0px'}); | |
| $$('.reveal').forEach(el => revealObserver.observe(el)); | |
| const sections = $$('main section[id]'); | |
| const navLinks = $$('.nav-links a'); | |
| const activeObserver = new IntersectionObserver(entries => { | |
| entries.forEach(entry => { | |
| if(entry.isIntersecting){ | |
| navLinks.forEach(a => a.classList.toggle('active', a.getAttribute('href') === `#${entry.target.id}`)); | |
| } | |
| }); | |
| }, {rootMargin:'-40% 0px -50% 0px'}); | |
| sections.forEach(section => activeObserver.observe(section)); | |
| } else { | |
| $$('.reveal').forEach(el => el.classList.add('in')); | |
| } | |
| function openMenu(){ | |
| menu?.classList.add('open'); | |
| menu?.setAttribute('aria-hidden','false'); | |
| menuToggle?.setAttribute('aria-expanded','true'); | |
| setLock(true); | |
| } | |
| function closeMenu(){ | |
| menu?.classList.remove('open'); | |
| menu?.setAttribute('aria-hidden','true'); | |
| menuToggle?.setAttribute('aria-expanded','false'); | |
| if(!accessPanel?.classList.contains('open')) setLock(false); | |
| } | |
| menuToggle?.addEventListener('click', openMenu); | |
| $('#closeMenu')?.addEventListener('click', closeMenu); | |
| menu?.addEventListener('click', e => { if(e.target === menu || e.target.closest('a')) closeMenu(); }); | |
| function openAccess(){ | |
| accessPanel?.classList.add('open'); | |
| accessPanel?.setAttribute('aria-hidden','false'); | |
| setLock(true); | |
| } | |
| function closeAccess(){ | |
| accessPanel?.classList.remove('open'); | |
| accessPanel?.setAttribute('aria-hidden','true'); | |
| if(!menu?.classList.contains('open')) setLock(false); | |
| } | |
| $$('.access-open').forEach(btn => btn.addEventListener('click', openAccess)); | |
| $('#closeAccess')?.addEventListener('click', closeAccess); | |
| accessPanel?.addEventListener('click', e => { if(e.target === accessPanel) closeAccess(); }); | |
| let saved = {}; | |
| try { saved = JSON.parse(localStorage.getItem('psrcAccess') || '{}'); } catch(_) {} | |
| if(saved.scale) root.style.setProperty('--font-scale', saved.scale); | |
| if(saved.contrast) document.body.classList.add('high-contrast'); | |
| if(saved.motion) document.body.classList.add('reduce-motion'); | |
| function saveAccess(){ | |
| localStorage.setItem('psrcAccess', JSON.stringify({ | |
| scale: getComputedStyle(root).getPropertyValue('--font-scale').trim() || '1', | |
| contrast: document.body.classList.contains('high-contrast'), | |
| motion: document.body.classList.contains('reduce-motion') | |
| })); | |
| } | |
| accessPanel?.addEventListener('click', e => { | |
| const action = e.target.dataset.access; | |
| if(!action) return; | |
| const scale = parseFloat(getComputedStyle(root).getPropertyValue('--font-scale')) || 1; | |
| if(action === 'fontPlus') root.style.setProperty('--font-scale', Math.min(1.18, scale + .06)); | |
| if(action === 'fontMinus') root.style.setProperty('--font-scale', Math.max(.92, scale - .06)); | |
| if(action === 'contrast') document.body.classList.toggle('high-contrast'); | |
| if(action === 'motion') document.body.classList.toggle('reduce-motion'); | |
| if(action === 'reset'){ | |
| root.style.setProperty('--font-scale', 1); | |
| document.body.classList.remove('high-contrast','reduce-motion'); | |
| localStorage.removeItem('psrcAccess'); | |
| return; | |
| } | |
| saveAccess(); | |
| }); | |
| toTop?.addEventListener('click', () => window.scrollTo({top:0, behavior: document.body.classList.contains('reduce-motion') ? 'auto' : 'smooth'})); | |
| addEventListener('keydown', e => { if(e.key === 'Escape'){ closeMenu(); closeAccess(); } }); | |
| })(); | |