You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1006 lines
29 KiB

  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. const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
  12. function _interopNamespace(e) {
  13. if (e && e.__esModule) return e;
  14. const n = Object.create(null);
  15. if (e) {
  16. for (const k in e) {
  17. if (k !== 'default') {
  18. const d = Object.getOwnPropertyDescriptor(e, k);
  19. Object.defineProperty(n, k, d.get ? d : {
  20. enumerable: true,
  21. get: () => e[k]
  22. });
  23. }
  24. }
  25. }
  26. n.default = e;
  27. return Object.freeze(n);
  28. }
  29. const Popper__namespace = /*#__PURE__*/_interopNamespace(Popper);
  30. const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
  31. const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
  32. const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
  33. const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
  34. const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
  35. /**
  36. * --------------------------------------------------------------------------
  37. * Bootstrap (v5.1.3): util/index.js
  38. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  39. * --------------------------------------------------------------------------
  40. */
  41. const MAX_UID = 1000000;
  42. const toType = obj => {
  43. if (obj === null || obj === undefined) {
  44. return `${obj}`;
  45. }
  46. return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
  47. };
  48. /**
  49. * --------------------------------------------------------------------------
  50. * Public Util Api
  51. * --------------------------------------------------------------------------
  52. */
  53. const getUID = prefix => {
  54. do {
  55. prefix += Math.floor(Math.random() * MAX_UID);
  56. } while (document.getElementById(prefix));
  57. return prefix;
  58. };
  59. const isElement = obj => {
  60. if (!obj || typeof obj !== 'object') {
  61. return false;
  62. }
  63. if (typeof obj.jquery !== 'undefined') {
  64. obj = obj[0];
  65. }
  66. return typeof obj.nodeType !== 'undefined';
  67. };
  68. const getElement = obj => {
  69. if (isElement(obj)) {
  70. // it's a jQuery object or a node element
  71. return obj.jquery ? obj[0] : obj;
  72. }
  73. if (typeof obj === 'string' && obj.length > 0) {
  74. return document.querySelector(obj);
  75. }
  76. return null;
  77. };
  78. const typeCheckConfig = (componentName, config, configTypes) => {
  79. Object.keys(configTypes).forEach(property => {
  80. const expectedTypes = configTypes[property];
  81. const value = config[property];
  82. const valueType = value && isElement(value) ? 'element' : toType(value);
  83. if (!new RegExp(expectedTypes).test(valueType)) {
  84. throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
  85. }
  86. });
  87. };
  88. const findShadowRoot = element => {
  89. if (!document.documentElement.attachShadow) {
  90. return null;
  91. } // Can find the shadow root otherwise it'll return the document
  92. if (typeof element.getRootNode === 'function') {
  93. const root = element.getRootNode();
  94. return root instanceof ShadowRoot ? root : null;
  95. }
  96. if (element instanceof ShadowRoot) {
  97. return element;
  98. } // when we don't find a shadow root
  99. if (!element.parentNode) {
  100. return null;
  101. }
  102. return findShadowRoot(element.parentNode);
  103. };
  104. const noop = () => {};
  105. const getjQuery = () => {
  106. const {
  107. jQuery
  108. } = window;
  109. if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
  110. return jQuery;
  111. }
  112. return null;
  113. };
  114. const DOMContentLoadedCallbacks = [];
  115. const onDOMContentLoaded = callback => {
  116. if (document.readyState === 'loading') {
  117. // add listener on the first call when the document is in loading state
  118. if (!DOMContentLoadedCallbacks.length) {
  119. document.addEventListener('DOMContentLoaded', () => {
  120. DOMContentLoadedCallbacks.forEach(callback => callback());
  121. });
  122. }
  123. DOMContentLoadedCallbacks.push(callback);
  124. } else {
  125. callback();
  126. }
  127. };
  128. const isRTL = () => document.documentElement.dir === 'rtl';
  129. const defineJQueryPlugin = plugin => {
  130. onDOMContentLoaded(() => {
  131. const $ = getjQuery();
  132. /* istanbul ignore if */
  133. if ($) {
  134. const name = plugin.NAME;
  135. const JQUERY_NO_CONFLICT = $.fn[name];
  136. $.fn[name] = plugin.jQueryInterface;
  137. $.fn[name].Constructor = plugin;
  138. $.fn[name].noConflict = () => {
  139. $.fn[name] = JQUERY_NO_CONFLICT;
  140. return plugin.jQueryInterface;
  141. };
  142. }
  143. });
  144. };
  145. /**
  146. * --------------------------------------------------------------------------
  147. * Bootstrap (v5.1.3): util/sanitizer.js
  148. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  149. * --------------------------------------------------------------------------
  150. */
  151. const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);
  152. const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
  153. /**
  154. * A pattern that recognizes a commonly useful subset of URLs that are safe.
  155. *
  156. * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
  157. */
  158. const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i;
  159. /**
  160. * A pattern that matches safe data URLs. Only matches image, video and audio types.
  161. *
  162. * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
  163. */
  164. 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;
  165. const allowedAttribute = (attribute, allowedAttributeList) => {
  166. const attributeName = attribute.nodeName.toLowerCase();
  167. if (allowedAttributeList.includes(attributeName)) {
  168. if (uriAttributes.has(attributeName)) {
  169. return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue));
  170. }
  171. return true;
  172. }
  173. const regExp = allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp); // Check if a regular expression validates the attribute.
  174. for (let i = 0, len = regExp.length; i < len; i++) {
  175. if (regExp[i].test(attributeName)) {
  176. return true;
  177. }
  178. }
  179. return false;
  180. };
  181. const DefaultAllowlist = {
  182. // Global attributes allowed on any supplied element below.
  183. '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
  184. a: ['target', 'href', 'title', 'rel'],
  185. area: [],
  186. b: [],
  187. br: [],
  188. col: [],
  189. code: [],
  190. div: [],
  191. em: [],
  192. hr: [],
  193. h1: [],
  194. h2: [],
  195. h3: [],
  196. h4: [],
  197. h5: [],
  198. h6: [],
  199. i: [],
  200. img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
  201. li: [],
  202. ol: [],
  203. p: [],
  204. pre: [],
  205. s: [],
  206. small: [],
  207. span: [],
  208. sub: [],
  209. sup: [],
  210. strong: [],
  211. u: [],
  212. ul: []
  213. };
  214. function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
  215. if (!unsafeHtml.length) {
  216. return unsafeHtml;
  217. }
  218. if (sanitizeFn && typeof sanitizeFn === 'function') {
  219. return sanitizeFn(unsafeHtml);
  220. }
  221. const domParser = new window.DOMParser();
  222. const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
  223. const elements = [].concat(...createdDocument.body.querySelectorAll('*'));
  224. for (let i = 0, len = elements.length; i < len; i++) {
  225. const element = elements[i];
  226. const elementName = element.nodeName.toLowerCase();
  227. if (!Object.keys(allowList).includes(elementName)) {
  228. element.remove();
  229. continue;
  230. }
  231. const attributeList = [].concat(...element.attributes);
  232. const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);
  233. attributeList.forEach(attribute => {
  234. if (!allowedAttribute(attribute, allowedAttributes)) {
  235. element.removeAttribute(attribute.nodeName);
  236. }
  237. });
  238. }
  239. return createdDocument.body.innerHTML;
  240. }
  241. /**
  242. * --------------------------------------------------------------------------
  243. * Bootstrap (v5.1.3): tooltip.js
  244. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  245. * --------------------------------------------------------------------------
  246. */
  247. /**
  248. * ------------------------------------------------------------------------
  249. * Constants
  250. * ------------------------------------------------------------------------
  251. */
  252. const NAME = 'tooltip';
  253. const DATA_KEY = 'bs.tooltip';
  254. const EVENT_KEY = `.${DATA_KEY}`;
  255. const CLASS_PREFIX = 'bs-tooltip';
  256. const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);
  257. const DefaultType = {
  258. animation: 'boolean',
  259. template: 'string',
  260. title: '(string|element|function)',
  261. trigger: 'string',
  262. delay: '(number|object)',
  263. html: 'boolean',
  264. selector: '(string|boolean)',
  265. placement: '(string|function)',
  266. offset: '(array|string|function)',
  267. container: '(string|element|boolean)',
  268. fallbackPlacements: 'array',
  269. boundary: '(string|element)',
  270. customClass: '(string|function)',
  271. sanitize: 'boolean',
  272. sanitizeFn: '(null|function)',
  273. allowList: 'object',
  274. popperConfig: '(null|object|function)'
  275. };
  276. const AttachmentMap = {
  277. AUTO: 'auto',
  278. TOP: 'top',
  279. RIGHT: isRTL() ? 'left' : 'right',
  280. BOTTOM: 'bottom',
  281. LEFT: isRTL() ? 'right' : 'left'
  282. };
  283. const Default = {
  284. animation: true,
  285. template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>',
  286. trigger: 'hover focus',
  287. title: '',
  288. delay: 0,
  289. html: false,
  290. selector: false,
  291. placement: 'top',
  292. offset: [0, 0],
  293. container: false,
  294. fallbackPlacements: ['top', 'right', 'bottom', 'left'],
  295. boundary: 'clippingParents',
  296. customClass: '',
  297. sanitize: true,
  298. sanitizeFn: null,
  299. allowList: DefaultAllowlist,
  300. popperConfig: null
  301. };
  302. const Event = {
  303. HIDE: `hide${EVENT_KEY}`,
  304. HIDDEN: `hidden${EVENT_KEY}`,
  305. SHOW: `show${EVENT_KEY}`,
  306. SHOWN: `shown${EVENT_KEY}`,
  307. INSERTED: `inserted${EVENT_KEY}`,
  308. CLICK: `click${EVENT_KEY}`,
  309. FOCUSIN: `focusin${EVENT_KEY}`,
  310. FOCUSOUT: `focusout${EVENT_KEY}`,
  311. MOUSEENTER: `mouseenter${EVENT_KEY}`,
  312. MOUSELEAVE: `mouseleave${EVENT_KEY}`
  313. };
  314. const CLASS_NAME_FADE = 'fade';
  315. const CLASS_NAME_MODAL = 'modal';
  316. const CLASS_NAME_SHOW = 'show';
  317. const HOVER_STATE_SHOW = 'show';
  318. const HOVER_STATE_OUT = 'out';
  319. const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
  320. const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;
  321. const EVENT_MODAL_HIDE = 'hide.bs.modal';
  322. const TRIGGER_HOVER = 'hover';
  323. const TRIGGER_FOCUS = 'focus';
  324. const TRIGGER_CLICK = 'click';
  325. const TRIGGER_MANUAL = 'manual';
  326. /**
  327. * ------------------------------------------------------------------------
  328. * Class Definition
  329. * ------------------------------------------------------------------------
  330. */
  331. class Tooltip extends BaseComponent__default.default {
  332. constructor(element, config) {
  333. if (typeof Popper__namespace === 'undefined') {
  334. throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)');
  335. }
  336. super(element); // private
  337. this._isEnabled = true;
  338. this._timeout = 0;
  339. this._hoverState = '';
  340. this._activeTrigger = {};
  341. this._popper = null; // Protected
  342. this._config = this._getConfig(config);
  343. this.tip = null;
  344. this._setListeners();
  345. } // Getters
  346. static get Default() {
  347. return Default;
  348. }
  349. static get NAME() {
  350. return NAME;
  351. }
  352. static get Event() {
  353. return Event;
  354. }
  355. static get DefaultType() {
  356. return DefaultType;
  357. } // Public
  358. enable() {
  359. this._isEnabled = true;
  360. }
  361. disable() {
  362. this._isEnabled = false;
  363. }
  364. toggleEnabled() {
  365. this._isEnabled = !this._isEnabled;
  366. }
  367. toggle(event) {
  368. if (!this._isEnabled) {
  369. return;
  370. }
  371. if (event) {
  372. const context = this._initializeOnDelegatedTarget(event);
  373. context._activeTrigger.click = !context._activeTrigger.click;
  374. if (context._isWithActiveTrigger()) {
  375. context._enter(null, context);
  376. } else {
  377. context._leave(null, context);
  378. }
  379. } else {
  380. if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {
  381. this._leave(null, this);
  382. return;
  383. }
  384. this._enter(null, this);
  385. }
  386. }
  387. dispose() {
  388. clearTimeout(this._timeout);
  389. EventHandler__default.default.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
  390. if (this.tip) {
  391. this.tip.remove();
  392. }
  393. this._disposePopper();
  394. super.dispose();
  395. }
  396. show() {
  397. if (this._element.style.display === 'none') {
  398. throw new Error('Please use show on visible elements');
  399. }
  400. if (!(this.isWithContent() && this._isEnabled)) {
  401. return;
  402. }
  403. const showEvent = EventHandler__default.default.trigger(this._element, this.constructor.Event.SHOW);
  404. const shadowRoot = findShadowRoot(this._element);
  405. const isInTheDom = shadowRoot === null ? this._element.ownerDocument.documentElement.contains(this._element) : shadowRoot.contains(this._element);
  406. if (showEvent.defaultPrevented || !isInTheDom) {
  407. return;
  408. } // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title`
  409. // This will be removed later in favor of a `setContent` method
  410. if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {
  411. this._disposePopper();
  412. this.tip.remove();
  413. this.tip = null;
  414. }
  415. const tip = this.getTipElement();
  416. const tipId = getUID(this.constructor.NAME);
  417. tip.setAttribute('id', tipId);
  418. this._element.setAttribute('aria-describedby', tipId);
  419. if (this._config.animation) {
  420. tip.classList.add(CLASS_NAME_FADE);
  421. }
  422. const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : this._config.placement;
  423. const attachment = this._getAttachment(placement);
  424. this._addAttachmentClass(attachment);
  425. const {
  426. container
  427. } = this._config;
  428. Data__default.default.set(tip, this.constructor.DATA_KEY, this);
  429. if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
  430. container.append(tip);
  431. EventHandler__default.default.trigger(this._element, this.constructor.Event.INSERTED);
  432. }
  433. if (this._popper) {
  434. this._popper.update();
  435. } else {
  436. this._popper = Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));
  437. }
  438. tip.classList.add(CLASS_NAME_SHOW);
  439. const customClass = this._resolvePossibleFunction(this._config.customClass);
  440. if (customClass) {
  441. tip.classList.add(...customClass.split(' '));
  442. } // If this is a touch-enabled device we add extra
  443. // empty mouseover listeners to the body's immediate children;
  444. // only needed because of broken event delegation on iOS
  445. // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
  446. if ('ontouchstart' in document.documentElement) {
  447. [].concat(...document.body.children).forEach(element => {
  448. EventHandler__default.default.on(element, 'mouseover', noop);
  449. });
  450. }
  451. const complete = () => {
  452. const prevHoverState = this._hoverState;
  453. this._hoverState = null;
  454. EventHandler__default.default.trigger(this._element, this.constructor.Event.SHOWN);
  455. if (prevHoverState === HOVER_STATE_OUT) {
  456. this._leave(null, this);
  457. }
  458. };
  459. const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
  460. this._queueCallback(complete, this.tip, isAnimated);
  461. }
  462. hide() {
  463. if (!this._popper) {
  464. return;
  465. }
  466. const tip = this.getTipElement();
  467. const complete = () => {
  468. if (this._isWithActiveTrigger()) {
  469. return;
  470. }
  471. if (this._hoverState !== HOVER_STATE_SHOW) {
  472. tip.remove();
  473. }
  474. this._cleanTipClass();
  475. this._element.removeAttribute('aria-describedby');
  476. EventHandler__default.default.trigger(this._element, this.constructor.Event.HIDDEN);
  477. this._disposePopper();
  478. };
  479. const hideEvent = EventHandler__default.default.trigger(this._element, this.constructor.Event.HIDE);
  480. if (hideEvent.defaultPrevented) {
  481. return;
  482. }
  483. tip.classList.remove(CLASS_NAME_SHOW); // If this is a touch-enabled device we remove the extra
  484. // empty mouseover listeners we added for iOS support
  485. if ('ontouchstart' in document.documentElement) {
  486. [].concat(...document.body.children).forEach(element => EventHandler__default.default.off(element, 'mouseover', noop));
  487. }
  488. this._activeTrigger[TRIGGER_CLICK] = false;
  489. this._activeTrigger[TRIGGER_FOCUS] = false;
  490. this._activeTrigger[TRIGGER_HOVER] = false;
  491. const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
  492. this._queueCallback(complete, this.tip, isAnimated);
  493. this._hoverState = '';
  494. }
  495. update() {
  496. if (this._popper !== null) {
  497. this._popper.update();
  498. }
  499. } // Protected
  500. isWithContent() {
  501. return Boolean(this.getTitle());
  502. }
  503. getTipElement() {
  504. if (this.tip) {
  505. return this.tip;
  506. }
  507. const element = document.createElement('div');
  508. element.innerHTML = this._config.template;
  509. const tip = element.children[0];
  510. this.setContent(tip);
  511. tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW);
  512. this.tip = tip;
  513. return this.tip;
  514. }
  515. setContent(tip) {
  516. this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER);
  517. }
  518. _sanitizeAndSetContent(template, content, selector) {
  519. const templateElement = SelectorEngine__default.default.findOne(selector, template);
  520. if (!content && templateElement) {
  521. templateElement.remove();
  522. return;
  523. } // we use append for html objects to maintain js events
  524. this.setElementContent(templateElement, content);
  525. }
  526. setElementContent(element, content) {
  527. if (element === null) {
  528. return;
  529. }
  530. if (isElement(content)) {
  531. content = getElement(content); // content is a DOM node or a jQuery
  532. if (this._config.html) {
  533. if (content.parentNode !== element) {
  534. element.innerHTML = '';
  535. element.append(content);
  536. }
  537. } else {
  538. element.textContent = content.textContent;
  539. }
  540. return;
  541. }
  542. if (this._config.html) {
  543. if (this._config.sanitize) {
  544. content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn);
  545. }
  546. element.innerHTML = content;
  547. } else {
  548. element.textContent = content;
  549. }
  550. }
  551. getTitle() {
  552. const title = this._element.getAttribute('data-bs-original-title') || this._config.title;
  553. return this._resolvePossibleFunction(title);
  554. }
  555. updateAttachment(attachment) {
  556. if (attachment === 'right') {
  557. return 'end';
  558. }
  559. if (attachment === 'left') {
  560. return 'start';
  561. }
  562. return attachment;
  563. } // Private
  564. _initializeOnDelegatedTarget(event, context) {
  565. return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());
  566. }
  567. _getOffset() {
  568. const {
  569. offset
  570. } = this._config;
  571. if (typeof offset === 'string') {
  572. return offset.split(',').map(val => Number.parseInt(val, 10));
  573. }
  574. if (typeof offset === 'function') {
  575. return popperData => offset(popperData, this._element);
  576. }
  577. return offset;
  578. }
  579. _resolvePossibleFunction(content) {
  580. return typeof content === 'function' ? content.call(this._element) : content;
  581. }
  582. _getPopperConfig(attachment) {
  583. const defaultBsPopperConfig = {
  584. placement: attachment,
  585. modifiers: [{
  586. name: 'flip',
  587. options: {
  588. fallbackPlacements: this._config.fallbackPlacements
  589. }
  590. }, {
  591. name: 'offset',
  592. options: {
  593. offset: this._getOffset()
  594. }
  595. }, {
  596. name: 'preventOverflow',
  597. options: {
  598. boundary: this._config.boundary
  599. }
  600. }, {
  601. name: 'arrow',
  602. options: {
  603. element: `.${this.constructor.NAME}-arrow`
  604. }
  605. }, {
  606. name: 'onChange',
  607. enabled: true,
  608. phase: 'afterWrite',
  609. fn: data => this._handlePopperPlacementChange(data)
  610. }],
  611. onFirstUpdate: data => {
  612. if (data.options.placement !== data.placement) {
  613. this._handlePopperPlacementChange(data);
  614. }
  615. }
  616. };
  617. return { ...defaultBsPopperConfig,
  618. ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
  619. };
  620. }
  621. _addAttachmentClass(attachment) {
  622. this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`);
  623. }
  624. _getAttachment(placement) {
  625. return AttachmentMap[placement.toUpperCase()];
  626. }
  627. _setListeners() {
  628. const triggers = this._config.trigger.split(' ');
  629. triggers.forEach(trigger => {
  630. if (trigger === 'click') {
  631. EventHandler__default.default.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event));
  632. } else if (trigger !== TRIGGER_MANUAL) {
  633. const eventIn = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSEENTER : this.constructor.Event.FOCUSIN;
  634. const eventOut = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSELEAVE : this.constructor.Event.FOCUSOUT;
  635. EventHandler__default.default.on(this._element, eventIn, this._config.selector, event => this._enter(event));
  636. EventHandler__default.default.on(this._element, eventOut, this._config.selector, event => this._leave(event));
  637. }
  638. });
  639. this._hideModalHandler = () => {
  640. if (this._element) {
  641. this.hide();
  642. }
  643. };
  644. EventHandler__default.default.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
  645. if (this._config.selector) {
  646. this._config = { ...this._config,
  647. trigger: 'manual',
  648. selector: ''
  649. };
  650. } else {
  651. this._fixTitle();
  652. }
  653. }
  654. _fixTitle() {
  655. const title = this._element.getAttribute('title');
  656. const originalTitleType = typeof this._element.getAttribute('data-bs-original-title');
  657. if (title || originalTitleType !== 'string') {
  658. this._element.setAttribute('data-bs-original-title', title || '');
  659. if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
  660. this._element.setAttribute('aria-label', title);
  661. }
  662. this._element.setAttribute('title', '');
  663. }
  664. }
  665. _enter(event, context) {
  666. context = this._initializeOnDelegatedTarget(event, context);
  667. if (event) {
  668. context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
  669. }
  670. if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
  671. context._hoverState = HOVER_STATE_SHOW;
  672. return;
  673. }
  674. clearTimeout(context._timeout);
  675. context._hoverState = HOVER_STATE_SHOW;
  676. if (!context._config.delay || !context._config.delay.show) {
  677. context.show();
  678. return;
  679. }
  680. context._timeout = setTimeout(() => {
  681. if (context._hoverState === HOVER_STATE_SHOW) {
  682. context.show();
  683. }
  684. }, context._config.delay.show);
  685. }
  686. _leave(event, context) {
  687. context = this._initializeOnDelegatedTarget(event, context);
  688. if (event) {
  689. context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);
  690. }
  691. if (context._isWithActiveTrigger()) {
  692. return;
  693. }
  694. clearTimeout(context._timeout);
  695. context._hoverState = HOVER_STATE_OUT;
  696. if (!context._config.delay || !context._config.delay.hide) {
  697. context.hide();
  698. return;
  699. }
  700. context._timeout = setTimeout(() => {
  701. if (context._hoverState === HOVER_STATE_OUT) {
  702. context.hide();
  703. }
  704. }, context._config.delay.hide);
  705. }
  706. _isWithActiveTrigger() {
  707. for (const trigger in this._activeTrigger) {
  708. if (this._activeTrigger[trigger]) {
  709. return true;
  710. }
  711. }
  712. return false;
  713. }
  714. _getConfig(config) {
  715. const dataAttributes = Manipulator__default.default.getDataAttributes(this._element);
  716. Object.keys(dataAttributes).forEach(dataAttr => {
  717. if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {
  718. delete dataAttributes[dataAttr];
  719. }
  720. });
  721. config = { ...this.constructor.Default,
  722. ...dataAttributes,
  723. ...(typeof config === 'object' && config ? config : {})
  724. };
  725. config.container = config.container === false ? document.body : getElement(config.container);
  726. if (typeof config.delay === 'number') {
  727. config.delay = {
  728. show: config.delay,
  729. hide: config.delay
  730. };
  731. }
  732. if (typeof config.title === 'number') {
  733. config.title = config.title.toString();
  734. }
  735. if (typeof config.content === 'number') {
  736. config.content = config.content.toString();
  737. }
  738. typeCheckConfig(NAME, config, this.constructor.DefaultType);
  739. if (config.sanitize) {
  740. config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn);
  741. }
  742. return config;
  743. }
  744. _getDelegateConfig() {
  745. const config = {};
  746. for (const key in this._config) {
  747. if (this.constructor.Default[key] !== this._config[key]) {
  748. config[key] = this._config[key];
  749. }
  750. } // In the future can be replaced with:
  751. // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])
  752. // `Object.fromEntries(keysWithDifferentValues)`
  753. return config;
  754. }
  755. _cleanTipClass() {
  756. const tip = this.getTipElement();
  757. const basicClassPrefixRegex = new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`, 'g');
  758. const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex);
  759. if (tabClass !== null && tabClass.length > 0) {
  760. tabClass.map(token => token.trim()).forEach(tClass => tip.classList.remove(tClass));
  761. }
  762. }
  763. _getBasicClassPrefix() {
  764. return CLASS_PREFIX;
  765. }
  766. _handlePopperPlacementChange(popperData) {
  767. const {
  768. state
  769. } = popperData;
  770. if (!state) {
  771. return;
  772. }
  773. this.tip = state.elements.popper;
  774. this._cleanTipClass();
  775. this._addAttachmentClass(this._getAttachment(state.placement));
  776. }
  777. _disposePopper() {
  778. if (this._popper) {
  779. this._popper.destroy();
  780. this._popper = null;
  781. }
  782. } // Static
  783. static jQueryInterface(config) {
  784. return this.each(function () {
  785. const data = Tooltip.getOrCreateInstance(this, config);
  786. if (typeof config === 'string') {
  787. if (typeof data[config] === 'undefined') {
  788. throw new TypeError(`No method named "${config}"`);
  789. }
  790. data[config]();
  791. }
  792. });
  793. }
  794. }
  795. /**
  796. * ------------------------------------------------------------------------
  797. * jQuery
  798. * ------------------------------------------------------------------------
  799. * add .Tooltip to jQuery only if jQuery is present
  800. */
  801. defineJQueryPlugin(Tooltip);
  802. return Tooltip;
  803. }));
  804. //# sourceMappingURL=tooltip.js.map