aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/twbs/bootstrap/js/dist/tooltip.js
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/twbs/bootstrap/js/dist/tooltip.js')
-rw-r--r--vendor/twbs/bootstrap/js/dist/tooltip.js879
1 files changed, 268 insertions, 611 deletions
diff --git a/vendor/twbs/bootstrap/js/dist/tooltip.js b/vendor/twbs/bootstrap/js/dist/tooltip.js
index 883c8dbb0..56744f188 100644
--- a/vendor/twbs/bootstrap/js/dist/tooltip.js
+++ b/vendor/twbs/bootstrap/js/dist/tooltip.js
@@ -1,19 +1,19 @@
/*!
- * Bootstrap tooltip.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap tooltip.js v5.2.0 (https://getbootstrap.com/)
+ * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- 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')) :
- typeof define === 'function' && define.amd ? define(['@popperjs/core', './dom/data', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global.Popper, global.Data, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
-})(this, (function (Popper, Data, EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./util/index'), require('./util/sanitizer'), require('./dom/event-handler'), require('./dom/manipulator'), require('./base-component'), require('./util/template-factory')) :
+ typeof define === 'function' && define.amd ? define(['@popperjs/core', './util/index', './util/sanitizer', './dom/event-handler', './dom/manipulator', './base-component', './util/template-factory'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global["@popperjs/core"], global.Index, global.Sanitizer, global.EventHandler, global.Manipulator, global.BaseComponent, global.TemplateFactory));
+})(this, (function (Popper, index, sanitizer, EventHandler, Manipulator, BaseComponent, TemplateFactory) { 'use strict';
const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
function _interopNamespace(e) {
if (e && e.__esModule) return e;
- const n = Object.create(null);
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
if (e) {
for (const k in e) {
if (k !== 'default') {
@@ -30,356 +30,90 @@
}
const Popper__namespace = /*#__PURE__*/_interopNamespace(Popper);
- const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
+ const TemplateFactory__default = /*#__PURE__*/_interopDefaultLegacy(TemplateFactory);
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap (v5.2.0): tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const MAX_UID = 1000000;
-
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
- /**
- * --------------------------------------------------------------------------
- * Public Util Api
- * --------------------------------------------------------------------------
- */
-
-
- const getUID = prefix => {
- do {
- prefix += Math.floor(Math.random() * MAX_UID);
- } while (document.getElementById(prefix));
-
- return prefix;
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const getElement = obj => {
- if (isElement(obj)) {
- // it's a jQuery object or a node element
- return obj.jquery ? obj[0] : obj;
- }
-
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj);
- }
-
- return null;
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
-
- const findShadowRoot = element => {
- if (!document.documentElement.attachShadow) {
- return null;
- } // Can find the shadow root otherwise it'll return the document
-
-
- if (typeof element.getRootNode === 'function') {
- const root = element.getRootNode();
- return root instanceof ShadowRoot ? root : null;
- }
-
- if (element instanceof ShadowRoot) {
- return element;
- } // when we don't find a shadow root
-
-
- if (!element.parentNode) {
- return null;
- }
-
- return findShadowRoot(element.parentNode);
- };
-
- const noop = () => {};
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const isRTL = () => document.documentElement.dir === 'rtl';
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
-
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/sanitizer.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);
- const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
- /**
- * A pattern that recognizes a commonly useful subset of URLs that are safe.
- *
- * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
- */
-
- const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i;
- /**
- * A pattern that matches safe data URLs. Only matches image, video and audio types.
- *
- * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
- */
-
- 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;
-
- const allowedAttribute = (attribute, allowedAttributeList) => {
- const attributeName = attribute.nodeName.toLowerCase();
-
- if (allowedAttributeList.includes(attributeName)) {
- if (uriAttributes.has(attributeName)) {
- return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue));
- }
-
- return true;
- }
-
- const regExp = allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp); // Check if a regular expression validates the attribute.
-
- for (let i = 0, len = regExp.length; i < len; i++) {
- if (regExp[i].test(attributeName)) {
- return true;
- }
- }
-
- return false;
- };
-
- const DefaultAllowlist = {
- // Global attributes allowed on any supplied element below.
- '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
- a: ['target', 'href', 'title', 'rel'],
- area: [],
- b: [],
- br: [],
- col: [],
- code: [],
- div: [],
- em: [],
- hr: [],
- h1: [],
- h2: [],
- h3: [],
- h4: [],
- h5: [],
- h6: [],
- i: [],
- img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
- li: [],
- ol: [],
- p: [],
- pre: [],
- s: [],
- small: [],
- span: [],
- sub: [],
- sup: [],
- strong: [],
- u: [],
- ul: []
- };
- function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
- if (!unsafeHtml.length) {
- return unsafeHtml;
- }
-
- if (sanitizeFn && typeof sanitizeFn === 'function') {
- return sanitizeFn(unsafeHtml);
- }
-
- const domParser = new window.DOMParser();
- const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
- const elements = [].concat(...createdDocument.body.querySelectorAll('*'));
-
- for (let i = 0, len = elements.length; i < len; i++) {
- const element = elements[i];
- const elementName = element.nodeName.toLowerCase();
-
- if (!Object.keys(allowList).includes(elementName)) {
- element.remove();
- continue;
- }
-
- const attributeList = [].concat(...element.attributes);
- const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);
- attributeList.forEach(attribute => {
- if (!allowedAttribute(attribute, allowedAttributes)) {
- element.removeAttribute(attribute.nodeName);
- }
- });
- }
-
- return createdDocument.body.innerHTML;
- }
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): tooltip.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'tooltip';
- const DATA_KEY = 'bs.tooltip';
- const EVENT_KEY = `.${DATA_KEY}`;
- const CLASS_PREFIX = 'bs-tooltip';
const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);
- const DefaultType = {
- animation: 'boolean',
- template: 'string',
- title: '(string|element|function)',
- trigger: 'string',
- delay: '(number|object)',
- html: 'boolean',
- selector: '(string|boolean)',
- placement: '(string|function)',
- offset: '(array|string|function)',
- container: '(string|element|boolean)',
- fallbackPlacements: 'array',
- boundary: '(string|element)',
- customClass: '(string|function)',
- sanitize: 'boolean',
- sanitizeFn: '(null|function)',
- allowList: 'object',
- popperConfig: '(null|object|function)'
- };
+ const CLASS_NAME_FADE = 'fade';
+ const CLASS_NAME_MODAL = 'modal';
+ const CLASS_NAME_SHOW = 'show';
+ const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
+ const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;
+ const EVENT_MODAL_HIDE = 'hide.bs.modal';
+ const TRIGGER_HOVER = 'hover';
+ const TRIGGER_FOCUS = 'focus';
+ const TRIGGER_CLICK = 'click';
+ const TRIGGER_MANUAL = 'manual';
+ const EVENT_HIDE = 'hide';
+ const EVENT_HIDDEN = 'hidden';
+ const EVENT_SHOW = 'show';
+ const EVENT_SHOWN = 'shown';
+ const EVENT_INSERTED = 'inserted';
+ const EVENT_CLICK = 'click';
+ const EVENT_FOCUSIN = 'focusin';
+ const EVENT_FOCUSOUT = 'focusout';
+ const EVENT_MOUSEENTER = 'mouseenter';
+ const EVENT_MOUSELEAVE = 'mouseleave';
const AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
- RIGHT: isRTL() ? 'left' : 'right',
+ RIGHT: index.isRTL() ? 'left' : 'right',
BOTTOM: 'bottom',
- LEFT: isRTL() ? 'right' : 'left'
+ LEFT: index.isRTL() ? 'right' : 'left'
};
const Default = {
+ allowList: sanitizer.DefaultAllowlist,
animation: true,
- template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>',
- trigger: 'hover focus',
- title: '',
+ boundary: 'clippingParents',
+ container: false,
+ customClass: '',
delay: 0,
+ fallbackPlacements: ['top', 'right', 'bottom', 'left'],
html: false,
- selector: false,
- placement: 'top',
offset: [0, 0],
- container: false,
- fallbackPlacements: ['top', 'right', 'bottom', 'left'],
- boundary: 'clippingParents',
- customClass: '',
+ placement: 'top',
+ popperConfig: null,
sanitize: true,
sanitizeFn: null,
- allowList: DefaultAllowlist,
- popperConfig: null
+ selector: false,
+ template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>',
+ title: '',
+ trigger: 'hover focus'
};
- const Event = {
- HIDE: `hide${EVENT_KEY}`,
- HIDDEN: `hidden${EVENT_KEY}`,
- SHOW: `show${EVENT_KEY}`,
- SHOWN: `shown${EVENT_KEY}`,
- INSERTED: `inserted${EVENT_KEY}`,
- CLICK: `click${EVENT_KEY}`,
- FOCUSIN: `focusin${EVENT_KEY}`,
- FOCUSOUT: `focusout${EVENT_KEY}`,
- MOUSEENTER: `mouseenter${EVENT_KEY}`,
- MOUSELEAVE: `mouseleave${EVENT_KEY}`
+ const DefaultType = {
+ allowList: 'object',
+ animation: 'boolean',
+ boundary: '(string|element)',
+ container: '(string|element|boolean)',
+ customClass: '(string|function)',
+ delay: '(number|object)',
+ fallbackPlacements: 'array',
+ html: 'boolean',
+ offset: '(array|string|function)',
+ placement: '(string|function)',
+ popperConfig: '(null|object|function)',
+ sanitize: 'boolean',
+ sanitizeFn: '(null|function)',
+ selector: '(string|boolean)',
+ template: 'string',
+ title: '(string|element|function)',
+ trigger: 'string'
};
- const CLASS_NAME_FADE = 'fade';
- const CLASS_NAME_MODAL = 'modal';
- const CLASS_NAME_SHOW = 'show';
- const HOVER_STATE_SHOW = 'show';
- const HOVER_STATE_OUT = 'out';
- const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
- const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;
- const EVENT_MODAL_HIDE = 'hide.bs.modal';
- const TRIGGER_HOVER = 'hover';
- const TRIGGER_FOCUS = 'focus';
- const TRIGGER_CLICK = 'click';
- const TRIGGER_MANUAL = 'manual';
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Tooltip extends BaseComponent__default.default {
@@ -388,15 +122,16 @@
throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)');
}
- super(element); // private
+ super(element, config); // Private
this._isEnabled = true;
this._timeout = 0;
- this._hoverState = '';
+ this._isHovered = false;
this._activeTrigger = {};
- this._popper = null; // Protected
+ this._popper = null;
+ this._templateFactory = null;
+ this._newContent = null; // Protected
- this._config = this._getConfig(config);
this.tip = null;
this._setListeners();
@@ -407,16 +142,12 @@
return Default;
}
- static get NAME() {
- return NAME;
- }
-
- static get Event() {
- return Event;
- }
-
static get DefaultType() {
return DefaultType;
+ }
+
+ static get NAME() {
+ return NAME;
} // Public
@@ -443,19 +174,21 @@
context._activeTrigger.click = !context._activeTrigger.click;
if (context._isWithActiveTrigger()) {
- context._enter(null, context);
+ context._enter();
} else {
- context._leave(null, context);
+ context._leave();
}
- } else {
- if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {
- this._leave(null, this);
- return;
- }
+ return;
+ }
+
+ if (this._isShown()) {
+ this._leave();
- this._enter(null, this);
+ return;
}
+
+ this._enter();
}
dispose() {
@@ -476,233 +209,206 @@
throw new Error('Please use show on visible elements');
}
- if (!(this.isWithContent() && this._isEnabled)) {
+ if (!(this._isWithContent() && this._isEnabled)) {
return;
}
- const showEvent = EventHandler__default.default.trigger(this._element, this.constructor.Event.SHOW);
- const shadowRoot = findShadowRoot(this._element);
- const isInTheDom = shadowRoot === null ? this._element.ownerDocument.documentElement.contains(this._element) : shadowRoot.contains(this._element);
+ const showEvent = EventHandler__default.default.trigger(this._element, this.constructor.eventName(EVENT_SHOW));
+ const shadowRoot = index.findShadowRoot(this._element);
+
+ const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);
if (showEvent.defaultPrevented || !isInTheDom) {
return;
- } // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title`
- // This will be removed later in favor of a `setContent` method
+ } // todo v6 remove this OR make it optional
- if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {
- this._disposePopper();
-
+ if (this.tip) {
this.tip.remove();
this.tip = null;
}
- const tip = this.getTipElement();
- const tipId = getUID(this.constructor.NAME);
- tip.setAttribute('id', tipId);
-
- this._element.setAttribute('aria-describedby', tipId);
-
- if (this._config.animation) {
- tip.classList.add(CLASS_NAME_FADE);
- }
+ const tip = this._getTipElement();
- const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : this._config.placement;
-
- const attachment = this._getAttachment(placement);
-
- this._addAttachmentClass(attachment);
+ this._element.setAttribute('aria-describedby', tip.getAttribute('id'));
const {
container
} = this._config;
- Data__default.default.set(tip, this.constructor.DATA_KEY, this);
if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
container.append(tip);
- EventHandler__default.default.trigger(this._element, this.constructor.Event.INSERTED);
+ EventHandler__default.default.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));
}
if (this._popper) {
this._popper.update();
} else {
- this._popper = Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));
+ this._popper = this._createPopper(tip);
}
- tip.classList.add(CLASS_NAME_SHOW);
-
- const customClass = this._resolvePossibleFunction(this._config.customClass);
-
- if (customClass) {
- tip.classList.add(...customClass.split(' '));
- } // If this is a touch-enabled device we add extra
+ tip.classList.add(CLASS_NAME_SHOW); // If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
-
if ('ontouchstart' in document.documentElement) {
- [].concat(...document.body.children).forEach(element => {
- EventHandler__default.default.on(element, 'mouseover', noop);
- });
+ for (const element of [].concat(...document.body.children)) {
+ EventHandler__default.default.on(element, 'mouseover', index.noop);
+ }
}
const complete = () => {
- const prevHoverState = this._hoverState;
- this._hoverState = null;
- EventHandler__default.default.trigger(this._element, this.constructor.Event.SHOWN);
+ const previousHoverState = this._isHovered;
+ this._isHovered = false;
+ EventHandler__default.default.trigger(this._element, this.constructor.eventName(EVENT_SHOWN));
- if (prevHoverState === HOVER_STATE_OUT) {
- this._leave(null, this);
+ if (previousHoverState) {
+ this._leave();
}
};
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
-
- this._queueCallback(complete, this.tip, isAnimated);
+ this._queueCallback(complete, this.tip, this._isAnimated());
}
hide() {
- if (!this._popper) {
+ if (!this._isShown()) {
return;
}
- const tip = this.getTipElement();
-
- const complete = () => {
- if (this._isWithActiveTrigger()) {
- return;
- }
-
- if (this._hoverState !== HOVER_STATE_SHOW) {
- tip.remove();
- }
-
- this._cleanTipClass();
-
- this._element.removeAttribute('aria-describedby');
-
- EventHandler__default.default.trigger(this._element, this.constructor.Event.HIDDEN);
-
- this._disposePopper();
- };
-
- const hideEvent = EventHandler__default.default.trigger(this._element, this.constructor.Event.HIDE);
+ const hideEvent = EventHandler__default.default.trigger(this._element, this.constructor.eventName(EVENT_HIDE));
if (hideEvent.defaultPrevented) {
return;
}
+ const tip = this._getTipElement();
+
tip.classList.remove(CLASS_NAME_SHOW); // If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
- [].concat(...document.body.children).forEach(element => EventHandler__default.default.off(element, 'mouseover', noop));
+ for (const element of [].concat(...document.body.children)) {
+ EventHandler__default.default.off(element, 'mouseover', index.noop);
+ }
}
this._activeTrigger[TRIGGER_CLICK] = false;
this._activeTrigger[TRIGGER_FOCUS] = false;
this._activeTrigger[TRIGGER_HOVER] = false;
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
+ this._isHovered = false;
- this._queueCallback(complete, this.tip, isAnimated);
+ const complete = () => {
+ if (this._isWithActiveTrigger()) {
+ return;
+ }
- this._hoverState = '';
+ if (!this._isHovered) {
+ tip.remove();
+ }
+
+ this._element.removeAttribute('aria-describedby');
+
+ EventHandler__default.default.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN));
+
+ this._disposePopper();
+ };
+
+ this._queueCallback(complete, this.tip, this._isAnimated());
}
update() {
- if (this._popper !== null) {
+ if (this._popper) {
this._popper.update();
}
} // Protected
- isWithContent() {
- return Boolean(this.getTitle());
+ _isWithContent() {
+ return Boolean(this._getTitle());
}
- getTipElement() {
- if (this.tip) {
- return this.tip;
+ _getTipElement() {
+ if (!this.tip) {
+ this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());
}
- const element = document.createElement('div');
- element.innerHTML = this._config.template;
- const tip = element.children[0];
- this.setContent(tip);
- tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW);
- this.tip = tip;
return this.tip;
}
- setContent(tip) {
- this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER);
- }
+ _createTipElement(content) {
+ const tip = this._getTemplateFactory(content).toHtml(); // todo: remove this check on v6
- _sanitizeAndSetContent(template, content, selector) {
- const templateElement = SelectorEngine__default.default.findOne(selector, template);
- if (!content && templateElement) {
- templateElement.remove();
- return;
- } // we use append for html objects to maintain js events
+ if (!tip) {
+ return null;
+ }
+ tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW); // todo: on v6 the following can be achieved with CSS only
- this.setElementContent(templateElement, content);
- }
+ tip.classList.add(`bs-${this.constructor.NAME}-auto`);
+ const tipId = index.getUID(this.constructor.NAME).toString();
+ tip.setAttribute('id', tipId);
- setElementContent(element, content) {
- if (element === null) {
- return;
+ if (this._isAnimated()) {
+ tip.classList.add(CLASS_NAME_FADE);
}
- if (isElement(content)) {
- content = getElement(content); // content is a DOM node or a jQuery
+ return tip;
+ }
- if (this._config.html) {
- if (content.parentNode !== element) {
- element.innerHTML = '';
- element.append(content);
- }
- } else {
- element.textContent = content.textContent;
- }
+ setContent(content) {
+ this._newContent = content;
- return;
- }
+ if (this._isShown()) {
+ this._disposePopper();
- if (this._config.html) {
- if (this._config.sanitize) {
- content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn);
- }
+ this.show();
+ }
+ }
- element.innerHTML = content;
+ _getTemplateFactory(content) {
+ if (this._templateFactory) {
+ this._templateFactory.changeContent(content);
} else {
- element.textContent = content;
+ this._templateFactory = new TemplateFactory__default.default({ ...this._config,
+ // the `content` var has to be after `this._config`
+ // to override config.content in case of popover
+ content,
+ extraClass: this._resolvePossibleFunction(this._config.customClass)
+ });
}
- }
- getTitle() {
- const title = this._element.getAttribute('data-bs-original-title') || this._config.title;
+ return this._templateFactory;
+ }
- return this._resolvePossibleFunction(title);
+ _getContentForTemplate() {
+ return {
+ [SELECTOR_TOOLTIP_INNER]: this._getTitle()
+ };
}
- updateAttachment(attachment) {
- if (attachment === 'right') {
- return 'end';
- }
+ _getTitle() {
+ return this._resolvePossibleFunction(this._config.title) || this._config.originalTitle;
+ } // Private
- if (attachment === 'left') {
- return 'start';
- }
- return attachment;
- } // Private
+ _initializeOnDelegatedTarget(event) {
+ return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());
+ }
+
+ _isAnimated() {
+ return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE);
+ }
+ _isShown() {
+ return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW);
+ }
- _initializeOnDelegatedTarget(event, context) {
- return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());
+ _createPopper(tip) {
+ const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : this._config.placement;
+ const attachment = AttachmentMap[placement.toUpperCase()];
+ return Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));
}
_getOffset() {
@@ -711,7 +417,7 @@
} = this._config;
if (typeof offset === 'string') {
- return offset.split(',').map(val => Number.parseInt(val, 10));
+ return offset.split(',').map(value => Number.parseInt(value, 10));
}
if (typeof offset === 'function') {
@@ -721,8 +427,8 @@
return offset;
}
- _resolvePossibleFunction(content) {
- return typeof content === 'function' ? content.call(this._element) : content;
+ _resolvePossibleFunction(arg) {
+ return typeof arg === 'function' ? arg.call(this._element) : arg;
}
_getPopperConfig(attachment) {
@@ -749,43 +455,46 @@
element: `.${this.constructor.NAME}-arrow`
}
}, {
- name: 'onChange',
+ name: 'preSetPlacement',
enabled: true,
- phase: 'afterWrite',
- fn: data => this._handlePopperPlacementChange(data)
- }],
- onFirstUpdate: data => {
- if (data.options.placement !== data.placement) {
- this._handlePopperPlacementChange(data);
+ phase: 'beforeMain',
+ fn: data => {
+ // Pre-set Popper's placement attribute in order to read the arrow sizes properly.
+ // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
+ this._getTipElement().setAttribute('data-popper-placement', data.state.placement);
}
- }
+ }]
};
return { ...defaultBsPopperConfig,
...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
};
}
- _addAttachmentClass(attachment) {
- this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`);
- }
-
- _getAttachment(placement) {
- return AttachmentMap[placement.toUpperCase()];
- }
-
_setListeners() {
const triggers = this._config.trigger.split(' ');
- triggers.forEach(trigger => {
+ for (const trigger of triggers) {
if (trigger === 'click') {
- EventHandler__default.default.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event));
+ EventHandler__default.default.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => this.toggle(event));
} else if (trigger !== TRIGGER_MANUAL) {
- const eventIn = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSEENTER : this.constructor.Event.FOCUSIN;
- const eventOut = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSELEAVE : this.constructor.Event.FOCUSOUT;
- EventHandler__default.default.on(this._element, eventIn, this._config.selector, event => this._enter(event));
- EventHandler__default.default.on(this._element, eventOut, this._config.selector, event => this._leave(event));
+ const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN);
+ const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT);
+ EventHandler__default.default.on(this._element, eventIn, this._config.selector, event => {
+ const context = this._initializeOnDelegatedTarget(event);
+
+ context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
+
+ context._enter();
+ });
+ EventHandler__default.default.on(this._element, eventOut, this._config.selector, event => {
+ const context = this._initializeOnDelegatedTarget(event);
+
+ context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);
+
+ context._leave();
+ });
}
- });
+ }
this._hideModalHandler = () => {
if (this._element) {
@@ -806,96 +515,79 @@
}
_fixTitle() {
- const title = this._element.getAttribute('title');
-
- const originalTitleType = typeof this._element.getAttribute('data-bs-original-title');
-
- if (title || originalTitleType !== 'string') {
- this._element.setAttribute('data-bs-original-title', title || '');
-
- if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
- this._element.setAttribute('aria-label', title);
- }
-
- this._element.setAttribute('title', '');
- }
- }
+ const title = this._config.originalTitle;
- _enter(event, context) {
- context = this._initializeOnDelegatedTarget(event, context);
-
- if (event) {
- context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
+ if (!title) {
+ return;
}
- if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
- context._hoverState = HOVER_STATE_SHOW;
- return;
+ if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {
+ this._element.setAttribute('aria-label', title);
}
- clearTimeout(context._timeout);
- context._hoverState = HOVER_STATE_SHOW;
+ this._element.removeAttribute('title');
+ }
- if (!context._config.delay || !context._config.delay.show) {
- context.show();
+ _enter() {
+ if (this._isShown() || this._isHovered) {
+ this._isHovered = true;
return;
}
- context._timeout = setTimeout(() => {
- if (context._hoverState === HOVER_STATE_SHOW) {
- context.show();
+ this._isHovered = true;
+
+ this._setTimeout(() => {
+ if (this._isHovered) {
+ this.show();
}
- }, context._config.delay.show);
+ }, this._config.delay.show);
}
- _leave(event, context) {
- context = this._initializeOnDelegatedTarget(event, context);
-
- if (event) {
- context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);
- }
-
- if (context._isWithActiveTrigger()) {
+ _leave() {
+ if (this._isWithActiveTrigger()) {
return;
}
- clearTimeout(context._timeout);
- context._hoverState = HOVER_STATE_OUT;
+ this._isHovered = false;
- if (!context._config.delay || !context._config.delay.hide) {
- context.hide();
- return;
- }
-
- context._timeout = setTimeout(() => {
- if (context._hoverState === HOVER_STATE_OUT) {
- context.hide();
+ this._setTimeout(() => {
+ if (!this._isHovered) {
+ this.hide();
}
- }, context._config.delay.hide);
+ }, this._config.delay.hide);
}
- _isWithActiveTrigger() {
- for (const trigger in this._activeTrigger) {
- if (this._activeTrigger[trigger]) {
- return true;
- }
- }
+ _setTimeout(handler, timeout) {
+ clearTimeout(this._timeout);
+ this._timeout = setTimeout(handler, timeout);
+ }
- return false;
+ _isWithActiveTrigger() {
+ return Object.values(this._activeTrigger).includes(true);
}
_getConfig(config) {
const dataAttributes = Manipulator__default.default.getDataAttributes(this._element);
- Object.keys(dataAttributes).forEach(dataAttr => {
- if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {
- delete dataAttributes[dataAttr];
+
+ for (const dataAttribute of Object.keys(dataAttributes)) {
+ if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {
+ delete dataAttributes[dataAttribute];
}
- });
- config = { ...this.constructor.Default,
- ...dataAttributes,
+ }
+
+ config = { ...dataAttributes,
...(typeof config === 'object' && config ? config : {})
};
- config.container = config.container === false ? document.body : getElement(config.container);
+ config = this._mergeConfigObj(config);
+ config = this._configAfterMerge(config);
+
+ this._typeCheckConfig(config);
+
+ return config;
+ }
+
+ _configAfterMerge(config) {
+ config.container = config.container === false ? document.body : index.getElement(config.container);
if (typeof config.delay === 'number') {
config.delay = {
@@ -904,6 +596,8 @@
};
}
+ config.originalTitle = this._element.getAttribute('title') || '';
+
if (typeof config.title === 'number') {
config.title = config.title.toString();
}
@@ -912,12 +606,6 @@
config.content = config.content.toString();
}
- typeCheckConfig(NAME, config, this.constructor.DefaultType);
-
- if (config.sanitize) {
- config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn);
- }
-
return config;
}
@@ -936,36 +624,6 @@
return config;
}
- _cleanTipClass() {
- const tip = this.getTipElement();
- const basicClassPrefixRegex = new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`, 'g');
- const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex);
-
- if (tabClass !== null && tabClass.length > 0) {
- tabClass.map(token => token.trim()).forEach(tClass => tip.classList.remove(tClass));
- }
- }
-
- _getBasicClassPrefix() {
- return CLASS_PREFIX;
- }
-
- _handlePopperPlacementChange(popperData) {
- const {
- state
- } = popperData;
-
- if (!state) {
- return;
- }
-
- this.tip = state.elements.popper;
-
- this._cleanTipClass();
-
- this._addAttachmentClass(this._getAttachment(state.placement));
- }
-
_disposePopper() {
if (this._popper) {
this._popper.destroy();
@@ -979,26 +637,25 @@
return this.each(function () {
const data = Tooltip.getOrCreateInstance(this, config);
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError(`No method named "${config}"`);
- }
+ if (typeof config !== 'string') {
+ return;
+ }
- data[config]();
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError(`No method named "${config}"`);
}
+
+ data[config]();
});
}
}
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Tooltip to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Tooltip);
+ index.defineJQueryPlugin(Tooltip);
return Tooltip;