bootstrap 5.1.3 → 5.3.5

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +61 -0
  3. data/CHANGELOG.md +9 -0
  4. data/Gemfile +1 -0
  5. data/README.md +35 -14
  6. data/Rakefile +16 -5
  7. data/assets/javascripts/bootstrap/alert.js +22 -167
  8. data/assets/javascripts/bootstrap/base-component.js +34 -133
  9. data/assets/javascripts/bootstrap/button.js +19 -86
  10. data/assets/javascripts/bootstrap/carousel.js +209 -564
  11. data/assets/javascripts/bootstrap/collapse.js +78 -324
  12. data/assets/javascripts/bootstrap/dom/data.js +8 -14
  13. data/assets/javascripts/bootstrap/dom/event-handler.js +89 -174
  14. data/assets/javascripts/bootstrap/dom/manipulator.js +22 -39
  15. data/assets/javascripts/bootstrap/dom/selector-engine.js +47 -71
  16. data/assets/javascripts/bootstrap/dropdown.js +135 -420
  17. data/assets/javascripts/bootstrap/modal.js +115 -837
  18. data/assets/javascripts/bootstrap/offcanvas.js +93 -714
  19. data/assets/javascripts/bootstrap/popover.js +42 -130
  20. data/assets/javascripts/bootstrap/scrollspy.js +180 -296
  21. data/assets/javascripts/bootstrap/tab.js +197 -245
  22. data/assets/javascripts/bootstrap/toast.js +52 -276
  23. data/assets/javascripts/bootstrap/tooltip.js +283 -744
  24. data/assets/javascripts/bootstrap/util/backdrop.js +138 -0
  25. data/assets/javascripts/bootstrap/util/component-functions.js +41 -0
  26. data/assets/javascripts/bootstrap/util/config.js +67 -0
  27. data/assets/javascripts/bootstrap/util/focustrap.js +112 -0
  28. data/assets/javascripts/bootstrap/util/index.js +280 -0
  29. data/assets/javascripts/bootstrap/util/sanitizer.js +113 -0
  30. data/assets/javascripts/bootstrap/util/scrollbar.js +112 -0
  31. data/assets/javascripts/bootstrap/util/swipe.js +134 -0
  32. data/assets/javascripts/bootstrap/util/template-factory.js +150 -0
  33. data/assets/javascripts/bootstrap-global-this-define.js +1 -1
  34. data/assets/javascripts/bootstrap-sprockets.js +15 -6
  35. data/assets/javascripts/bootstrap.js +2278 -2831
  36. data/assets/javascripts/bootstrap.min.js +3 -3
  37. data/assets/stylesheets/_bootstrap-grid.scss +4 -9
  38. data/assets/stylesheets/_bootstrap-reboot.scss +4 -7
  39. data/assets/stylesheets/_bootstrap-utilities.scss +19 -0
  40. data/assets/stylesheets/_bootstrap.scss +5 -6
  41. data/assets/stylesheets/bootstrap/_accordion.scss +68 -33
  42. data/assets/stylesheets/bootstrap/_alert.scss +25 -14
  43. data/assets/stylesheets/bootstrap/_badge.scss +14 -5
  44. data/assets/stylesheets/bootstrap/_breadcrumb.scss +22 -10
  45. data/assets/stylesheets/bootstrap/_button-group.scss +12 -4
  46. data/assets/stylesheets/bootstrap/_buttons.scss +133 -28
  47. data/assets/stylesheets/bootstrap/_card.scss +61 -39
  48. data/assets/stylesheets/bootstrap/_carousel.scss +22 -25
  49. data/assets/stylesheets/bootstrap/_close.scss +36 -10
  50. data/assets/stylesheets/bootstrap/_containers.scss +1 -1
  51. data/assets/stylesheets/bootstrap/_dropdown.scss +86 -76
  52. data/assets/stylesheets/bootstrap/_functions.scss +10 -10
  53. data/assets/stylesheets/bootstrap/_grid.scss +9 -3
  54. data/assets/stylesheets/bootstrap/_helpers.scss +3 -0
  55. data/assets/stylesheets/bootstrap/_list-group.scss +81 -56
  56. data/assets/stylesheets/bootstrap/_maps.scss +174 -0
  57. data/assets/stylesheets/bootstrap/_mixins.scss +1 -2
  58. data/assets/stylesheets/bootstrap/_modal.scss +76 -45
  59. data/assets/stylesheets/bootstrap/_nav.scss +87 -29
  60. data/assets/stylesheets/bootstrap/_navbar.scss +102 -148
  61. data/assets/stylesheets/bootstrap/_offcanvas.scss +125 -61
  62. data/assets/stylesheets/bootstrap/_pagination.scss +66 -21
  63. data/assets/stylesheets/bootstrap/_placeholders.scss +1 -1
  64. data/assets/stylesheets/bootstrap/_popover.scss +90 -52
  65. data/assets/stylesheets/bootstrap/_progress.scss +31 -11
  66. data/assets/stylesheets/bootstrap/_reboot.scss +32 -46
  67. data/assets/stylesheets/bootstrap/_root.scss +155 -22
  68. data/assets/stylesheets/bootstrap/_spinners.scss +38 -22
  69. data/assets/stylesheets/bootstrap/_tables.scss +40 -24
  70. data/assets/stylesheets/bootstrap/_toasts.scss +38 -16
  71. data/assets/stylesheets/bootstrap/_tooltip.scss +60 -56
  72. data/assets/stylesheets/bootstrap/_type.scss +3 -1
  73. data/assets/stylesheets/bootstrap/_utilities.scss +209 -33
  74. data/assets/stylesheets/bootstrap/_variables-dark.scss +102 -0
  75. data/assets/stylesheets/bootstrap/_variables.scss +415 -303
  76. data/assets/stylesheets/bootstrap/forms/_floating-labels.scss +39 -5
  77. data/assets/stylesheets/bootstrap/forms/_form-check.scss +51 -14
  78. data/assets/stylesheets/bootstrap/forms/_form-control.scss +36 -41
  79. data/assets/stylesheets/bootstrap/forms/_form-range.scss +3 -3
  80. data/assets/stylesheets/bootstrap/forms/_form-select.scss +12 -4
  81. data/assets/stylesheets/bootstrap/forms/_input-group.scss +20 -9
  82. data/assets/stylesheets/bootstrap/helpers/_color-bg.scss +7 -0
  83. data/assets/stylesheets/bootstrap/helpers/_colored-links.scss +20 -2
  84. data/assets/stylesheets/bootstrap/helpers/_focus-ring.scss +5 -0
  85. data/assets/stylesheets/bootstrap/helpers/_icon-link.scss +25 -0
  86. data/assets/stylesheets/bootstrap/helpers/_position.scss +7 -1
  87. data/assets/stylesheets/bootstrap/helpers/_ratio.scss +2 -2
  88. data/assets/stylesheets/bootstrap/helpers/_vr.scss +2 -2
  89. data/assets/stylesheets/bootstrap/mixins/_alert.scss +11 -4
  90. data/assets/stylesheets/bootstrap/mixins/_banner.scss +7 -0
  91. data/assets/stylesheets/bootstrap/mixins/_breakpoints.scss +8 -8
  92. data/assets/stylesheets/bootstrap/mixins/_buttons.scss +32 -95
  93. data/assets/stylesheets/bootstrap/mixins/_caret.scss +30 -25
  94. data/assets/stylesheets/bootstrap/mixins/_color-mode.scss +21 -0
  95. data/assets/stylesheets/bootstrap/mixins/_container.scss +4 -2
  96. data/assets/stylesheets/bootstrap/mixins/_forms.scss +38 -19
  97. data/assets/stylesheets/bootstrap/mixins/_gradients.scss +1 -1
  98. data/assets/stylesheets/bootstrap/mixins/_grid.scss +15 -15
  99. data/assets/stylesheets/bootstrap/mixins/_list-group.scss +2 -0
  100. data/assets/stylesheets/bootstrap/mixins/_pagination.scss +4 -25
  101. data/assets/stylesheets/bootstrap/mixins/_reset-text.scss +1 -1
  102. data/assets/stylesheets/bootstrap/mixins/_table-variants.scss +12 -9
  103. data/assets/stylesheets/bootstrap/mixins/_utilities.scss +14 -6
  104. data/assets/stylesheets/bootstrap/mixins/_visually-hidden.scss +6 -2
  105. data/assets/stylesheets/bootstrap/vendor/_rfs.scss +23 -29
  106. data/bootstrap.gemspec +3 -3
  107. data/lib/bootstrap/engine.rb +17 -2
  108. data/lib/bootstrap/version.rb +2 -2
  109. data/tasks/updater/js.rb +10 -5
  110. data/tasks/updater/network.rb +2 -2
  111. data/tasks/updater/scss.rb +2 -2
  112. data/tasks/updater.rb +2 -2
  113. data/test/dummy_rails/config/application.rb +0 -2
  114. data/test/dummy_rails/public/favicon.ico +0 -0
  115. data/test/gemfiles/rails_4_2.gemfile +2 -1
  116. data/test/gemfiles/rails_5_0.gemfile +1 -2
  117. data/test/gemfiles/rails_5_1.gemfile +1 -2
  118. data/test/gemfiles/rails_5_2.gemfile +7 -0
  119. data/test/gemfiles/rails_6_0.gemfile +1 -1
  120. data/test/gemfiles/rails_6_1.gemfile +1 -1
  121. data/test/gemfiles/rails_7_0_dartsass.gemfile +7 -0
  122. data/test/gemfiles/rails_7_0_sassc.gemfile +7 -0
  123. data/test/rails_test.rb +0 -5
  124. data/test/test_helper.rb +3 -2
  125. metadata +49 -29
  126. data/.travis.yml +0 -32
  127. data/assets/stylesheets/bootstrap/bootstrap-utilities.scss +0 -18
