Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
tooltip.js765 linesDownload Raw Back to src
1/**2 * --------------------------------------------------------------------------3 * Bootstrap (v4.6.2): tooltip.js4 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)5 * --------------------------------------------------------------------------6 */7 8import { DefaultWhitelist, sanitizeHtml } from './tools/sanitizer'9import $ from 'jquery'10import Popper from 'popper.js'11import Util from './util'12 13/**14 * Constants15 */16 17const NAME = 'tooltip'18const VERSION = '4.6.2'19const DATA_KEY = 'bs.tooltip'20const EVENT_KEY = `.${DATA_KEY}`21const JQUERY_NO_CONFLICT = $.fn[NAME]22const CLASS_PREFIX = 'bs-tooltip'23const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')24const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']25 26const CLASS_NAME_FADE = 'fade'27const CLASS_NAME_SHOW = 'show'28 29const HOVER_STATE_SHOW = 'show'30const HOVER_STATE_OUT = 'out'31 32const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'33const SELECTOR_ARROW = '.arrow'34 35const TRIGGER_HOVER = 'hover'36const TRIGGER_FOCUS = 'focus'37const TRIGGER_CLICK = 'click'38const TRIGGER_MANUAL = 'manual'39 40const AttachmentMap = {41  AUTO: 'auto',42  TOP: 'top',43  RIGHT: 'right',44  BOTTOM: 'bottom',45  LEFT: 'left'46}47 48const Default = {49  animation: true,50  template: '<div class="tooltip" role="tooltip">' +51                    '<div class="arrow"></div>' +52                    '<div class="tooltip-inner"></div></div>',53  trigger: 'hover focus',54  title: '',55  delay: 0,56  html: false,57  selector: false,58  placement: 'top',59  offset: 0,60  container: false,61  fallbackPlacement: 'flip',62  boundary: 'scrollParent',63  customClass: '',64  sanitize: true,65  sanitizeFn: null,66  whiteList: DefaultWhitelist,67  popperConfig: null68}69 70const DefaultType = {71  animation: 'boolean',72  template: 'string',73  title: '(string|element|function)',74  trigger: 'string',75  delay: '(number|object)',76  html: 'boolean',77  selector: '(string|boolean)',78  placement: '(string|function)',79  offset: '(number|string|function)',80  container: '(string|element|boolean)',81  fallbackPlacement: '(string|array)',82  boundary: '(string|element)',83  customClass: '(string|function)',84  sanitize: 'boolean',85  sanitizeFn: '(null|function)',86  whiteList: 'object',87  popperConfig: '(null|object)'88}89 90const Event = {91  HIDE: `hide${EVENT_KEY}`,92  HIDDEN: `hidden${EVENT_KEY}`,93  SHOW: `show${EVENT_KEY}`,94  SHOWN: `shown${EVENT_KEY}`,95  INSERTED: `inserted${EVENT_KEY}`,96  CLICK: `click${EVENT_KEY}`,97  FOCUSIN: `focusin${EVENT_KEY}`,98  FOCUSOUT: `focusout${EVENT_KEY}`,99  MOUSEENTER: `mouseenter${EVENT_KEY}`,100  MOUSELEAVE: `mouseleave${EVENT_KEY}`101}102 103/**104 * Class definition105 */106 107class Tooltip {108  constructor(element, config) {109    if (typeof Popper === 'undefined') {110      throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')111    }112 113    // Private114    this._isEnabled = true115    this._timeout = 0116    this._hoverState = ''117    this._activeTrigger = {}118    this._popper = null119 120    // Protected121    this.element = element122    this.config = this._getConfig(config)123    this.tip = null124 125    this._setListeners()126  }127 128  // Getters129  static get VERSION() {130    return VERSION131  }132 133  static get Default() {134    return Default135  }136 137  static get NAME() {138    return NAME139  }140 141  static get DATA_KEY() {142    return DATA_KEY143  }144 145  static get Event() {146    return Event147  }148 149  static get EVENT_KEY() {150    return EVENT_KEY151  }152 153  static get DefaultType() {154    return DefaultType155  }156 157  // Public158  enable() {159    this._isEnabled = true160  }161 162  disable() {163    this._isEnabled = false164  }165 166  toggleEnabled() {167    this._isEnabled = !this._isEnabled168  }169 170  toggle(event) {171    if (!this._isEnabled) {172      return173    }174 175    if (event) {176      const dataKey = this.constructor.DATA_KEY177      let context = $(event.currentTarget).data(dataKey)178 179      if (!context) {180        context = new this.constructor(181          event.currentTarget,182          this._getDelegateConfig()183        )184        $(event.currentTarget).data(dataKey, context)185      }186 187      context._activeTrigger.click = !context._activeTrigger.click188 189      if (context._isWithActiveTrigger()) {190        context._enter(null, context)191      } else {192        context._leave(null, context)193      }194    } else {195      if ($(this.getTipElement()).hasClass(CLASS_NAME_SHOW)) {196        this._leave(null, this)197        return198      }199 200      this._enter(null, this)201    }202  }203 204  dispose() {205    clearTimeout(this._timeout)206 207    $.removeData(this.element, this.constructor.DATA_KEY)208 209    $(this.element).off(this.constructor.EVENT_KEY)210    $(this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler)211 212    if (this.tip) {213      $(this.tip).remove()214    }215 216    this._isEnabled = null217    this._timeout = null218    this._hoverState = null219    this._activeTrigger = null220    if (this._popper) {221      this._popper.destroy()222    }223 224    this._popper = null225    this.element = null226    this.config = null227    this.tip = null228  }229 230  show() {231    if ($(this.element).css('display') === 'none') {232      throw new Error('Please use show on visible elements')233    }234 235    const showEvent = $.Event(this.constructor.Event.SHOW)236    if (this.isWithContent() && this._isEnabled) {237      $(this.element).trigger(showEvent)238 239      const shadowRoot = Util.findShadowRoot(this.element)240      const isInTheDom = $.contains(241        shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement,242        this.element243      )244 245      if (showEvent.isDefaultPrevented() || !isInTheDom) {246        return247      }248 249      const tip = this.getTipElement()250      const tipId = Util.getUID(this.constructor.NAME)251 252      tip.setAttribute('id', tipId)253      this.element.setAttribute('aria-describedby', tipId)254 255      this.setContent()256 257      if (this.config.animation) {258        $(tip).addClass(CLASS_NAME_FADE)259      }260 261      const placement = typeof this.config.placement === 'function' ?262        this.config.placement.call(this, tip, this.element) :263        this.config.placement264 265      const attachment = this._getAttachment(placement)266      this.addAttachmentClass(attachment)267 268      const container = this._getContainer()269      $(tip).data(this.constructor.DATA_KEY, this)270 271      if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {272        $(tip).appendTo(container)273      }274 275      $(this.element).trigger(this.constructor.Event.INSERTED)276 277      this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment))278 279      $(tip).addClass(CLASS_NAME_SHOW)280      $(tip).addClass(this.config.customClass)281 282      // If this is a touch-enabled device we add extra283      // empty mouseover listeners to the body's immediate children;284      // only needed because of broken event delegation on iOS285      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html286      if ('ontouchstart' in document.documentElement) {287        $(document.body).children().on('mouseover', null, $.noop)288      }289 290      const complete = () => {291        if (this.config.animation) {292          this._fixTransition()293        }294 295        const prevHoverState = this._hoverState296        this._hoverState = null297 298        $(this.element).trigger(this.constructor.Event.SHOWN)299 300        if (prevHoverState === HOVER_STATE_OUT) {301          this._leave(null, this)302        }303      }304 305      if ($(this.tip).hasClass(CLASS_NAME_FADE)) {306        const transitionDuration = Util.getTransitionDurationFromElement(this.tip)307 308        $(this.tip)309          .one(Util.TRANSITION_END, complete)310          .emulateTransitionEnd(transitionDuration)311      } else {312        complete()313      }314    }315  }316 317  hide(callback) {318    const tip = this.getTipElement()319    const hideEvent = $.Event(this.constructor.Event.HIDE)320    const complete = () => {321      if (this._hoverState !== HOVER_STATE_SHOW && tip.parentNode) {322        tip.parentNode.removeChild(tip)323      }324 325      this._cleanTipClass()326      this.element.removeAttribute('aria-describedby')327      $(this.element).trigger(this.constructor.Event.HIDDEN)328      if (this._popper !== null) {329        this._popper.destroy()330      }331 332      if (callback) {333        callback()334      }335    }336 337    $(this.element).trigger(hideEvent)338 339    if (hideEvent.isDefaultPrevented()) {340      return341    }342 343    $(tip).removeClass(CLASS_NAME_SHOW)344 345    // If this is a touch-enabled device we remove the extra346    // empty mouseover listeners we added for iOS support347    if ('ontouchstart' in document.documentElement) {348      $(document.body).children().off('mouseover', null, $.noop)349    }350 351    this._activeTrigger[TRIGGER_CLICK] = false352    this._activeTrigger[TRIGGER_FOCUS] = false353    this._activeTrigger[TRIGGER_HOVER] = false354 355    if ($(this.tip).hasClass(CLASS_NAME_FADE)) {356      const transitionDuration = Util.getTransitionDurationFromElement(tip)357 358      $(tip)359        .one(Util.TRANSITION_END, complete)360        .emulateTransitionEnd(transitionDuration)361    } else {362      complete()363    }364 365    this._hoverState = ''366  }367 368  update() {369    if (this._popper !== null) {370      this._popper.scheduleUpdate()371    }372  }373 374  // Protected375  isWithContent() {376    return Boolean(this.getTitle())377  }378 379  addAttachmentClass(attachment) {380    $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)381  }382 383  getTipElement() {384    this.tip = this.tip || $(this.config.template)[0]385    return this.tip386  }387 388  setContent() {389    const tip = this.getTipElement()390    this.setElementContent($(tip.querySelectorAll(SELECTOR_TOOLTIP_INNER)), this.getTitle())391    $(tip).removeClass(`${CLASS_NAME_FADE} ${CLASS_NAME_SHOW}`)392  }393 394  setElementContent($element, content) {395    if (typeof content === 'object' && (content.nodeType || content.jquery)) {396      // Content is a DOM node or a jQuery397      if (this.config.html) {398        if (!$(content).parent().is($element)) {399          $element.empty().append(content)400        }401      } else {402        $element.text($(content).text())403      }404 405      return406    }407 408    if (this.config.html) {409      if (this.config.sanitize) {410        content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)411      }412 413      $element.html(content)414    } else {415      $element.text(content)416    }417  }418 419  getTitle() {420    let title = this.element.getAttribute('data-original-title')421 422    if (!title) {423      title = typeof this.config.title === 'function' ?424        this.config.title.call(this.element) :425        this.config.title426    }427 428    return title429  }430 431  // Private432  _getPopperConfig(attachment) {433    const defaultBsConfig = {434      placement: attachment,435      modifiers: {436        offset: this._getOffset(),437        flip: {438          behavior: this.config.fallbackPlacement439        },440        arrow: {441          element: SELECTOR_ARROW442        },443        preventOverflow: {444          boundariesElement: this.config.boundary445        }446      },447      onCreate: data => {448        if (data.originalPlacement !== data.placement) {449          this._handlePopperPlacementChange(data)450        }451      },452      onUpdate: data => this._handlePopperPlacementChange(data)453    }454 455    return {456      ...defaultBsConfig,457      ...this.config.popperConfig458    }459  }460 461  _getOffset() {462    const offset = {}463 464    if (typeof this.config.offset === 'function') {465      offset.fn = data => {466        data.offsets = {467          ...data.offsets,468          ...this.config.offset(data.offsets, this.element)469        }470 471        return data472      }473    } else {474      offset.offset = this.config.offset475    }476 477    return offset478  }479 480  _getContainer() {481    if (this.config.container === false) {482      return document.body483    }484 485    if (Util.isElement(this.config.container)) {486      return $(this.config.container)487    }488 489    return $(document).find(this.config.container)490  }491 492  _getAttachment(placement) {493    return AttachmentMap[placement.toUpperCase()]494  }495 496  _setListeners() {497    const triggers = this.config.trigger.split(' ')498 499    triggers.forEach(trigger => {500      if (trigger === 'click') {501        $(this.element).on(502          this.constructor.Event.CLICK,503          this.config.selector,504          event => this.toggle(event)505        )506      } else if (trigger !== TRIGGER_MANUAL) {507        const eventIn = trigger === TRIGGER_HOVER ?508          this.constructor.Event.MOUSEENTER :509          this.constructor.Event.FOCUSIN510        const eventOut = trigger === TRIGGER_HOVER ?511          this.constructor.Event.MOUSELEAVE :512          this.constructor.Event.FOCUSOUT513 514        $(this.element)515          .on(eventIn, this.config.selector, event => this._enter(event))516          .on(eventOut, this.config.selector, event => this._leave(event))517      }518    })519 520    this._hideModalHandler = () => {521      if (this.element) {522        this.hide()523      }524    }525 526    $(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler)527 528    if (this.config.selector) {529      this.config = {530        ...this.config,531        trigger: 'manual',532        selector: ''533      }534    } else {535      this._fixTitle()536    }537  }538 539  _fixTitle() {540    const titleType = typeof this.element.getAttribute('data-original-title')541 542    if (this.element.getAttribute('title') || titleType !== 'string') {543      this.element.setAttribute(544        'data-original-title',545        this.element.getAttribute('title') || ''546      )547 548      this.element.setAttribute('title', '')549    }550  }551 552  _enter(event, context) {553    const dataKey = this.constructor.DATA_KEY554    context = context || $(event.currentTarget).data(dataKey)555 556    if (!context) {557      context = new this.constructor(558        event.currentTarget,559        this._getDelegateConfig()560      )561      $(event.currentTarget).data(dataKey, context)562    }563 564    if (event) {565      context._activeTrigger[566        event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER567      ] = true568    }569 570    if ($(context.getTipElement()).hasClass(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {571      context._hoverState = HOVER_STATE_SHOW572      return573    }574 575    clearTimeout(context._timeout)576 577    context._hoverState = HOVER_STATE_SHOW578 579    if (!context.config.delay || !context.config.delay.show) {580      context.show()581      return582    }583 584    context._timeout = setTimeout(() => {585      if (context._hoverState === HOVER_STATE_SHOW) {586        context.show()587      }588    }, context.config.delay.show)589  }590 591  _leave(event, context) {592    const dataKey = this.constructor.DATA_KEY593    context = context || $(event.currentTarget).data(dataKey)594 595    if (!context) {596      context = new this.constructor(597        event.currentTarget,598        this._getDelegateConfig()599      )600      $(event.currentTarget).data(dataKey, context)601    }602 603    if (event) {604      context._activeTrigger[605        event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER606      ] = false607    }608 609    if (context._isWithActiveTrigger()) {610      return611    }612 613    clearTimeout(context._timeout)614 615    context._hoverState = HOVER_STATE_OUT616 617    if (!context.config.delay || !context.config.delay.hide) {618      context.hide()619      return620    }621 622    context._timeout = setTimeout(() => {623      if (context._hoverState === HOVER_STATE_OUT) {624        context.hide()625      }626    }, context.config.delay.hide)627  }628 629  _isWithActiveTrigger() {630    for (const trigger in this._activeTrigger) {631      if (this._activeTrigger[trigger]) {632        return true633      }634    }635 636    return false637  }638 639  _getConfig(config) {640    const dataAttributes = $(this.element).data()641 642    Object.keys(dataAttributes)643      .forEach(dataAttr => {644        if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {645          delete dataAttributes[dataAttr]646        }647      })648 649    config = {650      ...this.constructor.Default,651      ...dataAttributes,652      ...(typeof config === 'object' && config ? config : {})653    }654 655    if (typeof config.delay === 'number') {656      config.delay = {657        show: config.delay,658        hide: config.delay659      }660    }661 662    if (typeof config.title === 'number') {663      config.title = config.title.toString()664    }665 666    if (typeof config.content === 'number') {667      config.content = config.content.toString()668    }669 670    Util.typeCheckConfig(671      NAME,672      config,673      this.constructor.DefaultType674    )675 676    if (config.sanitize) {677      config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn)678    }679 680    return config681  }682 683  _getDelegateConfig() {684    const config = {}685 686    if (this.config) {687      for (const key in this.config) {688        if (this.constructor.Default[key] !== this.config[key]) {689          config[key] = this.config[key]690        }691      }692    }693 694    return config695  }696 697  _cleanTipClass() {698    const $tip = $(this.getTipElement())699    const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)700    if (tabClass !== null && tabClass.length) {701      $tip.removeClass(tabClass.join(''))702    }703  }704 705  _handlePopperPlacementChange(popperData) {706    this.tip = popperData.instance.popper707    this._cleanTipClass()708    this.addAttachmentClass(this._getAttachment(popperData.placement))709  }710 711  _fixTransition() {712    const tip = this.getTipElement()713    const initConfigAnimation = this.config.animation714 715    if (tip.getAttribute('x-placement') !== null) {716      return717    }718 719    $(tip).removeClass(CLASS_NAME_FADE)720    this.config.animation = false721    this.hide()722    this.show()723    this.config.animation = initConfigAnimation724  }725 726  // Static727  static _jQueryInterface(config) {728    return this.each(function () {729      const $element = $(this)730      let data = $element.data(DATA_KEY)731      const _config = typeof config === 'object' && config732 733      if (!data && /dispose|hide/.test(config)) {734        return735      }736 737      if (!data) {738        data = new Tooltip(this, _config)739        $element.data(DATA_KEY, data)740      }741 742      if (typeof config === 'string') {743        if (typeof data[config] === 'undefined') {744          throw new TypeError(`No method named "${config}"`)745        }746 747        data[config]()748      }749    })750  }751}752 753/**754 * jQuery755 */756 757$.fn[NAME] = Tooltip._jQueryInterface758$.fn[NAME].Constructor = Tooltip759$.fn[NAME].noConflict = () => {760  $.fn[NAME] = JQUERY_NO_CONFLICT761  return Tooltip._jQueryInterface762}763 764export default Tooltip765 
codekingpro/portable-devtools · Team Ai