PageSourceSearch

https://a-therapy.com/assets/js/main.js

js a-therapy.com collected 2026-10-08 00:11:41 UTC 12,903 bytes, 331 lines download raw bytes

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.