modal.js 17 KB
Newer Older
1
2
/**
 * --------------------------------------------------------------------------
XhmikosR's avatar
XhmikosR committed
3
 * Bootstrap (v4.3.1): modal.js
4
5
6
7
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

8
9
10
11
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selectorEngine'
12
13
import Util from './util'

Johann-S's avatar
Johann-S committed
14
15
16
17
18
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
19

Johann-S's avatar
Johann-S committed
20
const NAME               = 'modal'
XhmikosR's avatar
XhmikosR committed
21
const VERSION            = '4.3.1'
Johann-S's avatar
Johann-S committed
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const DATA_KEY           = 'bs.modal'
const EVENT_KEY          = `.${DATA_KEY}`
const DATA_API_KEY       = '.data-api'
const ESCAPE_KEYCODE     = 27 // KeyboardEvent.which value for Escape (Esc) key

const Default = {
  backdrop : true,
  keyboard : true,
  focus    : true,
  show     : true
}

const DefaultType = {
  backdrop : '(boolean|string)',
  keyboard : 'boolean',
  focus    : 'boolean',
  show     : 'boolean'
}

const Event = {
  HIDE              : `hide${EVENT_KEY}`,
  HIDDEN            : `hidden${EVENT_KEY}`,
  SHOW              : `show${EVENT_KEY}`,
  SHOWN             : `shown${EVENT_KEY}`,
  FOCUSIN           : `focusin${EVENT_KEY}`,
  RESIZE            : `resize${EVENT_KEY}`,
  CLICK_DISMISS     : `click.dismiss${EVENT_KEY}`,
  KEYDOWN_DISMISS   : `keydown.dismiss${EVENT_KEY}`,
  MOUSEUP_DISMISS   : `mouseup.dismiss${EVENT_KEY}`,
  MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,
  CLICK_DATA_API    : `click${EVENT_KEY}${DATA_API_KEY}`
}

const ClassName = {
Shohei Yoshida's avatar
Shohei Yoshida committed
56
  SCROLLABLE         : 'modal-dialog-scrollable',
Johann-S's avatar
Johann-S committed
57
58
59
60
61
62
63
64
  SCROLLBAR_MEASURER : 'modal-scrollbar-measure',
  BACKDROP           : 'modal-backdrop',
  OPEN               : 'modal-open',
  FADE               : 'fade',
  SHOW               : 'show'
}

const Selector = {
65
  DIALOG         : '.modal-dialog',
Shohei Yoshida's avatar
Shohei Yoshida committed
66
  MODAL_BODY     : '.modal-body',
67
68
69
70
  DATA_TOGGLE    : '[data-toggle="modal"]',
  DATA_DISMISS   : '[data-dismiss="modal"]',
  FIXED_CONTENT  : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
  STICKY_CONTENT : '.sticky-top'
Johann-S's avatar
Johann-S committed
71
}
fat's avatar
fat committed
72

Johann-S's avatar
Johann-S committed
73
74
75
76
77
/**
 * ------------------------------------------------------------------------
 * Class Definition
 * ------------------------------------------------------------------------
 */
78

Johann-S's avatar
Johann-S committed
79
80
81
82
class Modal {
  constructor(element, config) {
    this._config              = this._getConfig(config)
    this._element             = element
83
    this._dialog              = SelectorEngine.findOne(Selector.DIALOG, element)
Johann-S's avatar
Johann-S committed
84
85
86
87
    this._backdrop            = null
    this._isShown             = false
    this._isBodyOverflowing   = false
    this._ignoreBackdropClick = false
88
    this._isTransitioning     = false
Johann-S's avatar
Johann-S committed
89
    this._scrollbarWidth      = 0
90
    Data.setData(element, DATA_KEY, this)
91
92
  }

Johann-S's avatar
Johann-S committed
93
94
95
96
  // Getters

