vendor: 6,474 bytes, lines 1-183
1/*! 2 * Bootstrap scrollspy.js v5.2.2 (https://getbootstrap.com/) 3 * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 */ 6(function (global, factory) { 7 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./util/index'), require('./dom/event-handler'), require('./dom/selector-engine'), require('./base-component')) : 8 typeof define === 'function' && define.amd ? define(['./util/index', './dom/event-handler', './dom/selector-engine', './base-component'], factory) : 9 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Scrollspy = factory(global.Index, global.EventHandler, global.SelectorEngine, global.BaseComponent)); 10})(this, (function (index, EventHandler, SelectorEngine, BaseComponent) { 'use strict'; 11 12 const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e }; 13 14 const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler); 15 const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine); 16 const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent); 17 18 /** 19 * -------------------------------------------------------------------------- 20 * Bootstrap (v5.2.2): scrollspy.js 21 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 22 * -------------------------------------------------------------------------- 23 */ 24 /** 25 * Constants 26 */ 27 28 const NAME = 'scrollspy'; 29 const DATA_KEY = 'bs.scrollspy'; 30 const EVENT_KEY = `.${DATA_KEY}`; 31 const DATA_API_KEY = '.data-api'; 32 const EVENT_ACTIVATE = `activate${EVENT_KEY}`; 33 const EVENT_CLICK = `click${EVENT_KEY}`; 34 const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`; 35 const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'; 36 const CLASS_NAME_ACTIVE = 'active'; 37 const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]'; 38 const SELECTOR_TARGET_LINKS = '[href]'; 39 const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'; 40 const SELECTOR_NAV_LINKS = '.nav-link'; 41 const SELECTOR_NAV_ITEMS = '.nav-item'; 42 const SELECTOR_LIST_ITEMS = '.list-group-item'; 43 const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`; 44 const SELECTOR_DROPDOWN = '.dropdown'; 45 const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'; 46 const Default = { 47 offset: null, 48 // TODO: v6 @deprecated, keep it for backwards compatibility reasons 49 rootMargin: '0px 0px -25%', 50 smoothScroll: false, 51 target: null, 52 threshold: [0.1, 0.5, 1] 53 }; 54 const DefaultType = { 55 offset: '(number|null)', 56 // TODO v6 @deprecated, keep it for backwards compatibility reasons 57 rootMargin: 'string', 58 smoothScroll: 'boolean', 59 target: 'element', 60 threshold: 'array' 61 }; 62 /** 63 * Class definition 64 */ 65 66 class ScrollSpy extends BaseComponent__default.default { 67 constructor(element, config) { 68 super(element, config); // this._element is the observablesContainer and config.target the menu links wrapper 69 70 this._targetLinks = new Map(); 71 this._observableSections = new Map(); 72 this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element; 73 this._activeTarget = null; 74 this._observer = null; 75 this._previousScrollData = { 76 visibleEntryTop: 0, 77 parentScrollTop: 0 78 }; 79 this.refresh(); // initialize 80 } // Getters 81 82 83 static get Default() { 84 return Default; 85 } 86 87 static get DefaultType() { 88 return DefaultType; 89 } 90 91 static get NAME() { 92 return NAME; 93 } // Public 94 95 96 refresh() { 97 this._initializeTargetsAndObservables(); 98 99 this._maybeEnableSmoothScroll(); 100 101 if (this._observer) { 102 this._observer.disconnect(); 103 } else { 104 this._observer = this._getNewObserver(); 105 } 106 107 for (const section of this._observableSections.values()) { 108 this._observer.observe(section); 109 } 110 } 111 112 dispose() { 113 this._observer.disconnect(); 114 115 super.dispose(); 116 } // Private 117 118 119 _configAfterMerge(config) { 120 // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case 121 config.target = index.getElement(config.target) || document.body; // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only 122 123 config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin; 124 125 if (typeof config.threshold === 'string') { 126 config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value)); 127 } 128 129 return config; 130 } 131 132 _maybeEnableSmoothScroll() { 133 if (!this._config.smoothScroll) { 134 return; 135 } // unregister any previous listeners 136 137 138 EventHandler__default.default.off(this._config.target, EVENT_CLICK); 139 EventHandler__default.default.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => { 140 const observableSection = this._observableSections.get(event.target.hash); 141 142 if (observableSection) { 143 event.preventDefault(); 144 const root = this._rootElement || window; 145 const height = observableSection.offsetTop - this._element.offsetTop; 146 147 if (root.scrollTo) { 148 root.scrollTo({ 149 top: height, 150 behavior: 'smooth' 151 }); 152 return; 153 } // Chrome 60 doesn't support `scrollTo` 154 155 156 root.scrollTop = height; 157 } 158 }); 159 } 160 161 _getNewObserver() { 162 const options = { 163 root: this._rootElement, 164 threshold: this._config.threshold, 165 rootMargin: this._config.rootMargin 166 }; 167 return new IntersectionObserver(entries => this._observerCallback(entries), options); 168 } // The logic of selection 169 170 171 _observerCallback(entries) { 172 const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`); 173 174 const activate = entry => { 175 this._previousScrollData.visibleEntryTop = entry.target.offsetTop; 176 177 this._process(targetElement(entry)); 178 }; 179 180 const parentScrollTop = (this._rootElement || document.documentElement).scrollTop; 181 const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop; 182 this._previousScrollData.parentScrollTop = parentScrollTop; 183
184 for (const entry of entries) { 185 if (!entry.isIntersecting) { 186 this._activeTarget = null; 187 188 this._clearActiveClass(targetElement(entry)); 189 190 continue; 191 } 192 193 const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; // if we are scrolling down, pick the bigger offsetTop 194 195 if (userScrollsDown && entryIsLowerThanPrevious) { 196 activate(entry); // if parent isn't scrolled, let's keep the first visible item, breaking the iteration 197 198 if (!parentScrollTop) { 199 return; 200 } 201 202 continue; 203 } // if we are scrolling up, pick the smallest offsetTop 204 205 206 if (!userScrollsDown && !entryIsLowerThanPrevious) { 207 activate(entry); 208 } 209 } 210 } 211 212 _initializeTargetsAndObservables() { 213 this._targetLinks = new Map(); 214 this._observableSections = new Map(); 215 const targetLinks = SelectorEngine__default.default.find(SELECTOR_TARGET_LINKS, this._config.target); 216 217 for (const anchor of targetLinks) { 218 // ensure that the anchor has an id and is not disabled 219 if (!anchor.hash || index.isDisabled(anchor)) { 220 continue; 221 } 222 223 const observableSection = SelectorEngine__default.default.findOne(anchor.hash, this._element); // ensure that the observableSection exists & is visible 224 225 if (index.isVisible(observableSection)) { 226 this._targetLinks.set(anchor.hash, anchor); 227 228 this._observableSections.set(anchor.hash, observableSection); 229 } 230 } 231 } 232 233 _process(target) { 234 if (this._activeTarget === target) { 235 return; 236 } 237 238 this._clearActiveClass(this._config.target); 239 240 this._activeTarget = target; 241 target.classList.add(CLASS_NAME_ACTIVE); 242 243 this._activateParents(target); 244 245 EventHandler__default.default.trigger(this._element, EVENT_ACTIVATE, { 246 relatedTarget: target 247 }); 248 } 249 250 _activateParents(target) { 251 // Activate dropdown parents 252 if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) { 253 SelectorEngine__default.default.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE); 254 return; 255 } 256 257 for (const listGroup of SelectorEngine__default.default.parents(target, SELECTOR_NAV_LIST_GROUP)) { 258 // Set triggered links parents as active 259 // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor 260 for (const item of SelectorEngine__default.default.prev(listGroup, SELECTOR_LINK_ITEMS)) { 261 item.classList.add(CLASS_NAME_ACTIVE); 262 } 263 } 264 } 265 266 _clearActiveClass(parent) { 267 parent.classList.remove(CLASS_NAME_ACTIVE); 268 const activeNodes = SelectorEngine__default.default.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent); 269 270 for (const node of activeNodes) { 271 node.classList.remove(CLASS_NAME_ACTIVE); 272 } 273 } // Static 274 275 276 static jQueryInterface(config) { 277 return this.each(function () { 278 const data = ScrollSpy.getOrCreateInstance(this, config); 279 280 if (typeof config !== 'string') { 281 return; 282 } 283 284 if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { 285 throw new TypeError(`No method named "${config}"`); 286 } 287 288 data[config](); 289 }); 290 } 291 292 } 293 /** 294 * Data API implementation 295 */ 296 297 298 EventHandler__default.default.on(window, EVENT_LOAD_DATA_API, () => { 299 for (const spy of SelectorEngine__default.default.find(SELECTOR_DATA_SPY)) { 300 ScrollSpy.getOrCreateInstance(spy); 301 } 302 }); 303 /** 304 * jQuery 305 */ 306 307 index.defineJQueryPlugin(ScrollSpy); 308 309 return ScrollSpy; 310 311})); 312//# sourceMappingURL=scrollspy.js.map
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.