1let isTablet = window.matchMedia('(max-width: 991px)').matches; 2 3window.addEventListener('resize', () => { 4 isTablet = window.matchMedia('(max-width: 991px)').matches; 5}); 6 7function getParameterByName(name, url) { 8 if (!url) url = window.location.href; 9 name = name.replace(/[\[\]]/g, "\\$&"); 10 var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 11 results = regex.exec(url); 12 if (!results) return null; 13 if (!results[2]) return ''; 14 return decodeURIComponent(results[2].replace(/\+/g, " ")); 15} 16 17function hexToRgba(hex) { 18 hex = hex.replace('#', ''); 19 20 const r = parseInt(hex.substring(0, 2), 16); 21 const g = parseInt(hex.substring(2, 4), 16); 22 const b = parseInt(hex.substring(4, 6), 16); 23 24 let opac_60 = `rgba(${r}, ${g}, ${b}, 0.6)`; 25 let opac_20 = `rgba(${r}, ${g}, ${b}, 0.15)`; 26 27 document.documentElement.style.setProperty('--text-color-60', opac_60); 28 document.documentElement.style.setProperty('--text-color-20', opac_20); 29} 30 31function loadFonts(fonts, doc = document){ 32 const data = fonts; 33 if(!data) return; 34 35 const ghostFonts = document.querySelector('link[href*="fonts.bunny.net/css"]'); 36 37 if(ghostFonts) return; 38 39 const root = document.documentElement; 40 41 let splitArray = data.toString().replace(/,\s*/g, ',').split(','); 42 43 let googleFontsURL = `https://fonts.googleapis.com/css2?`; 44 45 for(let i = 0; i < splitArray.length; i++){ 46 if(i == 2) break; 47 48 root.style.setProperty(`--font${i+1}`, `'${splitArray[i].toString()}'`); 49 50 splitArray[i] = splitArray[i].replace(' ', '+'); 51 googleFontsURL += `family=${splitArray[i]}:wght@300;400;500;600&` 52 } 53 54 googleFontsURL += 'display=swap' 55 56 const linkElement = doc.createElement('link'); 57 linkElement.rel = 'stylesheet'; 58 linkElement.href = googleFontsURL; 59 60 doc.head.appendChild(linkElement); 61} 62 63let tl; 64let resizeTimeout; 65 66function horizontalScroll() { 67 gsap.registerPlugin(ScrollTrigger); 68 69 let container = document.querySelector(".horizontal-container"); 70 container.style.width = container.scrollWidth + "px"; 71 72 //This fixes image hover in jittering. After the scrolling is done it moves all of the sections to a whole pixel (not float): example from 2599.43px to 2599px 73 window.addEventListener('scroll', () => { 74 if(isTablet) return; 75 76 isScrolling = true; 77 clearTimeout(scrollTimeout); 78 scrollTimeout = setTimeout(() => { 79 if (isScrolling) { 80 translateSections(); 81 isScrolling = false; 82 } 83 }, 600); 84 }); 85 86 createScrollTrigger(); 87 if(!isTablet){ 88 setTimeout(() => { 89 translateSections() 90 }, 600) 91 } 92 93 window.addEventListener('resize', debounce(() => { 94 createScrollTrigger(); 95 }, 30)); 96} 97 98function createScrollTrigger() { 99 let sections = gsap.utils.toArray(".horizontal-section"); 100 101 if(isTablet){ 102 if (tl && tl.scrollTrigger) { 103 tl.scrollTrigger.kill(); 104 } 105 } 106 if(isTablet) return; 107 108 let position = window.scrollY; 109 110 if (tl && tl.scrollTrigger) { 111 let scrollerWidth = getScrollbarWidth(); 112 113 tl.scrollTrigger.kill(); 114 document.querySelectorAll('.horizontal-section').forEach(section => { 115 section.style.transform = `translateX(${-position - scrollerWidth / 2}px)` 116 }); 117 } 118 119 if (resizeTimeout) { 120 clearTimeout(resizeTimeout); 121 } 122 123 // Adding a small delay to ensure DOM is updated 124 resizeTimeout = setTimeout(() => { 125 let size = 0; 126 document.querySelectorAll('.horizontal-section').forEach(section => { 127 size += section.offsetWidth; 128 }); 129 130 sections = gsap.utils.toArray(".horizontal-section"); 131 132 tl = gsap.to(sections, { 133 x: () => -(size - document.documentElement.clientWidth), 134 ease: "none", 135 scrollTrigger: { 136 trigger: ".horizontal-container", 137 pin: true, 138 scrub: 0.6, 139 end: () => (size - document.documentElement.clientWidth), 140 invalidateOnRefresh: true, 141 onRefresh: () => { 142 window.scrollTo(0, position); 143 } 144 } 145 });
146 147 // Reset the timeout variable 148 resizeTimeout = null; 149 }, 0); 150} 151 152let scrollTimeout; 153let isScrolling = false; 154 155function translateSections() { 156 document.querySelectorAll('.horizontal-section').forEach(section => { 157 let computedStyle = window.getComputedStyle(section); 158 let transformValue = computedStyle.getPropertyValue('transform'); 159 let matrix = transformValue.split('(')[1].split(')')[0].split(','); 160 let xTranslation = Math.round(parseFloat(matrix[4])); 161 162 section.style.transform = `translateX(${xTranslation}px)` 163 }); 164} 165 166function debounce(fn, delay) { 167 let timerId; 168 return function(...args) { 169 if (timerId) { 170 clearTimeout(timerId); 171 } 172 timerId = setTimeout(() => { 173 fn(...args); 174 timerId = null; 175 }, delay); 176 }; 177} 178 179let scrollPosition = 0; 180 181function disableScrolling() { 182 scrollPosition = window.pageYOffset; 183 document.body.style.overflow = 'hidden'; 184 document.body.style.position = 'fixed'; 185 document.body.style.top = `-${scrollPosition}px`; 186 document.body.style.width = '100%'; 187 document.documentElement.style.scrollBehavior = 'auto'; 188 189} 190 191function enableScrolling() { 192 document.body.style.removeProperty('overflow'); 193 document.body.style.removeProperty('position'); 194 document.body.style.removeProperty('top'); 195 document.body.style.removeProperty('width'); 196 window.scrollTo(0, scrollPosition); 197 document.documentElement.style.scrollBehavior = 'smooth'; 198} 199 200function scrollToNextSection() { 201 window.scroll({ 202 top: document.querySelectorAll('section')[0].getBoundingClientRect().right + window.pageYOffset, 203 behavior: 'smooth' 204 }); 205} 206 207function scrollToStart(){ 208 window.scroll({ 209 top: 0, 210 behavior: 'smooth' 211 }); 212} 213 214function pageLoadAnimations(container = document) { 215 const elements = container.querySelectorAll('.card-animation'); 216 const opacityElements = container.querySelectorAll('.opacity-animation'); 217 let stepDelay = 100; 218 219 function handleIntersection(entries, observer) { 220 let delay = 0; 221 222 entries.forEach(entry => { 223 const overlay = entry.target.querySelector('.card-overlay'); 224 const title = entry.target.querySelector('.card-title'); 225 const top = entry.target.querySelector('.card-top'); 226 const bottom = entry.target.querySelector('.card-bottom'); 227 const excerpt = entry.target.querySelector('.card-excerpt'); 228 const tagStepDelay = entry.target.getAttribute('data-step-delay'); 229 230 tagStepDelay ? stepDelay = parseInt(tagStepDelay) : stepDelay = 100; 231 232 if (entry.isIntersecting ) { 233 if(entry.target.classList.contains('card-animation-handler')) return; 234 235 overlay.animate( 236 { 237 height: ['100%', '0%'] 238 }, 239 { 240 duration: 1000, 241 delay: delay, 242 easing: "cubic-bezier(.19,1,.22,1)", 243 fill: "forwards" 244 } 245 ); 246 247 easeIn({element: title, delay: delay}); 248 easeIn({element: top, delay: delay}); 249 easeIn({element: bottom, value: '15px', delay: delay}); 250 easeIn({element: excerpt, value: '15px', delay: delay, opacity: 0.6}); 251 252 delay = delay + stepDelay; 253 254 entry.target.classList.add('card-animation-handler'); 255 } 256 257 entry.target.style.visibility = 'visible'; 258 entry.target.classList.remove('card-animation') 259 observer.unobserve(entry.target); 260 }); 261 } 262 263 const options = { 264 root: null, 265 threshold: 0, 266 }; 267 268 const observer = new IntersectionObserver(handleIntersection, options); 269 270 setTimeout(() => { 271 elements.forEach(element => { 272 observer.observe(element); 273 }); 274 }, 200); 275 276 277 //Opacity 278 opacityElements.forEach(el => { 279 el.animate( 280 { 281 opacity: [0, 1] 282 }, 283 { 284 duration: 800, 285 delay: 300, 286 easing: "ease", 287 fill: "forwards" 288 } 289 ); 290 }) 291 292 //Adding a class here instead of animation because there was a problem with Lightense & this animation 293 let postContent = document.querySelector('.post-content'); 294 if(postContent){ 295 postContent.classList.add('visible-content') 296 } 297} 298 299function easeIn({element: element, value: value = '-15px', delay: delay, opacity: opacity = 1}){ 300 if(!element) return; 301 302 element.style.opacity = 0; 303 element.animate( 304 { 305 opacity: [0, opacity], 306 transform: [`translateY(${value})`, "translateY(0)"] 307 }, 308 { 309 duration: 1000, 310 delay: delay + 300, 311 easing: "cubic-bezier(.19,1,.22,1)", 312 fill: "forwards" 313 } 314 ); 315} 316 317function noHeroHeadingText(){ 318 let hero = document.querySelector('.hidden-hero'); 319 if(!hero) return; 320 321 hero.querySelector('.subtitle-name').innerHTML = hero.getAttribute('data-hidden-title'); 322 hero.querySelector('.subtitle-name-desktop').innerHTML = hero.getAttribute('data-hidden-title-desktop'); 323} 324 325let stickyObserver; 326 327function stickyHeading(){ 328 let section = document.querySelector('.sticky-hero-section'); 329 330 if(!section) return; 331 332 let stopSection = document.querySelectorAll('section')[2]; 333 let main = document.querySelector('main'); 334 let posts = document.querySelector('.sticky-posts-section'); 335 336 if(!isTablet){ 337 section.querySelector('.title-and-subtitle').style.width = document.documentElement.clientWidth - parseFloat(window.getComputedStyle(posts).paddingLeft) * 2 + 'px'; 338 posts.prepend(section) 339 section.classList.remove('horizontal-section'); 340 341 if (stickyObserver) { 342 stickyObserver.disconnect(); 343 } 344 345 stickyObserver = new IntersectionObserver((entries) => {
346 entries.forEach(entry => { 347 if (entry.isIntersecting) { 348 posts.prepend(section) 349 section.style.position = 'absolute'; 350 section.style.transform = `translateY(-${window.getComputedStyle(posts).marginTop})`; 351 section.style.left = 'unset'; 352 section.style.right = parseFloat(window.getComputedStyle(posts).paddingRight) - parseFloat(window.getComputedStyle(posts).paddingLeft) - getScrollbarWidth() + 'px'; 353 } else { 354 main.prepend(section) 355 section.style.position = 'fixed'; 356 section.style.transform = 'translateY(0px)' 357 section.style.left = '0' 358 section.style.right = '0' 359 } 360 }); 361 }, { 362 root: null, 363 rootMargin: parseFloat(window.getComputedStyle(posts).paddingRight) - parseFloat(window.getComputedStyle(posts).paddingLeft) + 'px', 364 threshold: 0 365 }); 366 367 stickyObserver.observe(stopSection); 368 }else{ 369 section.classList.add('horizontal-section'); 370 main.prepend(section); 371 } 372} 373 374function getScrollbarWidth() { 375 return window.innerWidth - document.documentElement.clientWidth; 376} 377 378function featuredTagsOnHomepage(tagSlugs) { 379 if (!tagSlugs) return; 380 381 let tags = tagSlugs.split(',').map(item => item.trim()); 382 let fetchPromises = []; 383
384 tags.forEach(tag => { 385 let url = window.location.origin + `/tag/${tag}/page/1/`; 386 387 let fetchPromise = fetch(url) 388 .then(response => response.text()) 389 .then(data => { 390 let parser = new DOMParser(); 391 let html = parser.parseFromString(data, "text/html"); 392 let newPosts = html.querySelector("#posts-section"); 393 394 // Store the newPosts element and its tag for later reordering 395 return { tag, newPosts }; 396 }) 397 .catch(error => { 398 console.error("Error loading more posts for tag:", tag, error); 399 return null; // Return null in case of error to handle it gracefully 400 }); 401 402 fetchPromises.push(fetchPromise); 403 }); 404 405 Promise.all(fetchPromises) 406 .then(results => { 407 let imageLoadPromises = []; 408 409 results 410 .filter(result => result !== null) // Filter out any null results due to errors 411 .forEach(({ tag, newPosts }) => { 412 let latest = document.querySelector('.latest-posts'); 413 let parent = latest.parentNode; 414 415 parent.insertBefore(newPosts, latest); 416 417 newPosts.querySelector('.subtitle-name').innerHTML = newPosts.getAttribute('data-hidden-title'); 418 newPosts.setAttribute('data-section-name', newPosts.getAttribute('data-hidden-title')); 419 newPosts.classList.remove('padding-top-on-mobile', 'hidden-hero', 'sticky-posts-section'); 420 newPosts.querySelector('.grid').removeAttribute('id'); 421 newPosts.querySelector('#custom-pagination')?.removeAttribute('id'); 422 423 let button = newPosts.querySelector('#load-more-btn'); 424 if (button) { 425 button.removeAttribute('id'); 426 427 const anchor = document.createElement('a'); 428 anchor.classList.add('pagination-button', 'button'); 429 anchor.innerHTML = ` ${button.getAttribute('data-featured-tag-text')} `; 430 button.parentNode.replaceChild(anchor, button); 431 anchor.href = `/tag/${tag}`; 432 } 433 434 pageLoadAnimations(newPosts); 435 436 // Create promises for each image to ensure they are fully loaded 437 newPosts.querySelectorAll('img').forEach(img => { 438 let imgLoadPromise = new Promise(resolve => { 439 img.onload = img.onerror = resolve; 440 }); 441 imageLoadPromises.push(imgLoadPromise); 442 }); 443 }); 444 445 // Wait for all images to be loaded before running afterAllLoaded 446 Promise.all(imageLoadPromises).then(() => { 447 afterAllLoaded(); 448 }); 449 }) 450 .catch(error => { 451 console.error("Error processing fetch promises:", error); 452 afterAllLoaded(); 453 }); 454 455 function afterAllLoaded() { 456 horizontalScroll(); 457 const customIndicator = document.querySelector('custom-indicator'); 458 if (customIndicator && typeof customIndicator.init === 'function') { 459 customIndicator.init(); 460 } else { 461 console.error("Custom indicator element or init function not found."); 462 } 463 464 465 pageLoadAnimations(); 466 } 467} 468 469function setLightense(){ 470 window.addEventListener('DOMContentLoaded', function () { 471 const imagesInAnchors = document.querySelectorAll('.post-content a img, .post-content .kg-product-card img, .kg-signup-card img, .kg-header-card img'); 472 473 imagesInAnchors.forEach((img) => { 474 img.classList.add('no-lightense'); 475 }); 476 477 Lightense('.post-content img:not(.no-lightense)', { 478 background: 'var(--background-color)' 479 }); 480 }, false); 481} 482 483function copyUrlToClipboard(parentElement){ 484 let parent = document.querySelector(`.${parentElement}`) 485 let alert = parent.querySelector('.clipboard-alert'); 486 487 parent.querySelector('.clipboard-link').addEventListener('click', () => { 488 navigator.clipboard.writeText(window.location.href); 489 alert.style.display = "block"; 490 491 setTimeout(function () { 492 alert.style.display = "none"; 493 }, 3000); 494 }) 495} 496 497function deleteBorder() { 498 window.addEventListener('DOMContentLoaded', function () { 499 let relatedPosts = document.querySelector('.related-posts-inner'); 500 if(!relatedPosts) return; 501 502 if(isTablet){ 503 const prevPost = document.querySelector('.older-post .related-post-wrapper-inner'); 504 const nextPost = document.querySelector('.newer-post .related-post-wrapper-inner'); 505 506 prevPost && nextPost ? null : document.querySelector('.related-posts-separator').style.display = 'none'; 507 } 508 }) 509} 510 511function contentResizeHandler(){ 512 let item = document.querySelector('main') 513 if (item) { 514 const resizeObserver = new ResizeObserver(entries => {
515 for (let entry of entries) { 516 document.querySelector('custom-indicator').onResizeHandler(); 517 } 518 }); 519 520 resizeObserver.observe(item); 521 } 522} 523 524function setToggle() { 525 const toggleHeadingElements = document.getElementsByClassName("kg-toggle-heading"); 526 527 const dropdownIcon = document.createElement('div'); 528 dropdownIcon.classList.add('dropdown-icon'); 529 const verticalLine = document.createElement('div'); 530 verticalLine.classList.add('dropdown-line', 'dropdown-vertical-line'); 531 const horizontalLine = document.createElement('div'); 532 horizontalLine.classList.add('dropdown-line', 'dropdown-horizontal-line'); 533 534 dropdownIcon.appendChild(verticalLine); 535 dropdownIcon.appendChild(horizontalLine); 536 537 document.querySelectorAll('.kg-toggle-card').forEach(card => { 538 card.querySelector('.kg-toggle-heading svg').remove(); 539 540 const container = card.querySelector(".kg-toggle-card-icon"); 541 const clonedIcon = dropdownIcon.cloneNode(true); 542 543 container.appendChild(clonedIcon); 544 }) 545 546 const toggleFn = function(event) { 547 548 const targetElement = event.target; 549 const parentElement = targetElement.closest('.kg-toggle-card'); 550 var toggleState = parentElement.getAttribute("data-kg-toggle-state"); 551 if (toggleState === 'close') { 552 parentElement.setAttribute('data-kg-toggle-state', 'open'); 553 } else { 554 parentElement.setAttribute('data-kg-toggle-state', 'close'); 555 } 556 }; 557 558 for (let i = 0; i < toggleHeadingElements.length; i++) { 559 toggleHeadingElements[i].addEventListener('click', toggleFn, false); 560 } 561} 562 563let lenis; 564 565function setSmoothScroll() { 566 if(window.safari !== undefined) return; 567 568 if (lenis) { 569 lenis.destroy(); 570 } 571 572 let speed = 0.6; 573 574 lenis = new Lenis({ 575 duration: speed, 576 easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), 577 direction: 'vertical', 578 gestureDirection: 'vertical', 579 smooth: true, 580 smoothTouch: false, 581 touchMultiplier: 2, 582 infinite: false 583 }) 584 585 function raf(time) { 586 lenis.raf(time) 587 requestAnimationFrame(raf) 588 } 589 590 requestAnimationFrame(raf); 591}
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.