  static get VERSION() {
    return VERSION
97
98
  }

Johann-S's avatar
Johann-S committed
99
100
101
  static get Default() {
    return Default
  }
102

Johann-S's avatar
Johann-S committed
103
  // Public
104

Johann-S's avatar
Johann-S committed
105
106
107
  toggle(relatedTarget) {
    return this._isShown ? this.hide() : this.show(relatedTarget)
  }
108

Johann-S's avatar
Johann-S committed
109
  show(relatedTarget) {
110
    if (this._isShown || this._isTransitioning) {
Johann-S's avatar
Johann-S committed
111
      return
112
113
    }

114
    if (this._element.classList.contains(ClassName.FADE)) {
Johann-S's avatar
Johann-S committed
115
      this._isTransitioning = true
116
117
    }

118
    const showEvent = EventHandler.trigger(this._element, Event.SHOW, {
Johann-S's avatar
Johann-S committed
119
120
      relatedTarget
    })
121

Johann-S's avatar
Johann-S committed
122
    if (this._isShown || showEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
123
124
      return
    }
125

Johann-S's avatar
Johann-S committed
126
    this._isShown = true
127

Johann-S's avatar
Johann-S committed
128
129
    this._checkScrollbar()
    this._setScrollbar()
130

Johann-S's avatar
Johann-S committed
131
    this._adjustDialog()
132

Johann-S's avatar
Johann-S committed
133
134
    this._setEscapeEvent()
    this._setResizeEvent()
David Bailey's avatar
David Bailey committed
135

136
    EventHandler.on(this._element,
Johann-S's avatar
Johann-S committed
137
138
139
140
      Event.CLICK_DISMISS,
      Selector.DATA_DISMISS,
      (event) => this.hide(event)
    )
141

142
143
144
    EventHandler.on(this._dialog, Event.MOUSEDOWN_DISMISS, () => {
      EventHandler.one(this._element, Event.MOUSEUP_DISMISS, (event) => {
        if (event.target === this._element) {
Johann-S's avatar
Johann-S committed
145
146
147
148
          this._ignoreBackdropClick = true
        }
      })
    })
149

Johann-S's avatar
Johann-S committed
150
151
    this._showBackdrop(() => this._showElement(relatedTarget))
  }
152

Johann-S's avatar
Johann-S committed
153
154
155
156
  hide(event) {
    if (event) {
      event.preventDefault()
    }
157

158
    if (!this._isShown || this._isTransitioning) {
Johann-S's avatar
Johann-S committed
159
      return
160
161
    }

162
    const hideEvent = EventHandler.trigger(this._element, Event.HIDE)
163

Johann-S's avatar
Johann-S committed
164
    if (!this._isShown || hideEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
165
166
      return
    }
167

Johann-S's avatar
Johann-S committed
168
    this._isShown = false
169
    const transition = this._element.classList.contains(ClassName.FADE)
170

Johann-S's avatar
Johann-S committed
171
172
173
    if (transition) {
      this._isTransitioning = true
    }
174

Johann-S's avatar
Johann-S committed
175
176
    this._setEscapeEvent()
    this._setResizeEvent()
177

178
    EventHandler.off(document, Event.FOCUSIN)
179

180
    this._element.classList.remove(ClassName.SHOW)
181

182
183
    EventHandler.off(this._element, Event.CLICK_DISMISS)
    EventHandler.off(this._dialog, Event.MOUSEDOWN_DISMISS)
184
185


Johann-S's avatar
Johann-S committed
186
187
    if (transition) {
      const transitionDuration  = Util.getTransitionDurationFromElement(this._element)
188

189
      EventHandler.one(this._element, Util.TRANSITION_END, (event) => this._hideModal(event))
Johann-S's avatar
Johann-S committed
190
      Util.emulateTransitionEnd(this._element, transitionDuration)
Johann-S's avatar
Johann-S committed
191
192
193
194
    } else {
      this._hideModal()
    }
  }
195

Johann-S's avatar
Johann-S committed
196
  dispose() {
197
    [window, this._element, this._dialog]
198
      .forEach((htmlElement) => EventHandler.off(htmlElement, EVENT_KEY))
199
200
201
202
203
204

    /**
     * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`
     * Do not move `document` in `htmlElements` array
     * It will remove `Event.CLICK_DATA_API` event that should remain
     */
205
    EventHandler.off(document, Event.FOCUSIN)
206

207
    Data.removeData(this._element, DATA_KEY)
208

Johann-S's avatar
Johann-S committed
209
210
211
212
213
214
215
    this._config              = null
    this._element             = null
    this._dialog              = null
    this._backdrop            = null
    this._isShown             = null
    this._isBodyOverflowing   = null
    this._ignoreBackdropClick = null
216
    this._isTransitioning     = null
Johann-S's avatar
Johann-S committed
217
218
    this._scrollbarWidth      = null
  }
fat's avatar
fat committed
219

Johann-S's avatar
Johann-S committed
220
221
222
  handleUpdate() {
    this._adjustDialog()
  }
fat's avatar
fat committed
223

Johann-S's avatar
Johann-S committed
224
  // Private
fat's avatar
fat committed
225

Johann-S's avatar
Johann-S committed
226
227
228
229
  _getConfig(config) {
    config = {
      ...Default,
      ...config
230
    }
Johann-S's avatar
Johann-S committed
231
232
233
    Util.typeCheckConfig(NAME, config, DefaultType)
    return config
  }
234

Johann-S's avatar
Johann-S committed
235
  _showElement(relatedTarget) {
236
    const transition = this._element.classList.contains(ClassName.FADE)
237

Johann-S's avatar
Johann-S committed
238
239
240
241
    if (!this._element.parentNode ||
        this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
      // Don't move modal's DOM position
      document.body.appendChild(this._element)
fat's avatar
fat committed
242
243
    }

Johann-S's avatar
Johann-S committed
244
245
    this._element.style.display = 'block'
    this._element.removeAttribute('aria-hidden')
246
    this._element.setAttribute('aria-modal', true)
Shohei Yoshida's avatar
Shohei Yoshida committed
247

248
    if (this._dialog.classList.contains(ClassName.SCROLLABLE)) {
249
      SelectorEngine.findOne(Selector.MODAL_BODY, this._dialog).scrollTop = 0
Shohei Yoshida's avatar
Shohei Yoshida committed
250
251
252
    } else {
      this._element.scrollTop = 0
    }
253

Johann-S's avatar
Johann-S committed
254
255
256
    if (transition) {
      Util.reflow(this._element)
    }
257

258
    this._element.classList.add(ClassName.SHOW)
259

Johann-S's avatar
Johann-S committed
260
261
262
    if (this._config.focus) {
      this._enforceFocus()
    }
263

Johann-S's avatar
Johann-S committed
264
    const transitionComplete = () => {
Jacob Thornton's avatar
Jacob Thornton committed
265
      if (this._config.focus) {
Johann-S's avatar
Johann-S committed
266
        this._element.focus()
Jacob Thornton's avatar
Jacob Thornton committed
267
      }
Johann-S's avatar
Johann-S committed
268
      this._isTransitioning = false
269
270
271
      EventHandler.trigger(this._element, Event.SHOWN, {
        relatedTarget
      })
Johann-S's avatar
Johann-S committed
272
    }
273

Johann-S's avatar
Johann-S committed
274
    if (transition) {
275
      const transitionDuration  = Util.getTransitionDurationFromElement(this._dialog)
276

277
278
      EventHandler.one(this._dialog, Util.TRANSITION_END, transitionComplete)
      Util.emulateTransitionEnd(this._dialog, transitionDuration)
Johann-S's avatar
Johann-S committed
279
280
281
282
283
284
    } else {
      transitionComplete()
    }
  }