@@ -1,19 +1,16 @@
1
1
  /*!
2
- * Bootstrap tooltip.js v5.1.3 (https://getbootstrap.com/)
3
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
2
+ * Bootstrap tooltip.js v5.3.5 (https://getbootstrap.com/)
3
+ * Copyright 2011-2025 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('@popperjs/core'), require('./dom/data.js'), 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(['@popperjs/core', './dom/data', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
9
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global.Popper, global.Data, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
10
- })(this, (function (Popper, Data, EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
7
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./util/index.js'), require('./util/sanitizer.js'), require('./util/template-factory.js')) :
8
+ typeof define === 'function' && define.amd ? define(['@popperjs/core', './base-component', './dom/event-handler', './dom/manipulator', './util/index', './util/sanitizer', './util/template-factory'], factory) :
9
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global["@popperjs/core"], global.BaseComponent, global.EventHandler, global.Manipulator, global.Index, global.Sanitizer, global.TemplateFactory));
10
+ })(this, (function (Popper, BaseComponent, EventHandler, Manipulator, index_js, sanitizer_js, TemplateFactory) { 'use strict';
11
11
 
12
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
13
-
14
- function _interopNamespace(e) {
15
- if (e && e.__esModule) return e;
16
- const n = Object.create(null);
12
+ function _interopNamespaceDefault(e) {
13
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
17
14
  if (e) {
18
15
  for (const k in e) {
19
16
  if (k !== 'default') {
@@ -29,702 +26,330 @@
29
26
  return Object.freeze(n);
30
27
  }
31
28
 
32
- const Popper__namespace = /*#__PURE__*/_interopNamespace(Popper);
33
- const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
34
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
35
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
36
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
37
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
38
-
39
- /**
40
- * --------------------------------------------------------------------------
41
- * Bootstrap (v5.1.3): util/index.js
42
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
43
- * --------------------------------------------------------------------------
44
- */
45
- const MAX_UID = 1000000;
46
-
47
- const toType = obj => {
48
- if (obj === null || obj === undefined) {
49
- return `${obj}`;
50
- }
51
-
52
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
53
- };
54
- /**
55
- * --------------------------------------------------------------------------
56
- * Public Util Api
57
- * --------------------------------------------------------------------------
58
- */
59
-
60
-
61
- const getUID = prefix => {
62
- do {
63
- prefix += Math.floor(Math.random() * MAX_UID);
64
- } while (document.getElementById(prefix));
65
-
66
- return prefix;
67
- };
68
-
69
- const isElement = obj => {
70
- if (!obj || typeof obj !== 'object') {
71
- return false;
72
- }
73
-
74
- if (typeof obj.jquery !== 'undefined') {
75
- obj = obj[0];
76
- }
77
-
78
- return typeof obj.nodeType !== 'undefined';
79
- };
80
-
81
- const getElement = obj => {
82
- if (isElement(obj)) {
83
- // it's a jQuery object or a node element
84
- return obj.jquery ? obj[0] : obj;
85
- }
86
-
87
- if (typeof obj === 'string' && obj.length > 0) {
88
- return document.querySelector(obj);
89
- }
90
-
91
- return null;
92
- };
93
-
94
- const typeCheckConfig = (componentName, config, configTypes) => {
95
- Object.keys(configTypes).forEach(property => {
96
- const expectedTypes = configTypes[property];
97
- const value = config[property];
98
- const valueType = value && isElement(value) ? 'element' : toType(value);
99
-
100
- if (!new RegExp(expectedTypes).test(valueType)) {
101
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
102
- }
103
- });
104
- };
105
-
106
- const findShadowRoot = element => {
107
- if (!document.documentElement.attachShadow) {
108
- return null;
109
- } // Can find the shadow root otherwise it'll return the document
110
-
111
-
112
- if (typeof element.getRootNode === 'function') {
113
- const root = element.getRootNode();
114
- return root instanceof ShadowRoot ? root : null;
115
- }
116
-
117
- if (element instanceof ShadowRoot) {
118
- return element;
119
- } // when we don't find a shadow root
120
-
121
-
122
- if (!element.parentNode) {
123
- return null;
124
- }
125
-
126
- return findShadowRoot(element.parentNode);
127
- };
128
-
129
- const noop = () => {};
130
-
131
- const getjQuery = () => {
132
- const {
133
- jQuery
134
- } = window;
135
-
136
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
137
- return jQuery;
138
- }
139
-
140
- return null;
141
- };
142
-
143
- const DOMContentLoadedCallbacks = [];
144
-
145
- const onDOMContentLoaded = callback => {
146
- if (document.readyState === 'loading') {
147
- // add listener on the first call when the document is in loading state
148
- if (!DOMContentLoadedCallbacks.length) {
149
- document.addEventListener('DOMContentLoaded', () => {
150
- DOMContentLoadedCallbacks.forEach(callback => callback());
151
- });
152
- }
153
-
154
- DOMContentLoadedCallbacks.push(callback);
155
- } else {
156
- callback();
157
- }
158
- };
159
-
160
- const isRTL = () => document.documentElement.dir === 'rtl';
161
-
162
- const defineJQueryPlugin = plugin => {
163
- onDOMContentLoaded(() => {
164
- const $ = getjQuery();
165
- /* istanbul ignore if */
166
-
167
- if ($) {
168
- const name = plugin.NAME;
169
- const JQUERY_NO_CONFLICT = $.fn[name];
170
- $.fn[name] = plugin.jQueryInterface;
171
- $.fn[name].Constructor = plugin;
172
-
173
- $.fn[name].noConflict = () => {
174
- $.fn[name] = JQUERY_NO_CONFLICT;
175
- return plugin.jQueryInterface;
176
- };
177
- }
178
- });
179
- };
29
+ const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper);
180
30
 
