1jQuery(document).ready(function() { 2 3 // Header scroll 4 var position = jQuery(window).scrollTop(); 5 6 jQuery(window).scroll(function() { 7 8 var scroll = jQuery(window).scrollTop(); 9 10 if (scroll < 0) { 11 scroll = 0; 12 } 13 14 if ( scroll > position && scroll > 0 ) { 15 jQuery( 'body' ).removeClass( 'scroll-up' ); 16 if ( !jQuery( 'body' ).hasClass( 'scroll-down' ) ) { 17 jQuery( 'body' ).addClass( 'scroll-down' ); 18 } 19 } else if( scroll == 0 ) { 20 jQuery( 'body' ).removeClass( 'scroll-down' ); 21 jQuery( 'body' ).removeClass( 'scroll-up' ); 22 } else if ( scroll < position ) { 23 jQuery( 'body' ).removeClass( 'scroll-down' ); 24 if ( !jQuery( 'body' ).hasClass( 'scroll-up' ) ) { 25 jQuery( 'body' ).addClass( 'scroll-up' ); 26 } 27 } 28 29 position = Math.max(0, scroll); 30 31 }); 32 33 // Add class to body when user clicks on a mega menu item 34 jQuery('.jet-mega-menu-item').click( function() { 35 jQuery('body').addClass('mega-menu-opened'); 36 }); 37 38 // Create a MutationObserver to watch for class changes 39 const observer = new MutationObserver(function(mutations) { 40 mutations.forEach(function(mutation) { 41 if (mutation.attributeName === 'class') { 42 // If there are no more mega menu items with hover class, remove body class 43 if (jQuery('.jet-mega-menu-item.jet-mega-menu-item--hover').length === 0) { 44 jQuery('body').removeClass('mega-menu-opened'); 45 } 46 } 47 }); 48 }); 49 50 // Configure and start the observer 51 const observerConfig = { attributes: true, attributeFilter: ['class'], subtree: true }; 52 const targetNode = jQuery('.jet-mega-menu')[0]; // Adjust selector to match your menu container 53 54 // Only start observing if the target element exists 55 if (targetNode) { 56 observer.observe(targetNode, observerConfig); 57 } 58 59 // A11y functions 60 jQuery( 'a[target="_blank"]' ).attr( 'aria-label', 'link text opens a new window' ); 61 jQuery( '.menu' ).attr( 'role', 'menu' ); 62 63 // Hero animation 64 setTimeout(function() { 65 jQuery('body').addClass('video-header-loaded'); 66 jQuery('.hero-video-bg').addClass('loaded'); 67 }, 2000); 68 69 setTimeout(function() { 70 jQuery('body').addClass('video-header-expanded'); 71 jQuery('.hero-video-bg').addClass('expanded'); 72 }, 3000); 73 74 jQuery('.elementor-widget-image a, .elementor-button').click(function() { setTimeout(function() { 75 jQuery('#elementor-lightbox video').attr('controls', 'true'); }, 2000); 76 }); 77 78}); 79 80jQuery(window).on('load', function() { 81 82 function initWaypoint(selector, className, offsetDown = '80%', offsetUp = '-80%') { 83 // Keep track of the current active section ID 84 var currentActiveID = ''; 85 86 jQuery(selector).each(function() { 87 var element = jQuery(this); 88 if (element.length) { 89 // Create waypoint for scrolling down 90 new Waypoint({ 91 element: element[0], 92 handler: function(direction) { 93 if (direction === 'down') { 94 var elementID = element.attr('id'); 95 element.addClass(className); 96 97 // Remove any previous element ID from body and add the current one 98 if (currentActiveID && currentActiveID !== elementID) { 99 jQuery('body').removeClass(currentActiveID); 100 } 101 102 jQuery('body').addClass(elementID); 103 currentActiveID = elementID; 104 } 105 }, 106 offset: offsetDown 107 }); 108 109 // Create separate waypoint for scrolling up 110 new Waypoint({ 111 element: element[0], 112 handler: function(direction) { 113 if (direction === 'up') { 114 var elementID = element.attr('id'); 115 element.addClass(className); 116 117 // Remove any previous element ID from body and add the current one 118 if (currentActiveID && currentActiveID !== elementID) { 119 jQuery('body').removeClass(currentActiveID); 120 } 121 122 jQuery('body').addClass(elementID); 123 currentActiveID = elementID; 124 } 125 }, 126 offset: offsetUp 127 }); 128 } 129 }); 130 } 131 132 // Initialize Waypoints with different offsets for down and up 133 initWaypoint('.e-parent.e-con', 'section-in-view', '80%', '-80%'); 134 initWaypoint('.research-graphic-copy', 'section-in-view', '50%', '-50%'); 135 136 // Confetti setup for announcement modal with continuous animation 137 function setupAnnouncementConfetti() { 138 const modal = jQuery('.announcement-modal'); 139 140 if (modal.length) { 141 // Create a canvas specifically for this modal 142 const canvas = document.createElement('canvas'); 143 canvas.style.position = 'absolute'; 144 canvas.style.top = '0'; 145 canvas.style.left = '0'; 146 canvas.style.width = '100%'; 147 canvas.style.height = '100%'; 148 canvas.style.pointerEvents = 'none'; 149 canvas.style.zIndex = '9999'; 150 151 // Append canvas to the modal 152 modal.append(canvas); 153 154 // Create confetti instance for this specific canvas 155 const myConfetti = confetti.create(canvas, { 156 resize: true, 157 useWorker: true 158 }); 159 160 // Animation duration and settings 161 let confettiInterval; 162 163 // Function to trigger the continuous confetti effect 164 window.triggerModalConfetti = function() { 165 const duration = 15 * 1000; // 15 seconds 166 const animationEnd = Date.now() + duration; 167 const defaults = { 168 startVelocity: 30, 169 spread: 360, 170 ticks: 60, 171 zIndex: 0, 172 colors: ['#0047BB', '#009681', '#812C7D', '#93328E', '#0085CA', '#00B0B9', '#6CC24A', '#CB00A1'], 173 shapes: ['circle'], 174 disableForReducedMotion: true 175 }; 176 177 function randomInRange(min, max) { 178 return Math.random() * (max - min) + min; 179 } 180 181 confettiInterval = setInterval(function() { 182 const timeLeft = animationEnd - Date.now(); 183 184 if (timeLeft <= 0) { 185 clearInterval(confettiInterval); 186 // Remove canvas after animation completes 187 setTimeout(() => { 188 if (canvas.parentNode) { 189 canvas.parentNode.removeChild(canvas); 190 } 191 }, 1000); // Give extra time for particles to settle 192 return; 193 } 194 195 const particleCount = 50 * (timeLeft / duration); 196 197 // Left side confetti burst 198 myConfetti({ 199 ...defaults, 200 particleCount, 201 origin: { 202 x: randomInRange(0.1, 0.3), 203 y: Math.random() - 0.2 204 } 205 }); 206 207 // Right side confetti burst 208 myConfetti({ 209 ...defaults, 210 particleCount, 211 origin: { 212 x: randomInRange(0.7, 0.9), 213 y: Math.random() - 0.2 214 } 215 }); 216 217 // Top center confetti burst 218 myConfetti({ 219 ...defaults, 220 particleCount, 221 origin: { 222 x: randomInRange(0.4, 0.6), 223 y: Math.random() - 0.3 224 } 225 }); 226 }, 750); 227 }; 228 229 // Function to stop confetti early if needed
230 window.stopModalConfetti = function() { 231 if (confettiInterval) { 232 clearInterval(confettiInterval); 233 } 234 // Optional: immediately remove canvas 235 if (canvas.parentNode) { 236 canvas.parentNode.removeChild(canvas); 237 } 238 }; 239 } 240 } 241 242 // Initialize confetti setup 243 setupAnnouncementConfetti(); 244 245 // Trigger confetti when modal is present 246 if (jQuery('.announcement-modal').length) { 247 setTimeout(() => triggerModalConfetti(), 500); 248 } 249 250});
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.