corrade-http-templates – Blame information for rev 61

Subversion Repositories:
Rev:
Rev Author Line No. Line
61 office 1 import $ from 'jquery'
2 import Popper from 'popper.js'
3 import Util from './util'
4  
5 /**
6 * --------------------------------------------------------------------------
7 * Bootstrap (v4.1.3): tooltip.js
8 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
9 * --------------------------------------------------------------------------
10 */
11  
12 const Tooltip = (($) => {
13 /**
14 * ------------------------------------------------------------------------
15 * Constants
16 * ------------------------------------------------------------------------
17 */
18  
19 const NAME = 'tooltip'
20 const VERSION = '4.1.3'
21 const DATA_KEY = 'bs.tooltip'
22 const EVENT_KEY = `.${DATA_KEY}`
23 const JQUERY_NO_CONFLICT = $.fn[NAME]
24 const CLASS_PREFIX = 'bs-tooltip'
25 const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
26  
27 const DefaultType = {
28 animation : 'boolean',
29 template : 'string',
30 title : '(string|element|function)',
31 trigger : 'string',
32 delay : '(number|object)',
33 html : 'boolean',
34 selector : '(string|boolean)',
35 placement : '(string|function)',
36 offset : '(number|string)',
37 container : '(string|element|boolean)',
38 fallbackPlacement : '(string|array)',
39 boundary : '(string|element)'
40 }
41  
42 const AttachmentMap = {
43 AUTO : 'auto',
44 TOP : 'top',
45 RIGHT : 'right',
46 BOTTOM : 'bottom',
47 LEFT : 'left'
48 }
49  
50 const Default = {
51 animation : true,
52 template : '<div class="tooltip" role="tooltip">' +
53 '<div class="arrow"></div>' +
54 '<div class="tooltip-inner"></div></div>',
55 trigger : 'hover focus',
56 title : '',
57 delay : 0,
58 html : false,
59 selector : false,
60 placement : 'top',
61 offset : 0,
62 container : false,
63 fallbackPlacement : 'flip',
64 boundary : 'scrollParent'
65 }
66  
67 const HoverState = {
68 SHOW : 'show',
69 OUT : 'out'
70 }
71  
72 const Event = {
73 HIDE : `hide${EVENT_KEY}`,
74 HIDDEN : `hidden${EVENT_KEY}`,
75 SHOW : `show${EVENT_KEY}`,
76 SHOWN : `shown${EVENT_KEY}`,
77 INSERTED : `inserted${EVENT_KEY}`,
78 CLICK : `click${EVENT_KEY}`,
79 FOCUSIN : `focusin${EVENT_KEY}`,
80 FOCUSOUT : `focusout${EVENT_KEY}`,
81 MOUSEENTER : `mouseenter${EVENT_KEY}`,
82 MOUSELEAVE : `mouseleave${EVENT_KEY}`
83 }
84  
85 const ClassName = {
86 FADE : 'fade',
87 SHOW : 'show'
88 }
89  
90 const Selector = {
91 TOOLTIP : '.tooltip',
92 TOOLTIP_INNER : '.tooltip-inner',
93 ARROW : '.arrow'
94 }
95  
96 const Trigger = {
97 HOVER : 'hover',
98 FOCUS : 'focus',
99 CLICK : 'click',
100 MANUAL : 'manual'
101 }
102  
103  
104 /**
105 * ------------------------------------------------------------------------
106 * Class Definition
107 * ------------------------------------------------------------------------
108 */
109  
110 class Tooltip {
111 constructor(element, config) {
112 /**
113 * Check for Popper dependency
114 * Popper - https://popper.js.org
115 */
116 if (typeof Popper === 'undefined') {
117 throw new TypeError('Bootstrap tooltips require Popper.js (https://popper.js.org)')
118 }
119  
120 // private
121 this._isEnabled = true
122 this._timeout = 0
123 this._hoverState = ''
124 this._activeTrigger = {}
125 this._popper = null
126  
127 // Protected
128 this.element = element
129 this.config = this._getConfig(config)
130 this.tip = null
131  
132 this._setListeners()
133 }
134  
135 // Getters
136  
137 static get VERSION() {
138 return VERSION
139 }
140  
141 static get Default() {
142 return Default
143 }
144  
145 static get NAME() {
146 return NAME
147 }
148  
149 static get DATA_KEY() {
150 return DATA_KEY
151 }
152  
153 static get Event() {
154 return Event
155 }
156  
157 static get EVENT_KEY() {
158 return EVENT_KEY
159 }
160  
161 static get DefaultType() {
162 return DefaultType
163 }
164  
165 // Public
166  
167 enable() {
168 this._isEnabled = true
169 }
170  
171 disable() {
172 this._isEnabled = false
173 }
174  
175 toggleEnabled() {
176 this._isEnabled = !this._isEnabled
177 }
178  
179 toggle(event) {
180 if (!this._isEnabled) {
181 return
182 }
183  
184 if (event) {
185 const dataKey = this.constructor.DATA_KEY
186 let context = $(event.currentTarget).data(dataKey)
187  
188 if (!context) {
189 context = new this.constructor(
190 event.currentTarget,
191 this._getDelegateConfig()
192 )
193 $(event.currentTarget).data(dataKey, context)
194 }
195  
196 context._activeTrigger.click = !context._activeTrigger.click
197  
198 if (context._isWithActiveTrigger()) {
199 context._enter(null, context)
200 } else {
201 context._leave(null, context)
202 }
203 } else {
204 if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {
205 this._leave(null, this)
206 return
207 }
208  
209 this._enter(null, this)
210 }
211 }
212  
213 dispose() {
214 clearTimeout(this._timeout)
215  
216 $.removeData(this.element, this.constructor.DATA_KEY)
217  
218 $(this.element).off(this.constructor.EVENT_KEY)
219 $(this.element).closest('.modal').off('hide.bs.modal')
220  
221 if (this.tip) {
222 $(this.tip).remove()
223 }
224  
225 this._isEnabled = null
226 this._timeout = null
227 this._hoverState = null
228 this._activeTrigger = null
229 if (this._popper !== null) {
230 this._popper.destroy()
231 }
232  
233 this._popper = null
234 this.element = null
235 this.config = null
236 this.tip = null
237 }
238  
239 show() {
240 if ($(this.element).css('display') === 'none') {
241 throw new Error('Please use show on visible elements')
242 }
243  
244 const showEvent = $.Event(this.constructor.Event.SHOW)
245 if (this.isWithContent() && this._isEnabled) {
246 $(this.element).trigger(showEvent)
247  
248 const isInTheDom = $.contains(
249 this.element.ownerDocument.documentElement,
250 this.element
251 )
252  
253 if (showEvent.isDefaultPrevented() || !isInTheDom) {
254 return
255 }
256  
257 const tip = this.getTipElement()
258 const tipId = Util.getUID(this.constructor.NAME)
259  
260 tip.setAttribute('id', tipId)
261 this.element.setAttribute('aria-describedby', tipId)
262  
263 this.setContent()
264  
265 if (this.config.animation) {
266 $(tip).addClass(ClassName.FADE)
267 }
268  
269 const placement = typeof this.config.placement === 'function'
270 ? this.config.placement.call(this, tip, this.element)
271 : this.config.placement
272  
273 const attachment = this._getAttachment(placement)
274 this.addAttachmentClass(attachment)
275  
276 const container = this.config.container === false ? document.body : $(document).find(this.config.container)
277  
278 $(tip).data(this.constructor.DATA_KEY, this)
279  
280 if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
281 $(tip).appendTo(container)
282 }
283  
284 $(this.element).trigger(this.constructor.Event.INSERTED)
285  
286 this._popper = new Popper(this.element, tip, {
287 placement: attachment,
288 modifiers: {
289 offset: {
290 offset: this.config.offset
291 },
292 flip: {
293 behavior: this.config.fallbackPlacement
294 },
295 arrow: {
296 element: Selector.ARROW
297 },
298 preventOverflow: {
299 boundariesElement: this.config.boundary
300 }
301 },
302 onCreate: (data) => {
303 if (data.originalPlacement !== data.placement) {
304 this._handlePopperPlacementChange(data)
305 }
306 },
307 onUpdate: (data) => {
308 this._handlePopperPlacementChange(data)
309 }
310 })
311  
312 $(tip).addClass(ClassName.SHOW)
313  
314 // If this is a touch-enabled device we add extra
315 // empty mouseover listeners to the body's immediate children;
316 // only needed because of broken event delegation on iOS
317 // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
318 if ('ontouchstart' in document.documentElement) {
319 $(document.body).children().on('mouseover', null, $.noop)
320 }
321  
322 const complete = () => {
323 if (this.config.animation) {
324 this._fixTransition()
325 }
326 const prevHoverState = this._hoverState
327 this._hoverState = null
328  
329 $(this.element).trigger(this.constructor.Event.SHOWN)
330  
331 if (prevHoverState === HoverState.OUT) {
332 this._leave(null, this)
333 }
334 }
335  
336 if ($(this.tip).hasClass(ClassName.FADE)) {
337 const transitionDuration = Util.getTransitionDurationFromElement(this.tip)
338  
339 $(this.tip)
340 .one(Util.TRANSITION_END, complete)
341 .emulateTransitionEnd(transitionDuration)
342 } else {
343 complete()
344 }
345 }
346 }
347  
348 hide(callback) {
349 const tip = this.getTipElement()
350 const hideEvent = $.Event(this.constructor.Event.HIDE)
351 const complete = () => {
352 if (this._hoverState !== HoverState.SHOW && tip.parentNode) {
353 tip.parentNode.removeChild(tip)
354 }
355  
356 this._cleanTipClass()
357 this.element.removeAttribute('aria-describedby')
358 $(this.element).trigger(this.constructor.Event.HIDDEN)
359 if (this._popper !== null) {
360 this._popper.destroy()
361 }
362  
363 if (callback) {
364 callback()
365 }
366 }
367  
368 $(this.element).trigger(hideEvent)
369  
370 if (hideEvent.isDefaultPrevented()) {
371 return
372 }
373  
374 $(tip).removeClass(ClassName.SHOW)
375  
376 // If this is a touch-enabled device we remove the extra
377 // empty mouseover listeners we added for iOS support
378 if ('ontouchstart' in document.documentElement) {
379 $(document.body).children().off('mouseover', null, $.noop)
380 }
381  
382 this._activeTrigger[Trigger.CLICK] = false
383 this._activeTrigger[Trigger.FOCUS] = false
384 this._activeTrigger[Trigger.HOVER] = false
385  
386 if ($(this.tip).hasClass(ClassName.FADE)) {
387 const transitionDuration = Util.getTransitionDurationFromElement(tip)
388  
389 $(tip)
390 .one(Util.TRANSITION_END, complete)
391 .emulateTransitionEnd(transitionDuration)
392 } else {
393 complete()
394 }
395  
396 this._hoverState = ''
397 }
398  
399 update() {
400 if (this._popper !== null) {
401 this._popper.scheduleUpdate()
402 }
403 }
404  
405 // Protected
406  
407 isWithContent() {
408 return Boolean(this.getTitle())
409 }
410  
411 addAttachmentClass(attachment) {
412 $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
413 }
414  
415 getTipElement() {
416 this.tip = this.tip || $(this.config.template)[0]
417 return this.tip
418 }
419  
420 setContent() {
421 const tip = this.getTipElement()
422 this.setElementContent($(tip.querySelectorAll(Selector.TOOLTIP_INNER)), this.getTitle())
423 $(tip).removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)
424 }
425  
426 setElementContent($element, content) {
427 const html = this.config.html
428 if (typeof content === 'object' && (content.nodeType || content.jquery)) {
429 // Content is a DOM node or a jQuery
430 if (html) {
431 if (!$(content).parent().is($element)) {
432 $element.empty().append(content)
433 }
434 } else {
435 $element.text($(content).text())
436 }
437 } else {
438 $element[html ? 'html' : 'text'](content)
439 }
440 }
441  
442 getTitle() {
443 let title = this.element.getAttribute('data-original-title')
444  
445 if (!title) {
446 title = typeof this.config.title === 'function'
447 ? this.config.title.call(this.element)
448 : this.config.title
449 }
450  
451 return title
452 }
453  
454 // Private
455  
456 _getAttachment(placement) {
457 return AttachmentMap[placement.toUpperCase()]
458 }
459  
460 _setListeners() {
461 const triggers = this.config.trigger.split(' ')
462  
463 triggers.forEach((trigger) => {
464 if (trigger === 'click') {
465 $(this.element).on(
466 this.constructor.Event.CLICK,
467 this.config.selector,
468 (event) => this.toggle(event)
469 )
470 } else if (trigger !== Trigger.MANUAL) {
471 const eventIn = trigger === Trigger.HOVER
472 ? this.constructor.Event.MOUSEENTER
473 : this.constructor.Event.FOCUSIN
474 const eventOut = trigger === Trigger.HOVER
475 ? this.constructor.Event.MOUSELEAVE
476 : this.constructor.Event.FOCUSOUT
477  
478 $(this.element)
479 .on(
480 eventIn,
481 this.config.selector,
482 (event) => this._enter(event)
483 )
484 .on(
485 eventOut,
486 this.config.selector,
487 (event) => this._leave(event)
488 )
489 }
490  
491 $(this.element).closest('.modal').on(
492 'hide.bs.modal',
493 () => this.hide()
494 )
495 })
496  
497 if (this.config.selector) {
498 this.config = {
499 ...this.config,
500 trigger: 'manual',
501 selector: ''
502 }
503 } else {
504 this._fixTitle()
505 }
506 }
507  
508 _fixTitle() {
509 const titleType = typeof this.element.getAttribute('data-original-title')
510 if (this.element.getAttribute('title') ||
511 titleType !== 'string') {
512 this.element.setAttribute(
513 'data-original-title',
514 this.element.getAttribute('title') || ''
515 )
516 this.element.setAttribute('title', '')
517 }
518 }
519  
520 _enter(event, context) {
521 const dataKey = this.constructor.DATA_KEY
522  
523 context = context || $(event.currentTarget).data(dataKey)
524  
525 if (!context) {
526 context = new this.constructor(
527 event.currentTarget,
528 this._getDelegateConfig()
529 )
530 $(event.currentTarget).data(dataKey, context)
531 }
532  
533 if (event) {
534 context._activeTrigger[
535 event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER
536 ] = true
537 }
538  
539 if ($(context.getTipElement()).hasClass(ClassName.SHOW) ||
540 context._hoverState === HoverState.SHOW) {
541 context._hoverState = HoverState.SHOW
542 return
543 }
544  
545 clearTimeout(context._timeout)
546  
547 context._hoverState = HoverState.SHOW
548  
549 if (!context.config.delay || !context.config.delay.show) {
550 context.show()
551 return
552 }
553  
554 context._timeout = setTimeout(() => {
555 if (context._hoverState === HoverState.SHOW) {
556 context.show()
557 }
558 }, context.config.delay.show)
559 }
560  
561 _leave(event, context) {
562 const dataKey = this.constructor.DATA_KEY
563  
564 context = context || $(event.currentTarget).data(dataKey)
565  
566 if (!context) {
567 context = new this.constructor(
568 event.currentTarget,
569 this._getDelegateConfig()
570 )
571 $(event.currentTarget).data(dataKey, context)
572 }
573  
574 if (event) {
575 context._activeTrigger[
576 event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER
577 ] = false
578 }
579  
580 if (context._isWithActiveTrigger()) {
581 return
582 }
583  
584 clearTimeout(context._timeout)
585  
586 context._hoverState = HoverState.OUT
587  
588 if (!context.config.delay || !context.config.delay.hide) {
589 context.hide()
590 return
591 }
592  
593 context._timeout = setTimeout(() => {
594 if (context._hoverState === HoverState.OUT) {
595 context.hide()
596 }
597 }, context.config.delay.hide)
598 }
599  
600 _isWithActiveTrigger() {
601 for (const trigger in this._activeTrigger) {
602 if (this._activeTrigger[trigger]) {
603 return true
604 }
605 }
606  
607 return false
608 }
609  
610 _getConfig(config) {
611 config = {
612 ...this.constructor.Default,
613 ...$(this.element).data(),
614 ...typeof config === 'object' && config ? config : {}
615 }
616  
617 if (typeof config.delay === 'number') {
618 config.delay = {
619 show: config.delay,
620 hide: config.delay
621 }
622 }
623  
624 if (typeof config.title === 'number') {
625 config.title = config.title.toString()
626 }
627  
628 if (typeof config.content === 'number') {
629 config.content = config.content.toString()
630 }
631  
632 Util.typeCheckConfig(
633 NAME,
634 config,
635 this.constructor.DefaultType
636 )
637  
638 return config
639 }
640  
641 _getDelegateConfig() {
642 const config = {}
643  
644 if (this.config) {
645 for (const key in this.config) {
646 if (this.constructor.Default[key] !== this.config[key]) {
647 config[key] = this.config[key]
648 }
649 }
650 }
651  
652 return config
653 }
654  
655 _cleanTipClass() {
656 const $tip = $(this.getTipElement())
657 const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
658 if (tabClass !== null && tabClass.length) {
659 $tip.removeClass(tabClass.join(''))
660 }
661 }
662  
663 _handlePopperPlacementChange(popperData) {
664 const popperInstance = popperData.instance
665 this.tip = popperInstance.popper
666 this._cleanTipClass()
667 this.addAttachmentClass(this._getAttachment(popperData.placement))
668 }
669  
670 _fixTransition() {
671 const tip = this.getTipElement()
672 const initConfigAnimation = this.config.animation
673 if (tip.getAttribute('x-placement') !== null) {
674 return
675 }
676 $(tip).removeClass(ClassName.FADE)
677 this.config.animation = false
678 this.hide()
679 this.show()
680 this.config.animation = initConfigAnimation
681 }
682  
683 // Static
684  
685 static _jQueryInterface(config) {
686 return this.each(function () {
687 let data = $(this).data(DATA_KEY)
688 const _config = typeof config === 'object' && config
689  
690 if (!data && /dispose|hide/.test(config)) {
691 return
692 }
693  
694 if (!data) {
695 data = new Tooltip(this, _config)
696 $(this).data(DATA_KEY, data)
697 }
698  
699 if (typeof config === 'string') {
700 if (typeof data[config] === 'undefined') {
701 throw new TypeError(`No method named "${config}"`)
702 }
703 data[config]()
704 }
705 })
706 }
707 }
708  
709 /**
710 * ------------------------------------------------------------------------
711 * jQuery
712 * ------------------------------------------------------------------------
713 */
714  
715 $.fn[NAME] = Tooltip._jQueryInterface
716 $.fn[NAME].Constructor = Tooltip
717 $.fn[NAME].noConflict = function () {
718 $.fn[NAME] = JQUERY_NO_CONFLICT
719 return Tooltip._jQueryInterface
720 }
721  
722 return Tooltip
723 })($, Popper)
724  
725 export default Tooltip