181
31
  /**
182
32
  * --------------------------------------------------------------------------
183
- * Bootstrap (v5.1.3): util/sanitizer.js
33
+ * Bootstrap tooltip.js
184
34
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
185
35
  * --------------------------------------------------------------------------
186
36
  */
187
- const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);
188
- const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
189
- /**
190
- * A pattern that recognizes a commonly useful subset of URLs that are safe.
191
- *
192
- * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
193
- */
194
37
 
195
- const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i;
196
- /**
197
- * A pattern that matches safe data URLs. Only matches image, video and audio types.
198
- *
199
- * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
200
- */
201
-
202
- const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;
203
-
204
- const allowedAttribute = (attribute, allowedAttributeList) => {
205
- const attributeName = attribute.nodeName.toLowerCase();
206
-
207
- if (allowedAttributeList.includes(attributeName)) {
208
- if (uriAttributes.has(attributeName)) {
209
- return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue));
210
- }
211
-
212
- return true;
213
- }
214
-
215
- const regExp = allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp); // Check if a regular expression validates the attribute.
216
-
217
- for (let i = 0, len = regExp.length; i < len; i++) {
218
- if (regExp[i].test(attributeName)) {
219
- return true;
220
- }
221
- }
222
-
223
- return false;
224
- };
225
-
226
- const DefaultAllowlist = {
227
- // Global attributes allowed on any supplied element below.
228
- '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
229
- a: ['target', 'href', 'title', 'rel'],
230
- area: [],
231
- b: [],
232
- br: [],
233
- col: [],
234
- code: [],
235
- div: [],
236
- em: [],
237
- hr: [],
238
- h1: [],
239
- h2: [],
240
- h3: [],
241
- h4: [],
242
- h5: [],
243
- h6: [],
244
- i: [],
245
- img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
246
- li: [],
247
- ol: [],
248
- p: [],
249
- pre: [],
250
- s: [],
251
- small: [],
252
- span: [],
253
- sub: [],
254
- sup: [],
255
- strong: [],
256
- u: [],
257
- ul: []
258
- };
259
- function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
260
- if (!unsafeHtml.length) {
261
- return unsafeHtml;
262
- }
263
-
264
- if (sanitizeFn && typeof sanitizeFn === 'function') {
265
- return sanitizeFn(unsafeHtml);
266
- }
267
-
268
- const domParser = new window.DOMParser();
269
- const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
270
- const elements = [].concat(...createdDocument.body.querySelectorAll('*'));
271
-
272
- for (let i = 0, len = elements.length; i < len; i++) {
273
- const element = elements[i];
274
- const elementName = element.nodeName.toLowerCase();
275
-
276
- if (!Object.keys(allowList).includes(elementName)) {
277
- element.remove();
278
- continue;
279
- }
280
-
281
- const attributeList = [].concat(...element.attributes);
282
- const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);
283
- attributeList.forEach(attribute => {
284
- if (!allowedAttribute(attribute, allowedAttributes)) {
285
- element.removeAttribute(attribute.nodeName);
286
- }
287
- });
288
- }
289
-
290
- return createdDocument.body.innerHTML;
291
- }
292
38
 
293
39
  /**
294
- * --------------------------------------------------------------------------
295
- * Bootstrap (v5.1.3): tooltip.js
296
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
297
- * --------------------------------------------------------------------------
298
- */
299
- /**
300
- * ------------------------------------------------------------------------
301
40
  * Constants
302
- * ------------------------------------------------------------------------
303
41
  */
