Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
Línea 1... Línea 1...
1
/**
1
/**
2
 * --------------------------------------------------------------------------
2
 * --------------------------------------------------------------------------
3
 * Bootstrap (v4.6.2): tooltip.js
3
 * Bootstrap tooltip.js
4
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
4
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
 * --------------------------------------------------------------------------
5
 * --------------------------------------------------------------------------
6
 */
6
 */
Línea 7... Línea 7...
7
 
7
 
8
import { DefaultWhitelist, sanitizeHtml } from './tools/sanitizer'
8
import * as Popper from 'core/popper2'
9
import $ from 'jquery'
9
import BaseComponent from './base-component'
10
import Popper from 'core/popper'
10
import EventHandler from './dom/event-handler'
-
 
11
import Manipulator from './dom/manipulator'
-
 
12
import {
-
 
13
  defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop
-
 
14
} from './util/index'
-
 
15
import { DefaultAllowlist } from './util/sanitizer'
Línea 11... Línea 16...
11
import Util from './util'
16
import TemplateFactory from './util/template-factory'
12
 
17
 
13
/**
18
/**
Línea 14... Línea 19...
14
 * Constants
19
 * Constants
15
 */
-
 
16
 
-
 
17
const NAME = 'tooltip'
-
 
18
const VERSION = '4.6.2'
-
 
19
const DATA_KEY = 'bs.tooltip'
-
 
20
const EVENT_KEY = `.${DATA_KEY}`
-
 
21
const JQUERY_NO_CONFLICT = $.fn[NAME]
20
 */
Línea 22... Línea 21...
22
const CLASS_PREFIX = 'bs-tooltip'
21
 
-
 
22
const NAME = 'tooltip'
23
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
23
const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])
Línea 24... Línea -...
24
const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
-
 
25
 
-
 
26
const CLASS_NAME_FADE = 'fade'
-
 
27
const CLASS_NAME_SHOW = 'show'
24
 
-
 
25
const CLASS_NAME_FADE = 'fade'
-
 
26
const CLASS_NAME_MODAL = 'modal'
28
 
27
const CLASS_NAME_SHOW = 'show'
Línea 29... Línea 28...
29
const HOVER_STATE_SHOW = 'show'
28
 
30
const HOVER_STATE_OUT = 'out'
29
const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
31
 
