1(function() { 2 'use strict'; 3 4 // Version number for debugging 5 const SWIPER_INIT_VERSION = '1.0.0'; 6 window._swiperInitVersion = SWIPER_INIT_VERSION; 7 8 // Accessibility message templates by content type 9 const SWIPER_A11Y_CONTEXTS = { 10 'events': { 11 containerRoleDescriptionMessage: 'Events carousel', 12 itemRoleDescriptionMessage: 'slide', 13 prevSlideMessage: 'Previous event', 14 nextSlideMessage: 'Next event', 15 firstSlideMessage: 'This is the first event', 16 lastSlideMessage: 'This is the last event', 17 paginationBulletMessage: 'Go to event {{index}}' 18 }, 19 'news': { 20 containerRoleDescriptionMessage: 'News articles carousel', 21 itemRoleDescriptionMessage: 'slide', 22 prevSlideMessage: 'Previous article', 23 nextSlideMessage: 'Next article', 24 firstSlideMessage: 'This is the first article', 25 lastSlideMessage: 'This is the last article', 26 paginationBulletMessage: 'Go to article {{index}}' 27 }, 28 'links': { 29 containerRoleDescriptionMessage: 'Links carousel', 30 itemRoleDescriptionMessage: 'slide', 31 prevSlideMessage: 'Previous link', 32 nextSlideMessage: 'Next link', 33 firstSlideMessage: 'This is the first link', 34 lastSlideMessage: 'This is the last link', 35 paginationBulletMessage: 'Go to link {{index}}' 36 }, 37 'infographics': { 38 containerRoleDescriptionMessage: 'Infographics carousel', 39 itemRoleDescriptionMessage: 'slide', 40 prevSlideMessage: 'Previous infographic', 41 nextSlideMessage: 'Next infographic', 42 firstSlideMessage: 'This is the first infographic', 43 lastSlideMessage: 'This is the last infographic', 44 paginationBulletMessage: 'Go to infographic {{index}}' 45 }, 46 'shuffle-gallery': { 47 containerRoleDescriptionMessage: 'Image gallery carousel', 48 itemRoleDescriptionMessage: 'slide', 49 prevSlideMessage: 'Previous image', 50 nextSlideMessage: 'Next image', 51 firstSlideMessage: 'This is the first image', 52 lastSlideMessage: 'This is the last image', 53 paginationBulletMessage: 'Go to image {{index}}' 54 }, 55 'video': { 56 containerRoleDescriptionMessage: 'Video carousel', 57 itemRoleDescriptionMessage: 'slide', 58 prevSlideMessage: 'Previous video', 59 nextSlideMessage: 'Next video', 60 firstSlideMessage: 'This is the first video', 61 lastSlideMessage: 'This is the last video', 62 paginationBulletMessage: 'Go to video {{index}}' 63 } 64 }; 65 66 // Pause videos on slide change 67 function addVideoPauseHandler(swiperInstance) { 68 if (!swiperInstance || !swiperInstance.on) return; 69 70 swiperInstance.on('slideChangeTransitionStart', function() { 71 this.el.querySelectorAll('video').forEach(function(video) { 72 if (!video.paused) { 73 video.pause(); 74 } 75 }); 76 }); 77 } 78 79 // Resolve direction from breakpoints for current viewport width 80 // Swiper ignores direction in breakpoints natively, so we resolve it manually 81 function resolveDirectionForViewport(config) { 82 var direction = config.direction || 'horizontal'; 83 if (config.breakpoints) { 84 var width = window.innerWidth; 85 var bps = Object.keys(config.breakpoints).map(Number).sort(function(a, b) { return a - b; }); 86 for (var i = bps.length - 1; i >= 0; i--) { 87 if (width >= bps[i]) { 88 if (config.breakpoints[bps[i]].direction) { 89 direction = config.breakpoints[bps[i]].direction; 90 } 91 break; 92 } 93 } 94 } 95 return direction; 96 } 97 98 // Create and initialize a Swiper instance with merged config 99 function createSwiperInstance(config) { 100 const containerId = config.containerId; 101 const snippetType = config.snippetType || 'generic'; 102 const selector = '#' + containerId + ' .swiper'; 103 const a11yContext = SWIPER_A11Y_CONTEXTS[snippetType] || {}; 104 105 // Build defaults (lowest priority) 106 const defaults = { 107 normalizeSlideIndex: false, 108 a11y: { 109 enabled: true, 110 containerRole: 'region', 111 containerRoleDescriptionMessage: a11yContext.containerRoleDescriptionMessage || 'Carousel', 112 itemRoleDescriptionMessage: a11yContext.itemRoleDescriptionMessage || 'slide', 113 prevSlideMessage: a11yContext.prevSlideMessage || 'Previous slide', 114 nextSlideMessage: a11yContext.nextSlideMessage || 'Next slide', 115 firstSlideMessage: a11yContext.firstSlideMessage || 'This is the first slide', 116 lastSlideMessage: a11yContext.lastSlideMessage || 'This is the last slide', 117 paginationBulletMessage: a11yContext.paginationBulletMessage || 'Go to slide {{index}}' 118 }, 119 keyboard: { 120 enabled: true, 121 onlyInViewport: true, 122 pageUpDown: true 123 } 124 }; 125 126 // Auto-generate navigation/pagination if not provided and enabled 127 if (config.swiperNavigation && !config.navigation) { 128 defaults.navigation = { 129 nextEl: '#' + containerId + ' .swiper-button-next', 130 prevEl: '#' + containerId + ' .swiper-button-prev' 131 }; 132 } 133 134 if (config.swiperPagination && !config.pagination) { 135 defaults.pagination = { 136 el: '#' + containerId + ' .swiper-pagination', 137 clickable: true 138 }; 139 } 140 141 // Start with top-level merge (shallow is fine for primitives) 142 const enhancedConfig = Object.assign({}, defaults, config); 143 144 // Deep merge nested objects to preserve defaults (fix shallow merge bug) 145 // Without this, config.a11y completely replaces defaults.a11y, losing enabled/containerRole
146 const nestedObjects = ['a11y', 'keyboard', 'navigation', 'pagination']; 147 nestedObjects.forEach(function(key) { 148 if (defaults[key] && config[key]) { 149 // Both exist - merge them (config wins for duplicate keys) 150 enhancedConfig[key] = Object.assign({}, defaults[key], config[key]); 151 } 152 // If only one exists, Object.assign already handled it correctly 153 }); 154 155 // Defensive check: warn if thumbs swiper is null (timing issue) 156 if (config.thumbs && config.thumbs.swiper === null) { 157 console.warn('Swiper: Thumbnail swiper not initialized yet for:', containerId); 158 } 159 160 // Deep merge event handlers - combine default and custom handlers 161 const defaultHandlers = { 162 breakpoint: function(swiper) { 163 // Swiper doesn't support direction in breakpoints natively â resolve it manually 164 if (swiper._swiperInitConfig) { 165 var intendedDirection = resolveDirectionForViewport(swiper._swiperInitConfig); 166 if (intendedDirection !== swiper.currentBreakpointDirection) { 167 swiper.changeDirection(intendedDirection, false); 168 swiper.currentBreakpointDirection = intendedDirection; 169 } 170 } 171 172 swiper.update(); 173 174 // Swiper's autoHeight only sets height, never removes it 175 if (!swiper.params.autoHeight && swiper.wrapperEl.style.height) { 176 swiper.wrapperEl.style.height = ''; 177 } 178 } 179 }; 180 181 if (config.on) { 182 enhancedConfig.on = {}; 183 184 // Get union of all event names from both default and config handlers 185 const allEvents = new Set([ 186 ...Object.keys(defaultHandlers), 187 ...Object.keys(config.on) 188 ]); 189 190 allEvents.forEach(function(eventName) { 191 const defaultHandler = defaultHandlers[eventName]; 192 const customHandler = config.on[eventName]; 193 194 if (defaultHandler && customHandler) { 195 // Both exist - create composite handler that calls both 196 enhancedConfig.on[eventName] = function() { 197 defaultHandler.apply(this, arguments); 198 customHandler.apply(this, arguments); 199 }; 200 } else { 201 // Only one exists - use whichever is defined 202 enhancedConfig.on[eventName] = defaultHandler || customHandler; 203 } 204 }); 205 } else { 206 // No custom handlers - use defaults only 207 enhancedConfig.on = defaultHandlers; 208 } 209 210 // Clean up meta properties not used by Swiper 211 delete enhancedConfig.snippetType; 212 delete enhancedConfig.containerId; 213 delete enhancedConfig.responsiveDisable; 214 delete enhancedConfig.swiperNavigation; 215 delete enhancedConfig.swiperPagination; 216 217 const instance = new Swiper(selector, enhancedConfig); 218 instance._swiperInitConfig = config; 219 220 // Resolve initial direction from breakpoints and apply if needed 221 var initialDirection = resolveDirectionForViewport(config); 222 if (initialDirection !== (config.direction || 'horizontal')) { 223 instance.changeDirection(initialDirection, false); 224 } 225 instance.currentBreakpointDirection = initialDirection; 226 227 addVideoPauseHandler(instance); 228 229 return instance; 230 } 231 232 // Parse responsive disable config from HTML class 233 function getResponsiveDisableFromClasses(container) { 234 if (!container) return null; 235 236 const classList = container.className; 237 const match = classList.match(/swiper-disable-at-(\w+)/); 238 239 if (match) { 240 const breakpointMap = { 241 'small': 320, 242 'mid': 768, 243 'large': 1024, 244 'xl': 1366, 245 'xxl': 1920 246 }; 247 248 return { 249 enabled: true, 250 breakpoint: breakpointMap[match[1]] || 1024 251 }; 252 } 253 254 return null; 255 } 256 257 // Get responsive disable config (HTML class takes priority over config object) 258 // Returns {enabled: true, breakpoint: number} or null 259 function getResponsiveDisableConfig(config, container) { 260 const classConfig = getResponsiveDisableFromClasses(container); 261 if (classConfig) { 262 return classConfig; 263 } 264 265 if (config.responsiveDisable && config.responsiveDisable.enabled) { 266 // Normalize config-based responsive disable to consistent shape 267 const breakpoint = config.responsiveDisable.breakpoint || 1024; 268 return { 269 enabled: true, 270 breakpoint: breakpoint 271 }; 272 } 273 274 return null; 275 } 276 277 // Handle responsive destroy/recreate at breakpoints 278 function handleResponsiveDisable(config, swiperInstance) { 279 const containerId = config.containerId; 280 const container = document.getElementById(containerId); 281 282 if (!container) { 283 console.warn('Swiper: Container not found for responsive disable:', containerId); 284 return; 285 } 286 287 const responsiveConfig = getResponsiveDisableConfig(config, container); 288 289 if (!responsiveConfig || !responsiveConfig.enabled) { 290 return; 291 } 292 293 const breakpoint = responsiveConfig.breakpoint; 294 const mediaQuery = window.matchMedia('(min-width: ' + breakpoint + 'px)'); 295 let currentInstance = swiperInstance; 296 297 function handleBreakpointChange(e) { 298 if (e.matches) { 299 if (currentInstance && !currentInstance.destroyed) { 300 currentInstance.destroy(true, true); 301 currentInstance = null; 302 } 303 container.classList.add('swiper-disabled'); 304 } else { 305 if (!currentInstance || currentInstance.destroyed) { 306 currentInstance = createSwiperInstance(config); 307 handleResponsiveDisable(config, currentInstance); 308 } 309 container.classList.remove('swiper-disabled'); 310 } 311 } 312 313 mediaQuery.addEventListener('change', handleBreakpointChange); 314 handleBreakpointChange(mediaQuery); 315 } 316 317 // Auto-discover and initialize all Swiper configs on DOM ready 318 document.addEventListener('DOMContentLoaded', function() { 319 // Check if Swiper library is loaded 320 if (typeof Swiper === 'undefined') {
321 console.error('Swiper: Swiper library not loaded. Ensure swiper-bundle.js is included before swiper-init.js'); 322 return; 323 } 324 325 // Find all swiper containers and initialize them 326 document.querySelectorAll('.swiper-container').forEach(function(container) { 327 // Derive config key from container ID 328 const containerId = container.id; 329 if (!containerId) { 330 console.warn('Swiper: Container missing ID attribute, skipping:', container); 331 return; 332 } 333 334 // Look for matching config in window 335 // Pattern: container ID "events_123_container" -> config key "events_123SwiperConfig" 336 const snippetId = containerId.replace(/_container$/, ''); 337 const configKey = snippetId + 'SwiperConfig'; 338 const config = window[configKey]; 339 340 if (!config || typeof config !== 'object') { 341 // No config found - skip silently (might be a swiper initialized elsewhere) 342 return; 343 } 344 345 if (!config.containerId) { 346 console.warn('Swiper: Config missing containerId property:', configKey); 347 return; 348 } 349 350 try { 351 const instance = createSwiperInstance(config); 352 handleResponsiveDisable(config, instance); 353 config._instance = instance; 354 } catch (error) { 355 console.error('Swiper: Failed to initialize:', { 356 configKey: configKey, 357 containerId: config.containerId, 358 error: error.message 359 }); 360 361 // Graceful degradation: remove swiper classes to show static list 362 container.classList.remove('swiper-container'); 363 container.classList.add('swiper-error'); 364 } 365 }); 366 }); 367})();
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.