diff options
Diffstat (limited to 'vendor/twbs/bootstrap/js/src/tooltip.js')
-rw-r--r-- | vendor/twbs/bootstrap/js/src/tooltip.js | 1168 |
1 files changed, 614 insertions, 554 deletions
diff --git a/vendor/twbs/bootstrap/js/src/tooltip.js b/vendor/twbs/bootstrap/js/src/tooltip.js index 72d9ba177..81a2dedc6 100644 --- a/vendor/twbs/bootstrap/js/src/tooltip.js +++ b/vendor/twbs/bootstrap/js/src/tooltip.js @@ -1,725 +1,785 @@ -import $ from 'jquery' -import Popper from 'popper.js' -import Util from './util' - /** * -------------------------------------------------------------------------- - * Bootstrap (v4.1.3): tooltip.js + * Bootstrap (v4.3.1): tooltip.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ -const Tooltip = (($) => { - /** - * ------------------------------------------------------------------------ - * Constants - * ------------------------------------------------------------------------ - */ - - const NAME = 'tooltip' - const VERSION = '4.1.3' - const DATA_KEY = 'bs.tooltip' - const EVENT_KEY = `.${DATA_KEY}` - const JQUERY_NO_CONFLICT = $.fn[NAME] - const CLASS_PREFIX = 'bs-tooltip' - const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g') - - const DefaultType = { - animation : 'boolean', - template : 'string', - title : '(string|element|function)', - trigger : 'string', - delay : '(number|object)', - html : 'boolean', - selector : '(string|boolean)', - placement : '(string|function)', - offset : '(number|string)', - container : '(string|element|boolean)', - fallbackPlacement : '(string|array)', - boundary : '(string|element)' - } - - const AttachmentMap = { - AUTO : 'auto', - TOP : 'top', - RIGHT : 'right', - BOTTOM : 'bottom', - LEFT : 'left' - } - - const Default = { - animation : true, - template : '<div class="tooltip" role="tooltip">' + - '<div class="arrow"></div>' + - '<div class="tooltip-inner"></div></div>', - trigger : 'hover focus', - title : '', - delay : 0, - html : false, - selector : false, - placement : 'top', - offset : 0, - container : false, - fallbackPlacement : 'flip', - boundary : 'scrollParent' - } - - const HoverState = { - SHOW : 'show', - OUT : 'out' - } - - 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 ClassName = { - FADE : 'fade', - SHOW : 'show' - } - - const Selector = { - TOOLTIP : '.tooltip', - TOOLTIP_INNER : '.tooltip-inner', - ARROW : '.arrow' - } - - const Trigger = { - HOVER : 'hover', - FOCUS : 'focus', - CLICK : 'click', - MANUAL : 'manual' - } - - - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ - */ - - class Tooltip { - constructor(element, config) { - /** - * Check for Popper dependency - * Popper - https://popper.js.org - */ - if (typeof Popper === 'undefined') { - throw new TypeError('Bootstrap tooltips require Popper.js (https://popper.js.org)') - } +import { + DefaultWhitelist, + sanitizeHtml +} from './tools/sanitizer' +import $ from 'jquery' +import Popper from 'popper.js' +import Util from './util' - // private - this._isEnabled = true - this._timeout = 0 - this._hoverState = '' - this._activeTrigger = {} - this._popper = null +/** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ - // Protected - this.element = element - this.config = this._getConfig(config) - this.tip = null +const NAME = 'tooltip' +const VERSION = '4.3.1' +const DATA_KEY = 'bs.tooltip' +const EVENT_KEY = `.${DATA_KEY}` +const JQUERY_NO_CONFLICT = $.fn[NAME] +const CLASS_PREFIX = 'bs-tooltip' +const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g') +const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', '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 : '(number|string|function)', + container : '(string|element|boolean)', + fallbackPlacement : '(string|array)', + boundary : '(string|element)', + sanitize : 'boolean', + sanitizeFn : '(null|function)', + whiteList : 'object' +} + +const AttachmentMap = { + AUTO : 'auto', + TOP : 'top', + RIGHT : 'right', + BOTTOM : 'bottom', + LEFT : 'left' +} + +const Default = { + animation : true, + template : '<div class="tooltip" role="tooltip">' + + '<div class="arrow"></div>' + + '<div class="tooltip-inner"></div></div>', + trigger : 'hover focus', + title : '', + delay : 0, + html : false, + selector : false, + placement : 'top', + offset : 0, + container : false, + fallbackPlacement : 'flip', + boundary : 'scrollParent', + sanitize : true, + sanitizeFn : null, + whiteList : DefaultWhitelist +} + +const HoverState = { + SHOW : 'show', + OUT : 'out' +} + +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 ClassName = { + FADE : 'fade', + SHOW : 'show' +} + +const Selector = { + TOOLTIP : '.tooltip', + TOOLTIP_INNER : '.tooltip-inner', + ARROW : '.arrow' +} + +const Trigger = { + HOVER : 'hover', + FOCUS : 'focus', + CLICK : 'click', + MANUAL : 'manual' +} - this._setListeners() - } - // Getters +/** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ - static get VERSION() { - return VERSION +class Tooltip { + constructor(element, config) { + /** + * Check for Popper dependency + * Popper - https://popper.js.org + */ + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s tooltips require Popper.js (https://popper.js.org/)') } - static get Default() { - return Default - } + // private + this._isEnabled = true + this._timeout = 0 + this._hoverState = '' + this._activeTrigger = {} + this._popper = null - static get NAME() { - return NAME - } + // Protected + this.element = element + this.config = this._getConfig(config) + this.tip = null - static get DATA_KEY() { - return DATA_KEY - } + this._setListeners() + } - static get Event() { - return Event - } + // Getters - static get EVENT_KEY() { - return EVENT_KEY - } + static get VERSION() { + return VERSION + } - static get DefaultType() { - return DefaultType - } + static get Default() { + return Default + } - // Public + static get NAME() { + return NAME + } - enable() { - this._isEnabled = true - } + static get DATA_KEY() { + return DATA_KEY + } - disable() { - this._isEnabled = false - } + static get Event() { + return Event + } - toggleEnabled() { - this._isEnabled = !this._isEnabled - } + static get EVENT_KEY() { + return EVENT_KEY + } - toggle(event) { - if (!this._isEnabled) { - return - } + static get DefaultType() { + return DefaultType + } - if (event) { - const dataKey = this.constructor.DATA_KEY - let context = $(event.currentTarget).data(dataKey) + // Public - if (!context) { - context = new this.constructor( - event.currentTarget, - this._getDelegateConfig() - ) - $(event.currentTarget).data(dataKey, context) - } + enable() { + this._isEnabled = true + } - context._activeTrigger.click = !context._activeTrigger.click + disable() { + this._isEnabled = false + } - if (context._isWithActiveTrigger()) { - context._enter(null, context) - } else { - context._leave(null, context) - } - } else { - if ($(this.getTipElement()).hasClass(ClassName.SHOW)) { - this._leave(null, this) - return - } + toggleEnabled() { + this._isEnabled = !this._isEnabled + } - this._enter(null, this) - } + toggle(event) { + if (!this._isEnabled) { + return } - dispose() { - clearTimeout(this._timeout) + if (event) { + const dataKey = this.constructor.DATA_KEY + let context = $(event.currentTarget).data(dataKey) - $.removeData(this.element, this.constructor.DATA_KEY) + if (!context) { + context = new this.constructor( + event.currentTarget, + this._getDelegateConfig() + ) + $(event.currentTarget).data(dataKey, context) + } - $(this.element).off(this.constructor.EVENT_KEY) - $(this.element).closest('.modal').off('hide.bs.modal') + context._activeTrigger.click = !context._activeTrigger.click - if (this.tip) { - $(this.tip).remove() + if (context._isWithActiveTrigger()) { + context._enter(null, context) + } else { + context._leave(null, context) } - - this._isEnabled = null - this._timeout = null - this._hoverState = null - this._activeTrigger = null - if (this._popper !== null) { - this._popper.destroy() + } else { + if ($(this.getTipElement()).hasClass(ClassName.SHOW)) { + this._leave(null, this) + return } - this._popper = null - this.element = null - this.config = null - this.tip = null + this._enter(null, this) } + } - show() { - if ($(this.element).css('display') === 'none') { - throw new Error('Please use show on visible elements') - } + dispose() { + clearTimeout(this._timeout) - const showEvent = $.Event(this.constructor.Event.SHOW) - if (this.isWithContent() && this._isEnabled) { - $(this.element).trigger(showEvent) + $.removeData(this.element, this.constructor.DATA_KEY) - const isInTheDom = $.contains( - this.element.ownerDocument.documentElement, - this.element - ) + $(this.element).off(this.constructor.EVENT_KEY) + $(this.element).closest('.modal').off('hide.bs.modal') - if (showEvent.isDefaultPrevented() || !isInTheDom) { - return - } + if (this.tip) { + $(this.tip).remove() + } - const tip = this.getTipElement() - const tipId = Util.getUID(this.constructor.NAME) + this._isEnabled = null + this._timeout = null + this._hoverState = null + this._activeTrigger = null + if (this._popper !== null) { + this._popper.destroy() + } - tip.setAttribute('id', tipId) - this.element.setAttribute('aria-describedby', tipId) + this._popper = null + this.element = null + this.config = null + this.tip = null + } - this.setContent() + show() { + if ($(this.element).css('display') === 'none') { + throw new Error('Please use show on visible elements') + } - if (this.config.animation) { - $(tip).addClass(ClassName.FADE) - } + const showEvent = $.Event(this.constructor.Event.SHOW) + if (this.isWithContent() && this._isEnabled) { + $(this.element).trigger(showEvent) - const placement = typeof this.config.placement === 'function' - ? this.config.placement.call(this, tip, this.element) - : this.config.placement + const shadowRoot = Util.findShadowRoot(this.element) + const isInTheDom = $.contains( + shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, + this.element + ) - const attachment = this._getAttachment(placement) - this.addAttachmentClass(attachment) + if (showEvent.isDefaultPrevented() || !isInTheDom) { + return + } - const container = this.config.container === false ? document.body : $(document).find(this.config.container) + const tip = this.getTipElement() + const tipId = Util.getUID(this.constructor.NAME) - $(tip).data(this.constructor.DATA_KEY, this) + tip.setAttribute('id', tipId) + this.element.setAttribute('aria-describedby', tipId) - if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) { - $(tip).appendTo(container) - } + this.setContent() - $(this.element).trigger(this.constructor.Event.INSERTED) - - this._popper = new Popper(this.element, tip, { - placement: attachment, - modifiers: { - offset: { - offset: this.config.offset - }, - flip: { - behavior: this.config.fallbackPlacement - }, - arrow: { - element: Selector.ARROW - }, - preventOverflow: { - boundariesElement: this.config.boundary - } - }, - onCreate: (data) => { - if (data.originalPlacement !== data.placement) { - this._handlePopperPlacementChange(data) - } - }, - onUpdate: (data) => { - this._handlePopperPlacementChange(data) - } - }) + if (this.config.animation) { + $(tip).addClass(ClassName.FADE) + } - $(tip).addClass(ClassName.SHOW) + const placement = typeof this.config.placement === 'function' + ? this.config.placement.call(this, tip, this.element) + : this.config.placement - // 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) { - $(document.body).children().on('mouseover', null, $.noop) - } + const attachment = this._getAttachment(placement) + this.addAttachmentClass(attachment) - const complete = () => { - if (this.config.animation) { - this._fixTransition() - } - const prevHoverState = this._hoverState - this._hoverState = null + const container = this._getContainer() + $(tip).data(this.constructor.DATA_KEY, this) + + if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) { + $(tip).appendTo(container) + } - $(this.element).trigger(this.constructor.Event.SHOWN) + $(this.element).trigger(this.constructor.Event.INSERTED) - if (prevHoverState === HoverState.OUT) { - this._leave(null, this) + this._popper = new Popper(this.element, tip, { + placement: attachment, + modifiers: { + offset: this._getOffset(), + flip: { + behavior: this.config.fallbackPlacement + }, + arrow: { + element: Selector.ARROW + }, + preventOverflow: { + boundariesElement: this.config.boundary } - } + }, + onCreate: (data) => { + if (data.originalPlacement !== data.placement) { + this._handlePopperPlacementChange(data) + } + }, + onUpdate: (data) => this._handlePopperPlacementChange(data) + }) - if ($(this.tip).hasClass(ClassName.FADE)) { - const transitionDuration = Util.getTransitionDurationFromElement(this.tip) + $(tip).addClass(ClassName.SHOW) - $(this.tip) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) - } else { - complete() - } + // 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) { + $(document.body).children().on('mouseover', null, $.noop) } - } - hide(callback) { - const tip = this.getTipElement() - const hideEvent = $.Event(this.constructor.Event.HIDE) const complete = () => { - if (this._hoverState !== HoverState.SHOW && tip.parentNode) { - tip.parentNode.removeChild(tip) + if (this.config.animation) { + this._fixTransition() } + const prevHoverState = this._hoverState + this._hoverState = null - this._cleanTipClass() - this.element.removeAttribute('aria-describedby') - $(this.element).trigger(this.constructor.Event.HIDDEN) - if (this._popper !== null) { - this._popper.destroy() - } + $(this.element).trigger(this.constructor.Event.SHOWN) - if (callback) { - callback() + if (prevHoverState === HoverState.OUT) { + this._leave(null, this) } } - $(this.element).trigger(hideEvent) - - if (hideEvent.isDefaultPrevented()) { - return - } - - $(tip).removeClass(ClassName.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) { - $(document.body).children().off('mouseover', null, $.noop) - } - - this._activeTrigger[Trigger.CLICK] = false - this._activeTrigger[Trigger.FOCUS] = false - this._activeTrigger[Trigger.HOVER] = false - if ($(this.tip).hasClass(ClassName.FADE)) { - const transitionDuration = Util.getTransitionDurationFromElement(tip) + const transitionDuration = Util.getTransitionDurationFromElement(this.tip) - $(tip) + $(this.tip) .one(Util.TRANSITION_END, complete) .emulateTransitionEnd(transitionDuration) } else { complete() } - - this._hoverState = '' } + } - update() { + hide(callback) { + const tip = this.getTipElement() + const hideEvent = $.Event(this.constructor.Event.HIDE) + const complete = () => { + if (this._hoverState !== HoverState.SHOW && tip.parentNode) { + tip.parentNode.removeChild(tip) + } + + this._cleanTipClass() + this.element.removeAttribute('aria-describedby') + $(this.element).trigger(this.constructor.Event.HIDDEN) if (this._popper !== null) { - this._popper.scheduleUpdate() + this._popper.destroy() + } + + if (callback) { + callback() } } - // Protected + $(this.element).trigger(hideEvent) - isWithContent() { - return Boolean(this.getTitle()) + if (hideEvent.isDefaultPrevented()) { + return } - addAttachmentClass(attachment) { - $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`) + $(tip).removeClass(ClassName.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) { + $(document.body).children().off('mouseover', null, $.noop) } - getTipElement() { - this.tip = this.tip || $(this.config.template)[0] - return this.tip + this._activeTrigger[Trigger.CLICK] = false + this._activeTrigger[Trigger.FOCUS] = false + this._activeTrigger[Trigger.HOVER] = false + + if ($(this.tip).hasClass(ClassName.FADE)) { + const transitionDuration = Util.getTransitionDurationFromElement(tip) + + $(tip) + .one(Util.TRANSITION_END, complete) + .emulateTransitionEnd(transitionDuration) + } else { + complete() } - setContent() { - const tip = this.getTipElement() - this.setElementContent($(tip.querySelectorAll(Selector.TOOLTIP_INNER)), this.getTitle()) - $(tip).removeClass(`${ClassName.FADE} ${ClassName.SHOW}`) + this._hoverState = '' + } + + update() { + if (this._popper !== null) { + this._popper.scheduleUpdate() } + } - setElementContent($element, content) { - const html = this.config.html - if (typeof content === 'object' && (content.nodeType || content.jquery)) { - // Content is a DOM node or a jQuery - if (html) { - if (!$(content).parent().is($element)) { - $element.empty().append(content) - } - } else { - $element.text($(content).text()) + // Protected + + isWithContent() { + return Boolean(this.getTitle()) + } + + addAttachmentClass(attachment) { + $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`) + } + + getTipElement() { + this.tip = this.tip || $(this.config.template)[0] + return this.tip + } + + setContent() { + const tip = this.getTipElement() + this.setElementContent($(tip.querySelectorAll(Selector.TOOLTIP_INNER)), this.getTitle()) + $(tip).removeClass(`${ClassName.FADE} ${ClassName.SHOW}`) + } + + setElementContent($element, content) { + if (typeof content === 'object' && (content.nodeType || content.jquery)) { + // Content is a DOM node or a jQuery + if (this.config.html) { + if (!$(content).parent().is($element)) { + $element.empty().append(content) } } else { - $element[html ? 'html' : 'text'](content) + $element.text($(content).text()) } - } - getTitle() { - let title = this.element.getAttribute('data-original-title') + return + } - if (!title) { - title = typeof this.config.title === 'function' - ? this.config.title.call(this.element) - : this.config.title + if (this.config.html) { + if (this.config.sanitize) { + content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn) } - return title + $element.html(content) + } else { + $element.text(content) } + } - // Private + getTitle() { + let title = this.element.getAttribute('data-original-title') - _getAttachment(placement) { - return AttachmentMap[placement.toUpperCase()] + if (!title) { + title = typeof this.config.title === 'function' + ? this.config.title.call(this.element) + : this.config.title } - _setListeners() { - const triggers = this.config.trigger.split(' ') + return title + } - triggers.forEach((trigger) => { - if (trigger === 'click') { - $(this.element).on( - this.constructor.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 - - $(this.element) - .on( - eventIn, - this.config.selector, - (event) => this._enter(event) - ) - .on( - eventOut, - this.config.selector, - (event) => this._leave(event) - ) - } + // Private - $(this.element).closest('.modal').on( - 'hide.bs.modal', - () => this.hide() - ) - }) + _getOffset() { + const offset = {} - if (this.config.selector) { - this.config = { - ...this.config, - trigger: 'manual', - selector: '' + if (typeof this.config.offset === 'function') { + offset.fn = (data) => { + data.offsets = { + ...data.offsets, + ...this.config.offset(data.offsets, this.element) || {} } - } else { - this._fixTitle() + + return data } + } else { + offset.offset = this.config.offset } - _fixTitle() { - const titleType = typeof this.element.getAttribute('data-original-title') - if (this.element.getAttribute('title') || - titleType !== 'string') { - this.element.setAttribute( - 'data-original-title', - this.element.getAttribute('title') || '' - ) - this.element.setAttribute('title', '') - } + return offset + } + + _getContainer() { + if (this.config.container === false) { + return document.body } - _enter(event, context) { - const dataKey = this.constructor.DATA_KEY + if (Util.isElement(this.config.container)) { + return $(this.config.container) + } - context = context || $(event.currentTarget).data(dataKey) + return $(document).find(this.config.container) + } - if (!context) { - context = new this.constructor( - event.currentTarget, - this._getDelegateConfig() + _getAttachment(placement) { + return AttachmentMap[placement.toUpperCase()] + } + + _setListeners() { + const triggers = this.config.trigger.split(' ') + + triggers.forEach((trigger) => { + if (trigger === 'click') { + $(this.element).on( + this.constructor.Event.CLICK, + this.config.selector, + (event) => this.toggle(event) ) - $(event.currentTarget).data(dataKey, context) + } 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 + + $(this.element) + .on( + eventIn, + this.config.selector, + (event) => this._enter(event) + ) + .on( + eventOut, + this.config.selector, + (event) => this._leave(event) + ) } + }) - if (event) { - context._activeTrigger[ - event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER - ] = true + $(this.element).closest('.modal').on( + 'hide.bs.modal', + () => { + if (this.element) { + this.hide() + } } + ) - if ($(context.getTipElement()).hasClass(ClassName.SHOW) || - context._hoverState === HoverState.SHOW) { - context._hoverState = HoverState.SHOW - return + if (this.config.selector) { + this.config = { + ...this.config, + trigger: 'manual', + selector: '' } + } else { + this._fixTitle() + } + } - clearTimeout(context._timeout) + _fixTitle() { + const titleType = typeof this.element.getAttribute('data-original-title') - context._hoverState = HoverState.SHOW + if (this.element.getAttribute('title') || titleType !== 'string') { + this.element.setAttribute( + 'data-original-title', + this.element.getAttribute('title') || '' + ) - if (!context.config.delay || !context.config.delay.show) { - context.show() - return - } + this.element.setAttribute('title', '') + } + } - context._timeout = setTimeout(() => { - if (context._hoverState === HoverState.SHOW) { - context.show() - } - }, context.config.delay.show) + _enter(event, context) { + const dataKey = this.constructor.DATA_KEY + context = context || $(event.currentTarget).data(dataKey) + + if (!context) { + context = new this.constructor( + event.currentTarget, + this._getDelegateConfig() + ) + $(event.currentTarget).data(dataKey, context) } - _leave(event, context) { - const dataKey = this.constructor.DATA_KEY + if (event) { + context._activeTrigger[ + event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER + ] = true + } - context = context || $(event.currentTarget).data(dataKey) + if ($(context.getTipElement()).hasClass(ClassName.SHOW) || context._hoverState === HoverState.SHOW) { + context._hoverState = HoverState.SHOW + return + } - if (!context) { - context = new this.constructor( - event.currentTarget, - this._getDelegateConfig() - ) - $(event.currentTarget).data(dataKey, context) - } + clearTimeout(context._timeout) - if (event) { - context._activeTrigger[ - event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER - ] = false - } + context._hoverState = HoverState.SHOW - if (context._isWithActiveTrigger()) { - return + if (!context.config.delay || !context.config.delay.show) { + context.show() + return + } + + context._timeout = setTimeout(() => { + if (context._hoverState === HoverState.SHOW) { + context.show() } + }, context.config.delay.show) + } - clearTimeout(context._timeout) + _leave(event, context) { + const dataKey = this.constructor.DATA_KEY + context = context || $(event.currentTarget).data(dataKey) - context._hoverState = HoverState.OUT + if (!context) { + context = new this.constructor( + event.currentTarget, + this._getDelegateConfig() + ) + $(event.currentTarget).data(dataKey, context) + } - if (!context.config.delay || !context.config.delay.hide) { - context.hide() - return - } + if (event) { + context._activeTrigger[ + event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER + ] = false + } - context._timeout = setTimeout(() => { - if (context._hoverState === HoverState.OUT) { - context.hide() - } - }, context.config.delay.hide) + if (context._isWithActiveTrigger()) { + return } - _isWithActiveTrigger() { - for (const trigger in this._activeTrigger) { - if (this._activeTrigger[trigger]) { - return true - } - } + clearTimeout(context._timeout) + + context._hoverState = HoverState.OUT - return false + if (!context.config.delay || !context.config.delay.hide) { + context.hide() + return } - _getConfig(config) { - config = { - ...this.constructor.Default, - ...$(this.element).data(), - ...typeof config === 'object' && config ? config : {} + context._timeout = setTimeout(() => { + if (context._hoverState === HoverState.OUT) { + context.hide() } + }, context.config.delay.hide) + } - if (typeof config.delay === 'number') { - config.delay = { - show: config.delay, - hide: config.delay - } + _isWithActiveTrigger() { + for (const trigger in this._activeTrigger) { + if (this._activeTrigger[trigger]) { + return true } + } - if (typeof config.title === 'number') { - config.title = config.title.toString() - } + return false + } - if (typeof config.content === 'number') { - config.content = config.content.toString() - } + _getConfig(config) { + const dataAttributes = $(this.element).data() - Util.typeCheckConfig( - NAME, - config, - this.constructor.DefaultType - ) + Object.keys(dataAttributes) + .forEach((dataAttr) => { + if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) { + delete dataAttributes[dataAttr] + } + }) - return config + config = { + ...this.constructor.Default, + ...dataAttributes, + ...typeof config === 'object' && config ? config : {} } - _getDelegateConfig() { - const config = {} - - if (this.config) { - for (const key in this.config) { - if (this.constructor.Default[key] !== this.config[key]) { - config[key] = this.config[key] - } - } + if (typeof config.delay === 'number') { + config.delay = { + show: config.delay, + hide: config.delay } + } - return config + if (typeof config.title === 'number') { + config.title = config.title.toString() } - _cleanTipClass() { - const $tip = $(this.getTipElement()) - const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX) - if (tabClass !== null && tabClass.length) { - $tip.removeClass(tabClass.join('')) - } + if (typeof config.content === 'number') { + config.content = config.content.toString() } - _handlePopperPlacementChange(popperData) { - const popperInstance = popperData.instance - this.tip = popperInstance.popper - this._cleanTipClass() - this.addAttachmentClass(this._getAttachment(popperData.placement)) + Util.typeCheckConfig( + NAME, + config, + this.constructor.DefaultType + ) + + if (config.sanitize) { + config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn) } - _fixTransition() { - const tip = this.getTipElement() - const initConfigAnimation = this.config.animation - if (tip.getAttribute('x-placement') !== null) { - return + return config + } + + _getDelegateConfig() { + const config = {} + + if (this.config) { + for (const key in this.config) { + if (this.constructor.Default[key] !== this.config[key]) { + config[key] = this.config[key] + } } - $(tip).removeClass(ClassName.FADE) - this.config.animation = false - this.hide() - this.show() - this.config.animation = initConfigAnimation } - // Static + return config + } - static _jQueryInterface(config) { - return this.each(function () { - let data = $(this).data(DATA_KEY) - const _config = typeof config === 'object' && config + _cleanTipClass() { + const $tip = $(this.getTipElement()) + const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX) + if (tabClass !== null && tabClass.length) { + $tip.removeClass(tabClass.join('')) + } + } - if (!data && /dispose|hide/.test(config)) { - return - } + _handlePopperPlacementChange(popperData) { + const popperInstance = popperData.instance + this.tip = popperInstance.popper + this._cleanTipClass() + this.addAttachmentClass(this._getAttachment(popperData.placement)) + } - if (!data) { - data = new Tooltip(this, _config) - $(this).data(DATA_KEY, data) - } + _fixTransition() { + const tip = this.getTipElement() + const initConfigAnimation = this.config.animation - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } - data[config]() - } - }) + if (tip.getAttribute('x-placement') !== null) { + return } + + $(tip).removeClass(ClassName.FADE) + this.config.animation = false + this.hide() + this.show() + this.config.animation = initConfigAnimation } - /** - * ------------------------------------------------------------------------ - * jQuery - * ------------------------------------------------------------------------ - */ + // Static + + static _jQueryInterface(config) { + return this.each(function () { + let data = $(this).data(DATA_KEY) + const _config = typeof config === 'object' && config + + if (!data && /dispose|hide/.test(config)) { + return + } - $.fn[NAME] = Tooltip._jQueryInterface - $.fn[NAME].Constructor = Tooltip - $.fn[NAME].noConflict = function () { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Tooltip._jQueryInterface + if (!data) { + data = new Tooltip(this, _config) + $(this).data(DATA_KEY, data) + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) + } + data[config]() + } + }) } +} + +/** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ - return Tooltip -})($, Popper) +$.fn[NAME] = Tooltip._jQueryInterface +$.fn[NAME].Constructor = Tooltip +$.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Tooltip._jQueryInterface +} export default Tooltip |