304
42
 
305
43
  const NAME = 'tooltip';
306
- const DATA_KEY = 'bs.tooltip';
307
- const EVENT_KEY = `.${DATA_KEY}`;
308
- const CLASS_PREFIX = 'bs-tooltip';
309
44
  const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);
310
- const DefaultType = {
311
- animation: 'boolean',
312
- template: 'string',
313
- title: '(string|element|function)',
314
- trigger: 'string',
315
- delay: '(number|object)',
316
- html: 'boolean',
317
- selector: '(string|boolean)',
318
- placement: '(string|function)',
319
- offset: '(array|string|function)',
320
- container: '(string|element|boolean)',
321
- fallbackPlacements: 'array',
322
- boundary: '(string|element)',
323
- customClass: '(string|function)',
324
- sanitize: 'boolean',
325
- sanitizeFn: '(null|function)',
326
- allowList: 'object',
327
- popperConfig: '(null|object|function)'
328
- };
45
+ const CLASS_NAME_FADE = 'fade';
46
+ const CLASS_NAME_MODAL = 'modal';
47
+ const CLASS_NAME_SHOW = 'show';
48
+ const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
49
+ const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;
50
+ const EVENT_MODAL_HIDE = 'hide.bs.modal';
51
+ const TRIGGER_HOVER = 'hover';
52
+ const TRIGGER_FOCUS = 'focus';
53
+ const TRIGGER_CLICK = 'click';
54
+ const TRIGGER_MANUAL = 'manual';
55
+ const EVENT_HIDE = 'hide';
56
+ const EVENT_HIDDEN = 'hidden';
57
+ const EVENT_SHOW = 'show';
58
+ const EVENT_SHOWN = 'shown';
59
+ const EVENT_INSERTED = 'inserted';
60
+ const EVENT_CLICK = 'click';
61
+ const EVENT_FOCUSIN = 'focusin';
62
+ const EVENT_FOCUSOUT = 'focusout';
63
+ const EVENT_MOUSEENTER = 'mouseenter';
64
+ const EVENT_MOUSELEAVE = 'mouseleave';
329
65
  const AttachmentMap = {
330
66
  AUTO: 'auto',
331
67
  TOP: 'top',
332
- RIGHT: isRTL() ? 'left' : 'right',
68
+ RIGHT: index_js.isRTL() ? 'left' : 'right',
333
69
  BOTTOM: 'bottom',
334
- LEFT: isRTL() ? 'right' : 'left'
70
+ LEFT: index_js.isRTL() ? 'right' : 'left'
335
71
  };
336
72
  const Default = {
73
+ allowList: sanitizer_js.DefaultAllowlist,
337
74
  animation: true,
338
- template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>',
339
- trigger: 'hover focus',
340
- title: '',
75
+ boundary: 'clippingParents',
76
+ container: false,
77
+ customClass: '',
341
78
  delay: 0,
79
+ fallbackPlacements: ['top', 'right', 'bottom', 'left'],
342
80
  html: false,
343
- selector: false,
81
+ offset: [0, 6],
344
82
  placement: 'top',
345
- offset: [0, 0],
346
- container: false,
347
- fallbackPlacements: ['top', 'right', 'bottom', 'left'],
348
- boundary: 'clippingParents',
349
- customClass: '',
83
+ popperConfig: null,
350
84
  sanitize: true,
351
85
  sanitizeFn: null,
352
- allowList: DefaultAllowlist,
353
- popperConfig: null
86
+ selector: false,
87
+ template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>',
88
+ title: '',
89
+ trigger: 'hover focus'
354
90
  };
355
- const Event = {
356
- HIDE: `hide${EVENT_KEY}`,
357
- HIDDEN: `hidden${EVENT_KEY}`,
358
- SHOW: `show${EVENT_KEY}`,
359
- SHOWN: `shown${EVENT_KEY}`,
360
- INSERTED: `inserted${EVENT_KEY}`,
361
- CLICK: `click${EVENT_KEY}`,
362
- FOCUSIN: `focusin${EVENT_KEY}`,
363
- FOCUSOUT: `focusout${EVENT_KEY}`,
364
- MOUSEENTER: `mouseenter${EVENT_KEY}`,
365
- MOUSELEAVE: `mouseleave${EVENT_KEY}`
91
+ const DefaultType = {
92
+ allowList: 'object',
93
+ animation: 'boolean',
94
+ boundary: '(string|element)',
95
+ container: '(string|element|boolean)',
96
+ customClass: '(string|function)',
97
+ delay: '(number|object)',
98
+ fallbackPlacements: 'array',
99
+ html: 'boolean',
100
+ offset: '(array|string|function)',
101
+ placement: '(string|function)',
102
+ popperConfig: '(null|object|function)',
103
+ sanitize: 'boolean',
104
+ sanitizeFn: '(null|function)',
105
+ selector: '(string|boolean)',
106
+ template: 'string',
107
+ title: '(string|element|function)',
108
+ trigger: 'string'
366
109
  };
367
- const CLASS_NAME_FADE = 'fade';
368
- const CLASS_NAME_MODAL = 'modal';
369
- const CLASS_NAME_SHOW = 'show';
370
- const HOVER_STATE_SHOW = 'show';
371
- const HOVER_STATE_OUT = 'out';
372
- const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
373
- const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;
374
- const EVENT_MODAL_HIDE = 'hide.bs.modal';
375
- const TRIGGER_HOVER = 'hover';
376
- const TRIGGER_FOCUS = 'focus';
377
- const TRIGGER_CLICK = 'click';
378
- const TRIGGER_MANUAL = 'manual';
110
+
379
111
  /**
380
- * ------------------------------------------------------------------------
381
- * Class Definition
382
- * ------------------------------------------------------------------------
112
+ * Class definition
383
113
  */
384
114
 
385
- class Tooltip extends BaseComponent__default.default {
115
+ class Tooltip extends BaseComponent {
386
116
  constructor(element, config) {
387
117
  if (typeof Popper__namespace === 'undefined') {
388
- throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)');
118
+ throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org/docs/v2/)');
389
119
  }
120
+ super(element, config);
390
121
 
391
- super(element); // private
392
-
122
+ // Private
393
123
  this._isEnabled = true;
394
124
  this._timeout = 0;
395
- this._hoverState = '';
125
+ this._isHovered = null;
396
126
  this._activeTrigger = {};
397
- this._popper = null; // Protected
127
+ this._popper = null;
128
+ this._templateFactory = null;
129
+ this._newContent = null;
398
130
 
399
- this._config = this._getConfig(config);
131
+ // Protected
400
132
  this.tip = null;
401
-
402
133
  this._setListeners();
403
- } // Getters
404
-
134
+ if (!this._config.selector) {
135
+ this._fixTitle();
136
+ }
137
+ }
405
138
 
