1/* JS for preset "Menu V2" */ 2(function() { 3 $(function() { 4 $('.menu-wrapper').each(function() { 5 initMenu($(this)) 6 }); 7 }); 8 9 // Make :active pseudo classes work on iOS 10 document.addEventListener("touchstart", function() {}, false); 11 12 const initMenu = function($menuWrapper) { 13 const $body = $('body'); 14 const $menu = $('.ed-menu', $menuWrapper); 15 const $menuLinks = $('a', $menu); 16 const $menuTrigger = $('.menu-trigger', $menuWrapper); 17 const $banner = $('.banner').first(); 18 19 const smoothScrollOffset = 20; 20 21 // Set aria attributes 22 $menuTrigger.attr({ 23 'aria-expanded': 'false', 24 'aria-controls': $menu.attr('id'), 25 }); 26 27 toggleClassOnClick($body.add($menu), $menuTrigger, null, 'open open-menu'); // Keep open on $menu for backward compatibility 28 activateSmoothScroll($menuLinks.add($('.scroll a')), smoothScrollOffset); 29 addClassOnVisibleLinkTargets($menuLinks, 'active', 2 / 3); 30 handleSticky($menuWrapper, 'sticky', $banner); 31 }; 32 33 /** 34 * Observe element's height changes and reload the initMenu() function 35 * 36 * @param {HTMLElement} elm Element to observe 37 * @param {function} callback to call when elmement's height changed 38 */ 39 const observeHeightChange = function(elm, callback) { 40 if (!('ResizeObserver' in window) || elm == null) return; 41 42 const ro = new ResizeObserver(callback); 43 ro.observe(elm); 44 } 45 46 /** 47 * Toggles class on a target when a trigger is clicked 48 * 49 * @param {jQuery} $target The target to apply the CSS class to 50 * @param {jQuery} $trigger The Trigger 51 * @param {jQuery} $closeTrigger Optional close trigger 52 * @param {string} cssClass CSS Class to toggle on the target 53 */ 54 const toggleClassOnClick = function($target, $trigger, $closeTrigger, cssClass) { 55 56 // Reset in case class "open" was saved accidentally 57 $target.removeClass(cssClass); 58 $trigger.removeClass(cssClass).attr('aria-expanded', 'false'); 59 60 // Click on trigger toggles class "open" 61 $trigger.off('.toggle').on('click.toggle', function() { 62 const isExpanded = $(this).attr('aria-expanded') === 'true'; 63 const $menu = $target.filter('.ed-menu'); 64 $(this).toggleClass(cssClass).attr('aria-expanded', !isExpanded); 65 $target.toggleClass(cssClass); 66 67 // Set focus to menu on open 68 if (!isExpanded) { 69 $menu.attr('tabindex', '0'); 70 const focusMenu = () => { 71 $menu.focus(); 72 $menu.off('transitionend', focusMenu); 73 }; 74 $menu.on('transitionend', focusMenu); 75 } 76 77 $menu[0].addEventListener( 78 'blur', 79 function() { 80 $menu[0].removeAttribute('tabindex'); 81 }, { 82 once: true 83 } 84 ); 85 }); 86 87 // Close target when link inside is clicked 88 $target.find('a').click(function() { 89 $target.removeClass(cssClass); 90 $trigger.removeClass(cssClass).attr('aria-expanded', 'false'); 91 }); 92 93 if (!$closeTrigger || !$closeTrigger.length) { 94 return; 95 } 96 97 $closeTrigger.click(function() { 98 $target.removeClass(cssClass); 99 $trigger.removeClass(cssClass).attr('aria-expanded', 'false'); 100 }); 101 }; 102 103 /** 104 * Smooth scroll to link targets 105 * 106 * @param {jQuery} $scrollLinks The links 107 * @param {jQuery} scrollOffset Offset to subtract from the scroll target position (e.g. for fixed positioned elements like a menu) 108 */ 109 const activateSmoothScroll = function($scrollLinks, scrollOffset) { 110 if (typeof scrollOffset === 'undefined') { 111 scrollOffset = 0; 112 } 113 114 const determineTarget = function($trigger, hash) { 115 if (hash == '#!next') { 116 return $trigger.closest('.ed-element').next(); 117 } 118 // Prevent invalid selectors 119 try { 120 return $(hash); 121 } catch (e) { 122 return $(); 123 } 124 } 125 126 $scrollLinks.click(function(e) { 127 const $target = determineTarget($(this), this.hash); 128 if (!$target.length) return; 129 e.preventDefault(); 130 131 viewport.scrollTo($target, 'top', 500, 0); 132 133 }); 134 }; 135 136 /** 137 * We are using the fill property on an element to pass user's choices from CSS to JavaScript 138 * 139 * @param {jQuery} $element 140 */ 141 const getStickyMode = function($element) { 142 const fillValue = getComputedStyle($element[0]).fill; 143 144 return fillValue === 'rgb(255, 0, 0)' ? 145 'sticky_banner' : 146 fillValue === 'rgb(0, 255, 0)' ? 147 'sticky_menu' : 148 fillValue === 'rgb(0, 0, 255)' ? 149 'sticky_instant' : 150 fillValue === 'rgb(255, 255, 255)' ? 151 'sticky_reverse' : 152 'sticky_none'; 153 }; 154 155 /** 156 * Adds a class to an element when not currently visible 157 * 158 * @param {jQuery} $element The element to handle stickyness for 159 * @param {string} cssClass The actual CSS class to be applied to the element when it's above a certain scroll position 160 * @param {jQuery} $banner A banner to reference the scroll position to 161 */ 162 const handleSticky = function($element, cssClass, $banner) { 163 let triggerPos = 0, 164 offset = 0; 165 let menuWrapperHeight = $element.outerHeight(); 166 let mode; 167 let prevScroll = 0; 168 $element.removeClass(cssClass); 169 170 const toggleSpacer = function(toggle) { 171 document.body.style.setProperty('--spacer-height', toggle ? menuWrapperHeight + 'px' : ''); 172 }; 173 174 const handleScroll = function() { 175 if (!$element.length || mode === 'sticky_none') return; 176 //if (!$element.length || mode === 'sticky_none' || mode === 'sticky_instant') return; 177 178 const isReverse = mode === 'sticky_reverse', 179 curScroll = viewport.getScrollTop(); 180 181 if (triggerPos <= curScroll && (!isReverse || prevScroll > curScroll)) { 182 $element.addClass(cssClass); 183 toggleSpacer(true); 184 } else { 185 $element.removeClass(cssClass); 186 toggleSpacer(false); 187 } 188 189 prevScroll = curScroll; 190 }; 191
192 const updateOffset = function() { 193 mode = getStickyMode($element); 194 menuWrapperHeight = $element.outerHeight(); 195 if (!$element.hasClass(cssClass)) { 196 offset = $element.offset().top; 197 } 198 if (mode === 'sticky_banner' && !$banner.length) { 199 mode = 'sticky_menu'; 200 } 201 if (mode === 'sticky_banner') { 202 triggerPos = $banner.offset().top + ($banner.length ? $banner.outerHeight() : $element.outerHeight()); 203 } 204 if (mode === 'sticky_menu' || mode === 'sticky_reverse') { 205 triggerPos = offset + $element.outerHeight(); 206 } 207 if (mode === 'sticky_instant') { 208 triggerPos = offset; 209 } 210 211 handleScroll(); 212 } 213 214 viewport.observe('resize', updateOffset); 215 viewport.observe('animation.end', updateOffset); 216 observeHeightChange($element[0], updateOffset); 217 updateOffset(); 218 219 viewport.observe('scroll', handleScroll); 220 handleScroll(); 221 }; 222 223 /** 224 * Adds a class to links whose target is currently inside the viewport 225 * 226 * @param {jQuery} $links Link(s) to be observed 227 * @param {string} cssClass CSS Class to be applied 228 * @param {float} sectionViewportRatio Ratio by which the target should be within the viewport 229 */ 230 const addClassOnVisibleLinkTargets = function($links, cssClass, sectionViewportRatio) { 231 if (typeof sectionViewportRatio === 'undefined') { 232 sectionViewportRatio = 1 / 2; 233 } 234 235 const menuTargets = []; 236 const activeLink = $links.filter('.active'); 237 238 const links = $links.filter(function() { 239 // Prevent invalid selectors 240 let $target; 241 try { 242 $target = $(this.hash); 243 } catch (e) { 244 return false; 245 } 246 if (!this.hash || !$target.length) { 247 return false; 248 } 249 250 // Cache offset position to improve performance (update on resize) 251 const updateOffset = function() { 252 $target.data('offset', $target.offset().top); 253 }; 254 255 viewport.observe('resize', updateOffset); 256 viewport.observe('animation.end', updateOffset); 257 updateOffset(); 258 259 menuTargets.push($target); 260 return true; 261 }); 262 263 // No hash links found, so don't handle it at all 264 if (!links.length) { 265 return; 266 } 267 268 const checkVisibility = function() { 269 $links.removeClass('active'); 270 271 // Check section position reversely 272 for (let i = menuTargets.length - 1; i >= 0; i--) { 273 const desiredScrollPosition = menuTargets[i].data('offset') - viewport.getHeight() * (1 - sectionViewportRatio); 274 if (viewport.getScrollTop() >= desiredScrollPosition && menuTargets[i][0].offsetParent !== null) { 275 links.eq(i).addClass(cssClass); 276 return; 277 } 278 } 279 280 // Fallback to originally active item 281 activeLink.addClass(cssClass); 282 }; 283 284 viewport.observe('scroll', checkVisibility); 285 checkVisibility(); 286 }; 287})(); 288/* End JS for preset "Menu V2" */ 289 290/* JS for preset "Content Slider" */ 291$(function() { 292 if (document.body.classList.contains('edit') || document.body.classList.contains('preview')) { 293 return; 294 } 295 296 viewport.promise('api.slick.ready', function() { 297 viewport.jQuery('.content-slider-wrap > .inner').slick({ 298 autoplay: true, 299 arrows: true, 300 prevArrow: '<button type="button" data-role="none" class="slick-prev" tabindex="0" role="button"></button>', 301 nextArrow: '<button type="button" data-role="none" class="slick-next" tabindex="0" role="button"></button>', 302 speed: 300, 303 autoplaySpeed: 3000, 304 dots: true, 305 adaptiveHeight: true, 306 fade: false 307 }); 308 }).requireSlick(); 309}); 310 311/* End JS for preset "Content Slider" */ 312 313/* JS for preset "Countdown V3" */ 314$(function() { 315 var slice = Array.prototype.slice; 316 var valid = true; 317 318 var ready = function(callback) { 319 var fn = function() { 320 if (document.body.classList.contains('edit')) { 321 return; 322 } 323 callback(); 324 }; 325 326 if (window.readyState !== 'loading') { 327 fn(); 328 return; 329 } 330 331 document.addEventListener('DOMContentLoaded', fn); 332 }; 333 334 var countdown = function(date, tick) { 335 var now = new Date().getTime(), 336 running = false, 337 days = 0, 338 hours = 0, 339 minutes = 0, 340 seconds, 341 interval; 342 343 var updateCounter = function() { 344 if (!running) return; 345 346 now = new Date().getTime(); 347 seconds = Math.round((date - now) / 1000); 348 349 if (seconds > 86400) { 350 days = Math.floor(seconds / 86400); 351 seconds %= 86400; 352 } 353 354 if (seconds > 3600) { 355 hours = Math.floor(seconds / 3600); 356 seconds %= 3600; 357 } 358 359 if (seconds > 60) { 360 minutes = Math.floor(seconds / 60); 361 seconds %= 60; 362 } 363 364 tick(days, hours, minutes, seconds); 365 }; 366 367 if (isNaN(Date.parse(date))) { 368 date = date.replace(/\-/g, '/'); 369 } 370 371 if (isNaN(Date.parse(date))) { 372 date = date.replace(/\s/, 'T'); 373 } 374 375 date = new Date(date).getTime(); 376 377 if (now >= date) { 378 valid = false;
379 return; 380 } 381 382 tick = tick || (function() {}); 383 384 return { 385 start: function() { 386 interval = window.setInterval(updateCounter, 1000); 387 running = true; 388 updateCounter(); 389 }, 390 stop: function() { 391 if (interval) window.clearInterval(interval); 392 interval = undefined; 393 running = false; 394 } 395 }; 396 }; 397 398 var writeCountdown = function(element, days, hours, minutes, seconds) { 399 var daysElm = element.querySelector(".countdown-days"), 400 hoursElm = element.querySelector('.countdown-hours'), 401 minutesElm = element.querySelector('.countdown-minutes'), 402 secondsElm = element.querySelector('.countdown-seconds'); 403 404 if (daysElm) daysElm.innerHTML = days; 405 if (hoursElm) hoursElm.innerHTML = hours; 406 if (minutesElm) minutesElm.innerHTML = minutes; 407 if (secondsElm) secondsElm.innerHTML = seconds; 408 }; 409 410 var buildCountdown = function(element) { 411 var dataContent = window.getComputedStyle(element, ':before').content.slice(1, -1); 412 var date = dataContent; 413 414 415 element.countdown = countdown(date, function(days, hours, minutes, seconds) { 416 writeCountdown( 417 element, parseInt(days), 418 ("0" + parseInt(hours)).slice(-2), 419 ("0" + parseInt(minutes)).slice(-2), 420 ("0" + parseInt(seconds)).slice(-2) 421 ); 422 }); 423 424 if (valid) { 425 element.countdown.start(); 426 } 427 }; 428 429 var destroyCountdown = function() { 430 var instances = slice.call(document.querySelectorAll('.countdown-instance')), 431 len = instances.length, 432 i = 0, 433 element; 434 for (; i < len; i++) { 435 element = instances[i]; 436 writeCountdown(element, "0", "0", "0", "0"); 437 if (element.countdown) { 438 element.countdown.stop(); 439 } 440 } 441 }; 442 443 var observeInstances = function() { 444 const observer = new MutationObserver((mutations) => { 445 mutations.forEach((mutation) => { 446 mutation.addedNodes.forEach((node) => { 447 if (node.nodeType === 1 && node.classList.contains('countdown-instance')) { 448 if (document.body.classList.contains('preview') || !document.body.classList.contains('edit-full')) { 449 buildCountdown(node); 450 } else { 451 destroyCountdown(); 452 } 453 } 454 }); 455 }); 456 }); 457 458 observer.observe(document.body, { 459 childList: true, 460 subtree: true 461 }); 462 }; 463 464 var preview = false; 465 var listener = function() { 466 if (valid) { 467 if (!preview && document.body.classList.contains('preview') || !document.body.classList.contains('edit-full')) { 468 var instances = slice.call(document.querySelectorAll('.countdown-instance')); 469 instances.forEach(buildCountdown); 470 preview = true; 471 } else if (preview && !document.body.classList.contains('preview')) { 472 destroyCountdown(); 473 preview = false; 474 } 475 } 476 477 requestAnimationFrame(listener); 478 }; 479 480 requestAnimationFrame(listener); 481 ready(function() { 482 observeInstances(); 483 }); 484}); 485/* End JS for preset "Countdown V3" */
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.