  _enforceFocus() {
Johann-S's avatar
Johann-S committed
285
286
287
288
289
290
291
292
    EventHandler.off(document, Event.FOCUSIN) // guard against infinite focus loop
    EventHandler.on(document, Event.FOCUSIN, (event) => {
      if (document !== event.target &&
          this._element !== event.target &&
          !this._element.contains(event.target)) {
        this._element.focus()
      }
    })
Johann-S's avatar
Johann-S committed
293
  }
294

Johann-S's avatar
Johann-S committed
295
296
  _setEscapeEvent() {
    if (this._isShown && this._config.keyboard) {
297
      EventHandler.on(this._element, Event.KEYDOWN_DISMISS, (event) => {
Johann-S's avatar
Johann-S committed
298
299
300
301
302
303
        if (event.which === ESCAPE_KEYCODE) {
          event.preventDefault()
          this.hide()
        }
      })
    } else if (!this._isShown) {
304
      EventHandler.off(this._element, Event.KEYDOWN_DISMISS)
305
    }
Johann-S's avatar
Johann-S committed
306
  }
307

Johann-S's avatar
Johann-S committed
308
309
  _setResizeEvent() {
    if (this._isShown) {
310
      EventHandler.on(window, Event.RESIZE, (event) => this.handleUpdate(event))
Johann-S's avatar
Johann-S committed
311
    } else {
312
      EventHandler.off(window, Event.RESIZE)
313
    }
Johann-S's avatar
Johann-S committed
314
  }
315

Johann-S's avatar
Johann-S committed
316
317
318
  _hideModal() {
    this._element.style.display = 'none'
    this._element.setAttribute('aria-hidden', true)
319
    this._element.removeAttribute('aria-modal')
Johann-S's avatar
Johann-S committed
320
321
    this._isTransitioning = false
    this._showBackdrop(() => {
322
      document.body.classList.remove(ClassName.OPEN)
Johann-S's avatar
Johann-S committed
323
324
      this._resetAdjustments()
      this._resetScrollbar()
325
      EventHandler.trigger(this._element, Event.HIDDEN)
Johann-S's avatar
Johann-S committed
326
327
328
329
330
    })
  }