139
+ // Getters
406
140
  static get Default() {
407
141
  return Default;
408
142
  }
409
-
143
+ static get DefaultType() {
144
+ return DefaultType;
145
+ }
410
146
  static get NAME() {
411
147
  return NAME;
412
148
  }
413
149
 
414
- static get Event() {
415
- return Event;
416
- }
417
-
418
- static get DefaultType() {
419
- return DefaultType;
420
- } // Public
421
-
422
-
150
+ // Public
423
151
  enable() {
424
152
  this._isEnabled = true;
425
153
  }
426
-
427
154
  disable() {
428
155
  this._isEnabled = false;
429
156
  }
430
-
431
157
  toggleEnabled() {
432
158
  this._isEnabled = !this._isEnabled;
433
159
  }
434
-
435
- toggle(event) {
160
+ toggle() {
436
161
  if (!this._isEnabled) {
437
162
  return;
438
163
  }
439
-
440
- if (event) {
441
- const context = this._initializeOnDelegatedTarget(event);
442
-
443
- context._activeTrigger.click = !context._activeTrigger.click;
444
-
445
- if (context._isWithActiveTrigger()) {
446
- context._enter(null, context);
447
- } else {
448
- context._leave(null, context);
449
- }
450
- } else {
451
- if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {
452
- this._leave(null, this);
453
-
454
- return;
455
- }
456
-
457
- this._enter(null, this);
164
+ if (this._isShown()) {
165
+ this._leave();
166
+ return;
458
167
  }
168
+ this._enter();
459
169
  }
460
-
461
170
  dispose() {
462
171
  clearTimeout(this._timeout);
463
- EventHandler__default.default.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
464
-
465
- if (this.tip) {
466
- this.tip.remove();
172
+ EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
173
+ if (this._element.getAttribute('data-bs-original-title')) {
174
+ this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));
467
175
  }
468
-
469
176
  this._disposePopper();
470
-
471
177
  super.dispose();
472
178
  }
473
-
474
179
  show() {
475
180
  if (this._element.style.display === 'none') {
476
181
  throw new Error('Please use show on visible elements');
477
182
  }
478
-
479
- if (!(this.isWithContent() && this._isEnabled)) {
183
+ if (!(this._isWithContent() && this._isEnabled)) {
480
184
  return;
481
185
  }
482
-
483
- const showEvent = EventHandler__default.default.trigger(this._element, this.constructor.Event.SHOW);
484
- const shadowRoot = findShadowRoot(this._element);
485
- const isInTheDom = shadowRoot === null ? this._element.ownerDocument.documentElement.contains(this._element) : shadowRoot.contains(this._element);
486
-
186
+ const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW));
187
+ const shadowRoot = index_js.findShadowRoot(this._element);
188
+ const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);
487
189
  if (showEvent.defaultPrevented || !isInTheDom) {
488
190
  return;
489
- } // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title`
490
- // This will be removed later in favor of a `setContent` method
491
-
492
-
493
- if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {
494
- this._disposePopper();
495
-
496
- this.tip.remove();
497
- this.tip = null;
498
- }
499
-
500
- const tip = this.getTipElement();
501
- const tipId = getUID(this.constructor.NAME);
502
- tip.setAttribute('id', tipId);
503
-
504
- this._element.setAttribute('aria-describedby', tipId);
505
-
506
- if (this._config.animation) {
507
- tip.classList.add(CLASS_NAME_FADE);
508
191
  }
509
192
 
510
- const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : this._config.placement;
511
-
512
- const attachment = this._getAttachment(placement);
513
-
514
- this._addAttachmentClass(attachment);
515
-
193
+ // TODO: v6 remove this or make it optional
194
+ this._disposePopper();
195
+ const tip = this._getTipElement();
196
+ this._element.setAttribute('aria-describedby', tip.getAttribute('id'));
516
197
  const {
517
198
  container
518
199
  } = this._config;
519
- Data__default.default.set(tip, this.constructor.DATA_KEY, this);
520
-
521
200
  if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
522
201
  container.append(tip);
523
- EventHandler__default.default.trigger(this._element, this.constructor.Event.INSERTED);
202
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));
524
203
  }
525
-
526
- if (this._popper) {
527
- this._popper.update();
528
- } else {
529
- this._popper = Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));
530
- }
531
-
204
+ this._popper = this._createPopper(tip);
532
205
  tip.classList.add(CLASS_NAME_SHOW);
533
206
 
534
- const customClass = this._resolvePossibleFunction(this._config.customClass);
535
-
536
- if (customClass) {
537
- tip.classList.add(...customClass.split(' '));
538
- } // If this is a touch-enabled device we add extra
207
+ // If this is a touch-enabled device we add extra
539
208
  // empty mouseover listeners to the body's immediate children;
540
209
  // only needed because of broken event delegation on iOS
541
210
  // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
542
-
543
-
544
211
  if ('ontouchstart' in document.documentElement) {
545
- [].concat(...document.body.children).forEach(element => {
546
- EventHandler__default.default.on(element, 'mouseover', noop);
547
- });
212
+ for (const element of [].concat(...document.body.children)) {
213
+ EventHandler.on(element, 'mouseover', index_js.noop);
214
+ }
548
215
  }
549
-
550
216
  const complete = () => {
551
- const prevHoverState = this._hoverState;
552
- this._hoverState = null;
553
- EventHandler__default.default.trigger(this._element, this.constructor.Event.SHOWN);
554
-
555
- if (prevHoverState === HOVER_STATE_OUT) {
556
- this._leave(null, this);
217
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN));
218
+ if (this._isHovered === false) {
219
+ this._leave();
557
220
  }
221
+ this._isHovered = false;
558
222
  };
559
-
560
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
561
-
562
- this._queueCallback(complete, this.tip, isAnimated);
223
+ this._queueCallback(complete, this.tip, this._isAnimated());
563
224
  }
