1import Alpine from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/module.esm.js'; 2import collapse from 'https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/module.esm.js'; 3 4import alpinePhonePlugin from '../scripts/utils-alpine-phone-plugin.js'; 5import resolveCustomFieldsPlugin from "../scripts/resolve-custom-fields.js"; 6 7// === CANVAS Carousel Detection === 8import canvasCarouselDetector from "../scripts/utils-canvas-carousel-detector.js"; 9 10// === Core UI Modules === 11import navbarModule from "../scripts/js-navbars.js"; 12import heroCarousel from "../scripts/js-hero-carousel.js"; 13import modalComponent from "../scripts/js-modal.js"; 14 15// Import the Swiper component 16import swiperComponent from "../scripts/js-swiper-component.js"; 17 18// Vision Source - Services (Design Version 2) slider (loop + custom pagination on top of swiperComponent) 19import vsServicesSlider from "../scripts/vs-services-slider.js"; 20import vsServicesDatasetSlider from "../scripts/vs-services-dataset-slider.js"; 21 22// Import social sharing handler to make it available globally 23import '../scripts/social-sharing-handler.js'; 24 25 26// === Configuration Data === 27import { 28 generalConfig, 29 locationsConfig, 30 modalConfig, 31 heroConfig, 32 insurancesConfig, 33 welcomeConfig, 34 ctasConfig, 35 headingsConfig, 36 servicesConfig, 37 teamConfig, 38 brandsConfig, 39 testimonialsConfig, 40 faqConfig, 41 mapSettings, 42 uiState, 43 interiorConfig, 44} from "../scripts/config-index.js"; 45 46// === Vision Source (client design system) === 47import { visionSourceConfig } from "../scripts/vs-index.js"; 48import { vsFieldValues } from "../scripts/vs-field-values.js"; 49 50// === Alpine Stores (Make configs available before theme initialization) === 51 52Alpine.store("servicesData", servicesConfig.allServices); 53Alpine.store("pageServicesData", servicesConfig.pageServices); 54Alpine.store("vsTeam", { active: 0 }); 55Alpine.store("general", generalConfig); 56Alpine.store("locationsData", locationsConfig); 57Alpine.store("modalConfig", modalConfig); 58 59// Make generalConfig globally available for analytics and other early-loading scripts 60window.canvasSnapConfig = generalConfig; 61 62canvasCarouselDetector.init(); 63 64function snap() { 65 return { 66 general: generalConfig, 67 locations: locationsConfig, 68 heroSettings: heroConfig, 69 homepage: { 70 headings: headingsConfig, 71 welcome: welcomeConfig, 72 ctas: ctasConfig, 73 services: servicesConfig, 74 team: teamConfig, 75 brands: brandsConfig, 76 insurances: insurancesConfig, 77 testimonials: testimonialsConfig, 78 faq: faqConfig, 79 }, 80 interior: interiorConfig, 81 visionSource: visionSourceConfig, 82 mapSettings, // Make map settings available to Alpine components 83 84 // === UI Modules === 85 navbar: navbarModule, 86 heroCarousel, 87 88 89 // === CANVAS Carousel Detector === 90 canvasCarousels: canvasCarouselDetector, 91 92 // === UI State === 93 resizeObserver: null, 94 95 init() { 96 97 // Register CANVAS carousel detector as Alpine store 98 Alpine.store("canvasCarousels", canvasCarouselDetector); 99 100 // Initial setup with small delay to ensure DOM is ready 101 requestAnimationFrame(() => { 102 this.updateNavbarHeight(); 103 this.setupEventListeners(); 104 105 this.initializeMap(); // Dynamically load map 106 this.initializeGallery(); 107 }); 108 109 Alpine.effect(() => { 110 if (this.navbar.scrolled) { 111 this.updateNavbarHeight(); 112 } 113 }); 114 }, 115 116 waitForElement(selector, timeoutMs = 5000) { 117 const existing = document.querySelector(selector); 118 if (existing) return Promise.resolve(existing); 119 120 return new Promise((resolve) => { 121 const done = (el) => { 122 observer.disconnect(); 123 clearTimeout(timer); 124 resolve(el); 125 }; 126 const observer = new MutationObserver(() => { 127 const el = document.querySelector(selector); 128 if (el) done(el); 129 }); 130 const timer = setTimeout(() => done(null), timeoutMs); 131 observer.observe(document.body, { childList: true, subtree: true }); 132 }); 133 }, 134 135 /** 136 * Dynamically loads and initializes the map once its container exists on the page. 137 */ 138 initializeMap() { 139 this.waitForElement('.google-map-container').then((container) => { 140 if (!container) return; 141 import('../scripts/map-handler.js') 142 .then(module => { 143 const mapHandler = module.default(); 144 mapHandler.init(); 145 Alpine.store('mapHandler', mapHandler); 146 }) 147 .catch(err => console.error('Error loading map module', err)); 148 }); 149 150 // Vision Source V2 locations: the design's own pin map, separate from the shared 151 // multi-location map-handler above. 152 this.waitForElement('.vs-pin-map').then((pinMap) => { 153 if (!pinMap) return; 154 import('../scripts/map-vs-pin-map.js') 155 .then(module => module.initVsPinMaps()) 156 .catch(err => console.error('Error loading VS pin map module', err)); 157 }); 158 }, 159 160 initializeGallery() { 161 this.waitForElement('.blueimp-gallery').then((overlay) => { 162 if (!overlay) return; 163 import('../scripts/gallery-lightbox.js') 164 .then(module => module.initGalleryLightboxes()) 165 .catch(err => console.error('Error loading gallery lightbox module', err)); 166 }); 167 }, 168 169 initializeAnalytics() { 170 setTimeout(() => { 171 initPhoneTracking(this.general); 172 initFormRedirects(this.general); 173 }, 1000); 174 }, 175 176 setupEventListeners() { 177 this.setupResizeListener(); 178 this.setupThemeChangeListener(); 179 180 document.addEventListener('canvas-carousel-slide-change', (event) => { 181 console.log('CANVAS Carousel slide changed:', event.detail); 182 }); 183 184 this.$el.addEventListener('alpine:destroying', () => { 185 this.cleanup(); 186 }); 187 188 const externalFormUrl = (value) => { 189 const v = (value || "").trim(); 190 if (/^(https?:)?\/\//i.test(v)) return v; 191 if (/^www\./i.test(v)) return `https://${v}`; 192 return null; 193 }; 194 195 document.querySelectorAll('.ry-pdf-link').forEach((link) => { 196 if (externalFormUrl(link.getAttribute('href'))) link.textContent = 'Go to Form';
197 }); 198 199 // PDF Viewer using event delegation 200 document.body.addEventListener('click', function(e) { 201 const target = e.target.closest('.ry-pdf-link'); 202 if (target) { 203 e.preventDefault(); 204 const external = externalFormUrl(target.getAttribute("href")); 205 if (external) { 206 window.open(external, "_blank", "noopener"); 207 return; 208 } 209 const filename = (target.getAttribute("href") || "").trim(); 210 const url = new URL(window.location.href); 211 const dirname = url.origin + url.pathname.substring(0, url.pathname.lastIndexOf("/")); 212 window.open(`${dirname}/viewer.html?open_file=${filename}`, "_blank"); 213 } 214 }); 215 }, 216 217 cleanup() { 218 if (this.resizeObserver) { 219 this.resizeObserver.disconnect(); 220 } 221 222 if (canvasCarouselDetector) { 223 canvasCarouselDetector.destroy(); 224 } 225 }, 226 227 228 setupResizeListener() { 229 // Use ResizeObserver for more efficient resize handling when available 230 if (window.ResizeObserver) { 231 this.resizeObserver = new ResizeObserver( 232 this.debouncedNavbarHeightUpdate.bind(this), 233 ); 234 const navbarElement = document.querySelector("nav"); 235 if (navbarElement) { 236 this.resizeObserver.observe(navbarElement); 237 } 238 } else { 239 // Fallback to window resize event with debounce 240 let resizeTimeout; 241 window.addEventListener("resize", () => { 242 cancelAnimationFrame(resizeTimeout); 243 resizeTimeout = requestAnimationFrame(() => { 244 this.updateNavbarHeight(); 245 }); 246 }); 247 } 248 }, 249 250 251 setupThemeChangeListener() { 252 window.addEventListener("themechange", (e) => { 253 this.isDarkMode = e.detail.theme === "dark"; 254 }); 255 }, 256 257 258 debouncedNavbarHeightUpdate() { 259 cancelAnimationFrame(this._navbarUpdateFrame); 260 this._navbarUpdateFrame = requestAnimationFrame(() => { 261 this.updateNavbarHeight(); 262 }); 263 }, 264 265 /** 266 * Cleans up all event listeners 267 */ 268 cleanupEventListeners() { 269 if (this.resizeObserver) { 270 this.resizeObserver.disconnect(); 271 } 272 cancelAnimationFrame(this._navbarUpdateFrame); 273 }, 274 275 /** 276 * Updates the current navbar height and sets a CSS variable 277 */ 278 updateNavbarHeight() { 279 const navbarElement = document.querySelector("nav"); 280 if (navbarElement) { 281 const height = navbarElement.offsetHeight; 282 283 // Only update if height has changed 284 if (height !== this.navbar.height) { 285 this.navbar.height = height; 286 document.documentElement.style.setProperty( 287 "--navbar-height", 288 `${height}px`, 289 ); 290 } 291 } 292 }, 293 294 getHeroPositionClasses() { 295 return uiState.getHeroPositionClasses(this.heroSettings.contentPosition); 296 }, 297 }; 298} 299 300 301window.Alpine = Alpine; 302 303Alpine.data("servicesSection", servicesConfig.servicesComponent); 304Alpine.data("modalComponent", modalComponent); 305Alpine.data("swiperComponent", swiperComponent); 306Alpine.data("vsServicesSlider", vsServicesSlider); 307Alpine.data("vsServicesDatasetSlider", vsServicesDatasetSlider); 308Alpine.data("vsTeamAutoplay", () => ({ 309 _timer: null, 310 _delay: 5000, 311 _count() { 312 return document.querySelectorAll(".module-team .group-photo .swiper-slide").length; 313 }, 314 315 _scroll() { 316 const el = document.querySelector(".module-team .group-photo .swiper"); 317 if (el && el.swiper) el.swiper.slideTo(this.$store.vsTeam.active, 600, false); 318 }, 319 // Select a member (thumbnail / dot click) and bring it into view. 320 goTo(i) { 321 this.$store.vsTeam.active = i; 322 this._scroll(); 323 }, 324 _advance() { 325 const s = this.$store.vsTeam; 326 const n = this._count(); 327 if (!n) return; 328 s.active = (s.active + 1) % n; 329 this._scroll(); 330 }, 331 play() { 332 this.stop(); 333 this._timer = setInterval(() => this._advance(), this._delay); 334 }, 335 stop() { 336 if (this._timer) { 337 clearInterval(this._timer); 338 this._timer = null; 339 } 340 }, 341})); 342Alpine.data("faqSection", () => ({ 343 homepage: faqConfig.homepage, 344 activeFaq: null, 345})); 346 347Alpine.data("snap", snap); 348Alpine.plugin(collapse); 349window.canvasFieldValues = Object.assign({}, vsFieldValues, window.canvasFieldValues); 350Alpine.plugin(resolveCustomFieldsPlugin); 351Alpine.plugin(alpinePhonePlugin); 352 353document.addEventListener('DOMContentLoaded', () => { 354 Alpine.start(); 355});
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.