  _removeBackdrop() {
    if (this._backdrop) {
331
      this._backdrop.parentNode.removeChild(this._backdrop)
Johann-S's avatar
Johann-S committed
332
      this._backdrop = null
333
    }
Johann-S's avatar
Johann-S committed
334
  }
335

Johann-S's avatar
Johann-S committed
336
  _showBackdrop(callback) {
337
338
339
    const animate = this._element.classList.contains(ClassName.FADE)
      ? ClassName.FADE
      : ''
Johann-S's avatar
Johann-S committed
340
341
342
343
344
345
346

    if (this._isShown && this._config.backdrop) {
      this._backdrop = document.createElement('div')
      this._backdrop.className = ClassName.BACKDROP

      if (animate) {
        this._backdrop.classList.add(animate)
347
348
      }

349
      document.body.appendChild(this._backdrop)
Johann-S's avatar
Johann-S committed
350

351
      EventHandler.on(this._element, Event.CLICK_DISMISS, (event) => {
Johann-S's avatar
Johann-S committed
352
353
354
355
356
357
358
359
360
361
362
363
        if (this._ignoreBackdropClick) {
          this._ignoreBackdropClick = false
          return
        }
        if (event.target !== event.currentTarget) {
          return
        }
        if (this._config.backdrop === 'static') {
          this._element.focus()
        } else {
          this.hide()
        }
364
365
      })

Johann-S's avatar
Johann-S committed
366
367
      if (animate) {
        Util.reflow(this._backdrop)
368
369
      }

370
      this._backdrop.classList.add(ClassName.SHOW)
371

Johann-S's avatar
Johann-S committed
372
373
374
      if (!callback) {
        return
      }
375

Johann-S's avatar
Johann-S committed
376
377
378
379
      if (!animate) {
        callback()
        return
      }
380

Johann-S's avatar
Johann-S committed
381
      const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)
382

383
      EventHandler.one(this._backdrop, Util.TRANSITION_END, callback)
Johann-S's avatar
Johann-S committed
384
      Util.emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
Johann-S's avatar
Johann-S committed
385
    } else if (!this._isShown && this._backdrop) {
386
      this._backdrop.classList.remove(ClassName.SHOW)
387

Johann-S's avatar
Johann-S committed
388
389
390
      const callbackRemove = () => {
        this._removeBackdrop()
        if (callback) {
391
392
          callback()
        }
Johann-S's avatar
Johann-S committed
393
      }
394

395
      if (this._element.classList.contains(ClassName.FADE)) {
396
        const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)
397
        EventHandler.one(this._backdrop, Util.TRANSITION_END, callbackRemove)
398
        Util.emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
Johann-S's avatar
Johann-S committed
399
400
      } else {
        callbackRemove()
401
      }
Johann-S's avatar
Johann-S committed
402
403
    } else if (callback) {
      callback()
404
    }