564
-
565
225
  hide() {
566
- if (!this._popper) {
226
+ if (!this._isShown()) {
567
227
  return;
568
228
  }
569
-
570
- const tip = this.getTipElement();
571
-
572
- const complete = () => {
573
- if (this._isWithActiveTrigger()) {
574
- return;
575
- }
576
-
577
- if (this._hoverState !== HOVER_STATE_SHOW) {
578
- tip.remove();
579
- }
580
-
581
- this._cleanTipClass();
582
-
583
- this._element.removeAttribute('aria-describedby');
584
-
585
- EventHandler__default.default.trigger(this._element, this.constructor.Event.HIDDEN);
586
-
587
- this._disposePopper();
588
- };
589
-
590
- const hideEvent = EventHandler__default.default.trigger(this._element, this.constructor.Event.HIDE);
591
-
229
+ const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE));
592
230
  if (hideEvent.defaultPrevented) {
593
231
  return;
594
232
  }
233
+ const tip = this._getTipElement();
234
+ tip.classList.remove(CLASS_NAME_SHOW);
595
235
 
596
- tip.classList.remove(CLASS_NAME_SHOW); // If this is a touch-enabled device we remove the extra
236
+ // If this is a touch-enabled device we remove the extra
597
237
  // empty mouseover listeners we added for iOS support
598
-
599
238
  if ('ontouchstart' in document.documentElement) {
600
- [].concat(...document.body.children).forEach(element => EventHandler__default.default.off(element, 'mouseover', noop));
239
+ for (const element of [].concat(...document.body.children)) {
240
+ EventHandler.off(element, 'mouseover', index_js.noop);
241
+ }
601
242
  }
602
-
603
243
  this._activeTrigger[TRIGGER_CLICK] = false;
604
244
  this._activeTrigger[TRIGGER_FOCUS] = false;
605
245
  this._activeTrigger[TRIGGER_HOVER] = false;
606
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
607
-
608
- this._queueCallback(complete, this.tip, isAnimated);
246
+ this._isHovered = null; // it is a trick to support manual triggering
609
247
 
610
- this._hoverState = '';
248
+ const complete = () => {
249
+ if (this._isWithActiveTrigger()) {
250
+ return;
251
+ }
252
+ if (!this._isHovered) {
253
+ this._disposePopper();
254
+ }
255
+ this._element.removeAttribute('aria-describedby');
256
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN));
257
+ };
258
+ this._queueCallback(complete, this.tip, this._isAnimated());
611
259
  }
612
-
613
260
  update() {
614
- if (this._popper !== null) {
261
+ if (this._popper) {
615
262
  this._popper.update();
616
263
  }
617
- } // Protected
618
-
619
-
620
- isWithContent() {
621
- return Boolean(this.getTitle());
622
264
  }
623
265
 
624
- getTipElement() {
625
- if (this.tip) {
626
- return this.tip;
266
+ // Protected
267
+ _isWithContent() {
268
+ return Boolean(this._getTitle());
269
+ }
270
+ _getTipElement() {
271
+ if (!this.tip) {
272
+ this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());
627
273
  }
628
-
629
- const element = document.createElement('div');
630
- element.innerHTML = this._config.template;
631
- const tip = element.children[0];
632
- this.setContent(tip);
633
- tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW);
634
- this.tip = tip;
635
274
  return this.tip;
636
275
  }
276
+ _createTipElement(content) {
277
+ const tip = this._getTemplateFactory(content).toHtml();
637
278
 
638
- setContent(tip) {
639
- this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER);
640
- }
641
-
642
- _sanitizeAndSetContent(template, content, selector) {
643
- const templateElement = SelectorEngine__default.default.findOne(selector, template);
644
-
645
- if (!content && templateElement) {
646
- templateElement.remove();
647
- return;
648
- } // we use append for html objects to maintain js events
649
-
650
-
651
- this.setElementContent(templateElement, content);
652
- }
653
-
654
- setElementContent(element, content) {
655
- if (element === null) {
656
- return;
279
+ // TODO: remove this check in v6
280
+ if (!tip) {
281
+ return null;
657
282
  }
658
-
659
- if (isElement(content)) {
660
- content = getElement(content); // content is a DOM node or a jQuery
661
-
662
- if (this._config.html) {
663
- if (content.parentNode !== element) {
664
- element.innerHTML = '';
665
- element.append(content);
666
- }
667
- } else {
668
- element.textContent = content.textContent;
669
- }
670
-
671
- return;
283
+ tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW);
284
+ // TODO: v6 the following can be achieved with CSS only
285
+ tip.classList.add(`bs-${this.constructor.NAME}-auto`);
286
+ const tipId = index_js.getUID(this.constructor.NAME).toString();
287
+ tip.setAttribute('id', tipId);
288
+ if (this._isAnimated()) {
289
+ tip.classList.add(CLASS_NAME_FADE);
672
290
  }
673
-
674
- if (this._config.html) {
675
- if (this._config.sanitize) {
676
- content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn);
677
- }
678
-
679
- element.innerHTML = content;
291
+ return tip;
292
+ }
293
+ setContent(content) {
294
+ this._newContent = content;
295
+ if (this._isShown()) {
296
+ this._disposePopper();
297
+ this.show();
298
+ }
299
+ }
300
+ _getTemplateFactory(content) {
301
+ if (this._templateFactory) {
302
+ this._templateFactory.changeContent(content);
680
303
  } else {
681
- element.textContent = content;
304
+ this._templateFactory = new TemplateFactory({
305
+ ...this._config,
306
+ // the `content` var has to be after `this._config`
307
+ // to override config.content in case of popover
308
+ content,
309
+ extraClass: this._resolvePossibleFunction(this._config.customClass)
310
+ });
682
311
  }
312
+ return this._templateFactory;
683
313
  }
684
-
685
- getTitle() {
686
- const title = this._element.getAttribute('data-bs-original-title') || this._config.title;
687
-
688
- return this._resolvePossibleFunction(title);
314
+ _getContentForTemplate() {
315
+ return {
316
+ [SELECTOR_TOOLTIP_INNER]: this._getTitle()
317
+ };
689
318
  }
690
-
691
- updateAttachment(attachment) {
692
- if (attachment === 'right') {
693
- return 'end';
694
- }
695
-
696
- if (attachment === 'left') {
697
- return 'start';
698
- }
699
-
700
- return attachment;
701
- } // Private
702
-
703
-
704
- _initializeOnDelegatedTarget(event, context) {
705
- return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());
319
+ _getTitle() {
320
+ return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');
706
321
  }
707
322
 
323
+ // Private
324
+ _initializeOnDelegatedTarget(event) {
325
+ return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());
326
+ }
327
+ _isAnimated() {
328
+ return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE);
329
+ }
330
+ _isShown() {
331
+ return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW);
332
+ }
333
+ _createPopper(tip) {
334
+ const placement = index_js.execute(this._config.placement, [this, tip, this._element]);
335
+ const attachment = AttachmentMap[placement.toUpperCase()];
336
+ return Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));
337
+ }
708
338
  _getOffset() {
709
339
  const {
710
340
  offset
711
341
  } = this._config;
712
-
713
342
  if (typeof offset === 'string') {
714
- return offset.split(',').map(val => Number.parseInt(val, 10));
343
+ return offset.split(',').map(value => Number.parseInt(value, 10));
715
344
  }
716
-
717
345
  if (typeof offset === 'function') {
718
346
  return popperData => offset(popperData, this._element);
719
347
  }
720
-
721
348
  return offset;
722
349
  }
