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.