Johann-S's avatar
Johann-S committed
405
  }
406

Johann-S's avatar
Johann-S committed
407
408
409
410
  // ----------------------------------------------------------------------
  // the following methods are used to handle overflowing modals
  // todo (fat): these should probably be refactored out of modal.js
  // ----------------------------------------------------------------------
411

Johann-S's avatar
Johann-S committed
412
413
414
  _adjustDialog() {
    const isModalOverflowing =
      this._element.scrollHeight > document.documentElement.clientHeight
415

Johann-S's avatar
Johann-S committed
416
417
    if (!this._isBodyOverflowing && isModalOverflowing) {
      this._element.style.paddingLeft = `${this._scrollbarWidth}px`
418
419
    }

Johann-S's avatar
Johann-S committed
420
421
    if (this._isBodyOverflowing && !isModalOverflowing) {
      this._element.style.paddingRight = `${this._scrollbarWidth}px`
422
    }
Johann-S's avatar
Johann-S committed
423
  }
424

Johann-S's avatar
Johann-S committed
425
426
427
428
  _resetAdjustments() {
    this._element.style.paddingLeft = ''
    this._element.style.paddingRight = ''
  }
429

Johann-S's avatar
Johann-S committed
430
431
432
433
434
  _checkScrollbar() {
    const rect = document.body.getBoundingClientRect()
    this._isBodyOverflowing = rect.left + rect.right < window.innerWidth
    this._scrollbarWidth = this._getScrollbarWidth()
  }
435

Johann-S's avatar
Johann-S committed
436
437
438
439
440
441
  _setScrollbar() {
    if (this._isBodyOverflowing) {
      // Note: DOMNode.style.paddingRight returns the actual value or '' if not set
      //   while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set

      // Adjust fixed content padding
442
443
444
445
446
447
448
      Util.makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
        .forEach((element) => {
          const actualPadding = element.style.paddingRight
          const calculatedPadding = window.getComputedStyle(element)['padding-right']
          Manipulator.setDataAttribute(element, 'padding-right', actualPadding)
          element.style.paddingRight = `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`
        })
449

Johann-S's avatar
Johann-S committed
450
      // Adjust sticky content margin
451
452
453
454
455
456
457
      Util.makeArray(SelectorEngine.find(Selector.STICKY_CONTENT))
        .forEach((element) => {
          const actualMargin = element.style.marginRight
          const calculatedMargin = window.getComputedStyle(element)['margin-right']
          Manipulator.setDataAttribute(element, 'margin-right', actualMargin)
          element.style.marginRight = `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`
        })
458

Johann-S's avatar
Johann-S committed
459
460
      // Adjust body padding
      const actualPadding = document.body.style.paddingRight
461
462
463
464
      const calculatedPadding = window.getComputedStyle(document.body)['padding-right']

      Manipulator.setDataAttribute(document.body, 'padding-right', actualPadding)
      document.body.style.paddingRight = `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`
465
    }
466

467
    document.body.classList.add(ClassName.OPEN)
Johann-S's avatar
Johann-S committed
468
  }
469

Johann-S's avatar
Johann-S committed
470
471
  _resetScrollbar() {
    // Restore fixed content padding
472
473
    Util.makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
      .forEach((element) => {
474
        const padding = Manipulator.getDataAttribute(element, 'padding-right')
475
476
477
478
479
        if (typeof padding !== 'undefined') {
          Manipulator.removeDataAttribute(element, 'padding-right')
          element.style.paddingRight = padding
        }
      })
Johann-S's avatar
Johann-S committed
480

481
482
483
    // Restore sticky content and navbar-toggler margin
    Util.makeArray(SelectorEngine.find(`${Selector.STICKY_CONTENT}`))
      .forEach((element) => {
484
        const margin = Manipulator.getDataAttribute(element, 'margin-right')
485
486
487
488
489
        if (typeof margin !== 'undefined') {
          Manipulator.removeDataAttribute(element, 'margin-right')
          element.style.marginRight = margin
        }
      })
490

Johann-S's avatar
Johann-S committed
491
    // Restore body padding
492
    const padding = Manipulator.getDataAttribute(document.body, 'padding-right')
493
494
495
496
497
498
    if (typeof padding !== 'undefined') {
      Manipulator.removeDataAttribute(document.body, 'padding-right')
      document.body.style.paddingRight = padding
    } else {
      document.body.style.paddingRight = ''
    }
Johann-S's avatar
Johann-S committed
499
  }
