1document.addEventListener('DOMContentLoaded', function () { 2 if (!customElements.get('custom-header')) { 3 customElements.define('custom-header', class CustomHeader extends HTMLElement { 4 constructor() { 5 super(); 6 7 this.setNavigation(this); 8 this.secondLevelMenu(); 9 this.setAnnouncementBar(); 10 this.header = this.querySelector('.navbar-links-inner'); 11 this.blurTimeout; 12 this.blurEl = document.querySelector('.background-blur'); 13 14 this.header.addEventListener('mouseenter', this.blurHoverIn.bind(this)) 15 this.header.addEventListener('mouseleave', this.blurHoverOut.bind(this)); 16 17 if(this.blurEl) { 18 this.blurEl.addEventListener('click', this.blurHoverOut.bind(this)) 19 } 20 21 window.addEventListener('resize', debounce(() => { 22 this.setNavbarPosition(); 23 this.setPageWithNoHeader(); 24 }, 50)); 25 } 26 27 blurHoverIn(){ 28 const bgBlur = document.querySelector('.background-blur'); 29 if(document.querySelector('html').getAttribute('data-navigation-bar-background-color') != 'Blur Content On Hover') return; 30 31 bgBlur.style.pointerEvents = 'auto'; 32 bgBlur.style.display = 'block'; 33 34 if(this.blurTimeout) { 35 clearTimeout(this.blurTimeout); 36 } 37 38 setTimeout(() => { 39 bgBlur.style.opacity = 1; 40 }, 10); 41 } 42 43 blurHoverOut() { 44 const bgBlur = document.querySelector('.background-blur'); 45 if(document.querySelector('html').getAttribute('data-navigation-bar-background-color') != 'Blur Content On Hover') return; 46 47 bgBlur.style.opacity = 0; 48 this.blurTimeout = setTimeout(() => { 49 bgBlur.style.pointerEvents = 'none'; 50 bgBlur.style.display = 'none'; 51 }, 200); 52 } 53 54 setAnnouncementBar() { 55 let announcementBar = document.querySelector('#announcement-bar-root'); 56 57 if(announcementBar){ 58 if(announcementBar.children.length > 0){ 59 this.setNavbarPosition(); 60 } 61 62 const handleChildChange = (mutationsList, observer) => { 63 for (const mutation of mutationsList) { 64 if (mutation.type === "childList") { 65 // Child elements have been added or removed 66 const numChildren = announcementBar.children.length; 67 if(numChildren > 0){ 68 this.setNavbarPosition(); 69 this.setPageWithNoHeader(); 70 } 71 } 72 } 73 }; 74 75 const observer = new MutationObserver(handleChildChange); 76 const config = { childList: true }; 77 observer.observe(announcementBar, config); 78 } 79 80 this.setPageWithNoHeader(); 81 } 82 83 setPageWithNoHeader() { 84 let article = document.querySelector('.article'); 85 let announcementBar = document.querySelector('#announcement-bar-root'); 86 if(!article) return; 87 88 let postContent = document.querySelector('.post-content') 89 90 if(article.getAttribute('data-has-header') == 'false'){ 91 postContent.style.marginTop = `${document.querySelector('.header').offsetHeight}px`; 92 93 if(announcementBar){ 94 postContent.style.marginTop = `${document.querySelector('.header').offsetHeight + announcementBar.offsetHeight}px`; 95 } 96 } 97 } 98 99 setNavbarPosition(){ 100 let announcementBar = document.querySelector('#announcement-bar-root'); 101 102 if(announcementBar){ 103 let announcementBar = document.querySelector('#announcement-bar-root'); 104 105 document.querySelector('.header').style.top = announcementBar.offsetHeight + 'px'; 106 } 107 } 108 109 setNavigation(){ 110 const menuBtn = this.querySelector('.menu-button'); 111 const menu = this.querySelector('.navbar-links-outer'); 112 const navbar = this; 113 114 115 menu.style.transition = 'opacity 0.3s var(--ease-transition)'; 116 117 menuBtn.addEventListener('click', e => menuHandler(e)); 118 window.addEventListener('resize', debounce(() => {menuOnResize()}, 100)); //leave at 100, if smaller there is a bug with scrolling to the top 119 120 function menuHandler(e){ 121 if(menu.getAttribute('isopen') == 'true'){ 122 closeMenu(); 123 }else{ 124 openMenu(this); 125 } 126 } 127 128 function closeMenu(){ 129 enableScrolling(); 130 131 menuBtn.innerHTML = menuBtn.getAttribute('data-menu-closed-label'); 132 133 if(window.matchMedia('(max-width: 991px)').matches){ 134 setTimeout(() => { 135 menu.style.display = 'none'; 136 menu.setAttribute("isopen", false); 137 }, 300); 138 menu.style.opacity = '0'; 139 } 140 141 var announcementBar = document.querySelector('#announcement-bar-root'); 142 var announcementBarHeight = 0; 143 144 if(announcementBar) { 145 announcementBarHeight = announcementBar.offsetHeight; 146 document.querySelector('.header').style.top = announcementBar.offsetHeight + 'px'; 147 } 148 } 149 150 function openMenu(){ 151 disableScrolling(); 152 menu.setAttribute("isopen", true); 153 154 menuBtn.innerHTML = menuBtn.getAttribute('data-menu-opened-label'); 155 156 menu.style.display = 'flex'; 157 setTimeout(() => { 158 menu.style.opacity = '1'; 159 }, 10); 160 161 var announcementBar = document.querySelector('#announcement-bar-root'); 162 var announcementBarHeight = 0; 163 164 if(announcementBar) { 165 announcementBarHeight = announcementBar.offsetHeight; 166 document.querySelector('.header').style.top = announcementBar.offsetHeight + 'px'; 167 } 168
169 menu.style.height = `calc(100dvh - ${navbar.offsetHeight}px - ${announcementBarHeight}px)`; 170 } 171 172 function menuOnResize(){ 173 if(window.matchMedia('(max-width: 991px)').matches){ 174 menu.classList.remove('desktop-navbar') 175 }else{ 176 menu.classList.add('desktop-navbar'); 177 } 178 } 179 } 180 181 secondLevelMenu(){ 182 let navArray = []; 183 const navbar = this.querySelector('.nav'); 184 if(navbar){ 185 navbar.querySelectorAll('li').forEach(link => { 186 if (link.dataset.label.charAt(0) === "-") { 187 link.dataset.parent = link.dataset.label.substring(1); 188 if(link.dataset.label.includes("--")){ 189 var data = link.dataset.parent.split("--") 190 link.dataset.parent = data[0]; 191 link.dataset.child = data[1]; 192 193 link.querySelector('.nav-link').innerHTML = link.dataset.child; 194 link.querySelector('.nav-link').setAttribute('tabindex', '0'); 195 navArray.push({parent: data[0], child: link}); 196 }else{ 197 link.querySelector('.nav-link').innerHTML = link.dataset.parent; 198 199 const anchor = link.querySelector('a'); 200 const div = document.createElement('div'); 201 const ul = document.createElement('ul'); 202 const container = document.createElement('div') 203 204 div.innerHTML = anchor.innerHTML; 205 div.classList.add('links-label'); 206 div.dataset.label = link.dataset.parent; 207 div.innerHTML += ` 208 <div class="dropdown-icon"> 209 <div class="dropdown-line dropdown-vertical-line"></div> 210 <div class="dropdown-line dropdown-horizontal-line"></div> 211 </div> 212 ` 213 anchor.parentNode.replaceChild(div, anchor); 214 215 link.appendChild(container); 216 container.classList.add('secondary-links'); 217 container.appendChild(ul) 218 ul.classList.add('secondary-links-inner'); 219 220 div.parentElement.addEventListener('mouseenter', this.secondaryHoverIn.bind(this)); 221 div.parentElement.addEventListener('mouseleave', this.secondaryHoverOut.bind(this)); 222 223 //secondary links handler for mobile 224 div.parentElement.addEventListener('click', e => this.openCloseLinksOnMobile(link)); 225 } 226 } 227 }) 228 229 //move links 230 navArray.forEach(item => { 231 var secondaryList = document.querySelector(`.nav div.links-label[data-label="${item.parent}"]`).parentNode.querySelector('ul'); 232 item.child.querySelector('a').classList.remove('hover-underline'); 233 secondaryList.appendChild(item.child); 234 }) 235 236 navbar.style.display = 'flex'; 237 } 238 } 239 240 secondaryHoverIn(e) { 241 if(window.matchMedia('(max-width: 991px)').matches) return; 242 243 const navLinks = e.currentTarget.querySelectorAll('.secondary-links-inner li'); 244 245 e.currentTarget.querySelector('.nav li .dropdown-icon .dropdown-vertical-line').style.height = "0%"; 246 247 navLinks.forEach((link, index) => { 248 link.style.transition = `transform 1s var(--transition) ${0.075 * index}s, opacity 1s cubic-bezier(.19,1,.22,1) ${0.075 * index}s, background-size .3s cubic-bezier(.34,1,.58,1)`; 249 link.style.transform = 'translateY(0px)'; 250 link.style.opacity = '1'; 251 }); 252 } 253 254 secondaryHoverOut(e) { 255 if(window.matchMedia('(max-width: 991px)').matches) return; 256 257 const navLinks = e.currentTarget.querySelectorAll('.secondary-links-inner li'); 258 259 e.currentTarget.querySelector('.nav li .dropdown-icon .dropdown-vertical-line').style.height = "100%"; 260 261 navLinks.forEach((link) => { 262 link.style.transition = `transform 0s var(--transition) 0.3s, opacity 0s cubic-bezier(.19,1,.22,1) 0.3s, background-size .3s cubic-bezier(.34,1,.58,1)`; 263 link.style.transform = 'translateY(10px)'; 264 link.style.opacity = '0'; 265 }); 266 } 267 268 openCloseLinksOnMobile(link){ 269 if(window.matchMedia('(max-width: 991px)').matches){ 270 let container = link.querySelector('.secondary-links'); 271 if(container.offsetHeight == 0){ 272 container.style.height = 'auto'; 273 link.querySelector('.nav li .dropdown-icon .dropdown-vertical-line').style.height = "0%"; 274 }else{ 275 container.style.height = '0px' 276 link.querySelector('.nav li .dropdown-icon .dropdown-vertical-line').style.height = "100%"; 277 } 278 } 279 } 280 }) 281 } 282 283 if (!customElements.get('custom-socials')) { 284 customElements.define('custom-socials', class CustomSocials extends HTMLElement { 285 constructor() { 286 super(); 287 288 this.setSocials(this); 289 this.container = this; 290 } 291 292 setSocials(){ 293 const url = window.location.origin + "/" + "socials/" 294 fetch(url) 295 .then(response => response.text()) 296 .then(data => { 297 let parser = new DOMParser(); 298 let html = parser.parseFromString(data, "text/html"); 299 let content = html.querySelector('.socials-main'); 300 301 if(!content) return; 302 303 content.querySelectorAll('figure').forEach(figure => { 304 const a = document.createElement('a'); 305 a.classList.add('social', 'default-social'); 306 a.setAttribute('target', '_blank'); 307 308 let figcaption = figure.querySelector('figcaption'); 309 if(figcaption){ 310 a.setAttribute('aria-label', figcaption.textContent) 311 } 312 313 a.href = figure.querySelector('a').href; 314 315 const div = document.createElement('div'); 316 div.classList.add('social-inner'); 317 a.appendChild(div) 318 319 const image = document.createElement('img'); 320 image.src = figure.querySelector('img').src; 321 div.appendChild(image) 322 323 this.container.appendChild(a) 324 }) 325 }) 326 .catch(error => { 327 console.error("Error loading socials:", error); 328 }); 329 } 330 }) 331 } 332 333 if (!customElements.get('custom-pagination')) { 334 customElements.define('custom-pagination', class CustomPagination extends HTMLElement { 335 constructor() { 336 super(); 337 338 this.loadMoreBtn = this.querySelector("#load-more-btn"); 339 340 if(this.loadMoreBtn){ 341 this.loadMoreBtn.addEventListener("click", () => { 342 this.loadMorePosts();
343 }); 344 } 345 } 346 347 loadMorePosts(cleanList = false) { 348 let currentPage = parseInt(this.getAttribute("data-current-page")); 349 let nextPage = cleanList ? currentPage = 1 : currentPage + 1; 350 let startUrl = window.location; 351 let tagTitle; 352 353 if(this.getAttribute('data-is-archivepage') == "true"){ 354 let tagName = document.querySelector('archive-tags').getAttribute('data-current-tag'); 355 tagTitle = document.querySelector('archive-tags').getAttribute('data-current-tag-name'); 356 tagName == "all-tags" ? startUrl = window.location.origin + "/": startUrl = window.location.origin + `/tag/${tagName}/` 357 } 358 359 let url = startUrl + `page/${nextPage}/`; 360 361 // Make the AJAX request 362 fetch(url) 363 .then(response => response.text()) 364 .then(data => { 365 let parser = new DOMParser(); 366 let html = parser.parseFromString(data, "text/html"); 367 let grid = document.querySelector("#pagination-grid"); 368 let newPosts = html.querySelector("#pagination-grid").innerHTML; 369 370 // Append the new posts to the existing ones 371 cleanList ? grid.innerHTML = newPosts : grid.insertAdjacentHTML("beforeend", newPosts); 372 373 if(cleanList){ 374 let postNumber = html.querySelector('#posts-section'); 375 if(postNumber){ 376 document.querySelector(".hero-subtitle").textContent = postNumber.getAttribute('data-hidden-title-desktop').replace("- ", "") + " "; 377 document.querySelector('.custom-hero-bottom-text').textContent = postNumber.getAttribute('data-hidden-title-desktop').replace("- ", "") + " "; 378 document.querySelector('.subtitle-name').textContent = tagTitle; 379 380 if(isTablet) { 381 document.querySelectorAll('.section-text')[1].textContent = '02 ' + tagTitle; 382 }else{ 383 document.querySelectorAll('.section-text')[0].textContent = '01 ' + tagTitle; 384 } 385 } 386 } 387 388 // Update the "Load more" button attributes 389 this.setAttribute("data-current-page", nextPage); 390 if(this.loadMoreBtn){ 391 this.loadMoreBtn.style.display = html.querySelector("#load-more-btn") ? "block" : "none"; 392 } 393 394 pageLoadAnimations(grid); 395 396 createScrollTrigger(); 397 document.querySelector('custom-indicator').onResizeHandler(); 398 }) 399 .catch(error => { 400 console.error("Error loading more posts:", error); 401 }); 402 } 403 }) 404 } 405 406 if (!customElements.get('custom-notifications')) { 407 customElements.define('custom-notifications', class CustomNotifications extends HTMLElement { 408 constructor() { 409 super(); 410 411 this.setNotification(); 412 } 413 414 setNotification() { 415 var action = getParameterByName('action'); 416 var stripe = getParameterByName('stripe'); 417 var success = getParameterByName('success'); 418 419 if(success == null && action == null && stripe == null) return; 420 421 var notifications = document.querySelector('.global-notifications'); 422 if(stripe){ 423 notifications.classList.add(`stripe-${stripe}`); 424 425 notifications.addEventListener('animationend', () => { 426 notifications.classList.remove(`stripe-${stripe}`); 427 }); 428 }else{ 429 notifications.classList.add(`${action}-${success}`); 430 431 notifications.addEventListener('animationend', () => { 432 notifications.classList.remove(`${action}-${success}`); 433 }); 434 } 435 } 436 }) 437 } 438 439 if (!customElements.get('archive-tags')) { 440 customElements.define('archive-tags', class ArchiveTags extends HTMLElement { 441 constructor() { 442 super(); 443 444 this.querySelectorAll('.archive-tag-button').forEach(button => { 445 button.addEventListener('change', this.archiveButtonChange.bind(this)) 446 }) 447 } 448 449 archiveButtonChange(e){ 450 this.setAttribute('data-current-tag', e.target.id); 451 this.setAttribute('data-current-tag-name', e.target.getAttribute('data-name')); 452 453 document.querySelector('.mobile-subtitle-number').textContent = `(${e.target.getAttribute('data-number')})`; 454 455 // Make the AJAX request 456 document.getElementById('custom-pagination').loadMorePosts(true); 457 } 458 }) 459 } 460 461 if (!customElements.get('custom-form')) { 462 customElements.define('custom-form', class CustomForm extends HTMLElement { 463 constructor() { 464 super(); 465 466 const formElement = this.querySelector('form'); 467 const formPageContent = document.querySelector('.form-page-content'); 468 const heading = document.querySelector('.form-page-content h1'); 469 const description = document.querySelector('.paragraph-info'); 470 const homeButton = document.querySelector('.form-success-button'); 471 const successHeading = this.querySelector("#form-success-heading-text").getAttribute('data-success-heading'); 472 const successParagraph = this.querySelector("#form-success-paragraph-text").getAttribute('data-success-paragraph'); 473 474 let success = false; 475 const observer = new MutationObserver(function(mutationsList) { 476 for (const mutation of mutationsList) { 477 if (mutation.type === 'attributes' && mutation.attributeName === 'class') { 478 const classList = mutation.target.classList; 479 if (classList.contains('success') && !success) { 480 success = true; 481 482 formElement.style.display = "none"; 483 484 heading.innerHTML = successHeading; 485 homeButton.style.display = "block"; 486 487 description.innerHTML = successParagraph; 488 formPageContent.style.paddingTop = '0px'; 489 formPageContent.style.justifyContent = 'center'; 490 formElement.style.height = 'auto'; 491 document.querySelector('.form-container').style.height = 'auto'; 492 description.classList.add('success-paragraph'); 493 } 494 } 495 } 496 }); 497 498 observer.observe(formElement, { attributes: true });
499 } 500 }) 501 } 502 503 if (!customElements.get('custom-membership')) { 504 customElements.define('custom-membership', class CustomMembership extends HTMLElement { 505 constructor() { 506 super(); 507 508 this.querySelectorAll('.membership-button').forEach(button => { 509 button.addEventListener('change', this.tabChange.bind(this)) 510 }) 511 512 this.querySelectorAll('.tier-card').forEach(card => { 513 this.tierCardContent(card, 1); 514 }) 515 516 window.addEventListener('resize', debounce(() => { 517 this.querySelectorAll('.tier-card').forEach(card => { 518 this.tierCardContent(card, 1); 519 }) 520 }, 10)) 521 } 522 523 tierCardContent(card, columns){ 524 if(isTablet) return; 525 526 let benefits = card.querySelector('.benefits-outer'); 527 if(!benefits) return; 528 529 let top = card.querySelector('.tier-card-top').offsetHeight; 530 let button = card.querySelector('.tier-button-wrapper').offsetHeight; 531 532 benefits.querySelector('ul').style.columns = '1'; 533 534 changeColumns(card, columns) 535 536 function changeColumns(card, columns){ 537 let maxHeight = card.offsetHeight - top - button - parseFloat(window.getComputedStyle(card).paddingTop) - parseFloat(window.getComputedStyle(card).paddingBottom); 538 let benefitsHeight = benefits.offsetHeight; 539 540 if(benefitsHeight > maxHeight && benefitsHeight != 0){ 541 benefits.querySelector('ul').style.columns = `${columns + 1}`; 542 changeColumns(card, columns + 1); 543 } 544 } 545 } 546 547 tabChange(e) { 548 if(e.target.getAttribute('data-inactive') == "true"){ 549 e.target.setAttribute('data-inactive', "false"); 550 let name = e.target.getAttribute('data-tab') 551 this.querySelector(`.membership-tiers[data-tab-content=${name}]`).setAttribute('data-inactive', "false") 552 553 let oposite; 554 name == "yearly" ? oposite = "monthly" : oposite = "yearly" 555 556 document.querySelector(`.membership-button[data-tab=${oposite}]`).setAttribute('data-inactive', "true"); 557 this.querySelector(`.membership-tiers[data-tab-content=${oposite}]`).setAttribute('data-inactive', "true") 558 559 this.querySelectorAll('.tier-card').forEach(card => { 560 this.tierCardContent(card, 1); 561 }) 562 } 563 } 564 }) 565 } 566 567 if (!customElements.get('custom-indicator')) { 568 customElements.define('custom-indicator', class CustomIndicator extends HTMLElement { 569 constructor() { 570 super(); 571 572 let tagSlugs = document.querySelector('#tags-on-homepage') 573 574 if(!tagSlugs){ 575 this.init(); 576 } 577 } 578 579 init(){ 580 //this needs to happen before the indicator is initialized 581 stickyHeading(); 582 583 document.querySelector('main[is-vertical]') ? this.isVertical = true : this.isVertical = false; 584 document.querySelector('main[add-headings]') ? this.addHeadings = true : this.addHeadings = false; 585 586 this.sections = []; 587 588 if(this.isVertical){ 589 let hero = document.querySelector('.hero') 590 if(hero){ 591 this.sections.push(hero) 592 } 593 594 if(this.addHeadings){ 595 document.querySelector('.post-content').querySelectorAll('h1[id], h2[id], h3[id], h4[id]').forEach(heading => { 596 heading.setAttribute('data-section-name', heading.textContent) 597 this.sections.push(heading); 598 }) 599 } 600 601 let comments = document.querySelector('.comments-section') 602 if(comments){ 603 this.sections.push(comments); 604 } 605 606 let relatedPosts = document.querySelector('.related-posts') 607 if(relatedPosts){ 608 this.sections.push(relatedPosts); 609 } 610 611 this.sections.push(document.querySelector('.footer-section')) 612 }else{ 613 this.sections = document.querySelectorAll('.horizontal-section'); 614 } 615 616 this.sectionsInfo = [] 617 this.animationOut = false; 618 this.size = 0; 619 620 this.createIndicators(); 621 this.onResizeHandler(); 622 623 window.addEventListener('resize', debounce(() => { 624 this.onResizeHandler() 625 }, 50)); 626 627 window.addEventListener('scroll', debounce(() => { 628 this.onScrollHandler() 629 }, 10)); 630 } 631 632 createIndicators() { 633 let text; 634 let publishedWith = document.querySelector('#footer-info').getAttribute('data-published-with'); 635 let siteTitle = document.querySelector('#footer-info').getAttribute('data-site-title'); 636 637 let temp = document.querySelector("#indicator-line-template"); 638 let textTemp = document.querySelector("#section-text-template"); 639 640 this.sections.forEach((section, index) => { 641 let clone = temp.content.cloneNode(true); 642 let textClone = textTemp.content.cloneNode(true); 643 644 let number = String(index + 1).padStart(2, '0'); 645 index == this.sections.length - 1 ? text = `${publishedWith} <a href="https://ghost.org/" target="_blank">Ghost</a> & <a href="https://www.kusa-projects.com/" target="_blank">${siteTitle}</a>` : text = section.getAttribute('data-section-name'); 646 647 clone.querySelector('.indicator-line').setAttribute('data-index', index); 648 textClone.querySelector('.section-text').setAttribute('data-index', index); 649 650 textClone.querySelector('.section-text').innerHTML = number + " " + text; 651 652 section.id = section.getAttribute('data-section-name').toLowerCase().replace(/\s+/g, '-'); 653 654 this.querySelector(".indicator-lines").appendChild(clone); 655 this.querySelector(".current-section-wrapper").appendChild(textClone); 656 657 658 this.sectionsInfo.push({ 659 section: section, 660 width: section.getBoundingClientRect().width, 661 indicator: this.querySelectorAll('.indicator-line')[index], 662 number: number, 663 text: text, 664 textElement: this.querySelectorAll('.section-text')[index] 665 }) 666 667 this.querySelectorAll('.indicator-line')[index].addEventListener('mouseenter', (e) => { 668 this.querySelectorAll('.section-text').forEach(text => { 669 text.classList.remove('active-text') 670 }) 671 this.querySelectorAll('.section-text')[index].classList.add('active-text'); 672 }) 673 674 this.querySelectorAll('.indicator-line')[index].addEventListener('mouseleave', (e) => { 675 this.onScrollHandler() 676 }) 677 }) 678 } 679 680 setIndicators() { 681 const element = document.querySelector('.header .wide-container'); 682 const paddingLeftValue = parseFloat(window.getComputedStyle(element).getPropertyValue('padding-left')); 683 684 let position = paddingLeftValue; 685 686 isTablet ? position = 0 : position = paddingLeftValue; 687 688 // Remove existing event listeners and add new ones 689 this.sections.forEach((section, index) => { 690 if(isTablet || this.isVertical){ 691 position = section.getBoundingClientRect().top + (window.scrollY || window.pageYOffset); 692 } 693 694 const boundScrollToSection = this.scrollToSection.bind(this, index == 0 ? 0 : position); 695 const indicatorLine = this.querySelectorAll('.indicator-line')[index]; 696 697 indicatorLine.removeEventListener('click', indicatorLine.boundScrollToSection); 698 indicatorLine.boundScrollToSection = boundScrollToSection; // Store reference 699 indicatorLine.addEventListener('click', boundScrollToSection); 700 701 if(!isTablet && !this.isVertical){ 702 position += section.getBoundingClientRect().width; 703 } 704 }); 705 706 this.onScrollHandler(); 707 } 708 709 onResizeHandler(){ 710 this.setIndicators(); 711 712 isTablet || this.isVertical ? this.offset = document.documentElement.clientHeight * 0.9: this.offset = document.documentElement.clientWidth / 4; 713 this.size = 0; 714 715 this.sections.forEach((section, index) => { 716 if(isTablet || this.isVertical){ 717 this.sectionsInfo[index].width = section.getBoundingClientRect().top + (window.scrollY || window.pageYOffset); 718 this.size = section.getBoundingClientRect().top + (window.scrollY || window.pageYOffset); 719 }else{ 720 this.sectionsInfo[index].width = section.offsetWidth; 721 this.size += section.offsetWidth; 722 } 723 }) 724 725 this.onScrollHandler(); 726 } 727 728 scrollToSection(position) { 729 if(isTablet){ 730 position = position - document.querySelector('.header').offsetHeight; 731 } 732 733 window.scroll({ 734 top: position, 735 behavior: 'smooth' 736 }); 737 } 738 739 onScrollHandler(){ 740 const scrollX = (window.scrollY || window.pageYOffset) + this.offset; 741 742 let cumulativeWidth = 0; 743 let activeSectionIndex = 0; 744 let endOffset = 0; 745 746 isTablet || this.isVertical ? endOffset = 0 : endOffset = document.documentElement.clientWidth; 747 748 for (let i = 0; i < this.sectionsInfo.length; i++) { 749 isTablet || this.isVertical ? cumulativeWidth = this.sectionsInfo[i].width : cumulativeWidth += this.sectionsInfo[i].width; 750 751 //if at the end of screen - offset 752 if (scrollX >= this.size - endOffset) { 753 activeSectionIndex = i; 754 } else { 755 // Check if scroll position is within current section 756 if (scrollX < cumulativeWidth) { 757 isTablet || this.isVertical ? activeSectionIndex = i - 1 : activeSectionIndex = i; 758 break; 759 } 760 } 761 } 762 763 this.sectionsInfo.forEach((sectionInfo, index) => { 764 if (index === activeSectionIndex) { 765 sectionInfo.indicator.classList.add('active-indicator'); 766 sectionInfo.textElement.classList.add('active-text'); 767 } else { 768 sectionInfo.indicator.classList.remove('active-indicator'); 769 sectionInfo.textElement.classList.remove('active-text'); 770 } 771 }); 772 } 773 }) 774 } 775}) 776 777
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.