1+ // JavaScript for Interactive Components
2+
3+ // Tab functionality
4+ function showTab ( tabIndex ) {
5+ // Get all tab links and content
6+ const tabLinks = document . querySelectorAll ( '.tab-link' ) ;
7+ const tabContents = document . querySelectorAll ( '.tab-content' ) ;
8+
9+ // Remove active class from all tabs and content
10+ tabLinks . forEach ( link => link . classList . remove ( 'active' ) ) ;
11+ tabContents . forEach ( content => content . classList . remove ( 'active' ) ) ;
12+
13+ // Add active class to selected tab and content
14+ tabLinks [ tabIndex ] . classList . add ( 'active' ) ;
15+ document . getElementById ( `tab-${ tabIndex } ` ) . classList . add ( 'active' ) ;
16+ }
17+
18+ // Accordion functionality
19+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
20+ const accordionHeaders = document . querySelectorAll ( '.accordion-header' ) ;
21+
22+ accordionHeaders . forEach ( header => {
23+ header . addEventListener ( 'click' , function ( ) {
24+ const accordionItem = this . parentElement ;
25+ const content = accordionItem . querySelector ( '.accordion-content' ) ;
26+
27+ // Toggle active class
28+ accordionItem . classList . toggle ( 'active' ) ;
29+
30+ // Close other accordion items (optional - remove if you want multiple open)
31+ accordionHeaders . forEach ( otherHeader => {
32+ if ( otherHeader !== this ) {
33+ otherHeader . parentElement . classList . remove ( 'active' ) ;
34+ }
35+ } ) ;
36+ } ) ;
37+ } ) ;
38+ } ) ;
39+
40+ // Photo Gallery functionality
41+ let currentGallerySet = 0 ;
42+ let galleryImages = [ ] ;
43+ let imagesPerView = 3 ;
44+
45+ // Initialize gallery when page loads
46+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
47+ initializeGallery ( ) ;
48+ updateGalleryLayout ( ) ;
49+ window . addEventListener ( 'resize' , updateGalleryLayout ) ;
50+ } ) ;
51+
52+ function initializeGallery ( ) {
53+ const galleryItems = document . querySelectorAll ( '.gallery-item' ) ;
54+ galleryImages = Array . from ( galleryItems ) ;
55+
56+ updateGalleryInfo ( ) ;
57+ updateIndicators ( ) ;
58+ updateNavigationButtons ( ) ;
59+ }
60+
61+ function updateGalleryLayout ( ) {
62+ const width = window . innerWidth ;
63+
64+ // Determine images per view based on screen size
65+ if ( width <= 480 ) {
66+ imagesPerView = 1 ;
67+ } else if ( width <= 768 ) {
68+ imagesPerView = 2 ;
69+ } else {
70+ imagesPerView = 3 ;
71+ }
72+
73+ // Reset to first set if current set is out of bounds
74+ const maxSets = Math . ceil ( galleryImages . length / imagesPerView ) ;
75+ if ( currentGallerySet >= maxSets ) {
76+ currentGallerySet = 0 ;
77+ }
78+
79+ updateGalleryDisplay ( ) ;
80+ updateGalleryInfo ( ) ;
81+ updateIndicators ( ) ;
82+ updateNavigationButtons ( ) ;
83+ }
84+
85+ function updateGalleryDisplay ( ) {
86+ const track = document . getElementById ( 'gallery-track' ) ;
87+ if ( ! track ) return ;
88+
89+ const translateX = - ( currentGallerySet * 100 ) ;
90+ track . style . transform = `translateX(${ translateX } %)` ;
91+
92+ // Update gallery item widths based on current view
93+ galleryImages . forEach ( item => {
94+ if ( imagesPerView === 1 ) {
95+ item . style . flex = '0 0 100%' ;
96+ } else if ( imagesPerView === 2 ) {
97+ item . style . flex = '0 0 calc(50% - 10px)' ;
98+ } else {
99+ item . style . flex = '0 0 calc(33.333% - 14px)' ;
100+ }
101+ } ) ;
102+ }
103+
104+ function nextImages ( ) {
105+ const maxSets = Math . ceil ( galleryImages . length / imagesPerView ) ;
106+
107+ // Loop back to beginning when reaching the end
108+ if ( currentGallerySet < maxSets - 1 ) {
109+ currentGallerySet ++ ;
110+ } else {
111+ currentGallerySet = 0 ; // Loop back to start
112+ }
113+
114+ updateGalleryDisplay ( ) ;
115+ updateGalleryInfo ( ) ;
116+ updateIndicators ( ) ;
117+ updateNavigationButtons ( ) ;
118+ }
119+
120+ function previousImages ( ) {
121+ const maxSets = Math . ceil ( galleryImages . length / imagesPerView ) ;
122+
123+ // Loop to end when going back from beginning
124+ if ( currentGallerySet > 0 ) {
125+ currentGallerySet -- ;
126+ } else {
127+ currentGallerySet = maxSets - 1 ; // Loop to last set
128+ }
129+
130+ updateGalleryDisplay ( ) ;
131+ updateGalleryInfo ( ) ;
132+ updateIndicators ( ) ;
133+ updateNavigationButtons ( ) ;
134+ }
135+
136+ function goToSet ( setIndex ) {
137+ const maxSets = Math . ceil ( galleryImages . length / imagesPerView ) ;
138+
139+ if ( setIndex >= 0 && setIndex < maxSets ) {
140+ currentGallerySet = setIndex ;
141+ updateGalleryDisplay ( ) ;
142+ updateGalleryInfo ( ) ;
143+ updateIndicators ( ) ;
144+ updateNavigationButtons ( ) ;
145+ }
146+ }
147+
148+ function updateGalleryInfo ( ) {
149+ const currentImagesSpan = document . getElementById ( 'current-images' ) ;
150+ const totalImagesSpan = document . getElementById ( 'total-images' ) ;
151+
152+ if ( currentImagesSpan && totalImagesSpan ) {
153+ const startImage = ( currentGallerySet * imagesPerView ) + 1 ;
154+ const endImage = Math . min ( startImage + imagesPerView - 1 , galleryImages . length ) ;
155+
156+ currentImagesSpan . textContent = `${ startImage } -${ endImage } ` ;
157+ totalImagesSpan . textContent = galleryImages . length ;
158+ }
159+ }
160+
161+ function updateIndicators ( ) {
162+ const indicators = document . querySelectorAll ( '.indicator' ) ;
163+ const maxSets = Math . ceil ( galleryImages . length / imagesPerView ) ;
164+
165+ // Hide/show indicators based on number of sets needed
166+ indicators . forEach ( ( indicator , index ) => {
167+ if ( index < maxSets ) {
168+ indicator . style . display = 'block' ;
169+ indicator . classList . toggle ( 'active' , index === currentGallerySet ) ;
170+ } else {
171+ indicator . style . display = 'none' ;
172+ }
173+ } ) ;
174+ }
175+
176+ function updateNavigationButtons ( ) {
177+ const prevBtn = document . querySelector ( '.gallery-prev' ) ;
178+ const nextBtn = document . querySelector ( '.gallery-next' ) ;
179+
180+ // Since we now have looping, navigation buttons are never disabled
181+ if ( prevBtn ) {
182+ prevBtn . disabled = false ;
183+ }
184+
185+ if ( nextBtn ) {
186+ nextBtn . disabled = false ;
187+ }
188+ }
189+
190+ // Add keyboard navigation for accessibility
191+ document . addEventListener ( 'keydown' , function ( e ) {
192+ if ( e . target . closest ( '.photo-gallery-section' ) ) {
193+ if ( e . key === 'ArrowLeft' ) {
194+ e . preventDefault ( ) ;
195+ previousImages ( ) ;
196+ } else if ( e . key === 'ArrowRight' ) {
197+ e . preventDefault ( ) ;
198+ nextImages ( ) ;
199+ }
200+ }
201+ } ) ;
202+
203+ // Touch/swipe support for mobile
204+ let touchStartX = 0 ;
205+ let touchEndX = 0 ;
206+
207+ document . addEventListener ( 'touchstart' , function ( e ) {
208+ if ( e . target . closest ( '.gallery-viewport' ) ) {
209+ touchStartX = e . changedTouches [ 0 ] . screenX ;
210+ }
211+ } ) ;
212+
213+ document . addEventListener ( 'touchend' , function ( e ) {
214+ if ( e . target . closest ( '.gallery-viewport' ) ) {
215+ touchEndX = e . changedTouches [ 0 ] . screenX ;
216+ handleSwipe ( ) ;
217+ }
218+ } ) ;
219+
220+ function handleSwipe ( ) {
221+ const swipeThreshold = 50 ;
222+ const swipeLength = touchEndX - touchStartX ;
223+
224+ if ( Math . abs ( swipeLength ) > swipeThreshold ) {
225+ if ( swipeLength > 0 ) {
226+ previousImages ( ) ; // Swipe right
227+ } else {
228+ nextImages ( ) ; // Swipe left
229+ }
230+ }
231+ }
0 commit comments