30
const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`
32
const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
31
 
Línea -... Línea 32...
-
 
32
const EVENT_MODAL_HIDE = 'hide.bs.modal'
-
 
33
 
-
 
34
const TRIGGER_HOVER = 'hover'
-
 
35
const TRIGGER_FOCUS = 'focus'
-
 
36
const TRIGGER_CLICK = 'click'
-
 
37
const TRIGGER_MANUAL = 'manual'
-
 
38
 
-
 
39
const EVENT_HIDE = 'hide'
-
 
40
const EVENT_HIDDEN = 'hidden'
-
 
41
const EVENT_SHOW = 'show'
-
 
42
const EVENT_SHOWN = 'shown'
33
const SELECTOR_ARROW = '.arrow'
43
const EVENT_INSERTED = 'inserted'
34
 
44
const EVENT_CLICK = 'click'
35
const TRIGGER_HOVER = 'hover'
45
const EVENT_FOCUSIN = 'focusin'
36
const TRIGGER_FOCUS = 'focus'
46
const EVENT_FOCUSOUT = 'focusout'
37
const TRIGGER_CLICK = 'click'
47
const EVENT_MOUSEENTER = 'mouseenter'
38
const TRIGGER_MANUAL = 'manual'
48
const EVENT_MOUSELEAVE = 'mouseleave'
39
 
49
 
Línea 40... Línea 50...
40
const AttachmentMap = {
50
const AttachmentMap = {
-
 
51
  AUTO: 'auto',
41
  AUTO: 'auto',
52
  TOP: 'top',
42
  TOP: 'top',
53
  RIGHT: isRTL() ? 'left' : 'right',
43
  RIGHT: 'right',
-
 
44
  BOTTOM: 'bottom',
-
 
45
  LEFT: 'left'
54
  BOTTOM: 'bottom',
46
}
55
  LEFT: isRTL() ? 'right' : 'left'
47
 
56
}
-
 
57
 
48
const Default = {
58
const Default = {
49
  animation: true,
59
  allowList: DefaultAllowlist,
50
  template: '<div class="tooltip" role="tooltip">' +
60
  animation: true,
51
                    '<div class="arrow"></div>' +
-
 
52
                    '<div class="tooltip-inner"></div></div>',
61
  boundary: 'clippingParents',
53
  trigger: 'hover focus',
-
 
54
  title: '',
-
 
55
  delay: 0,
-
 
56
  html: false,
62
  container: false,
57
  selector: false,
63
  customClass: '',
58
  placement: 'top',
64
  delay: 0,
-
 
65
  fallbackPlacements: ['top', 'right', 'bottom', 'left'],
-
 
66
  html: false,
-
 
67
  offset: [0, 6],
-
 
68
  placement: 'top',
-
 
69
  popperConfig: null,
59
  offset: 0,
70
  sanitize: true,
60
  container: false,
71
  sanitizeFn: null,
Línea 61... Línea 72...
61
  fallbackPlacement: 'flip',
72
  selector: false,
-
 
73
  template: '<div class="tooltip" role="tooltip">' +
62
  boundary: 'scrollParent',
74
            '<div class="tooltip-arrow"></div>' +
63
  customClass: '',
75
            '<div class="tooltip-inner"></div>' +
64
  sanitize: true,
76
            '</div>',
65
  sanitizeFn: null,
77
  title: '',
66
  whiteList: DefaultWhitelist,
78
  trigger: 'hover focus'
-
 
79
}
67
  popperConfig: null
80
 
68
}
81
const DefaultType = {
69
 
82
  allowList: 'object',
70
const DefaultType = {
83
  animation: 'boolean',
71
  animation: 'boolean',
-
 
72
  template: 'string',
-
 
73
  title: '(string|element|function)',
-
 
74
  trigger: 'string',
-
 
75
  delay: '(number|object)',
84
  boundary: '(string|element)',
76
  html: 'boolean',
85
  container: '(string|element|boolean)',
77
  selector: '(string|boolean)',
-
 
78
  placement: '(string|function)',
86
  customClass: '(string|function)',
79
  offset: '(number|string|function)',
-
 
80
  container: '(string|element|boolean)',
-
 
81
  fallbackPlacement: '(string|array)',
-
 
82
  boundary: '(string|element)',
87
  delay: '(number|object)',
83
  customClass: '(string|function)',
-
 
84
  sanitize: 'boolean',
-
 
85
  sanitizeFn: '(null|function)',
-
 
86
  whiteList: 'object',
88
  fallbackPlacements: 'array',
87
  popperConfig: '(null|object)'
-
 
88
}
89
  html: 'boolean',
89
 
-
 
90
const Event = {
-
 
91
  HIDE: `hide${EVENT_KEY}`,
-
 
92
  HIDDEN: `hidden${EVENT_KEY}`,
90
  offset: '(array|string|function)',
Línea 93... Línea 91...
93
  SHOW: `show${EVENT_KEY}`,
91
  placement: '(string|function)',
94
  SHOWN: `shown${EVENT_KEY}`,
92
  popperConfig: '(null|object|function)',
95
  INSERTED: `inserted${EVENT_KEY}`,
93
  sanitize: 'boolean',
Línea 96... Línea 94...
96
  CLICK: `click${EVENT_KEY}`,
94
  sanitizeFn: '(null|function)',
97
  FOCUSIN: `focusin${EVENT_KEY}`,
95
  selector: '(string|boolean)',
98
  FOCUSOUT: `focusout${EVENT_KEY}`,
96
  template: 'string',
99
  MOUSEENTER: `mouseenter${EVENT_KEY}`,
97
  title: '(string|element|function)',
100
  MOUSELEAVE: `mouseleave${EVENT_KEY}`
98
  trigger: 'string'
Línea -... Línea 99...
-
 
99
}
-
 
100
 
101
}
101
/**
102
 
102
 * Class definition
103
/**
103
 */
104
 * Class definition
104
 
105
 */
105
class Tooltip extends BaseComponent {
106
 
106
  constructor(element, config) {
-
 
107
    if (typeof Popper === 'undefined') {
-
 
108
      throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')
Línea 107... Línea 109...
107
class Tooltip {
109
    }
108
  constructor(element, config) {
-
 
109
    if (typeof Popper === 'undefined') {
-
 
110
      throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')
110
 
Línea 111... Línea 111...
111
    }
111
    super(element, config)
112
 
-
 
Línea 113... Línea 112...
113
    // Private
112
 
114
    this._isEnabled = true
113
    // Private
115
    this._timeout = 0
114
    this._isEnabled = true
116
    this._hoverState = ''
115
    this._timeout = 0
Línea -... Línea 116...
-
 
116
    this._isHovered = null
117
    this._activeTrigger = {}
117
    this._activeTrigger = {}
118
    this._popper = null
118
    this._popper = null
119
 
119
    this._templateFactory = null
Línea 120... Línea -...
120
    // Protected
-
 
121
    this.element = element
-
 
122
    this.config = this._getConfig(config)
-
 
123
    this.tip = null
-
 
124
 
-
 
125
    this._setListeners()
-
 
126
  }
-
 
127
 
-
 
128
  // Getters
-
 
129
  static get VERSION() {
-
 
130
    return VERSION
-
 
131
  }
-
 
132
 
-
 
133
  static get Default() {
-
 
134
    return Default
-
 
135
  }
-
 
136
 
120
    this._newContent = null
137
  static get NAME() {
121
 
138
    return NAME
122
    // Protected
Línea -... Línea 123...
-
 
123
    this.tip = null
-
 
124
 
-
 
125
    this._setListeners()
-
 
126
 
139
  }
127
    if (!this._config.selector) {
140
 
128
      this._fixTitle()
141
  static get DATA_KEY() {
129
    }
142
    return DATA_KEY
130
  }
Línea 165... Línea 153...
165
 
153
 
166
  toggleEnabled() {
154
  toggleEnabled() {
167
    this._isEnabled = !this._isEnabled
155
    this._isEnabled = !this._isEnabled
Línea 168... Línea 156...
168
  }
156
  }
169
 
157
 
170
  toggle(event) {
158
  toggle() {
171
    if (!this._isEnabled) {
159
    if (!this._isEnabled) {
Línea 172... Línea -...
172
      return
-
 
173
    }
-
 
174
 
-
 
175
    if (event) {
-
 
176
      const dataKey = this.constructor.DATA_KEY
-
 
177
      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)
160
      return
185
      }
-
 
186
 
161
    }
187
      context._activeTrigger.click = !context._activeTrigger.click
-
 
188
 
-
 
189
      if (context._isWithActiveTrigger()) {
-
 
190
        context._enter(null, context)
-
 
191
      } else {
-
 
192
        context._leave(null, context)
-
 
193
      }
162
 
194
    } else {
163
    this._activeTrigger.click = !this._activeTrigger.click
195
      if ($(this.getTipElement()).hasClass(CLASS_NAME_SHOW)) {
-
 
196
        this._leave(null, this)
-
 
197
        return
-
 
198
      }
164
    if (this._isShown()) {
-
 
165
      this._leave()
-
 
166
      return
199
 
167
    }
Línea 200... Línea 168...
200
      this._enter(null, this)
168
 
201
    }
169
    this._enter()
Línea 202... Línea -...
202
  }
-
 
203
 
-
 
204
  dispose() {
-
 
205
    clearTimeout(this._timeout)
170
  }
Línea 206... Línea -...
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()
171
 
214
    }
-
 
215
 
172
  dispose() {
216
    this._isEnabled = null
173
    clearTimeout(this._timeout)
Línea 217... Línea 174...
217
    this._timeout = null
174
 
218
    this._hoverState = null
-
 
219
    this._activeTrigger = null
-
 
220
    if (this._popper) {
175
    EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)
221
      this._popper.destroy()
176
 
Línea 222... Línea 177...
222
    }
177
    if (this._element.getAttribute('data-bs-original-title')) {
223
 
178
      this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))
224
    this._popper = null
179
    }
225
    this.element = null
180
 
Línea 226... Línea -...
226
    this.config = null
-
 
227
    this.tip = null
181
    this._disposePopper()
228
  }
-
 
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)
182
    super.dispose()
238
 
-
 
239
      const shadowRoot = Util.findShadowRoot(this.element)
-
 
240
      const isInTheDom = $.contains(
-
 
241
        shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement,
-
 
242
        this.element
-
 
243
      )
-
 
244
 
-
 
245
      if (showEvent.isDefaultPrevented() || !isInTheDom) {
-
 
246
        return
-
 
247
      }
-
 
248
 
-
 
249
      const tip = this.getTipElement()
-
 
250
      const tipId = Util.getUID(this.constructor.NAME)
183
  }
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
 
-
 
Línea 261... Línea 184...
261
      const placement = typeof this.config.placement === 'function' ?
184
 
262
        this.config.placement.call(this, tip, this.element) :
185
  show() {
263
        this.config.placement
-
 
-
 
186
    if (this._element.style.display === 'none') {
Línea 264... Línea 187...
264
 
187
      throw new Error('Please use show on visible elements')
-
 
188
    }
-
 
189
 
Línea 265... Línea 190...
265
      const attachment = this._getAttachment(placement)
190
    if (!(this._isWithContent() && this._isEnabled)) {
-
 
191
      return
Línea 266... Línea -...
266
      this.addAttachmentClass(attachment)
-
 
267
 
192
    }
Línea 268... Línea -...
268
      const container = this._getContainer()
-
 
269
      $(tip).data(this.constructor.DATA_KEY, this)
193
 
270
 
-
 
271
      if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
-
 
272
        $(tip).appendTo(container)
-
 
273
      }
-
 
274
 
-
 
Línea 275... Línea 194...
275
      $(this.element).trigger(this.constructor.Event.INSERTED)
194
    const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW))
276
 
-
 
277
      this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment))
-
 
278
 
-
 
Línea 279... Línea 195...
279
      $(tip).addClass(CLASS_NAME_SHOW)
195
    const shadowRoot = findShadowRoot(this._element)
280
      $(tip).addClass(this.config.customClass)
196
    const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)
281
 
-
 
282
      // If this is a touch-enabled device we add extra
197
 
-
 
198
    if (showEvent.defaultPrevented || !isInTheDom) {
Línea 283... Línea -...
283
      // empty mouseover listeners to the body's immediate children;
-
 
284
      // only needed because of broken event delegation on iOS
199
      return
285
      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
-
 
286
      if ('ontouchstart' in document.documentElement) {
-
 
Línea 287... Línea 200...
287
        $(document.body).children().on('mouseover', null, $.noop)
200
    }
288
      }
-
 
Línea 289... Línea 201...
289
 
201
 
290
      const complete = () => {
202
    // TODO: v6 remove this or make it optional
291
        if (this.config.animation) {
203
    this._disposePopper()
-
 
204
 
292
          this._fixTransition()
205
    const tip = this._getTipElement()
-
 
206
 
293
        }
207
    this._element.setAttribute('aria-describedby', tip.getAttribute('id'))
294
 
208
 
295
        const prevHoverState = this._hoverState
209
    const { container } = this._config
296
        this._hoverState = null
-
 
Línea 297... Línea -...
297
 
-
 
298
        $(this.element).trigger(this.constructor.Event.SHOWN)
-
 
299
 
-
 
300
        if (prevHoverState === HOVER_STATE_OUT) {
210
 
301
          this._leave(null, this)
211
    if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
302
        }
-
 
303
      }
-
 
Línea 304... Línea -...
304
 
-
 
305
      if ($(this.tip).hasClass(CLASS_NAME_FADE)) {
-
 
306
        const transitionDuration = Util.getTransitionDurationFromElement(this.tip)
-
 
307
 
212
      container.append(tip)
308
        $(this.tip)
213
      EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))
309
          .one(Util.TRANSITION_END, complete)
214
    }
Línea 310... Línea 215...
310
          .emulateTransitionEnd(transitionDuration)
215
 
311
      } else {
-
 
312
        complete()
-
 
313
      }
216
    this._popper = this._createPopper(tip)
Línea -... Línea 217...
-
 
217
 
-
 
218
    tip.classList.add(CLASS_NAME_SHOW)
-
 
219
 
-
 
220
    // If this is a touch-enabled device we add extra
314
    }
221
    // empty mouseover listeners to the body's immediate children;
-
 
222
    // only needed because of broken event delegation on iOS
-
 
223
    // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
Línea -... Línea 224...
-
 
224
    if ('ontouchstart' in document.documentElement) {
315
  }
225
      for (const element of [].concat(...document.body.children)) {
316
 
226
        EventHandler.on(element, 'mouseover', noop)
317
  hide(callback) {
227
      }
Línea -... Línea 228...
-
 
228
    }
318
    const tip = this.getTipElement()
229
 
Línea 319... Línea 230...
319
    const hideEvent = $.Event(this.constructor.Event.HIDE)
230
    const complete = () => {
320
    const complete = () => {
231
      EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))
321
      if (this._hoverState !== HOVER_STATE_SHOW && tip.parentNode) {
232
 
-
 
233
      if (this._isHovered === false) {
322
        tip.parentNode.removeChild(tip)
234
        this._leave()
-
 
235
      }
323
      }
236
 
Línea 324... Línea 237...
324
 
237
      this._isHovered = false
325
      this._cleanTipClass()
238
    }
326
      this.element.removeAttribute('aria-describedby')
239
 
-
 
240
    this._queueCallback(complete, this.tip, this._isAnimated())
Línea 327... Línea 241...
327
      $(this.element).trigger(this.constructor.Event.HIDDEN)
241
  }
328
      if (this._popper !== null) {
242
 
-
 
243
  hide() {
-
 
244
    if (!this._isShown()) {
Línea 329... Línea 245...
329
        this._popper.destroy()
245
      return
330
      }
246
    }
331
 
-
 
332
      if (callback) {
247
 
-
 
248
    const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE))
333
        callback()
249
    if (hideEvent.defaultPrevented) {
-
 
250
      return
334
      }
251
    }
Línea 335... Línea 252...
335
    }
252
 
336
 
253
    const tip = this._getTipElement()
Línea 337... Línea 254...
337
    $(this.element).trigger(hideEvent)
254
    tip.classList.remove(CLASS_NAME_SHOW)
338
 
255
 
339
    if (hideEvent.isDefaultPrevented()) {
256
    // If this is a touch-enabled device we remove the extra
340
      return
257
    // empty mouseover listeners we added for iOS support
341
    }
258
    if ('ontouchstart' in document.documentElement) {
Línea 342... Línea 259...
342
 
259
      for (const element of [].concat(...document.body.children)) {
343
    $(tip).removeClass(CLASS_NAME_SHOW)
260
        EventHandler.off(element, 'mouseover', noop)
344
 
261
      }
345
    // If this is a touch-enabled device we remove the extra
262
    }
Línea 346... Línea 263...
346
    // empty mouseover listeners we added for iOS support
263
 
-
 
264
    this._activeTrigger[TRIGGER_CLICK] = false
347
    if ('ontouchstart' in document.documentElement) {
265
    this._activeTrigger[TRIGGER_FOCUS] = false
348
      $(document.body).children().off('mouseover', null, $.noop)
266
    this._activeTrigger[TRIGGER_HOVER] = false
Línea 349... Línea -...
349
    }
-
 
350
 
-
 
351
    this._activeTrigger[TRIGGER_CLICK] = false
267
    this._isHovered = null // it is a trick to support manual triggering
352
    this._activeTrigger[TRIGGER_FOCUS] = false
268
 
Línea 353... Línea 269...
353
    this._activeTrigger[TRIGGER_HOVER] = false
269
    const complete = () => {
354
 
270
      if (this._isWithActiveTrigger()) {
355
    if ($(this.tip).hasClass(CLASS_NAME_FADE)) {
-
 
356
      const transitionDuration = Util.getTransitionDurationFromElement(tip)
-
 
357
 
-
 
Línea 358... Línea -...
358
      $(tip)
-
 
359
        .one(Util.TRANSITION_END, complete)
-
 
360
        .emulateTransitionEnd(transitionDuration)
-
 
361
    } else {
-
 
362
      complete()
-
 
363
    }
271
        return
364
 
272
      }
365
    this._hoverState = ''
273
 
366
  }
-
 
367
 
274
      if (!this._isHovered) {
Línea -... Línea 275...
-
 
275
        this._disposePopper()
-
 
276
      }
-
 
277
 
-
 
278
      this._element.removeAttribute('aria-describedby')
-
 
279
      EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))
-
 
280
    }
-
 
281
 
-
 
282
    this._queueCallback(complete, this.tip, this._isAnimated())
368
  update() {
283
  }
-
 
284
 
369
    if (this._popper !== null) {
285
  update() {
Línea 370... Línea 286...
370
      this._popper.scheduleUpdate()
286
    if (this._popper) {
371
    }
-
 
372
  }
-
 
373
 
287
      this._popper.update()
Línea -... Línea 288...
-
 
288
    }
374
  // Protected
289
  }
375
  isWithContent() {
290
 
376
    return Boolean(this.getTitle())
291
  // Protected
-
 
292
  _isWithContent() {
377
  }
293
    return Boolean(this._getTitle())
378
 
294
  }
Línea -... Línea 295...
-
 
295
 
-
 
296
  _getTipElement() {
-
 
297
    if (!this.tip) {
379
  addAttachmentClass(attachment) {
298
      this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())
-
 
299
    }
-
 
300
 
-
 
301
    return this.tip
-
 
302
  }
-
 
303
 
380
    $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
304
  _createTipElement(content) {
-
 
305
    const tip = this._getTemplateFactory(content).toHtml()
-
 
306
 
Línea 381... Línea 307...
381
  }
307
    // TODO: remove this check in v6
-
 
308
    if (!tip) {
-
 
309
      return null
382
 
310
    }
383
  getTipElement() {
311
 
384
    this.tip = this.tip || $(this.config.template)[0]
312
    tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
385
    return this.tip
313
    // TODO: v6 the following can be achieved with CSS only
-
 
314
    tip.classList.add(`bs-${this.constructor.NAME}-auto`)
Línea 386... Línea 315...
386
  }
315
 
-
 
316
    const tipId = getUID(this.constructor.NAME).toString()
387
 
317
 
Línea 388... Línea 318...
388
  setContent() {
318
    tip.setAttribute('id', tipId)
389
    const tip = this.getTipElement()
319
 
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 jQuery
-
 
397
      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
      return
-
 
406
    }
-
 
407
 
-
 
408
    if (this.config.html) {
-
 
409
      if (this.config.sanitize) {
320
    if (this._isAnimated()) {
410
        content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)
321
      tip.classList.add(CLASS_NAME_FADE)
Línea 411... Línea 322...
411
      }
322
    }
-
 
323
 
-
 
324
    return tip
-
 
325
  }
412
 
326
 
413
      $element.html(content)
327
  setContent(content) {
414
    } else {
328
    this._newContent = content
-
 
329
    if (this._isShown()) {
-
 
330
      this._disposePopper()
-
 
331
      this.show()
-
 
332
    }
-
 
333
  }
415
      $element.text(content)
334
 
Línea 416... Línea 335...
416
    }
335
  _getTemplateFactory(content) {
417
  }
336
    if (this._templateFactory) {
Línea 418... Línea 337...
418
 
337
      this._templateFactory.changeContent(content)
419
  getTitle() {
-
 
420
    let title = this.element.getAttribute('data-original-title')
-
 
421
 
-
 
422
    if (!title) {
338
    } else {
423
      title = typeof this.config.title === 'function' ?
339
      this._templateFactory = new TemplateFactory({
Línea 424... Línea 340...
424
        this.config.title.call(this.element) :
340
        ...this._config,
425
        this.config.title
-
 
426
    }
-
 
427
 
341
        // the `content` var has to be after `this._config`
428
    return title
342
        // to override config.content in case of popover
Línea 429... Línea 343...
429
  }
343
        content,
430
 
344
        extraClass: this._resolvePossibleFunction(this._config.customClass)
Línea 431... Línea 345...
431
  // Private
345
      })
432
  _getPopperConfig(attachment) {
-
 
433
    const defaultBsConfig = {
346
    }
434
      placement: attachment,
347
 
Línea -... Línea 348...
-
 
348
    return this._templateFactory
-
 
349
  }
-
 
350
 
-
 
351
  _getContentForTemplate() {
-
 
352
    return {
-
 
353
      [SELECTOR_TOOLTIP_INNER]: this._getTitle()
-
 
354
    }
435
      modifiers: {
355
  }
-
 
356
 
-
 
357
  _getTitle() {
-
 
358
    return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')
-
 
359
  }
-
 
360
 
-
 
361
  // Private
-
 
362
  _initializeOnDelegatedTarget(event) {
-
 
363
    return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())
-
 
364
  }
-
 
365
 
-
 
366
  _isAnimated() {
436
        offset: this._getOffset(),
367
    return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))
-
 
368
  }
-
 
369
 
-
 
370
  _isShown() {
-
 
371
    return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)
-
 
372
  }
-
 
373
 
-
 
374
  _createPopper(tip) {
-
 
375
    const placement = execute(this._config.placement, [this, tip, this._element])
-
 
376
    const attachment = AttachmentMap[placement.toUpperCase()]
-
 
377
    return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
-
 
378
  }
-
 
379
 
-
 
380
  _getOffset() {
-
 
381
    const { offset } = this._config
-
 
382
 
-
 
383
    if (typeof offset === 'string') {
-
 
384
      return offset.split(',').map(value => Number.parseInt(value, 10))
-
 
385
    }
-
 
386
 
437
        flip: {
387
    if (typeof offset === 'function') {
Línea 438... Línea -...
438
          behavior: this.config.fallbackPlacement
-
 
439
        },
388
      return popperData => offset(popperData, this._element)
440
        arrow: {
-
 
441
          element: SELECTOR_ARROW
389
    }
442
        },
390
 
-
 
391
    return offset
443
        preventOverflow: {
392
  }
Línea 444... Línea 393...
444
          boundariesElement: this.config.boundary
393
 
445
        }
394
  _resolvePossibleFunction(arg) {
Línea 446... Línea 395...
446
      },
395
    return execute(arg, [this._element])
447
      onCreate: data => {
396
  }
448
        if (data.originalPlacement !== data.placement) {
-
 
449
          this._handlePopperPlacementChange(data)
397
 
450
        }
398
  _getPopperConfig(attachment) {
451
      },
399
    const defaultBsPopperConfig = {
452
      onUpdate: data => this._handlePopperPlacementChange(data)
400
      placement: attachment,
453
    }
401
      modifiers: [
454
 
402
        {
455
    return {
403
          name: 'flip',
456
      ...defaultBsConfig,
404
          options: {
457
      ...this.config.popperConfig
405
            fallbackPlacements: this._config.fallbackPlacements
458
    }
406
          }
459
  }
407
        },
-
 
408
        {
-
 
409
          name: 'offset',
-
 
410
          options: {
-
 
411
            offset: this._getOffset()
-
 
412
          }
-
 
413
        },
-
 
414
        {
-
 
415
          name: 'preventOverflow',
-
 
416
          options: {
-
 
417
            boundary: this._config.boundary
Línea 460... Línea 418...
460
 
418
          }
461
  _getOffset() {
419
        },
462
    const offset = {}
-
 
463
 
420
        {
464
    if (typeof this.config.offset === 'function') {
421
          name: 'arrow',
Línea 465... Línea 422...
465
      offset.fn = data => {
422
          options: {
466
        data.offsets = {
423
            element: `.${this.constructor.NAME}-arrow`
467
          ...data.offsets,
424
          }
468
          ...this.config.offset(data.offsets, this.element)
425
        },
469
        }
426
        {
Línea 470... Línea 427...
470
 
427
          name: 'preSetPlacement',
471
        return data
-
 
472
      }
-
 
473
    } else {
-
 
474
      offset.offset = this.config.offset
-
 
475
    }
-
 
476
 
-
 
477
    return offset
-
 
478
  }
-
 
479
 
-
 
480
  _getContainer() {
-
 
481
    if (this.config.container === false) {
428
          enabled: true,
Línea 482... Línea 429...
482
      return document.body
429
          phase: 'beforeMain',
483
    }
-
 
484
 
-
 
485
    if (Util.isElement(this.config.container)) {
-
 
486
      return $(this.config.container)
-
 
487
    }
-
 
488
 
430
          fn: data => {
489
    return $(document).find(this.config.container)
-
 
Línea 490... Línea 431...
490
  }
431
            // Pre-set Popper's placement attribute in order to read the arrow sizes properly.
-
 
432
            // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
491
 
433
            this._getTipElement().setAttribute('data-popper-placement', data.state.placement)
492
  _getAttachment(placement) {
-
 
493
    return AttachmentMap[placement.toUpperCase()]
-
 
494
  }
-
 
495
 
-
 
496
  _setListeners() {
-
 
Línea 497... Línea -...
497
    const triggers = this.config.trigger.split(' ')
-
 
498
 
-
 
499
    triggers.forEach(trigger => {
-
 
500
      if (trigger === 'click') {
434
          }
501
        $(this.element).on(
-
 
502
          this.constructor.Event.CLICK,
435
        }
503
          this.config.selector,
436
      ]
Línea 504... Línea -...
504
          event => this.toggle(event)
-
 
505
        )
-
 
506
      } else if (trigger !== TRIGGER_MANUAL) {
437
    }
507
        const eventIn = trigger === TRIGGER_HOVER ?
438
 
508
          this.constructor.Event.MOUSEENTER :
439
    return {
Línea -... Línea 440...
-
 
440
      ...defaultBsPopperConfig,
509
          this.constructor.Event.FOCUSIN
441
      ...execute(this._config.popperConfig, [defaultBsPopperConfig])
510
        const eventOut = trigger === TRIGGER_HOVER ?
442
    }
511
          this.constructor.Event.MOUSELEAVE :
443
  }
512
          this.constructor.Event.FOCUSOUT
444
 
Línea 513... Línea 445...
513
 
445
  _setListeners() {
514
        $(this.element)
-
 
515
          .on(eventIn, this.config.selector, event => this._enter(event))
-
 
Línea 516... Línea -...
516
          .on(eventOut, this.config.selector, event => this._leave(event))
-
 
517
      }
-
 
518
    })
-
 
519
 
-
 
520
    this._hideModalHandler = () => {
-
 
521
      if (this.element) {
446
    const triggers = this._config.trigger.split(' ')
522
        this.hide()
447
 
523
      }
448
    for (const trigger of triggers) {
524
    }
449
      if (trigger === 'click') {
525
 
450
        EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {
526
    $(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler)
451
          const context = this._initializeOnDelegatedTarget(event)
Línea 527... Línea -...
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() {
452
          context.toggle()
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') || ''
453
        })
546
      )
454
      } else if (trigger !== TRIGGER_MANUAL) {
547
 
455
        const eventIn = trigger === TRIGGER_HOVER ?
Línea 548... Línea -...
548
      this.element.setAttribute('title', '')
-
 
549
    }
-
 
550
  }
456
          this.constructor.eventName(EVENT_MOUSEENTER) :
551
 
-
 
552
  _enter(event, context) {
-
 
553
    const dataKey = this.constructor.DATA_KEY
-
 
554
    context = context || $(event.currentTarget).data(dataKey)
-
 
555
 
-
 
Línea 556... Línea 457...
556
    if (!context) {
457
          this.constructor.eventName(EVENT_FOCUSIN)
557
      context = new this.constructor(
458
        const eventOut = trigger === TRIGGER_HOVER ?
558
        event.currentTarget,
459
          this.constructor.eventName(EVENT_MOUSELEAVE) :
559
        this._getDelegateConfig()
460
          this.constructor.eventName(EVENT_FOCUSOUT)
560
      )
461
 
561
      $(event.currentTarget).data(dataKey, context)
462
        EventHandler.on(this._element, eventIn, this._config.selector, event => {
Línea 562... Línea 463...
562
    }
463
          const context = this._initializeOnDelegatedTarget(event)
563
 
464
          context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true
564
    if (event) {
465
          context._enter()
565
      context._activeTrigger[
-
 
566
        event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER
-
 
567
      ] = true
466
        })
Línea 568... Línea 467...
568
    }
467
        EventHandler.on(this._element, eventOut, this._config.selector, event => {
-
 
468
          const context = this._initializeOnDelegatedTarget(event)
569
 
469
          context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =
Línea 570... Línea 470...
570
    if ($(context.getTipElement()).hasClass(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
470
            context._element.contains(event.relatedTarget)
571
      context._hoverState = HOVER_STATE_SHOW
471
 
Línea 572... Línea 472...
572
      return
472
          context._leave()
573
    }
-
 
574
 
473
        })
575
    clearTimeout(context._timeout)
474
      }
576
 
475
    }
577
    context._hoverState = HOVER_STATE_SHOW
476
 
Línea 578... Línea 477...
578
 
477
    this._hideModalHandler = () => {
579
    if (!context.config.delay || !context.config.delay.show) {
-
 
580
      context.show()
478
      if (this._element) {
581
      return
479
        this.hide()
582
    }
480
      }
-
 
481
    }
-
 
482
 
-
 
483
    EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)
-
 
484
  }
-
 
485
 
-
 
486
  _fixTitle() {
-
 
487
    const title = this._element.getAttribute('title')
-
 
488
 
Línea 583... Línea 489...
583
 
489
    if (!title) {
584
    context._timeout = setTimeout(() => {
490
      return
585
      if (context._hoverState === HOVER_STATE_SHOW) {
491
    }
586
        context.show()
492
 
Línea 665... Línea 571...
665
 
571
 
666
    if (typeof config.content === 'number') {
572
    if (typeof config.content === 'number') {
667
      config.content = config.content.toString()
573
      config.content = config.content.toString()
Línea 668... Línea -...
668
    }
-
 
669
 
-
 
670
    Util.typeCheckConfig(
-
 
671
      NAME,
-
 
672
      config,
-
 
673
      this.constructor.DefaultType
-
 
674
    )
-
 
675
 
-
 
676
    if (config.sanitize) {
-
 
677
      config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn)
-
 
678
    }
574
    }
679
 
575
 
Línea 680... Línea 576...
680
    return config
576
    return config
681
  }
577
  }
Línea 682... Línea -...
682
 
-
 
683
  _getDelegateConfig() {
578
 
684
    const config = {}
579
  _getDelegateConfig() {
685
 
580
    const config = {}
686
    if (this.config) {
-
 
687
      for (const key in this.config) {
581
 
688
        if (this.constructor.Default[key] !== this.config[key]) {
582
    for (const [key, value] of Object.entries(this._config)) {
Línea -... Línea 583...
-
 
583
      if (this.constructor.Default[key] !== value) {
-
 
584
        config[key] = value
-
 
585
      }
-
 
586
    }
-
 
587
 
-
 
588
    config.selector = false
689
          config[key] = this.config[key]
589
    config.trigger = 'manual'
690
        }
590
 
Línea 691... Línea 591...
691
      }
591
    // In the future can be replaced with:
692
    }
592
    // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])
693
 
-
 
694
    return config
593
    // `Object.fromEntries(keysWithDifferentValues)`
695
  }
594
    return config
696
 
595
  }
697
  _cleanTipClass() {
-
 
Línea 698... Línea -...
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
 
596
 
705
  _handlePopperPlacementChange(popperData) {
597
  _disposePopper() {
706
    this.tip = popperData.instance.popper
-
 
707
    this._cleanTipClass()
-
 
708
    this.addAttachmentClass(this._getAttachment(popperData.placement))
-
 
709
  }
598
    if (this._popper) {
710
 
599
      this._popper.destroy()
711
  _fixTransition() {
-
 
712
    const tip = this.getTipElement()
-
 
713
    const initConfigAnimation = this.config.animation
-
 
714
 
-
 
715
    if (tip.getAttribute('x-placement') !== null) {
-
 
716
      return
-
 
717
    }
600
      this._popper = null
Línea 718... Línea 601...
718
 
601
    }
719
    $(tip).removeClass(CLASS_NAME_FADE)
602
 
720
    this.config.animation = false
603
    if (this.tip) {
721
    this.hide()
-
 
722
    this.show()
-
 
723
    this.config.animation = initConfigAnimation
604
      this.tip.remove()
Línea 724... Línea 605...
724
  }
605
      this.tip = null
725
 
606
    }
726
  // Static
607
  }
Línea 727... Línea 608...
727
  static _jQueryInterface(config) {
608
 
728
    return this.each(function () {
609
  // Static
729
      const $element = $(this)
-
 
730
      let data = $element.data(DATA_KEY)
610
  static jQueryInterface(config) {
Línea 731... Línea -...
731
      const _config = typeof config === 'object' && config
-
 
732
 
-
 
733
      if (!data && /dispose|hide/.test(config)) {
-
 
734
        return
-
 
735
      }
-
 
736
 
611
    return this.each(function () {
737
      if (!data) {
-
 
738
        data = new Tooltip(this, _config)
612
      const data = Tooltip.getOrCreateInstance(this, config)
739
        $element.data(DATA_KEY, data)
613
 
740
      }
614
      if (typeof config !== 'string') {
Línea 741... Línea 615...
741
 
615
        return
742
      if (typeof config === 'string') {
616
      }
743
        if (typeof data[config] === 'undefined') {
617
 
Línea 744... Línea -...
744
          throw new TypeError(`No method named "${config}"`)
-
 
745
        }
618
      if (typeof data[config] === 'undefined') {
746
 
-
 
747
        data[config]()
-
 
748
      }
-
 
749
    })
-
 
Línea 750... Línea 619...
750
  }
619
        throw new TypeError(`No method named "${config}"`)