tooltip.js 28 KB

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