723
-
724
- _resolvePossibleFunction(content) {
725
- return typeof content === 'function' ? content.call(this._element) : content;
350
+ _resolvePossibleFunction(arg) {
351
+ return index_js.execute(arg, [this._element, this._element]);
726
352
  }
727
-
728
353
  _getPopperConfig(attachment) {
729
354
  const defaultBsPopperConfig = {
730
355
  placement: attachment,
@@ -749,256 +374,170 @@
749
374
  element: `.${this.constructor.NAME}-arrow`
750
375
  }
751
376
  }, {
752
- name: 'onChange',
377
+ name: 'preSetPlacement',
753
378
  enabled: true,
754
- phase: 'afterWrite',
755
- fn: data => this._handlePopperPlacementChange(data)
756
- }],
757
- onFirstUpdate: data => {
758
- if (data.options.placement !== data.placement) {
759
- this._handlePopperPlacementChange(data);
379
+ phase: 'beforeMain',
380
+ fn: data => {
381
+ // Pre-set Popper's placement attribute in order to read the arrow sizes properly.
382
+ // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
383
+ this._getTipElement().setAttribute('data-popper-placement', data.state.placement);
760
384
  }
761
- }
385
+ }]
762
386
  };
763
- return { ...defaultBsPopperConfig,
764
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
387
+ return {
388
+ ...defaultBsPopperConfig,
389
+ ...index_js.execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])
765
390
  };
766
391
  }