500

Johann-S's avatar
Johann-S committed
501
502
503
504
505
506
507
508
  _getScrollbarWidth() { // thx d.walsh
    const scrollDiv = document.createElement('div')
    scrollDiv.className = ClassName.SCROLLBAR_MEASURER
    document.body.appendChild(scrollDiv)
    const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
    document.body.removeChild(scrollDiv)
    return scrollbarWidth
  }
509

Johann-S's avatar
Johann-S committed
510
511
512
513
  // Static

  static _jQueryInterface(config, relatedTarget) {
    return this.each(function () {
514
      let data = Data.getData(this, DATA_KEY)
Johann-S's avatar
Johann-S committed
515
516
      const _config = {
        ...Default,
517
        ...Manipulator.getDataAttributes(this),
Johann-S's avatar
Johann-S committed
518
519
520
521
522
523
        ...typeof config === 'object' && config ? config : {}
      }

      if (!data) {
        data = new Modal(this, _config)
      }
524

Johann-S's avatar
Johann-S committed
525
526
527
      if (typeof config === 'string') {
        if (typeof data[config] === 'undefined') {
          throw new TypeError(`No method named "${config}"`)
528
        }
Johann-S's avatar
Johann-S committed
529
530
531
532
533
        data[config](relatedTarget)
      } else if (_config.show) {
        data.show(relatedTarget)
      }
    })
534
  }
535
536
537
538

  static _getInstance(element) {
    return Data.getData(element, DATA_KEY)
  }
Johann-S's avatar
Johann-S committed
539
540
541
542
543
544
545
}

/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */
546

547
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
Johann-S's avatar
Johann-S committed
548
549
  let target
  const selector = Util.getSelectorFromElement(this)
550

Johann-S's avatar
Johann-S committed
551
  if (selector) {
552
    target = SelectorEngine.findOne(selector)
Johann-S's avatar
Johann-S committed
553
  }
554

555
  const config = Data.getData(target, DATA_KEY)
Johann-S's avatar
Johann-S committed
556
    ? 'toggle' : {
557
558
      ...Manipulator.getDataAttributes(target),
      ...Manipulator.getDataAttributes(this)
559
560
    }

Johann-S's avatar
Johann-S committed
561
562
563
  if (this.tagName === 'A' || this.tagName === 'AREA') {
    event.preventDefault()
  }
564

565
566
567
  EventHandler.one(target, Event.SHOW, (showEvent) => {
    if (showEvent.defaultPrevented) {
      // only register focus restorer if modal will actually get shown
Johann-S's avatar
Johann-S committed
568
      return
569
570
    }

571
572
    EventHandler.one(target, Event.HIDDEN, () => {
      if (Util.isVisible(this)) {
Johann-S's avatar
Johann-S committed
573
        this.focus()
574
575
576
577
      }
    })
  })

578
579
580
581
582
583
  let data = Data.getData(target, DATA_KEY)
  if (!data) {
    data = new Modal(target, config)
  }

  data.show(this)
Johann-S's avatar
Johann-S committed
584
585
586
587
588
589
590
})

/**
 * ------------------------------------------------------------------------
 * jQuery
 * ------------------------------------------------------------------------
 */
591

592
593
594
595
596
597
598
599
600
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
  const JQUERY_NO_CONFLICT = $.fn[NAME]
  $.fn[NAME]               = Modal._jQueryInterface
  $.fn[NAME].Constructor   = Modal
  $.fn[NAME].noConflict    = () => {
    $.fn[NAME] = JQUERY_NO_CONFLICT
    return Modal._jQueryInterface
  }
Johann-S's avatar
Johann-S committed
601
}
602
603

export default Modal