bootstrap 5.1.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/assets/javascripts/bootstrap/alert.js +18 -153
  4. data/assets/javascripts/bootstrap/base-component.js +44 -127
  5. data/assets/javascripts/bootstrap/button.js +16 -80
  6. data/assets/javascripts/bootstrap/carousel.js +225 -497
  7. data/assets/javascripts/bootstrap/collapse.js +79 -262
  8. data/assets/javascripts/bootstrap/dom/data.js +6 -8
  9. data/assets/javascripts/bootstrap/dom/event-handler.js +95 -133
  10. data/assets/javascripts/bootstrap/dom/manipulator.js +25 -29
  11. data/assets/javascripts/bootstrap/dom/selector-engine.js +17 -59
  12. data/assets/javascripts/bootstrap/dropdown.js +124 -342
  13. data/assets/javascripts/bootstrap/modal.js +122 -767
  14. data/assets/javascripts/bootstrap/offcanvas.js +102 -671
  15. data/assets/javascripts/bootstrap/popover.js +42 -124
  16. data/assets/javascripts/bootstrap/scrollspy.js +186 -269
  17. data/assets/javascripts/bootstrap/tab.js +222 -221
  18. data/assets/javascripts/bootstrap/toast.js +41 -227
  19. data/assets/javascripts/bootstrap/tooltip.js +283 -629
  20. data/assets/javascripts/bootstrap/util/backdrop.js +165 -0
  21. data/assets/javascripts/bootstrap/util/component-functions.js +46 -0
  22. data/assets/javascripts/bootstrap/util/config.js +79 -0
  23. data/assets/javascripts/bootstrap/util/focustrap.js +129 -0
  24. data/assets/javascripts/bootstrap/util/index.js +350 -0
  25. data/assets/javascripts/bootstrap/util/sanitizer.js +122 -0
  26. data/assets/javascripts/bootstrap/util/scrollbar.js +138 -0
  27. data/assets/javascripts/bootstrap/util/swipe.js +155 -0
  28. data/assets/javascripts/bootstrap/util/template-factory.js +177 -0
  29. data/assets/javascripts/bootstrap-global-this-define.js +1 -1
  30. data/assets/javascripts/bootstrap-sprockets.js +16 -7
  31. data/assets/javascripts/bootstrap.js +2094 -1891
  32. data/assets/javascripts/bootstrap.min.js +3 -3
  33. data/assets/stylesheets/_bootstrap-grid.scss +3 -6
  34. data/assets/stylesheets/_bootstrap-reboot.scss +3 -7
  35. data/assets/stylesheets/_bootstrap.scss +4 -6
  36. data/assets/stylesheets/bootstrap/_accordion.scss +52 -24
  37. data/assets/stylesheets/bootstrap/_alert.scss +18 -4
  38. data/assets/stylesheets/bootstrap/_badge.scss +14 -5
  39. data/assets/stylesheets/bootstrap/_breadcrumb.scss +22 -10
  40. data/assets/stylesheets/bootstrap/_button-group.scss +3 -0
  41. data/assets/stylesheets/bootstrap/_buttons.scss +97 -22
  42. data/assets/stylesheets/bootstrap/_card.scss +55 -37
  43. data/assets/stylesheets/bootstrap/_close.scss +1 -1
  44. data/assets/stylesheets/bootstrap/_containers.scss +1 -1
  45. data/assets/stylesheets/bootstrap/_dropdown.scss +83 -75
  46. data/assets/stylesheets/bootstrap/_functions.scss +7 -7
  47. data/assets/stylesheets/bootstrap/_grid.scss +3 -3
  48. data/assets/stylesheets/bootstrap/_helpers.scss +1 -0
  49. data/assets/stylesheets/bootstrap/_list-group.scss +44 -27
  50. data/assets/stylesheets/bootstrap/_maps.scss +54 -0
  51. data/assets/stylesheets/bootstrap/_modal.scss +71 -43
  52. data/assets/stylesheets/bootstrap/_nav.scss +53 -20
  53. data/assets/stylesheets/bootstrap/_navbar.scss +91 -150
  54. data/assets/stylesheets/bootstrap/_offcanvas.scss +119 -59
  55. data/assets/stylesheets/bootstrap/_pagination.scss +66 -21
  56. data/assets/stylesheets/bootstrap/_placeholders.scss +1 -1
  57. data/assets/stylesheets/bootstrap/_popover.scss +90 -52
  58. data/assets/stylesheets/bootstrap/_progress.scss +20 -9
  59. data/assets/stylesheets/bootstrap/_reboot.scss +25 -40
  60. data/assets/stylesheets/bootstrap/_root.scss +40 -21
  61. data/assets/stylesheets/bootstrap/_spinners.scss +38 -22
  62. data/assets/stylesheets/bootstrap/_tables.scss +38 -25
  63. data/assets/stylesheets/bootstrap/_toasts.scss +35 -16
  64. data/assets/stylesheets/bootstrap/_tooltip.scss +61 -56
  65. data/assets/stylesheets/bootstrap/_type.scss +2 -0
  66. data/assets/stylesheets/bootstrap/_utilities.scss +43 -26
  67. data/assets/stylesheets/bootstrap/_variables.scss +118 -124
  68. data/assets/stylesheets/bootstrap/bootstrap-utilities.scss +3 -6
  69. data/assets/stylesheets/bootstrap/forms/_floating-labels.scss +14 -3
  70. data/assets/stylesheets/bootstrap/forms/_form-check.scss +28 -5
  71. data/assets/stylesheets/bootstrap/forms/_form-control.scss +12 -37
  72. data/assets/stylesheets/bootstrap/forms/_form-select.scss +2 -1
  73. data/assets/stylesheets/bootstrap/forms/_input-group.scss +15 -7
  74. data/assets/stylesheets/bootstrap/helpers/_color-bg.scss +10 -0
  75. data/assets/stylesheets/bootstrap/helpers/_colored-links.scss +2 -2
  76. data/assets/stylesheets/bootstrap/helpers/_position.scss +7 -1
  77. data/assets/stylesheets/bootstrap/helpers/_ratio.scss +2 -2
  78. data/assets/stylesheets/bootstrap/helpers/_vr.scss +1 -1
  79. data/assets/stylesheets/bootstrap/mixins/_alert.scss +7 -3
  80. data/assets/stylesheets/bootstrap/mixins/_banner.scss +9 -0
  81. data/assets/stylesheets/bootstrap/mixins/_breakpoints.scss +8 -8
  82. data/assets/stylesheets/bootstrap/mixins/_buttons.scss +32 -95
  83. data/assets/stylesheets/bootstrap/mixins/_container.scss +4 -2
  84. data/assets/stylesheets/bootstrap/mixins/_forms.scss +8 -0
  85. data/assets/stylesheets/bootstrap/mixins/_gradients.scss +1 -1
  86. data/assets/stylesheets/bootstrap/mixins/_grid.scss +13 -12
  87. data/assets/stylesheets/bootstrap/mixins/_pagination.scss +4 -25
  88. data/assets/stylesheets/bootstrap/mixins/_reset-text.scss +1 -1
  89. data/assets/stylesheets/bootstrap/mixins/_table-variants.scss +12 -9
  90. data/assets/stylesheets/bootstrap/mixins/_utilities.scss +12 -4
  91. data/assets/stylesheets/bootstrap/mixins/_visually-hidden.scss +1 -1
  92. data/bootstrap.gemspec +1 -1
  93. data/lib/bootstrap/version.rb +2 -2
  94. data/tasks/updater/js.rb +9 -4
  95. metadata +16 -4