767
-
768
- _addAttachmentClass(attachment) {
769
- this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`);
770
- }
771
-
772
- _getAttachment(placement) {
773
- return AttachmentMap[placement.toUpperCase()];
774
- }
775
-
776
392
  _setListeners() {
777
393
  const triggers = this._config.trigger.split(' ');
778
-
779
- triggers.forEach(trigger => {
394
+ for (const trigger of triggers) {
780
395
  if (trigger === 'click') {
781
- EventHandler__default.default.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event));
396
+ EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {
397
+ const context = this._initializeOnDelegatedTarget(event);
398
+ context.toggle();
399
+ });
782
400
  } else if (trigger !== TRIGGER_MANUAL) {
783
- const eventIn = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSEENTER : this.constructor.Event.FOCUSIN;
784
- const eventOut = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSELEAVE : this.constructor.Event.FOCUSOUT;
785
- EventHandler__default.default.on(this._element, eventIn, this._config.selector, event => this._enter(event));
786
- EventHandler__default.default.on(this._element, eventOut, this._config.selector, event => this._leave(event));
401
+ const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN);
402
+ const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT);
403
+ EventHandler.on(this._element, eventIn, this._config.selector, event => {
404
+ const context = this._initializeOnDelegatedTarget(event);
405
+ context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
406
+ context._enter();
407
+ });
408
+ EventHandler.on(this._element, eventOut, this._config.selector, event => {
409
+ const context = this._initializeOnDelegatedTarget(event);
410
+ context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);
411
+ context._leave();
412
+ });
787
413
  }
788
- });
789
-
414
+ }
790
415
  this._hideModalHandler = () => {
791
416
  if (this._element) {
792
417
  this.hide();
793
418
  }
794
419
  };
795
-
796
- EventHandler__default.default.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
797
-
798
- if (this._config.selector) {
799
- this._config = { ...this._config,
800
- trigger: 'manual',
801
- selector: ''
802
- };
803
- } else {
804
- this._fixTitle();
805
- }
420
+ EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
806
421
  }
807
-
808
422
  _fixTitle() {
809
423
  const title = this._element.getAttribute('title');
810
-
811
- const originalTitleType = typeof this._element.getAttribute('data-bs-original-title');
812
-
813
- if (title || originalTitleType !== 'string') {
814
- this._element.setAttribute('data-bs-original-title', title || '');
815
-
816
- if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
817
- this._element.setAttribute('aria-label', title);
818
- }
819
-
820
- this._element.setAttribute('title', '');
821
- }
822
- }
823
-
824
- _enter(event, context) {
825
- context = this._initializeOnDelegatedTarget(event, context);
826
-
827
- if (event) {
828
- context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
829
- }
830
-
831
- if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
832
- context._hoverState = HOVER_STATE_SHOW;
424
+ if (!title) {
833
425
  return;
834
426
  }
835
-
836
- clearTimeout(context._timeout);
837
- context._hoverState = HOVER_STATE_SHOW;
838
-
839
- if (!context._config.delay || !context._config.delay.show) {
840
- context.show();
841
- return;
427
+ if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {
428
+ this._element.setAttribute('aria-label', title);
842
429
  }
843
-
844
- context._timeout = setTimeout(() => {
845
- if (context._hoverState === HOVER_STATE_SHOW) {
846
- context.show();
847
- }
848
- }, context._config.delay.show);
430
+ this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility
431
+ this._element.removeAttribute('title');
849
432
  }
850
-
851
- _leave(event, context) {
852
- context = this._initializeOnDelegatedTarget(event, context);
853
-
854
- if (event) {
855
- context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);
856
- }
857
-
858
- if (context._isWithActiveTrigger()) {
433
+ _enter() {
434
+ if (this._isShown() || this._isHovered) {
435
+ this._isHovered = true;
859
436
  return;
860
437
  }
861
-
862
- clearTimeout(context._timeout);
863
- context._hoverState = HOVER_STATE_OUT;
864
-
865
- if (!context._config.delay || !context._config.delay.hide) {
866
- context.hide();
438
+ this._isHovered = true;
439
+ this._setTimeout(() => {
440
+ if (this._isHovered) {
441
+ this.show();
442
+ }
443
+ }, this._config.delay.show);
444
+ }
445
+ _leave() {
446
+ if (this._isWithActiveTrigger()) {
867
447
  return;
868
448
  }
869
-
870
- context._timeout = setTimeout(() => {
871
- if (context._hoverState === HOVER_STATE_OUT) {
872
- context.hide();
449
+ this._isHovered = false;
450
+ this._setTimeout(() => {
451
+ if (!this._isHovered) {
452
+ this.hide();
873
453
  }
874
- }, context._config.delay.hide);
454
+ }, this._config.delay.hide);
455
+ }
456
+ _setTimeout(handler, timeout) {
457
+ clearTimeout(this._timeout);
458
+ this._timeout = setTimeout(handler, timeout);
875
459
  }
876
-
877
460
  _isWithActiveTrigger() {
878
- for (const trigger in this._activeTrigger) {
879
- if (this._activeTrigger[trigger]) {
880
- return true;
881
- }
882
- }
883
-
884
- return false;
461
+ return Object.values(this._activeTrigger).includes(true);
885
462
  }
886
-
887
463
  _getConfig(config) {
888
- const dataAttributes = Manipulator__default.default.getDataAttributes(this._element);
889
- Object.keys(dataAttributes).forEach(dataAttr => {
890
- if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {
891
- delete dataAttributes[dataAttr];
464
+ const dataAttributes = Manipulator.getDataAttributes(this._element);
465
+ for (const dataAttribute of Object.keys(dataAttributes)) {
466
+ if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {
467
+ delete dataAttributes[dataAttribute];
892
468
  }
893
- });
894
- config = { ...this.constructor.Default,
469
+ }
470
+ config = {
895
471
  ...dataAttributes,
896
472
  ...(typeof config === 'object' && config ? config : {})
897
473
  };
898
- config.container = config.container === false ? document.body : getElement(config.container);
899
-
474
+ config = this._mergeConfigObj(config);
475
+ config = this._configAfterMerge(config);
476
+ this._typeCheckConfig(config);
477
+ return config;
478
+ }
479
+ _configAfterMerge(config) {
480
+ config.container = config.container === false ? document.body : index_js.getElement(config.container);
900
481
  if (typeof config.delay === 'number') {
901
482
  config.delay = {
902
483
  show: config.delay,
903
484
  hide: config.delay
904
485
  };
905
486
  }
906
-
907
487
  if (typeof config.title === 'number') {
908
488
  config.title = config.title.toString();
909
489
  }
910
-
911
490
  if (typeof config.content === 'number') {
912
491
  config.content = config.content.toString();
913
492
  }
914
-
915
- typeCheckConfig(NAME, config, this.constructor.DefaultType);
916
-
917
- if (config.sanitize) {
918
- config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn);
919
- }
920
-
921
493
  return config;
922
494
  }
923
-
924
495
  _getDelegateConfig() {
925
496
  const config = {};
926
-
927
- for (const key in this._config) {
928
- if (this.constructor.Default[key] !== this._config[key]) {
929
- config[key] = this._config[key];
497
+ for (const [key, value] of Object.entries(this._config)) {
498
+ if (this.constructor.Default[key] !== value) {
499
+ config[key] = value;
930
500
  }
931
- } // In the future can be replaced with:
501
+ }
502
+ config.selector = false;
503
+ config.trigger = 'manual';
504
+
505
+ // In the future can be replaced with:
932
506
  // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])
933
507
  // `Object.fromEntries(keysWithDifferentValues)`
934
-
935
-
936
508
  return config;
937
509
  }
938
-
939
- _cleanTipClass() {
940
- const tip = this.getTipElement();
941
- const basicClassPrefixRegex = new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`, 'g');
942
- const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex);
943
-
944
- if (tabClass !== null && tabClass.length > 0) {
945
- tabClass.map(token => token.trim()).forEach(tClass => tip.classList.remove(tClass));
946
- }
947
- }
948
-
949
- _getBasicClassPrefix() {
950
- return CLASS_PREFIX;
951
- }
952
-
953
- _handlePopperPlacementChange(popperData) {
954
- const {
955
- state
956
- } = popperData;
957
-
958
- if (!state) {
959
- return;
960
- }
961
-
962
- this.tip = state.elements.popper;
963
-
964
- this._cleanTipClass();
965
-
966
- this._addAttachmentClass(this._getAttachment(state.placement));
967
- }
968
-
969
510
  _disposePopper() {
970
511
  if (this._popper) {
971
512
  this._popper.destroy();
972
-
973
513
  this._popper = null;
974
514
  }
975
- } // Static
976
-
515
+ if (this.tip) {
516
+ this.tip.remove();
517
+ this.tip = null;
518
+ }
519
+ }
977
520
 
521
+ // Static
978
522
  static jQueryInterface(config) {
979
523
  return this.each(function () {
980
524
  const data = Tooltip.getOrCreateInstance(this, config);
981
-
982
- if (typeof config === 'string') {
983
- if (typeof data[config] === 'undefined') {
984
- throw new TypeError(`No method named "${config}"`);
985
- }
986
-
987
- data[config]();
525
+ if (typeof config !== 'string') {
526
+ return;
527
+ }
528
+ if (typeof data[config] === 'undefined') {
529
+ throw new TypeError(`No method named "${config}"`);
988
530
  }
531
+ data[config]();
989
532
  });
990
533
  }
991
-
992
534
  }
535
+
993
536
  /**
994
- * ------------------------------------------------------------------------
995
537
  * jQuery
996
- * ------------------------------------------------------------------------
997
- * add .Tooltip to jQuery only if jQuery is present
998
538
  */
999
539
 
1000
-
1001
- defineJQueryPlugin(Tooltip);
540
+ index_js.defineJQueryPlugin(Tooltip);
1002
541
 
1003
542
  return Tooltip;
1004
543