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)
|
---|
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('@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';
|
---|
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);
|
---|
17 | if (e) {
|
---|
18 | for (const k in e) {
|
---|
19 | if (k !== 'default') {
|
---|
20 | const d = Object.getOwnPropertyDescriptor(e, k);
|
---|
21 | Object.defineProperty(n, k, d.get ? d : {
|
---|
22 | enumerable: true,
|
---|
23 | get: () => e[k]
|
---|
24 | });
|
---|
25 | }
|
---|
26 | }
|
---|
27 | }
|
---|
28 | n.default = e;
|
---|
29 | return Object.freeze(n);
|
---|
30 | }
|
---|
31 |
|
---|
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 | };
|
---|
180 |
|
---|
181 | /**
|
---|
182 | * --------------------------------------------------------------------------
|
---|
183 | * Bootstrap (v5.1.3): util/sanitizer.js
|
---|
184 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
---|
185 | * --------------------------------------------------------------------------
|
---|
186 | */
|
---|
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 |
|
---|
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 |
|
---|
293 | /**
|
---|
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 | * Constants
|
---|
302 | * ------------------------------------------------------------------------
|
---|
303 | */
|
---|
304 |
|
---|
305 | const NAME = 'tooltip';
|
---|
306 | const DATA_KEY = 'bs.tooltip';
|
---|
307 | const EVENT_KEY = `.${DATA_KEY}`;
|
---|
308 | const CLASS_PREFIX = 'bs-tooltip';
|
---|
309 | 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 | };
|
---|
329 | const AttachmentMap = {
|
---|
330 | AUTO: 'auto',
|
---|
331 | TOP: 'top',
|
---|
332 | RIGHT: isRTL() ? 'left' : 'right',
|
---|
333 | BOTTOM: 'bottom',
|
---|
334 | LEFT: isRTL() ? 'right' : 'left'
|
---|
335 | };
|
---|
336 | const Default = {
|
---|
337 | 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: '',
|
---|
341 | delay: 0,
|
---|
342 | html: false,
|
---|
343 | selector: false,
|
---|
344 | placement: 'top',
|
---|
345 | offset: [0, 0],
|
---|
346 | container: false,
|
---|
347 | fallbackPlacements: ['top', 'right', 'bottom', 'left'],
|
---|
348 | boundary: 'clippingParents',
|
---|
349 | customClass: '',
|
---|
350 | sanitize: true,
|
---|
351 | sanitizeFn: null,
|
---|
352 | allowList: DefaultAllowlist,
|
---|
353 | popperConfig: null
|
---|
354 | };
|
---|
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}`
|
---|
366 | };
|
---|
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';
|
---|
379 | /**
|
---|
380 | * ------------------------------------------------------------------------
|
---|
381 | * Class Definition
|
---|
382 | * ------------------------------------------------------------------------
|
---|
383 | */
|
---|
384 |
|
---|
385 | class Tooltip extends BaseComponent__default.default {
|
---|
386 | constructor(element, config) {
|
---|
387 | if (typeof Popper__namespace === 'undefined') {
|
---|
388 | throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)');
|
---|
389 | }
|
---|
390 |
|
---|
391 | super(element); // private
|
---|
392 |
|
---|
393 | this._isEnabled = true;
|
---|
394 | this._timeout = 0;
|
---|
395 | this._hoverState = '';
|
---|
396 | this._activeTrigger = {};
|
---|
397 | this._popper = null; // Protected
|
---|
398 |
|
---|
399 | this._config = this._getConfig(config);
|
---|
400 | this.tip = null;
|
---|
401 |
|
---|
402 | this._setListeners();
|
---|
403 | } // Getters
|
---|
404 |
|
---|
405 |
|
---|
406 | static get Default() {
|
---|
407 | return Default;
|
---|
408 | }
|
---|
409 |
|
---|
410 | static get NAME() {
|
---|
411 | return NAME;
|
---|
412 | }
|
---|
413 |
|
---|
414 | static get Event() {
|
---|
415 | return Event;
|
---|
416 | }
|
---|
417 |
|
---|
418 | static get DefaultType() {
|
---|
419 | return DefaultType;
|
---|
420 | } // Public
|
---|
421 |
|
---|
422 |
|
---|
423 | enable() {
|
---|
424 | this._isEnabled = true;
|
---|
425 | }
|
---|
426 |
|
---|
427 | disable() {
|
---|
428 | this._isEnabled = false;
|
---|
429 | }
|
---|
430 |
|
---|
431 | toggleEnabled() {
|
---|
432 | this._isEnabled = !this._isEnabled;
|
---|
433 | }
|
---|
434 |
|
---|
435 | toggle(event) {
|
---|
436 | if (!this._isEnabled) {
|
---|
437 | return;
|
---|
438 | }
|
---|
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);
|
---|
458 | }
|
---|
459 | }
|
---|
460 |
|
---|
461 | dispose() {
|
---|
462 | 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();
|
---|
467 | }
|
---|
468 |
|
---|
469 | this._disposePopper();
|
---|
470 |
|
---|
471 | super.dispose();
|
---|
472 | }
|
---|
473 |
|
---|
474 | show() {
|
---|
475 | if (this._element.style.display === 'none') {
|
---|
476 | throw new Error('Please use show on visible elements');
|
---|
477 | }
|
---|
478 |
|
---|
479 | if (!(this.isWithContent() && this._isEnabled)) {
|
---|
480 | return;
|
---|
481 | }
|
---|
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 |
|
---|
487 | if (showEvent.defaultPrevented || !isInTheDom) {
|
---|
488 | 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 | }
|
---|
509 |
|
---|
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 |
|
---|
516 | const {
|
---|
517 | container
|
---|
518 | } = this._config;
|
---|
519 | Data__default.default.set(tip, this.constructor.DATA_KEY, this);
|
---|
520 |
|
---|
521 | if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
|
---|
522 | container.append(tip);
|
---|
523 | EventHandler__default.default.trigger(this._element, this.constructor.Event.INSERTED);
|
---|
524 | }
|
---|
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 |
|
---|
532 | tip.classList.add(CLASS_NAME_SHOW);
|
---|
533 |
|
---|
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
|
---|
539 | // empty mouseover listeners to the body's immediate children;
|
---|
540 | // only needed because of broken event delegation on iOS
|
---|
541 | // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
|
---|
542 |
|
---|
543 |
|
---|
544 | if ('ontouchstart' in document.documentElement) {
|
---|
545 | [].concat(...document.body.children).forEach(element => {
|
---|
546 | EventHandler__default.default.on(element, 'mouseover', noop);
|
---|
547 | });
|
---|
548 | }
|
---|
549 |
|
---|
550 | 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);
|
---|
557 | }
|
---|
558 | };
|
---|
559 |
|
---|
560 | const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
|
---|
561 |
|
---|
562 | this._queueCallback(complete, this.tip, isAnimated);
|
---|
563 | }
|
---|
564 |
|
---|
565 | hide() {
|
---|
566 | if (!this._popper) {
|
---|
567 | return;
|
---|
568 | }
|
---|
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 |
|
---|
592 | if (hideEvent.defaultPrevented) {
|
---|
593 | return;
|
---|
594 | }
|
---|
595 |
|
---|
596 | tip.classList.remove(CLASS_NAME_SHOW); // If this is a touch-enabled device we remove the extra
|
---|
597 | // empty mouseover listeners we added for iOS support
|
---|
598 |
|
---|
599 | if ('ontouchstart' in document.documentElement) {
|
---|
600 | [].concat(...document.body.children).forEach(element => EventHandler__default.default.off(element, 'mouseover', noop));
|
---|
601 | }
|
---|
602 |
|
---|
603 | this._activeTrigger[TRIGGER_CLICK] = false;
|
---|
604 | this._activeTrigger[TRIGGER_FOCUS] = false;
|
---|
605 | this._activeTrigger[TRIGGER_HOVER] = false;
|
---|
606 | const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
|
---|
607 |
|
---|
608 | this._queueCallback(complete, this.tip, isAnimated);
|
---|
609 |
|
---|
610 | this._hoverState = '';
|
---|
611 | }
|
---|
612 |
|
---|
613 | update() {
|
---|
614 | if (this._popper !== null) {
|
---|
615 | this._popper.update();
|
---|
616 | }
|
---|
617 | } // Protected
|
---|
618 |
|
---|
619 |
|
---|
620 | isWithContent() {
|
---|
621 | return Boolean(this.getTitle());
|
---|
622 | }
|
---|
623 |
|
---|
624 | getTipElement() {
|
---|
625 | if (this.tip) {
|
---|
626 | return this.tip;
|
---|
627 | }
|
---|
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 | return this.tip;
|
---|
636 | }
|
---|
637 |
|
---|
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;
|
---|
657 | }
|
---|
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;
|
---|
672 | }
|
---|
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;
|
---|
680 | } else {
|
---|
681 | element.textContent = content;
|
---|
682 | }
|
---|
683 | }
|
---|
684 |
|
---|
685 | getTitle() {
|
---|
686 | const title = this._element.getAttribute('data-bs-original-title') || this._config.title;
|
---|
687 |
|
---|
688 | return this._resolvePossibleFunction(title);
|
---|
689 | }
|
---|
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());
|
---|
706 | }
|
---|
707 |
|
---|
708 | _getOffset() {
|
---|
709 | const {
|
---|
710 | offset
|
---|
711 | } = this._config;
|
---|
712 |
|
---|
713 | if (typeof offset === 'string') {
|
---|
714 | return offset.split(',').map(val => Number.parseInt(val, 10));
|
---|
715 | }
|
---|
716 |
|
---|
717 | if (typeof offset === 'function') {
|
---|
718 | return popperData => offset(popperData, this._element);
|
---|
719 | }
|
---|
720 |
|
---|
721 | return offset;
|
---|
722 | }
|
---|
723 |
|
---|
724 | _resolvePossibleFunction(content) {
|
---|
725 | return typeof content === 'function' ? content.call(this._element) : content;
|
---|
726 | }
|
---|
727 |
|
---|
728 | _getPopperConfig(attachment) {
|
---|
729 | const defaultBsPopperConfig = {
|
---|
730 | placement: attachment,
|
---|
731 | modifiers: [{
|
---|
732 | name: 'flip',
|
---|
733 | options: {
|
---|
734 | fallbackPlacements: this._config.fallbackPlacements
|
---|
735 | }
|
---|
736 | }, {
|
---|
737 | name: 'offset',
|
---|
738 | options: {
|
---|
739 | offset: this._getOffset()
|
---|
740 | }
|
---|
741 | }, {
|
---|
742 | name: 'preventOverflow',
|
---|
743 | options: {
|
---|
744 | boundary: this._config.boundary
|
---|
745 | }
|
---|
746 | }, {
|
---|
747 | name: 'arrow',
|
---|
748 | options: {
|
---|
749 | element: `.${this.constructor.NAME}-arrow`
|
---|
750 | }
|
---|
751 | }, {
|
---|
752 | name: 'onChange',
|
---|
753 | 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);
|
---|
760 | }
|
---|
761 | }
|
---|
762 | };
|
---|
763 | return { ...defaultBsPopperConfig,
|
---|
764 | ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
|
---|
765 | };
|
---|
766 | }
|
---|
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 | _setListeners() {
|
---|
777 | const triggers = this._config.trigger.split(' ');
|
---|
778 |
|
---|
779 | triggers.forEach(trigger => {
|
---|
780 | if (trigger === 'click') {
|
---|
781 | EventHandler__default.default.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event));
|
---|
782 | } 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));
|
---|
787 | }
|
---|
788 | });
|
---|
789 |
|
---|
790 | this._hideModalHandler = () => {
|
---|
791 | if (this._element) {
|
---|
792 | this.hide();
|
---|
793 | }
|
---|
794 | };
|
---|
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 | }
|
---|
806 | }
|
---|
807 |
|
---|
808 | _fixTitle() {
|
---|
809 | 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;
|
---|
833 | return;
|
---|
834 | }
|
---|
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;
|
---|
842 | }
|
---|
843 |
|
---|
844 | context._timeout = setTimeout(() => {
|
---|
845 | if (context._hoverState === HOVER_STATE_SHOW) {
|
---|
846 | context.show();
|
---|
847 | }
|
---|
848 | }, context._config.delay.show);
|
---|
849 | }
|
---|
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()) {
|
---|
859 | return;
|
---|
860 | }
|
---|
861 |
|
---|
862 | clearTimeout(context._timeout);
|
---|
863 | context._hoverState = HOVER_STATE_OUT;
|
---|
864 |
|
---|
865 | if (!context._config.delay || !context._config.delay.hide) {
|
---|
866 | context.hide();
|
---|
867 | return;
|
---|
868 | }
|
---|
869 |
|
---|
870 | context._timeout = setTimeout(() => {
|
---|
871 | if (context._hoverState === HOVER_STATE_OUT) {
|
---|
872 | context.hide();
|
---|
873 | }
|
---|
874 | }, context._config.delay.hide);
|
---|
875 | }
|
---|
876 |
|
---|
877 | _isWithActiveTrigger() {
|
---|
878 | for (const trigger in this._activeTrigger) {
|
---|
879 | if (this._activeTrigger[trigger]) {
|
---|
880 | return true;
|
---|
881 | }
|
---|
882 | }
|
---|
883 |
|
---|
884 | return false;
|
---|
885 | }
|
---|
886 |
|
---|
887 | _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];
|
---|
892 | }
|
---|
893 | });
|
---|
894 | config = { ...this.constructor.Default,
|
---|
895 | ...dataAttributes,
|
---|
896 | ...(typeof config === 'object' && config ? config : {})
|
---|
897 | };
|
---|
898 | config.container = config.container === false ? document.body : getElement(config.container);
|
---|
899 |
|
---|
900 | if (typeof config.delay === 'number') {
|
---|
901 | config.delay = {
|
---|
902 | show: config.delay,
|
---|
903 | hide: config.delay
|
---|
904 | };
|
---|
905 | }
|
---|
906 |
|
---|
907 | if (typeof config.title === 'number') {
|
---|
908 | config.title = config.title.toString();
|
---|
909 | }
|
---|
910 |
|
---|
911 | if (typeof config.content === 'number') {
|
---|
912 | config.content = config.content.toString();
|
---|
913 | }
|
---|
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 | return config;
|
---|
922 | }
|
---|
923 |
|
---|
924 | _getDelegateConfig() {
|
---|
925 | 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];
|
---|
930 | }
|
---|
931 | } // In the future can be replaced with:
|
---|
932 | // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])
|
---|
933 | // `Object.fromEntries(keysWithDifferentValues)`
|
---|
934 |
|
---|
935 |
|
---|
936 | return config;
|
---|
937 | }
|
---|
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 | _disposePopper() {
|
---|
970 | if (this._popper) {
|
---|
971 | this._popper.destroy();
|
---|
972 |
|
---|
973 | this._popper = null;
|
---|
974 | }
|
---|
975 | } // Static
|
---|
976 |
|
---|
977 |
|
---|
978 | static jQueryInterface(config) {
|
---|
979 | return this.each(function () {
|
---|
980 | 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]();
|
---|
988 | }
|
---|
989 | });
|
---|
990 | }
|
---|
991 |
|
---|
992 | }
|
---|
993 | /**
|
---|
994 | * ------------------------------------------------------------------------
|
---|
995 | * jQuery
|
---|
996 | * ------------------------------------------------------------------------
|
---|
997 | * add .Tooltip to jQuery only if jQuery is present
|
---|
998 | */
|
---|
999 |
|
---|
1000 |
|
---|
1001 | defineJQueryPlugin(Tooltip);
|
---|
1002 |
|
---|
1003 | return Tooltip;
|
---|
1004 |
|
---|
1005 | }));
|
---|
1006 | //# sourceMappingURL=tooltip.js.map
|
---|