@@ -1,215 +1,80 @@
1
1
  /*!
2
- * Bootstrap scrollspy.js v5.1.1 (https://getbootstrap.com/)
3
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
2
+ * Bootstrap scrollspy.js v5.2.0 (https://getbootstrap.com/)
3
+ * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
5
  */
6
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';
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
11
 
12
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
+ const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
13
13
 
14
- var EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
15
- var Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
16
- var SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
17
- var BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
14
+ const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
15
+ const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
16
+ const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
18
17
 
19
18
  /**
20
19
  * --------------------------------------------------------------------------
21
- * Bootstrap (v5.1.1): util/index.js
20
+ * Bootstrap (v5.2.0): scrollspy.js
22
21
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
23
22
  * --------------------------------------------------------------------------
24
23
  */
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
24
  /**
154
- * --------------------------------------------------------------------------
155
- * Bootstrap (v5.1.1): scrollspy.js
156
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
157
- * --------------------------------------------------------------------------
158
- */
159
- /**
160
- * ------------------------------------------------------------------------
161
25
  * Constants
162
- * ------------------------------------------------------------------------
163
26
  */
164
27
 
165
28
  const NAME = 'scrollspy';
166
29
  const DATA_KEY = 'bs.scrollspy';
167
30
  const EVENT_KEY = `.${DATA_KEY}`;
