1// (function(){ 2// const toggle = document.querySelector('.nav-toggle'); 3// const nav = document.querySelector('.primary-nav'); 4// if(toggle && nav){ 5// toggle.addEventListener('click', () => { 6// const open = nav.classList.toggle('open'); 7// toggle.setAttribute('aria-expanded', open ? 'true' : 'false'); 8// }); 9// // close on ESC 10// document.addEventListener('keydown', (e) => { 11// if(e.key === 'Escape'){ nav.classList.remove('open'); toggle.setAttribute('aria-expanded','false'); } 12// }); 13// } 14// })(); 15 16 17"use strict"; 18 19document.addEventListener('DOMContentLoaded', function() { 20 21 // Helper functions 22 const $ = (selector, parent = document) => parent.querySelector(selector); 23 const $$ = (selector, parent = document) => parent.querySelectorAll(selector); 24 25 const headerHeight = function() { 26 const header = $("header.wst-header.static-header"); 27 return header ? header.offsetHeight : 0; 28 }; 29 30 let hH; 31 32 function resizeHeader() { 33 const e = headerHeight(); 34 const hero = $(".hero"); 35 const childHeader = $(".child-header"); 36 37 if (hero) hero.style.paddingTop = e + "px"; 38 if (childHeader) childHeader.style.paddingTop = e + "px"; 39 } 40 41 setTimeout(function() { 42 hH = headerHeight(); 43 }, 800); 44 45 setTimeout(function() { 46 resizeHeader(); 47 }, 800); 48 49 window.addEventListener('resize', resizeHeader); 50 51 // Initial scroll check 52 const stickyHeader = $("header.sticky-header"); 53 const staticHeader = $("header.static-header"); 54 const subMenus = $$(".sub-menu"); 55 56 if (window.scrollY > hH) { 57 if (stickyHeader) stickyHeader.classList.add("active"); 58 if (staticHeader) staticHeader.classList.add("hidden"); 59 subMenus.forEach(menu => menu.classList.add("scrollMenu")); 60 document.body.classList.add("sticky-active"); 61 } 62 63 // Mobile menu links with hash 64 $$('#mobile-menu :not(.no-link).menu-item a[href*="#"]:not([href="#"])').forEach(link => { 65 link.addEventListener('click', function() { 66 if (this.pathname === window.location.pathname) { 67 const mobileButton = $(".mobile-button"); 68 if (mobileButton) mobileButton.click(); 69 } 70 }); 71 }); 72 73 // Prevent default for hash-only links 74 $$('.wst-header .menu .menu-item a[href="#"]').forEach(link => { 75 link.addEventListener('click', function(e) { 76 e.preventDefault(); 77 }); 78 }); 79 80 // Scroll handler for sticky header 81 function handleScroll() { 82 if (window.scrollY > hH) { 83 if (stickyHeader) stickyHeader.classList.add("active"); 84 if (staticHeader) staticHeader.classList.add("hidden"); 85 subMenus.forEach(menu => menu.classList.add("scrollMenu")); 86 } else { 87 if (stickyHeader) stickyHeader.classList.remove("active"); 88 if (staticHeader) staticHeader.classList.remove("hidden"); 89 subMenus.forEach(menu => menu.classList.remove("scrollMenu")); 90 } 91 } 92 93 window.addEventListener('scroll', handleScroll); 94 window.addEventListener('ready', handleScroll); 95 96 // Mobile button toggle 97 const mobileButtons = $$(".mobile-button"); 98 mobileButtons.forEach(mobileButton => { 99 if (mobileButton) { 100 mobileButton.addEventListener('click', function () { 101 const bars = $$(".bar"); 102 const mobileMenu = $(".mobile-menu"); 103 const logo = $("header .logo"); 104 const html = document.documentElement; 105 106 bars.forEach(bar => { 107 bar.classList.remove("back"); 108 if (bar.classList.contains("change")) { 109 bar.classList.add("back"); 110 } 111 bar.classList.toggle("change"); 112 bar.classList.toggle("active"); 113 }); 114 115 if (mobileMenu) mobileMenu.classList.toggle("active"); 116 document.body.classList.toggle("overlay-active"); 117 if (logo) logo.classList.toggle("active"); 118 html.classList.toggle("mobileScroll"); 119 120 if (window.scrollY > hH && stickyHeader && !stickyHeader.classList.contains("active")) { 121 stickyHeader.classList.add("active"); 122 } 123 if (window.scrollY > hH && staticHeader && !staticHeader.classList.contains("hidden")) { 124 staticHeader.classList.add("hidden"); 125 } 126 }); 127 } 128 });
129 130 // Mobile overlay click 131 const mobileOverlay = $(".mobile-overlay"); 132 if (mobileOverlay) { 133 mobileOverlay.addEventListener('click', function() { 134 const bars = $$(".bar"); 135 const mobileMenu = $(".mobile-menu"); 136 const logo = $("header .logo"); 137 138 document.body.classList.remove("overlay-active"); 139 if (mobileMenu) mobileMenu.classList.remove("active"); 140 document.documentElement.classList.remove("mobileScroll"); 141 if (logo) logo.classList.remove("active"); 142 143 bars.forEach(bar => { 144 if (bar.classList.contains("change")) { 145 bar.classList.remove("change", "active"); 146 bar.classList.add("back"); 147 } 148 }); 149 }); 150 } 151 152 // Add toggle buttons to mobile menu items with submenus 153 $$(".mobile-menu ul li").forEach(li => { 154 if (li.querySelector('ul')) { 155 const button = document.createElement('button'); 156 button.className = 'wst-nav-toggle'; 157 button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 7.33l2.829-2.83 9.175 9.339 9.167-9.339 2.829 2.83-11.996 12.17z" fill="#ffffff"/></svg>'; 158 li.appendChild(button); 159 } 160 }); 161 162 // Mobile nav toggle functionality 163 $$(".wst-nav-toggle").forEach(toggle => { 164 toggle.addEventListener('click', function(e) { 165 e.preventDefault(); 166 this.parentElement.classList.toggle("toggled"); 167 168 const subMenu = this.previousElementSibling; 169 if (subMenu && subMenu.classList.contains('sub-menu')) { 170 if (subMenu.style.display === 'none' || !subMenu.style.display) { 171 subMenu.style.display = 'block'; 172 slideDown(subMenu); 173 } else { 174 slideUp(subMenu); 175 } 176 } 177 return false; 178 }); 179 }); 180 181 // Hide mobile submenus initially 182 $$(".mobile-menu .sub-menu").forEach(menu => { 183 menu.style.display = 'none'; 184 }); 185 186 // Page banner functionality 187 $$(".page-banner").forEach(banner => { 188 const id = banner.getAttribute("id"); 189 if (sessionStorage.getItem(id) === "hidden") { 190 banner.style.display = 'none'; 191 } 192 }); 193 194 $$(".page-banner .close-page-banner").forEach(closeBtn => { 195 closeBtn.addEventListener('click', function() { 196 const bannerId = this.getAttribute("data-banner"); 197 const banner = $("#" + bannerId); 198 if (banner) { 199 slideUp(banner, () => { 200 banner.style.display = 'none'; 201 }); 202 } 203 sessionStorage.setItem(bannerId, "hidden"); 204 }); 205 }); 206 207 // Intersection Observer for lazy loading animations 208 const loaders = $$(".wsta"); 209 const lazyOptions = { 210 rootMargin: "0px 0px 0px 0px" 211 }; 212 213 const observer = new IntersectionObserver(function(entries, observerInstance) { 214 entries.forEach(function(entry) { 215 if (entry.isIntersecting) { 216 entry.target.classList.add("activate"); 217 observerInstance.unobserve(entry.target); 218 } 219 }); 220 }, lazyOptions); 221 222 loaders.forEach(function(loader) { 223 observer.observe(loader); 224 }); 225 226 // Search Modal functionality 227 // Event listener for opening the search window 228// Get all matching buttons 229 const searchButtons = $$(".header__nav .header__tools-search"); 230 231// Loop through each button and add the same event listener 232 searchButtons.forEach(button => { 233 button.addEventListener('click', function() { 234 console.log("search display"); 235 $(".search-window").classList.add('window-active'); 236 document.body.classList.add('hide-scroll', 'search-window-open'); 237 $("#search").focus(); 238 }); 239 }); 240 // Event listener for closing the search window 241 242 $(".search-window-screen").addEventListener('click', function (event) { 243 // Find the parent element and remove the class 244 event.target.parentElement.classList.remove('window-active'); 245 $("body").classList.remove('hide-scroll', 'search-window-open'); 246 });
247 248 249 250 // Hero video functionality 251 function heroVideo() { 252 setTimeout(function() { 253 if (window.innerWidth > 1024) { 254 const container = $("#heroVideoContainer"); 255 const video = $("#heroVideo"); 256 257 if (container && video && !video.getAttribute("src")) { 258 const videoSrc = container.getAttribute("data-video"); 259 video.setAttribute("src", videoSrc); 260 } 261 } 262 }, 1000); 263 } 264 265 if ($(".hero.hero-video")) { 266 heroVideo(); 267 window.addEventListener('orientationchange', function() { 268 heroVideo(); 269 }); 270 } 271 272 // Home page hero links 273 if (document.body.classList.contains("home")) { 274 const heroLinks = $$(".hero-content ul.content-bottom li a"); 275 276 heroLinks.forEach(function(link) { 277 if (window.innerWidth >= 1025) { 278 link.addEventListener('mouseenter', function() { 279 const span = this.querySelector("span"); 280 if (span) slideToggle(span); 281 }); 282 link.addEventListener('mouseleave', function() { 283 const span = this.querySelector("span"); 284 if (span) slideToggle(span); 285 }); 286 } else { 287 const span = link.querySelector("span"); 288 if (span) span.style.display = 'block'; 289 } 290 }); 291 } 292 293 294 // Tabs functionality 295 const initializeTabs = function(tabComponent) { 296 297 const tabButtons = tabComponent.querySelectorAll('.tab-component__nav-btn'); 298 const tabPanels = tabComponent.querySelectorAll('.tab-component__panel'); 299 300 tabButtons.forEach(button => { 301 button.addEventListener('click', () => { 302 const tabIndex = button.getAttribute('data-tab-index'); 303 304 // Update buttons 305 tabButtons.forEach(btn => { 306 btn.classList.remove('active'); 307 btn.setAttribute('aria-selected', 'false'); 308 }); 309 button.classList.add('active'); 310 button.setAttribute('aria-selected', 'true'); 311 312 // Update panels 313 tabPanels.forEach(panel => { 314 if (panel.getAttribute('data-tab-index') === tabIndex) { 315 panel.classList.add('active'); 316 panel.removeAttribute('hidden'); 317 } else { 318 panel.classList.remove('active'); 319 panel.setAttribute('hidden', ''); 320 } 321 }); 322 }); 323 324 // Keyboard navigation 325 button.addEventListener('keydown', (e) => { 326 let newIndex; 327 328 if (e.key === 'ArrowRight') { 329 e.preventDefault(); 330 newIndex = (Array.from(tabButtons).indexOf(button) + 1) % tabButtons.length; 331 tabButtons[newIndex].click(); 332 tabButtons[newIndex].focus(); 333 } else if (e.key === 'ArrowLeft') { 334 e.preventDefault(); 335 newIndex = (Array.from(tabButtons).indexOf(button) - 1 + tabButtons.length) % tabButtons.length; 336 tabButtons[newIndex].click(); 337 tabButtons[newIndex].focus(); 338 } 339 }); 340 }); 341 }; 342 343 $$(".tab-component__container").forEach(function(tabs) { 344 initializeTabs(tabs); 345 }); 346 347 // Accordion functionality 348 const initializeAccordion = function(accordion) { 349 const accordionItems = accordion.querySelectorAll('.accordion-item'); 350 const images = accordion.querySelectorAll('.accordion-image'); 351 let currentImageIndex = 0; 352 accordionItems.forEach(function(item,index) { 353 // if (!item.classList.contains("active")) { 354 // const slide = item.querySelector(".image-container"); 355 // if (slide) slide.style.display = 'none'; 356 // } 357 const title = item.querySelector('.accordion-title'); 358 const icon = item.querySelector('.accordion-icon'); 359 360 title.addEventListener('click', function() { 361 const isActive = item.classList.contains('active'); 362 363 if (isActive) { 364 item.classList.remove('active'); 365 icon.textContent = '+'; 366 } else { 367 // Remove active class from all items
368 accordionItems.forEach(i => { 369 i.classList.remove('active'); 370 i.querySelector('.accordion-icon').textContent = '+'; 371 }); 372 373 // Add active class to clicked item 374 item.classList.add('active'); 375 icon.textContent = 'â'; 376 377 // Change image with slide animation 378 if (window.innerWidth >= 768 && currentImageIndex !== index) { 379 const currentImage = images[currentImageIndex]; 380 const newImage = images[index]; 381 382 // Exit current image (slide right) 383 currentImage.classList.add('exit'); 384 currentImage.classList.remove('active'); 385 386 // Enter new image (slide from left) 387 setTimeout(() => { 388 newImage.classList.add('active'); 389 newImage.classList.remove('exit'); 390 }, 50); 391 392 // Clean up exit class after animation 393 setTimeout(() => { 394 currentImage.classList.remove('exit'); 395 }, 550); 396 397 currentImageIndex = index; 398 } 399 } 400 }); 401 402 403 404 }); 405 406 407 }; 408 409 $$(".accordion-container").forEach(function(acc) { 410 initializeAccordion(acc); 411 }); 412 413 // Post Slider (requires Slick alternative) 414 415 416 // Testimonial Block (requires Slick alternative) 417 const testimonialBlock = function(container) { 418 console.warn('Testimonial slider requires a carousel library - Slick equivalent needed'); 419 }; 420 421 $$(".testimonial-con").forEach(function(testimonial) { 422 testimonialBlock(testimonial); 423 }); 424 425 // Video Modal functionality 426 const initializeVideo = function(showcase) { 427 const modal = showcase.querySelector('.videoModal'); 428 const videoContainer = showcase.querySelector('.video-container'); 429 const closeBtn = showcase.querySelector('.closeModal'); 430 const triggers = showcase.querySelectorAll('.video-trigger'); 431 432 // Check if all necessary elements exist before adding listeners 433 if (!modal || !videoContainer || !closeBtn || triggers.length === 0) { 434 console.error("Missing modal, video container, close button, or triggers in showcase.", showcase); 435 return; 436 } 437 438 function openModal(e) { 439 const trigger = e.currentTarget; 440 const videoType = trigger.dataset.videoType; 441 const videoId = trigger.dataset.videoId; 442 const videoSrc = trigger.dataset.videoSrc; 443 444 let videoHTML = ''; 445 446 switch(videoType) { 447 case 'youtube': 448 videoHTML = `<iframe src="https://www.youtube.com/embed/${videoId}?autoplay=1" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>`; 449 break; 450 case 'vimeo': 451 videoHTML = `<iframe src="https://player.vimeo.com/video/${videoId}?autoplay=1" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>`; 452 break; 453 case 'html5': 454 videoHTML = `<video controls autoplay><source src="${videoSrc}" type="video/mp4">Your browser does not support the video tag.</video>`; 455 break; 456 } 457 458 videoContainer.innerHTML = videoHTML; // Corrected: Use scoped variable 459 modal.classList.add('active'); // Corrected: Use scoped variable 460 document.body.style.overflow = 'hidden'; 461 } 462 463 function closeModal() { 464 modal.classList.remove('active'); // Corrected: Use scoped variable 465 // Clear video to stop playback 466 setTimeout(() => { 467 videoContainer.innerHTML = ''; // Corrected: Use scoped variable 468 }, 300); 469 document.body.style.overflow = ''; 470 } 471 472 // Add click events to all triggers
473 triggers.forEach(trigger => { 474 trigger.addEventListener('click', (e) => openModal(e)); 475 }); 476 477 // Close button event 478 closeBtn.addEventListener('click', () => closeModal()); 479 480 // Close on overlay click 481 modal.addEventListener('click', (e) => { 482 if (e.target === modal) { 483 closeModal(); 484 } 485 }); 486 487 // Close on Escape key 488 document.addEventListener('keydown', (e) => { 489 if (e.key === 'Escape' && modal.classList.contains('active')) { 490 closeModal(); 491 } 492 }); 493 }; 494 495 $$(".video-wrapper").forEach(function(showcase) { 496 initializeVideo(showcase); 497 }); 498 499 500 // Carrousel initialization 501 502 const initializeCarrousel = function(carousel) { 503 let currentIndex = 0; 504 let itemsPerView = 1; 505 506 const track = carousel.querySelector('.carousel-track'); 507 const cards =carousel.querySelectorAll('.carousel-card'); 508 const indicatorsContainer = carousel.querySelector('.carousel-indicators'); 509 const prevBtn = carousel.querySelector('.carousel-btn.prev'); 510 const nextBtn = carousel.querySelector('.carousel-btn.next'); 511 512 // Configuration: Set to true for wrap-around loop, false to stop at edges 513 const enableLoop = true; 514
515 function getItemsPerView() { 516 if (window.innerWidth >= 1024) { 517 return 4; 518 } else if (window.innerWidth >= 768) { 519 return 3; 520 } 521 return 1; 522 } 523 524 525 function getMaxIndex() { 526 return cards.length ; 527 } 528 529 function updateCarousel() { 530 if (!track) return; 531 532 const cardWidth = 100 / itemsPerView; 533 const offset = -(currentIndex * cardWidth); 534 track.style.transform = `translateX(${offset}%)`; 535 updateIndicators(); 536 } 537 538 function createIndicators() { 539 if (!indicatorsContainer) return; 540 541 indicatorsContainer.innerHTML = ' '; 542 const totalIndicators = getMaxIndex() ; 543 544 for (let i = 0; i < totalIndicators; i++) { 545 const indicator = document.createElement('button'); 546 indicator.className = 'indicator'; 547 indicator.onclick = () => goToSlide(i); 548 indicatorsContainer.appendChild(indicator); 549 } 550 updateIndicators(); 551 } 552 553 function updateIndicators() { 554 if (!indicatorsContainer) return; 555 556 const indicators = indicatorsContainer.querySelectorAll('.indicator'); 557 indicators.forEach((indicator, index) => { 558 indicator.classList.toggle('active', index === currentIndex); 559 }); 560 } 561 562 function moveCarousel(direction) { 563 const maxIndex = getMaxIndex(); 564 currentIndex += direction; 565 566 if (currentIndex < 0) { 567 currentIndex = maxIndex-1; 568 } else if (currentIndex >= maxIndex) { 569 currentIndex = 0; 570 } 571 572 updateCarousel(); 573 } 574 575 function goToSlide(index) { 576 currentIndex = index; 577 updateCarousel(); 578 } 579 580 function handleResize() {
581 const newItemsPerView = getItemsPerView(); 582 if (newItemsPerView !== itemsPerView) { 583 itemsPerView = newItemsPerView; 584 currentIndex = 0; 585 createIndicators(); 586 updateCarousel(); 587 } 588 } 589 590 // Initialize 591 itemsPerView = getItemsPerView(); 592 createIndicators(); 593 updateCarousel(); 594 595 // Event listeners for navigation buttons 596 prevBtn.addEventListener('click', () => moveCarousel(-1)); 597 nextBtn.addEventListener('click', () => moveCarousel(1)); 598 599 // Handle window resize 600 window.addEventListener('resize', handleResize); 601 602 // Touch support for mobile 603 let touchStartX = 0; 604 let touchEndX = 0; 605 606 track.addEventListener('touchstart', (e) => { 607 touchStartX = e.changedTouches[0].screenX; 608 }); 609 610 track.addEventListener('touchend', (e) => { 611 touchEndX = e.changedTouches[0].screenX; 612 handleSwipe(); 613 }); 614 function handleSwipe() { 615 if (touchStartX - touchEndX > 50) { 616 moveCarousel(1); 617 } 618 if (touchEndX - touchStartX > 50) { 619 moveCarousel(-1); 620 } 621 } 622 }; 623 624 $$(".carousel-container").forEach(function(carousel) { 625 initializeCarrousel(carousel); 626 }); 627 628 629 // Masonry Image Gallery - part of text and image component 630 const initializeMasonry = function (gallery) { 631 const grids = $$('[data-masonry]'); 632 633 grids.forEach(grid => { 634 // Simple masonry layout using CSS Grid 635 const items = grid.querySelectorAll('.two-column-gallery__item'); 636 637 items.forEach(item => { 638 const img = item.querySelector('img'); 639 640 img.addEventListener('load', function() { 641 const height = this.naturalHeight; 642 const width = this.naturalWidth; 643 const ratio = height / width; 644 645 // Assign span based on aspect ratio 646 if (ratio > 1.5) { 647 item.classList.add('tall'); 648 } else if (ratio > 1) { 649 item.classList.add('medium'); 650 } else { 651 item.classList.add('standard'); 652 } 653 }); 654 655 // Trigger if already loaded 656 if (img.complete) { 657 img.dispatchEvent(new Event('load')); 658 } 659 }); 660 }); 661 662 663 } 664 665 $$(".two-column-gallery__gallery").forEach(function(gallery) { 666 initializeMasonry(gallery); 667 }); 668 669 670 671 // Anime.js parallax gallery setup 672 if (typeof anime !== 'undefined') { 673 anime.set(".para-gallery.left .para-gall-image", { 674 translateX: "-100vh", 675 translateY: "0vh" 676 }); 677 678 anime.set(".para-gallery.right .para-gall-image", { 679 translateX: "100vh", 680 translateY: "0vh" 681 }); 682 } 683 684 const paraImages = $$(".para-gallery"); 685 let thresh = 0.4; 686 687 if (window.innerWidth < 1024) { 688 thresh = 0.2; 689 } 690 691 const paraOptions = { 692 rootMargin: "0px 0px 0px 0px", 693 threshold: thresh 694 }; 695 696 const paraImagesObserver = new IntersectionObserver(function(entries, paraImagesObserver) { 697 entries.forEach(function(entry) { 698 if (entry.isIntersecting && typeof anime !== 'undefined') { 699 const images = entry.target.querySelectorAll(".para-gall-image"); 700 const heroTL = anime.timeline({ 701 easing: "easeInOutExpo" 702 }); 703 704 heroTL.add({ 705 targets: images, 706 translateX: "0", 707 duration: 2500, 708 delay: anime.stagger(100, { 709 start: 0 710 }) 711 }, 0); 712
713 images.forEach(function(image) { 714 const animetl = anime.timeline({ 715 autoplay: false 716 }); 717 718 const paraSpeed = image.getAttribute("parallax-speed") || 1; 719 const properties = { 720 targets: image, 721 easing: "linear", 722 duration: 3000 723 }; 724 725 heroTL.finished.then(function() { 726 const parallaxAttr = image.getAttribute("parallax"); 727 if (parallaxAttr) { 728 parallaxAttr.split(/[,\s]+(?={)/).forEach(function(value) { 729 try { 730 Object.assign(properties, eval("(" + value + ")")); 731 } catch(e) { 732 console.error("Error parsing parallax value:", e); 733 } 734 }); 735 } 736 737 animetl.add(properties); 738 739 const baselineScrollTop = window.scrollY; 740 741 const parallaxSetup = function() { 742 const currentScroll = window.scrollY; 743 animetl.seek(animetl.duration * (0.5 * (currentScroll - baselineScrollTop) * (0.001 * paraSpeed))); 744 }; 745 746 parallaxSetup(); 747 window.addEventListener('resize', parallaxSetup); 748 window.addEventListener('scroll', parallaxSetup); 749 }); 750 }); 751 752 paraImagesObserver.unobserve(entry.target); 753 } 754 }); 755 }, paraOptions); 756 757 paraImages.forEach(function(image) { 758 paraImagesObserver.observe(image); 759 }); 760 761 // Bottom area paragraph cleanup 762 const bottomPs = $$(".bottom_area p"); 763 bottomPs.forEach(function(p) { 764 const text = p.textContent.trim(); 765 if (text.length <= 0 || text === " ") { 766 p.style.display = 'none'; 767 } 768 }); 769 770 // Hover box functionality
771 function enableHoverBox() { 772 const hoverBoxes = $$(".wst-hover-box"); 773 774 if (window.innerWidth >= 1025) { 775 $$(".hover-content").forEach(content => { 776 slideUp(content); 777 }); 778 779 hoverBoxes.forEach(function(box) { 780 // Remove existing listeners 781 const newBox = box.cloneNode(true); 782 box.parentNode.replaceChild(newBox, box); 783 784 newBox.addEventListener('mouseenter', function() { 785 this.classList.add("hover-active"); 786 const hoverContent = this.querySelector(".hover-content"); 787 if (hoverContent) slideToggle(hoverContent); 788 }); 789 790 newBox.addEventListener('mouseleave', function() { 791 this.classList.remove("hover-active"); 792 const hoverContent = this.querySelector(".hover-content"); 793 if (hoverContent) slideToggle(hoverContent); 794 }); 795 }); 796 } else { 797 $$(".hover-content").forEach(content => { 798 content.style.display = 'block'; 799 }); 800 } 801 } 802
803 enableHoverBox(); 804 window.addEventListener('resize', enableHoverBox); 805 806 // Helper functions for animations 807 function slideUp(element, callback) { 808 element.style.height = element.scrollHeight + 'px'; 809 element.style.transitionProperty = 'height, margin, padding'; 810 element.style.transitionDuration = '300ms'; 811 element.offsetHeight; // trigger reflow 812 element.style.overflow = 'hidden'; 813 element.style.height = 0; 814 element.style.paddingTop = 0; 815 element.style.paddingBottom = 0; 816 element.style.marginTop = 0; 817 element.style.marginBottom = 0; 818 819 setTimeout(() => { 820 element.style.display = 'none'; 821 element.style.removeProperty('height'); 822 element.style.removeProperty('padding-top'); 823 element.style.removeProperty('padding-bottom'); 824 element.style.removeProperty('margin-top'); 825 element.style.removeProperty('margin-bottom'); 826 element.style.removeProperty('overflow'); 827 element.style.removeProperty('transition-duration'); 828 element.style.removeProperty('transition-property'); 829 if (callback) callback(); 830 }, 300); 831 } 832 833 function slideDown(element, callback) { 834 element.style.removeProperty('display'); 835 let display = window.getComputedStyle(element).display; 836 if (display === 'none') display = 'block'; 837 element.style.display = display; 838 839 const height = element.scrollHeight; 840 element.style.overflow = 'hidden'; 841 element.style.height = 0; 842 element.style.paddingTop = 0; 843 element.style.paddingBottom = 0; 844 element.style.marginTop = 0; 845 element.style.marginBottom = 0; 846 element.offsetHeight; // trigger reflow 847 element.style.transitionProperty = 'height, margin, padding'; 848 element.style.transitionDuration = '300ms'; 849 element.style.height = height + 'px'; 850 element.style.removeProperty('padding-top'); 851 element.style.removeProperty('padding-bottom'); 852 element.style.removeProperty('margin-top'); 853 element.style.removeProperty('margin-bottom'); 854 855 setTimeout(() => { 856 element.style.removeProperty('height'); 857 element.style.removeProperty('overflow'); 858 element.style.removeProperty('transition-duration'); 859 element.style.removeProperty('transition-property'); 860 if (callback) callback(); 861 }, 300); 862 } 863 864 function slideToggle(element, callback) { 865 if (window.getComputedStyle(element).display === 'none') { 866 slideDown(element, callback); 867 } else { 868 slideUp(element, callback); 869 } 870 } 871 872});
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.