1/** 2 * A-Therapy Theme JavaScript 3 * 4 * Handles: video controls, team slider, quotes carousel, 5 * mobile nav toggle, article year filtering. 6 * 7 * @package A-Therapy 8 */ 9 10document.addEventListener('DOMContentLoaded', function() { 11 12 // ================================================================ 13 // Mobile Navigation Toggle 14 // ================================================================ 15 16 const navToggle = document.querySelector('.at-nav-toggle'); 17 const nav = document.querySelector('.at-nav'); 18 19 if (navToggle && nav) { 20 navToggle.addEventListener('click', function() { 21 const isOpen = nav.classList.toggle('at-nav-open'); 22 this.setAttribute('aria-expanded', isOpen); 23 this.querySelector('span').textContent = isOpen ? '\u2715' : '\u2630'; 24 }); 25 } 26 27 // ================================================================ 28 // Video Controls 29 // ================================================================ 30 31 const video = document.getElementById('at-main-video'); 32 if (video) { 33 document.querySelectorAll('.at-video-btn').forEach(function(btn) { 34 btn.addEventListener('click', function() { 35 const action = this.dataset.action; 36 if (action === 'toggle-play') { 37 var pauseIcon = this.querySelector('.icon-pause'); 38 var playIcon = this.querySelector('.icon-play'); 39 if (video.paused) { 40 video.play(); 41 if (pauseIcon) pauseIcon.style.display = 'block'; 42 if (playIcon) playIcon.style.display = 'none'; 43 } else { 44 video.pause(); 45 if (pauseIcon) pauseIcon.style.display = 'none'; 46 if (playIcon) playIcon.style.display = 'block'; 47 } 48 } else if (action === 'toggle-mute') { 49 video.muted = !video.muted; 50 var mutedIcon = this.querySelector('.icon-muted'); 51 var unmutedIcon = this.querySelector('.icon-unmuted'); 52 if (video.muted) { 53 if (mutedIcon) mutedIcon.style.display = 'block'; 54 if (unmutedIcon) unmutedIcon.style.display = 'none'; 55 } else { 56 if (mutedIcon) mutedIcon.style.display = 'none'; 57 if (unmutedIcon) unmutedIcon.style.display = 'block'; 58 } 59 } 60 }); 61 }); 62 } 63 64 // ================================================================ 65 // Team Carousel (auto-scroll, 2 visible, advance by 1) 66 // ================================================================ 67 68 var teamTrack = document.getElementById('at-team-track'); 69 if (teamTrack) { 70 var teamMembers = teamTrack.querySelectorAll('.at-team-member'); 71 var totalMembers = teamMembers.length; 72 var teamDots = document.querySelectorAll('.at-team-dot'); 73 var teamPos = 0; 74 var teamTransitioning = false; 75 76 // Clone all members for infinite loop 77 if (totalMembers > 2) { 78 for (var ci = 0; ci < totalMembers; ci++) { 79 teamTrack.appendChild(teamMembers[ci].cloneNode(true)); 80 } 81 for (var cj = totalMembers - 1; cj >= 0; cj--) { 82 teamTrack.insertBefore(teamMembers[cj].cloneNode(true), teamTrack.firstChild); 83 } 84 teamPos = totalMembers; 85 setTeamPos(false); 86 } 87 88 function getSlideW() { 89 var s = teamTrack.querySelector('.at-team-member'); 90 if (!s) return 0; 91 return s.getBoundingClientRect().width + 32; // 32 = CSS gap 92 } 93 94 function setTeamPos(animate) { 95 var px = teamPos * getSlideW(); 96 teamTrack.style.transition = animate !== false ? 'transform 0.6s cubic-bezier(0.25, 0.1, 0.25, 1)' : 'none'; 97 teamTrack.style.transform = 'translateX(-' + px + 'px)'; 98 } 99 100 function updateTeamDots() { 101 if (!teamDots.length) return; 102 var realPos = ((teamPos - totalMembers) % totalMembers + totalMembers) % totalMembers;
103 teamDots.forEach(function(d, i) { 104 var second = (realPos + 1) % totalMembers; 105 d.classList.toggle('active', i === realPos || i === second); 106 }); 107 } 108 109 function teamAdvance() { 110 if (teamTransitioning) return; 111 teamTransitioning = true; 112 teamPos++; 113 setTeamPos(true); 114 updateTeamDots(); 115 } 116 117 teamTrack.addEventListener('transitionend', function() { 118 teamTransitioning = false; 119 if (teamPos >= totalMembers * 2) { 120 teamPos = totalMembers; 121 setTeamPos(false); 122 } 123 }); 124 125 // Auto-advance every 5 seconds 126 if (totalMembers > 2) { 127 setInterval(teamAdvance, 5000); 128 } 129 130 // Dots click to jump 131 teamDots.forEach(function(dot) { 132 dot.addEventListener('click', function() { 133 if (teamTransitioning) return; 134 var target = parseInt(this.dataset.teamDot); 135 teamPos = totalMembers + target; 136 teamTransitioning = true; 137 setTeamPos(true); 138 updateTeamDots(); 139 }); 140 }); 141 142 // Recalculate on resize 143 var teamResizeT; 144 window.addEventListener('resize', function() { 145 clearTimeout(teamResizeT); 146 teamResizeT = setTimeout(function() { setTeamPos(false); }, 150); 147 }); 148 149 updateTeamDots(); 150 } 151 152 // ================================================================ 153 // Endorsements Carousel 154 // ================================================================ 155 156 const endorseCarousel = document.getElementById('at-endorse-carousel'); 157 if (endorseCarousel) { 158 var endorseSlides = endorseCarousel.querySelectorAll('.at-nsc-endorsement-slide'); 159 var endorseCounter = document.getElementById('at-endorse-counter'); 160 var endorsePrev = document.getElementById('at-endorse-prev'); 161 var endorseNext = document.getElementById('at-endorse-next'); 162 var currentEndorse = 0; 163 var totalEndorse = endorseSlides.length; 164 165 // Equalize heights: show all slides briefly, find max, set container height 166 var maxH = 0; 167 endorseSlides.forEach(function(s) { s.style.display = 'block'; }); 168 endorseSlides.forEach(function(s) { if (s.offsetHeight > maxH) maxH = s.offsetHeight; }); 169 endorseCarousel.style.minHeight = maxH + 'px'; 170 endorseSlides.forEach(function(s, i) { s.style.display = i === 0 ? 'block' : 'none'; }); 171 172 function showEndorsement(index) { 173 // Wrap around 174 if (index < 0) index = totalEndorse - 1; 175 if (index >= totalEndorse) index = 0; 176 177 endorseSlides.forEach(function(slide, i) { 178 slide.style.display = i === index ? 'block' : 'none'; 179 }); 180 181 currentEndorse = index; 182 if (endorseCounter) { 183 endorseCounter.textContent = (index + 1) + ' / ' + totalEndorse; 184 } 185 } 186 187 if (endorsePrev) { 188 endorsePrev.addEventListener('click', function() { 189 showEndorsement(currentEndorse - 1); 190 }); 191 } 192 if (endorseNext) { 193 endorseNext.addEventListener('click', function() { 194 showEndorsement(currentEndorse + 1); 195 }); 196 } 197 198 // Auto-advance every 8 seconds 199 if (totalEndorse > 1) { 200 setInterval(function() { 201 showEndorsement(currentEndorse + 1); 202 }, 8000); 203 } 204 } 205 206 // ================================================================ 207 // Quotes Carousel 208 // ================================================================ 209 210 const quotesSection = document.querySelector('.at-quotes'); 211 if (quotesSection) { 212 const slides = quotesSection.querySelectorAll('.at-quote-slide'); 213 const dots = quotesSection.querySelectorAll('[data-quote-dot]'); 214 let currentQuote = 0; 215 216 function showQuote(index) {
217 slides.forEach(function(s, i) { 218 s.style.display = i === index ? 'block' : 'none'; 219 }); 220 dots.forEach(function(d, i) { 221 d.style.background = i === index ? 'var(--at-deep-green)' : '#ccc'; 222 d.classList.toggle('active', i === index); 223 }); 224 currentQuote = index; 225 } 226 227 dots.forEach(function(dot) { 228 dot.addEventListener('click', function() { 229 showQuote(parseInt(this.dataset.quoteDot)); 230 }); 231 }); 232 233 // Auto-advance every 8 seconds 234 if (slides.length > 1) { 235 setInterval(function() { 236 showQuote((currentQuote + 1) % slides.length); 237 }, 8000); 238 } 239 } 240 241 // ================================================================ 242 // Article Year Filtering (client-side on homepage) 243 // ================================================================ 244 245 const filterPills = document.querySelectorAll('.at-articles-filters .at-filter-pill'); 246 const articleCards = document.querySelectorAll('.at-articles-grid .at-article-card'); 247 248 if (filterPills.length && articleCards.length) { 249 filterPills.forEach(function(pill) { 250 pill.addEventListener('click', function(e) { 251 e.preventDefault(); 252 const year = this.dataset.year; 253 254 filterPills.forEach(function(p) { p.classList.remove('active'); }); 255 this.classList.add('active'); 256 257 articleCards.forEach(function(card) { 258 if (year === 'all' || card.dataset.year === year) { 259 card.style.display = 'flex'; 260 } else { 261 card.style.display = 'none'; 262 } 263 }); 264 }); 265 }); 266 } 267 268 // ================================================================ 269 // Approach Cards Auto-scroll (scrolls within container, no page jump) 270 // ================================================================ 271 272 var approachGrid = document.getElementById('at-approach-grid'); 273 if (approachGrid) { 274 var approachCards = approachGrid.querySelectorAll('.at-approach-card'); 275 if (approachCards.length > 1) { 276 var approachIndex = 0; 277 var approachScrolling = true; 278 var cardWidth = 340; // 320px card + 20px gap 279 280 setInterval(function() { 281 if (!approachScrolling) return; 282 approachIndex++; 283 if (approachIndex >= approachCards.length) { 284 approachIndex = 0; 285 } 286 approachGrid.scrollTo({ 287 left: approachIndex * cardWidth, 288 behavior: 'smooth' 289 }); 290 }, 4000); 291 292 // Pause autoscroll on manual interaction 293 approachGrid.addEventListener('pointerdown', function() { approachScrolling = false; }); 294 approachGrid.addEventListener('wheel', function() { approachScrolling = false; }, { passive: true }); 295 // Resume after 10s of no interaction 296 approachGrid.addEventListener('pointerup', function() { 297 setTimeout(function() { approachScrolling = true; }, 10000); 298 }); 299 } 300 } 301 302 // ================================================================ 303 // Show More Testimonials 304 // ================================================================ 305 306 var showMoreBtn = document.getElementById('at-show-more-btn'); 307 if (showMoreBtn) { 308 showMoreBtn.addEventListener('click', function() { 309 var hidden = document.querySelectorAll('.at-nsc-testimonial-hidden'); 310 hidden.forEach(function(card) { 311 card.classList.remove('at-nsc-testimonial-hidden'); 312 }); 313 document.getElementById('at-show-more-wrap').style.display = 'none'; 314 }); 315 } 316 317 // ================================================================ 318 // Smooth Scroll for Anchor Links 319 // ================================================================ 320 321 document.querySelectorAll('a[href^="#"]').forEach(function(link) { 322 link.addEventListener('click', function(e) { 323 const target = document.querySelector(this.getAttribute('href')); 324 if (target) { 325 e.preventDefault(); 326 target.scrollIntoView({ behavior: 'smooth', block: 'start' }); 327 } 328 }); 329 }); 330 331});
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.