168
31
  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
32
  const EVENT_ACTIVATE = `activate${EVENT_KEY}`;
180
- const EVENT_SCROLL = `scroll${EVENT_KEY}`;
33
+ const EVENT_CLICK = `click${EVENT_KEY}`;
181
34
  const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;
182
35
  const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';
183
36
  const CLASS_NAME_ACTIVE = 'active';
184
37
  const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]';
38
+ const SELECTOR_TARGET_LINKS = '[href]';
185
39
  const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
186
40
  const SELECTOR_NAV_LINKS = '.nav-link';
187
41
  const SELECTOR_NAV_ITEMS = '.nav-item';
188
42
  const SELECTOR_LIST_ITEMS = '.list-group-item';
189
- const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}, .${CLASS_NAME_DROPDOWN_ITEM}`;
43
+ const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;
190
44
  const SELECTOR_DROPDOWN = '.dropdown';
191
45
  const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
192
- const METHOD_OFFSET = 'offset';
193
- const METHOD_POSITION = 'position';
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
+ };
53
+ const DefaultType = {
54
+ offset: '(number|null)',
55
+ // TODO v6 @deprecated, keep it for backwards compatibility reasons
56
+ rootMargin: 'string',
57
+ smoothScroll: 'boolean',
58
+ target: 'element'
59
+ };
194
60
  /**
195
- * ------------------------------------------------------------------------
196
- * Class Definition
197
- * ------------------------------------------------------------------------
61
+ * Class definition
198
62
  */
199
63
 
200
- class ScrollSpy extends BaseComponent__default['default'] {
64
+ class ScrollSpy extends BaseComponent__default.default {
201
65
  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();
66
+ super(element, config); // this._element is the observablesContainer and config.target the menu links wrapper
211
67
 
212
- this._process();
68
+ this._targetLinks = new Map();
69
+ this._observableSections = new Map();
70
+ this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;
71
+ this._activeTarget = null;
72
+ this._observer = null;
73
+ this._previousScrollData = {
74
+ visibleEntryTop: 0,
75
+ parentScrollTop: 0
76
+ };
77
+ this.refresh(); // initialize
213
78
  } // Getters
214
79
 
215
80
 
@@ -217,135 +82,190 @@
217
82
  return Default;
218
83
  }
219
84
 
85
+ static get DefaultType() {
86
+ return DefaultType;
87
+ }
88
+
220
89
  static get NAME() {
221
90
  return NAME;
222
91
  } // Public
223
92
 
224
93
 
225
94
  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
- }
95
+ this._initializeTargetsAndObservables();
244
96
 
245
- return null;
246
- }).filter(item => item).sort((a, b) => a[0] - b[0]).forEach(item => {
247
- this._offsets.push(item[0]);
97
+ this._maybeEnableSmoothScroll();
248
98
 
249
- this._targets.push(item[1]);
250
- });
99
+ if (this._observer) {
100
+ this._observer.disconnect();
101
+ } else {
102
+ this._observer = this._getNewObserver();
103
+ }
104
+
105
+ for (const section of this._observableSections.values()) {
106
+ this._observer.observe(section);
107
+ }
251
108
  }
252
109
 
253
110
  dispose() {
254
- EventHandler__default['default'].off(this._scrollElement, EVENT_KEY);
111
+ this._observer.disconnect();
112
+
255
113
  super.dispose();
256
114
  } // Private
257
115
 
258
116
 
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);
117
+ _configAfterMerge(config) {
118
+ // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case
119
+ config.target = index.getElement(config.target) || document.body;
266
120
  return config;
267
121
  }
268
122
 
269
- _getScrollTop() {
270
- return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
271
- }
123
+ _maybeEnableSmoothScroll() {
124
+ if (!this._config.smoothScroll) {
125
+ return;
126
+ } // unregister any previous listeners
272
127
 
273
- _getScrollHeight() {
274
- return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
275
- }
276
128
 
277
- _getOffsetHeight() {
278
- return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
129
+ EventHandler__default.default.off(this._config.target, EVENT_CLICK);
130
+ EventHandler__default.default.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {
131
+ const observableSection = this._observableSections.get(event.target.hash);
132
+
133
+ if (observableSection) {
134
+ event.preventDefault();
135
+ const root = this._rootElement || window;
136
+ const height = observableSection.offsetTop - this._element.offsetTop;
137
+
138
+ if (root.scrollTo) {
139
+ root.scrollTo({
140
+ top: height,
141
+ behavior: 'smooth'
142
+ });
143
+ return;
144
+ } // Chrome 60 doesn't support `scrollTo`
145
+
146
+
147
+ root.scrollTop = height;
148
+ }
149
+ });
279
150
  }
280
151
 
281
- _process() {
282
- const scrollTop = this._getScrollTop() + this._config.offset;
152
+ _getNewObserver() {
153
+ const options = {
154
+ root: this._rootElement,
155
+ threshold: [0.1, 0.5, 1],
156
+ rootMargin: this._getRootMargin()
157
+ };
158
+ return new IntersectionObserver(entries => this._observerCallback(entries), options);
159
+ } // The logic of selection
160
+
283
161
 
284
- const scrollHeight = this._getScrollHeight();
162
+ _observerCallback(entries) {
163
+ const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);
285
164
 
286
- const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
165
+ const activate = entry => {
166
+ this._previousScrollData.visibleEntryTop = entry.target.offsetTop;
287
167
 
288
- if (this._scrollHeight !== scrollHeight) {
289
- this.refresh();
290
- }
168
+ this._process(targetElement(entry));
169
+ };
170
+
171
+ const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;
172
+ const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;
173
+ this._previousScrollData.parentScrollTop = parentScrollTop;
174
+
175
+ for (const entry of entries) {
176
+ if (!entry.isIntersecting) {
177
+ this._activeTarget = null;
291
178
 
292
- if (scrollTop >= maxScroll) {
293
- const target = this._targets[this._targets.length - 1];
179
+ this._clearActiveClass(targetElement(entry));
294
180
 
295
- if (this._activeTarget !== target) {
296
- this._activate(target);
181
+ continue;
297
182
  }
298
183
 
299
- return;
300
- }
184
+ const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; // if we are scrolling down, pick the bigger offsetTop
301
185
 
302
- if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
303
- this._activeTarget = null;
186
+ if (userScrollsDown && entryIsLowerThanPrevious) {
187
+ activate(entry); // if parent isn't scrolled, let's keep the first visible item, breaking the iteration
304
188
 
305
- this._clear();
189
+ if (!parentScrollTop) {
190
+ return;
191
+ }
306
192
 
307
- return;
308
- }
193
+ continue;
194
+ } // if we are scrolling up, pick the smallest offsetTop
309
195
 
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
196
 
313
- if (isActiveTarget) {
314
- this._activate(this._targets[i]);
197
+ if (!userScrollsDown && !entryIsLowerThanPrevious) {
198
+ activate(entry);
315
199
  }
316
200
  }
201
+ } // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only
202
+
203
+
204
+ _getRootMargin() {
205
+ return this._config.offset ? `${this._config.offset}px 0px -30%` : this._config.rootMargin;
317
206
  }
318
207
 
319
- _activate(target) {
320
- this._activeTarget = target;
208
+ _initializeTargetsAndObservables() {
209
+ this._targetLinks = new Map();
210
+ this._observableSections = new Map();
211
+ const targetLinks = SelectorEngine__default.default.find(SELECTOR_TARGET_LINKS, this._config.target);
321
212
 
322
- this._clear();
213
+ for (const anchor of targetLinks) {
214
+ // ensure that the anchor has an id and is not disabled
215
+ if (!anchor.hash || index.isDisabled(anchor)) {
216
+ continue;
217
+ }
323
218
 
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);
219
+ const observableSection = SelectorEngine__default.default.findOne(anchor.hash, this._element); // ensure that the observableSection exists & is visible
327
220
 
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
- });
221
+ if (index.isVisible(observableSection)) {
222
+ this._targetLinks.set(anchor.hash, anchor);
223
+
224
+ this._observableSections.set(anchor.hash, observableSection);
225
+ }
226
+ }
227
+ }
228
+
229
+ _process(target) {
230
+ if (this._activeTarget === target) {
231
+ return;
340
232
  }
341
233
 
342
- EventHandler__default['default'].trigger(this._scrollElement, EVENT_ACTIVATE, {
234
+ this._clearActiveClass(this._config.target);
235
+
236
+ this._activeTarget = target;
237
+ target.classList.add(CLASS_NAME_ACTIVE);
238
+
239
+ this._activateParents(target);
240
+
241
+ EventHandler__default.default.trigger(this._element, EVENT_ACTIVATE, {
343
242
  relatedTarget: target
344
243
  });
345
244
  }
346
245
 
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));
246
+ _activateParents(target) {
247
+ // Activate dropdown parents
248
+ if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
249
+ SelectorEngine__default.default.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE);
250
+ return;
251
+ }
252
+
253
+ for (const listGroup of SelectorEngine__default.default.parents(target, SELECTOR_NAV_LIST_GROUP)) {
254
+ // Set triggered links parents as active
255
+ // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
256
+ for (const item of SelectorEngine__default.default.prev(listGroup, SELECTOR_LINK_ITEMS)) {
257
+ item.classList.add(CLASS_NAME_ACTIVE);
258
+ }
259
+ }
260
+ }
261
+
262
+ _clearActiveClass(parent) {
263
+ parent.classList.remove(CLASS_NAME_ACTIVE);
264
+ const activeNodes = SelectorEngine__default.default.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent);
265
+
266
+ for (const node of activeNodes) {
267
+ node.classList.remove(CLASS_NAME_ACTIVE);
268
+ }
349
269
  } // Static
350
270
 
351
271
 
@@ -357,7 +277,7 @@
357
277
  return;
358
278
  }
359
279
 
360
- if (typeof data[config] === 'undefined') {
280
+ if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
361
281
  throw new TypeError(`No method named "${config}"`);
362
282
  }
363
283
 
@@ -367,24 +287,21 @@
367
287
 
368
288
  }
369
289
  /**
370
- * ------------------------------------------------------------------------
371
- * Data Api implementation
372
- * ------------------------------------------------------------------------
290
+ * Data API implementation
373
291
  */
374
292
 
375
293
 
376
- EventHandler__default['default'].on(window, EVENT_LOAD_DATA_API, () => {
377
- SelectorEngine__default['default'].find(SELECTOR_DATA_SPY).forEach(spy => new ScrollSpy(spy));
294
+ EventHandler__default.default.on(window, EVENT_LOAD_DATA_API, () => {
295
+ for (const spy of SelectorEngine__default.default.find(SELECTOR_DATA_SPY)) {
296
+ ScrollSpy.getOrCreateInstance(spy);
297
+ }
378
298
  });
379
299
  /**
380
- * ------------------------------------------------------------------------
381
300
  * jQuery
382
- * ------------------------------------------------------------------------
383
- * add .ScrollSpy to jQuery only if jQuery is present
384
301
  */
385
302
 
386
- defineJQueryPlugin(ScrollSpy);
303
+ index.defineJQueryPlugin(ScrollSpy);
387
304
 
388
305
  return ScrollSpy;
389
306
 
390
- })));
307
+ }));