PageSourceSearch

https://bblur.studio/assets/js/custom-elements.js?v=jdfnKEfUSM4NNttd

js bblur.studio collected 2026-10-04 08:05:41 UTC 36,701 bytes, 777 lines download raw bytes

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.