PageSourceSearch

https://www.gbrailfreight.com/wp-content/themes/gbrf/js/easyScrollDots.min.js?ver=1771941416

js gbrailfreight.com collected 2026-09-24 22:44:17 UTC 7,822 bytes, 183 lines download raw bytes

1/* Easy Scroll Dots 2.0.2 --- https://github.com/Superhands89/EasyScrollDots
2* Quickly add anchor points throughout your web page/application and have navigational dots automatically appear
3* in a fixed position on the side of the page. This allows the user to click to scroll though sections of the page,
4* and it updates as they scroll.
5*
6* By Superhands89 2019-2022 --- https://github.com/Superhands89/
7*
8* GPLv3 Licence for open source use only
9* or purchase a Single Commercial License at https://gum.co/TdtEX
10* or purchase an Extended Commercial License at https://gum.co/NGfmlw
11*/
12
13// a throttle function
14function dotsThrottle(func, wait, options) {
15    let context, args, result;
16    let timeout = null;
17    let previous = 0;
18    if (!options) options = {};
19    const later = function () {
20        previous = options.leading === false ? 0 : Date.now();
21        timeout = null;
22        result = func.apply(context, args);
23        if (!timeout) context = args = null;
24    };
25    return function () {
26        const now = Date.now();
27        if (!previous && options.leading === false) previous = now;
28        const remaining = wait - (now - previous);
29        context = this;
30        args = arguments;
31        if (remaining <= 0 || remaining > wait) {
32            if (timeout) {
33                clearTimeout(timeout);
34                timeout = null;
35            }
36            previous = now;
37            result = func.apply(context, args);
38            if (!timeout) context = args = null;
39        } else if (!timeout && options.trailing !== false) {
40            timeout = setTimeout(later, remaining);
41        }
42        return result;
43    };
44};
45
46// global fixed nav and offset options
47let dotFixedNavPresent = false;
48let dotFixedNavId = '';
49let dotFixedNavUp = false;
50let dotOffset = 0;
51let dotNavHeight = 0;
52
53// scroll indicator controller
54function easyScrollDots(dotfixedOptions) {
55    let scrollIndi = document.querySelectorAll('[data-scroll-indicator-title]');
56    dotfixedOptions.fixedNav === true ? dotFixedNavPresent = true : dotFixedNavPresent;
57    dotfixedOptions.fixedNavId === '' ? dotFixedNavId = false : dotFixedNavId = dotfixedOptions.fixedNavId;
58    dotfixedOptions.fixedNavUpward === true ? dotFixedNavUp = true : dotFixedNavUp;
59    dotfixedOptions.offset > 0 ? dotOffset = dotfixedOptions.offset : dotOffset;
60
61    if (dotFixedNavPresent === true && dotFixedNavId.length) {
62    // there is a fixed nav and its id has been defined
63    const dotNavHeightElem = document.getElementById(dotFixedNavId);
64        if (dotNavHeightElem !== null) {
65	    dotNavHeight = dotNavHeightElem.clientHeight;
66        }
67        else {
68	    console.error('easyScrollDots Error: A fixed navigation ID has been defined, but an element with the corresponding ID cannot be found in the DOM. Check you have spelled the dotFixedNavId correctly.')
69        }
70    }
71
72    if (scrollIndi.length) {
73        const scrollIndiTemplate = '<div class="scroll-indicator-controller"><span></span></div>';
74        document.querySelector('body').lastElementChild.insertAdjacentHTML('afterend', scrollIndiTemplate);
75        const scrollIndiController = document.querySelector('.scroll-indicator-controller');
76        if ((typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1)) { scrollIndiController.classList.add('indi-mobile'); }
77        const scrollIndiElems = Array.prototype.slice.call(scrollIndi);
78
79        scrollIndiElems.forEach(function (e, i) {
80            const scrollIndiTitle = e.getAttribute('data-scroll-indicator-title');
81            let firstActiveClass = '';
82
83            if (i == 0) {
84                firstActiveClass = 'active';
85            }
86            scrollIndiController.lastElementChild.insertAdjacentHTML('afterend', '<div class="' + firstActiveClass + '" data-indi-controller-id="' + scrollIndiTitle + '" onclick="scrollIndiClicked(\'' + scrollIndiTitle + '\');"><span>' + scrollIndiTitle + '</span><div></div></div>');
87            if (i == scrollIndiElems.length - 1) { // insert additional anchor so that we can always refer to previousElementSibling
88                scrollIndiController.lastElementChild.insertAdjacentHTML('afterend', '<div data-indi-controller-id="indiEnd" style="display: none;"></div>');
89                e.insertAdjacentHTML('afterend', '<div data-scroll-indicator-title="indiEnd"></div>');
90                scrollIndiElems.push(document.querySelector('[data-scroll-indicator-title="indiEnd"]'));
91            }
92        });
93
94        const scrollIndiControllerDots = scrollIndiController.querySelectorAll('[data-indi-controller-id]');
95
96        const handleIndiScroll = dotsThrottle(function () {
97            let indiScrollTopCollection = {};
98            
99
100            scrollIndiElems.forEach(function (e) {
101                const scrollIndiIdScroll = e.getAttribute('data-scroll-indicator-title');
102                const indiScrollTop = e.getBoundingClientRect().top;
103
104                indiScrollTopCollection[scrollIndiIdScroll] = indiScrollTop;
105            });
106
107            // const indiOffsetValues = Object.values(indiScrollTopCollection); not supported in IE
108            const indiOffsetValues = Object.keys(indiScrollTopCollection).map(function (itm) { return indiScrollTopCollection[itm]; });
109			
110            const indiOffsetMin = function () {
111                const indiRemoveMinuses = indiOffsetValues.filter(function (x) { return x > 0 + ((window.innerHeight-80) + 2) + dotNavHeight; });
112
113                return Math.min.apply(null, indiRemoveMinuses);
114            };
115
116            Object.keys(indiScrollTopCollection).forEach(function (e) {
117
118                if (indiScrollTopCollection[e] == indiOffsetMin()) {
119
120                    Array.prototype.forEach.call(scrollIndiControllerDots, function (el) {
121                        if (el.classList.contains('active')) {
122                            el.classList.remove('active');
123                        }
124                    });
125
126                    const activeItemCandidate = scrollIndiController.querySelector('[data-indi-controller-id="' + e + '"]');
127                    const activeItemCandidatePrevEl = activeItemCandidate.previousElementSibling;
128                    const activeItem = activeItemCandidatePrevEl.tagName.toLowerCase() !== 'span' ? activeItemCandidatePrevEl : activeItemCandidate;
129
130                    activeItem.classList.add('active');
131       
132                }
133                
134            });
135
136        }, 300);
137
138        window.addEventListener('scroll', handleIndiScroll);
139    }
140};
141
142function scrollIndiClicked(indiId) {
143
144    const indiElement = document.querySelector('[data-scroll-indicator-title="' + indiId + '"]');
145
146    if (dotNavHeight !== 0) {
147
148        if (dotFixedNavUp === true) {
149            // fix nav on upward scroll only
150            window.scrollTo({
151                top: indiElement.offsetTop - dotOffset,
152                left: 0,
153                behavior: 'smooth'
154            });
155            const scrollPos = document.body.getBoundingClientRect().top;
156            setTimeout(function () {
157                if (document.body.getBoundingClientRect().top > scrollPos) {
158                    window.scrollTo({
159                        top: indiElement.offsetTop - dotNavHeight - dotOffset,
160                        left: 0,
161                        behavior: 'smooth'
162                    });
163                }
164            }, 400);
165        }
166        else {
167            // fixed nav scroll
168            window.scrollTo({
169                top: indiElement.offsetTop - dotNavHeight - dotOffset,
170                left: 0,
171                behavior: 'smooth'
172            });
173        }
174    }
175    else {
176        // normal scroll
177        window.scrollTo({
178            top: indiElement.offsetTop - dotOffset,
179            left: 0,
180            behavior: 'smooth'
181        });
182    }
183};

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.