1 | /*!
|
---|
2 | * Bootstrap scrollspy.js v5.1.3 (https://getbootstrap.com/)
|
---|
3 | * Copyright 2011-2021 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('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
|
---|
8 | typeof define === 'function' && define.amd ? define(['./dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
|
---|
9 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ScrollSpy = factory(global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
|
---|
10 | })(this, (function (EventHandler, Manipulator, 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 Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
|
---|
16 | const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
|
---|
17 | const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
|
---|
18 |
|
---|
19 | /**
|
---|
20 | * --------------------------------------------------------------------------
|
---|
21 | * Bootstrap (v5.1.3): util/index.js
|
---|
22 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
---|
23 | * --------------------------------------------------------------------------
|
---|
24 | */
|
---|
25 |
|
---|
26 | const toType = obj => {
|
---|
27 | if (obj === null || obj === undefined) {
|
---|
28 | return `${obj}`;
|
---|
29 | }
|
---|
30 |
|
---|
31 | return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
|
---|
32 | };
|
---|
33 |
|
---|
34 | const getSelector = element => {
|
---|
35 | let selector = element.getAttribute('data-bs-target');
|
---|
36 |
|
---|
37 | if (!selector || selector === '#') {
|
---|
38 | let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
|
---|
39 | // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
|
---|
40 | // `document.querySelector` will rightfully complain it is invalid.
|
---|
41 | // See https://github.com/twbs/bootstrap/issues/32273
|
---|
42 |
|
---|
43 | if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
|
---|
44 | return null;
|
---|
45 | } // Just in case some CMS puts out a full URL with the anchor appended
|
---|
46 |
|
---|
47 |
|
---|
48 | if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
|
---|
49 | hrefAttr = `#${hrefAttr.split('#')[1]}`;
|
---|
50 | }
|
---|
51 |
|
---|
52 | selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
|
---|
53 | }
|
---|
54 |
|
---|
55 | return selector;
|
---|
56 | };
|
---|
57 |
|
---|
58 | const getSelectorFromElement = element => {
|
---|
59 | const selector = getSelector(element);
|
---|
60 |
|
---|
61 | if (selector) {
|
---|
62 | return document.querySelector(selector) ? selector : null;
|
---|
63 | }
|
---|
64 |
|
---|
65 | return null;
|
---|
66 | };
|
---|
67 |
|
---|
68 | const isElement = obj => {
|
---|
69 | if (!obj || typeof obj !== 'object') {
|
---|
70 | return false;
|
---|
71 | }
|
---|
72 |
|
---|
73 | if (typeof obj.jquery !== 'undefined') {
|
---|
74 | obj = obj[0];
|
---|
75 | }
|
---|
76 |
|
---|
77 | return typeof obj.nodeType !== 'undefined';
|
---|
78 | };
|
---|
79 |
|
---|
80 | const getElement = obj => {
|
---|
81 | if (isElement(obj)) {
|
---|
82 | // it's a jQuery object or a node element
|
---|
83 | return obj.jquery ? obj[0] : obj;
|
---|
84 | }
|
---|
85 |
|
---|
86 | if (typeof obj === 'string' && obj.length > 0) {
|
---|
87 | return document.querySelector(obj);
|
---|
88 | }
|
---|
89 |
|
---|
90 | return null;
|
---|
91 | };
|
---|
92 |
|
---|
93 | const typeCheckConfig = (componentName, config, configTypes) => {
|
---|
94 | Object.keys(configTypes).forEach(property => {
|
---|
95 | const expectedTypes = configTypes[property];
|
---|
96 | const value = config[property];
|
---|
97 | const valueType = value && isElement(value) ? 'element' : toType(value);
|
---|
98 |
|
---|
99 | if (!new RegExp(expectedTypes).test(valueType)) {
|
---|
100 | throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
|
---|
101 | }
|
---|
102 | });
|
---|
103 | };
|
---|
104 |
|
---|
105 | const getjQuery = () => {
|
---|
106 | const {
|
---|
107 | jQuery
|
---|
108 | } = window;
|
---|
109 |
|
---|
110 | if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
|
---|
111 | return jQuery;
|
---|
112 | }
|
---|
113 |
|
---|
114 | return null;
|
---|
115 | };
|
---|
116 |
|
---|
117 | const DOMContentLoadedCallbacks = [];
|
---|
118 |
|
---|
119 | const onDOMContentLoaded = callback => {
|
---|
120 | if (document.readyState === 'loading') {
|
---|
121 | // add listener on the first call when the document is in loading state
|
---|
122 | if (!DOMContentLoadedCallbacks.length) {
|
---|
123 | document.addEventListener('DOMContentLoaded', () => {
|
---|
124 | DOMContentLoadedCallbacks.forEach(callback => callback());
|
---|
125 | });
|
---|
126 | }
|
---|
127 |
|
---|
128 | DOMContentLoadedCallbacks.push(callback);
|
---|
129 | } else {
|
---|
130 | callback();
|
---|
131 | }
|
---|
132 | };
|
---|
133 |
|
---|
134 | const defineJQueryPlugin = plugin => {
|
---|
135 | onDOMContentLoaded(() => {
|
---|
136 | const $ = getjQuery();
|
---|
137 | /* istanbul ignore if */
|
---|
138 |
|
---|
139 | if ($) {
|
---|
140 | const name = plugin.NAME;
|
---|
141 | const JQUERY_NO_CONFLICT = $.fn[name];
|
---|
142 | $.fn[name] = plugin.jQueryInterface;
|
---|
143 | $.fn[name].Constructor = plugin;
|
---|
144 |
|
---|
145 | $.fn[name].noConflict = () => {
|
---|
146 | $.fn[name] = JQUERY_NO_CONFLICT;
|
---|
147 | return plugin.jQueryInterface;
|
---|
148 | };
|
---|
149 | }
|
---|
150 | });
|
---|
151 | };
|
---|
152 |
|
---|
153 | /**
|
---|
154 | * --------------------------------------------------------------------------
|
---|
155 | * Bootstrap (v5.1.3): scrollspy.js
|
---|
156 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
---|
157 | * --------------------------------------------------------------------------
|
---|
158 | */
|
---|
159 | /**
|
---|
160 | * ------------------------------------------------------------------------
|
---|
161 | * Constants
|
---|
162 | * ------------------------------------------------------------------------
|
---|
163 | */
|
---|
164 |
|
---|
165 | const NAME = 'scrollspy';
|
---|
166 | const DATA_KEY = 'bs.scrollspy';
|
---|
167 | const EVENT_KEY = `.${DATA_KEY}`;
|
---|
168 | const DATA_API_KEY = '.data-api';
|
---|
169 | const Default = {
|
---|
170 | offset: 10,
|
---|
171 | method: 'auto',
|
---|
172 | target: ''
|
---|
173 | };
|
---|
174 | const DefaultType = {
|
---|
175 | offset: 'number',
|
---|
176 | method: 'string',
|
---|
177 | target: '(string|element)'
|
---|
178 | };
|
---|
179 | const EVENT_ACTIVATE = `activate${EVENT_KEY}`;
|
---|
180 | const EVENT_SCROLL = `scroll${EVENT_KEY}`;
|
---|
181 | const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;
|
---|
182 | const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';
|
---|
183 | const CLASS_NAME_ACTIVE = 'active';
|
---|
184 | const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]';
|
---|
185 | const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
|
---|
186 | const SELECTOR_NAV_LINKS = '.nav-link';
|
---|
187 | const SELECTOR_NAV_ITEMS = '.nav-item';
|
---|
188 | const SELECTOR_LIST_ITEMS = '.list-group-item';
|
---|
189 | const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}, .${CLASS_NAME_DROPDOWN_ITEM}`;
|
---|
190 | const SELECTOR_DROPDOWN = '.dropdown';
|
---|
191 | const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
|
---|
192 | const METHOD_OFFSET = 'offset';
|
---|
193 | const METHOD_POSITION = 'position';
|
---|
194 | /**
|
---|
195 | * ------------------------------------------------------------------------
|
---|
196 | * Class Definition
|
---|
197 | * ------------------------------------------------------------------------
|
---|
198 | */
|
---|
199 |
|
---|
200 | class ScrollSpy extends BaseComponent__default.default {
|
---|
201 | constructor(element, config) {
|
---|
202 | super(element);
|
---|
203 | this._scrollElement = this._element.tagName === 'BODY' ? window : this._element;
|
---|
204 | this._config = this._getConfig(config);
|
---|
205 | this._offsets = [];
|
---|
206 | this._targets = [];
|
---|
207 | this._activeTarget = null;
|
---|
208 | this._scrollHeight = 0;
|
---|
209 | EventHandler__default.default.on(this._scrollElement, EVENT_SCROLL, () => this._process());
|
---|
210 | this.refresh();
|
---|
211 |
|
---|
212 | this._process();
|
---|
213 | } // Getters
|
---|
214 |
|
---|
215 |
|
---|
216 | static get Default() {
|
---|
217 | return Default;
|
---|
218 | }
|
---|
219 |
|
---|
220 | static get NAME() {
|
---|
221 | return NAME;
|
---|
222 | } // Public
|
---|
223 |
|
---|
224 |
|
---|
225 | refresh() {
|
---|
226 | const autoMethod = this._scrollElement === this._scrollElement.window ? METHOD_OFFSET : METHOD_POSITION;
|
---|
227 | const offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
|
---|
228 | const offsetBase = offsetMethod === METHOD_POSITION ? this._getScrollTop() : 0;
|
---|
229 | this._offsets = [];
|
---|
230 | this._targets = [];
|
---|
231 | this._scrollHeight = this._getScrollHeight();
|
---|
232 | const targets = SelectorEngine__default.default.find(SELECTOR_LINK_ITEMS, this._config.target);
|
---|
233 | targets.map(element => {
|
---|
234 | const targetSelector = getSelectorFromElement(element);
|
---|
235 | const target = targetSelector ? SelectorEngine__default.default.findOne(targetSelector) : null;
|
---|
236 |
|
---|
237 | if (target) {
|
---|
238 | const targetBCR = target.getBoundingClientRect();
|
---|
239 |
|
---|
240 | if (targetBCR.width || targetBCR.height) {
|
---|
241 | return [Manipulator__default.default[offsetMethod](target).top + offsetBase, targetSelector];
|
---|
242 | }
|
---|
243 | }
|
---|
244 |
|
---|
245 | return null;
|
---|
246 | }).filter(item => item).sort((a, b) => a[0] - b[0]).forEach(item => {
|
---|
247 | this._offsets.push(item[0]);
|
---|
248 |
|
---|
249 | this._targets.push(item[1]);
|
---|
250 | });
|
---|
251 | }
|
---|
252 |
|
---|
253 | dispose() {
|
---|
254 | EventHandler__default.default.off(this._scrollElement, EVENT_KEY);
|
---|
255 | super.dispose();
|
---|
256 | } // Private
|
---|
257 |
|
---|
258 |
|
---|
259 | _getConfig(config) {
|
---|
260 | config = { ...Default,
|
---|
261 | ...Manipulator__default.default.getDataAttributes(this._element),
|
---|
262 | ...(typeof config === 'object' && config ? config : {})
|
---|
263 | };
|
---|
264 | config.target = getElement(config.target) || document.documentElement;
|
---|
265 | typeCheckConfig(NAME, config, DefaultType);
|
---|
266 | return config;
|
---|
267 | }
|
---|
268 |
|
---|
269 | _getScrollTop() {
|
---|
270 | return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
|
---|
271 | }
|
---|
272 |
|
---|
273 | _getScrollHeight() {
|
---|
274 | return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
|
---|
275 | }
|
---|
276 |
|
---|
277 | _getOffsetHeight() {
|
---|
278 | return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
|
---|
279 | }
|
---|
280 |
|
---|
281 | _process() {
|
---|
282 | const scrollTop = this._getScrollTop() + this._config.offset;
|
---|
283 |
|
---|
284 | const scrollHeight = this._getScrollHeight();
|
---|
285 |
|
---|
286 | const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
|
---|
287 |
|
---|
288 | if (this._scrollHeight !== scrollHeight) {
|
---|
289 | this.refresh();
|
---|
290 | }
|
---|
291 |
|
---|
292 | if (scrollTop >= maxScroll) {
|
---|
293 | const target = this._targets[this._targets.length - 1];
|
---|
294 |
|
---|
295 | if (this._activeTarget !== target) {
|
---|
296 | this._activate(target);
|
---|
297 | }
|
---|
298 |
|
---|
299 | return;
|
---|
300 | }
|
---|
301 |
|
---|
302 | if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
|
---|
303 | this._activeTarget = null;
|
---|
304 |
|
---|
305 | this._clear();
|
---|
306 |
|
---|
307 | return;
|
---|
308 | }
|
---|
309 |
|
---|
310 | for (let i = this._offsets.length; i--;) {
|
---|
311 | const isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
|
---|
312 |
|
---|
313 | if (isActiveTarget) {
|
---|
314 | this._activate(this._targets[i]);
|
---|
315 | }
|
---|
316 | }
|
---|
317 | }
|
---|
318 |
|
---|
319 | _activate(target) {
|
---|
320 | this._activeTarget = target;
|
---|
321 |
|
---|
322 | this._clear();
|
---|
323 |
|
---|
324 | const queries = SELECTOR_LINK_ITEMS.split(',').map(selector => `${selector}[data-bs-target="${target}"],${selector}[href="${target}"]`);
|
---|
325 | const link = SelectorEngine__default.default.findOne(queries.join(','), this._config.target);
|
---|
326 | link.classList.add(CLASS_NAME_ACTIVE);
|
---|
327 |
|
---|
328 | if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
|
---|
329 | SelectorEngine__default.default.findOne(SELECTOR_DROPDOWN_TOGGLE, link.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE);
|
---|
330 | } else {
|
---|
331 | SelectorEngine__default.default.parents(link, SELECTOR_NAV_LIST_GROUP).forEach(listGroup => {
|
---|
332 | // Set triggered links parents as active
|
---|
333 | // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
|
---|
334 | SelectorEngine__default.default.prev(listGroup, `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`).forEach(item => item.classList.add(CLASS_NAME_ACTIVE)); // Handle special case when .nav-link is inside .nav-item
|
---|
335 |
|
---|
336 | SelectorEngine__default.default.prev(listGroup, SELECTOR_NAV_ITEMS).forEach(navItem => {
|
---|
337 | SelectorEngine__default.default.children(navItem, SELECTOR_NAV_LINKS).forEach(item => item.classList.add(CLASS_NAME_ACTIVE));
|
---|
338 | });
|
---|
339 | });
|
---|
340 | }
|
---|
341 |
|
---|
342 | EventHandler__default.default.trigger(this._scrollElement, EVENT_ACTIVATE, {
|
---|
343 | relatedTarget: target
|
---|
344 | });
|
---|
345 | }
|
---|
346 |
|
---|
347 | _clear() {
|
---|
348 | SelectorEngine__default.default.find(SELECTOR_LINK_ITEMS, this._config.target).filter(node => node.classList.contains(CLASS_NAME_ACTIVE)).forEach(node => node.classList.remove(CLASS_NAME_ACTIVE));
|
---|
349 | } // Static
|
---|
350 |
|
---|
351 |
|
---|
352 | static jQueryInterface(config) {
|
---|
353 | return this.each(function () {
|
---|
354 | const data = ScrollSpy.getOrCreateInstance(this, config);
|
---|
355 |
|
---|
356 | if (typeof config !== 'string') {
|
---|
357 | return;
|
---|
358 | }
|
---|
359 |
|
---|
360 | if (typeof data[config] === 'undefined') {
|
---|
361 | throw new TypeError(`No method named "${config}"`);
|
---|
362 | }
|
---|
363 |
|
---|
364 | data[config]();
|
---|
365 | });
|
---|
366 | }
|
---|
367 |
|
---|
368 | }
|
---|
369 | /**
|
---|
370 | * ------------------------------------------------------------------------
|
---|
371 | * Data Api implementation
|
---|
372 | * ------------------------------------------------------------------------
|
---|
373 | */
|
---|
374 |
|
---|
375 |
|
---|
376 | EventHandler__default.default.on(window, EVENT_LOAD_DATA_API, () => {
|
---|
377 | SelectorEngine__default.default.find(SELECTOR_DATA_SPY).forEach(spy => new ScrollSpy(spy));
|
---|
378 | });
|
---|
379 | /**
|
---|
380 | * ------------------------------------------------------------------------
|
---|
381 | * jQuery
|
---|
382 | * ------------------------------------------------------------------------
|
---|
383 | * add .ScrollSpy to jQuery only if jQuery is present
|
---|
384 | */
|
---|
385 |
|
---|
386 | defineJQueryPlugin(ScrollSpy);
|
---|
387 |
|
---|
388 | return ScrollSpy;
|
---|
389 |
|
---|
390 | }));
|
---|
391 | //# sourceMappingURL=scrollspy.js.map
|
---|