modal.js 17.2 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
import {
9
  getjQuery,
10
11
  TRANSITION_END,
  emulateTransitionEnd,
12
  getElementFromSelector,
13
14
15
16
  getTransitionDurationFromElement,
  isVisible,
  reflow,
  typeCheckConfig
17
18
19
20
21
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
22

Johann-S's avatar
Johann-S committed
23
24
25
26
27
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
28

XhmikosR's avatar
XhmikosR committed
29
30
31
32
33
34
const NAME = 'modal'
const VERSION = '4.3.1'
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
Johann-S's avatar
Johann-S committed
35
36

const Default = {
XhmikosR's avatar
XhmikosR committed
37
38
39
40
  backdrop: true,
  keyboard: true,
  focus: true,
  show: true
Johann-S's avatar
Johann-S committed
41
42
43
}

const DefaultType = {
XhmikosR's avatar
XhmikosR committed
44
45
46
47
  backdrop: '(boolean|string)',
  keyboard: 'boolean',
  focus: 'boolean',
  show: 'boolean'
Johann-S's avatar
Johann-S committed
48
49
}

XhmikosR's avatar
XhmikosR committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
const EVENT_RESIZE = `resize${EVENT_KEY}`
const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`
const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`

const CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable'
const CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure'
const CLASS_NAME_BACKDROP = 'modal-backdrop'
const CLASS_NAME_OPEN = 'modal-open'
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_STATIC = 'modal-static'

const SELECTOR_DIALOG = '.modal-dialog'
const SELECTOR_MODAL_BODY = '.modal-body'
const SELECTOR_DATA_TOGGLE = '[data-toggle="modal"]'
const SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]'
const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
const SELECTOR_STICKY_CONTENT = '.sticky-top'
fat's avatar
fat committed
77

Johann-S's avatar
Johann-S committed
78
79
80
81
82
/**
 * ------------------------------------------------------------------------
 * Class Definition
 * ------------------------------------------------------------------------
 */
83

Johann-S's avatar
Johann-S committed
84
85
class Modal {
  constructor(element, config) {
XhmikosR's avatar
XhmikosR committed
86
87
    this._config = this._getConfig(config)
    this._element = element
XhmikosR's avatar
XhmikosR committed
88
    this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, element)
XhmikosR's avatar
XhmikosR committed
89
90
91
    this._backdrop = null
    this._isShown = false
    this._isBodyOverflowing = false
Johann-S's avatar
Johann-S committed
92
    this._ignoreBackdropClick = false
XhmikosR's avatar
XhmikosR committed
93
94
    this._isTransitioning = false
    this._scrollbarWidth = 0
95
    Data.setData(element, DATA_KEY, this)
96
97
  }

Johann-S's avatar
Johann-S committed
98
99
100
101
  // Getters

  static get VERSION() {
    return VERSION
102
103
  }

Johann-S's avatar
Johann-S committed
104
105
106
  static get Default() {
    return Default
  }
107

Johann-S's avatar
Johann-S committed
108
  // Public
109

Johann-S's avatar
Johann-S committed
110
111
112
  toggle(relatedTarget) {
    return this._isShown ? this.hide() : this.show(relatedTarget)
  }
113

Johann-S's avatar
Johann-S committed
114
  show(relatedTarget) {
115
    if (this._isShown || this._isTransitioning) {
Johann-S's avatar
Johann-S committed
116
      return
117
118
    }

XhmikosR's avatar
XhmikosR committed
119
    if (this._element.classList.contains(CLASS_NAME_FADE)) {
Johann-S's avatar
Johann-S committed
120
      this._isTransitioning = true
121
122
    }

XhmikosR's avatar
XhmikosR committed
123
    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
Johann-S's avatar
Johann-S committed
124
125
      relatedTarget
    })
126

Johann-S's avatar
Johann-S committed
127
    if (this._isShown || showEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
128
129
      return
    }
130

Johann-S's avatar
Johann-S committed
131
    this._isShown = true
132

Johann-S's avatar
Johann-S committed
133
134
    this._checkScrollbar()
    this._setScrollbar()
135

Johann-S's avatar
Johann-S committed
136
    this._adjustDialog()
137

Johann-S's avatar
Johann-S committed
138
139
    this._setEscapeEvent()
    this._setResizeEvent()
David Bailey's avatar
David Bailey committed
140

141
    EventHandler.on(this._element,
XhmikosR's avatar
XhmikosR committed
142
143
      EVENT_CLICK_DISMISS,
      SELECTOR_DATA_DISMISS,
XhmikosR's avatar
XhmikosR committed
144
      event => this.hide(event)
Johann-S's avatar
Johann-S committed
145
    )
146

XhmikosR's avatar
XhmikosR committed
147
148
    EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {
      EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => {
149
        if (event.target === this._element) {
Johann-S's avatar
Johann-S committed
150
151
152
153
          this._ignoreBackdropClick = true
        }
      })
    })
154

Johann-S's avatar
Johann-S committed
155
156
    this._showBackdrop(() => this._showElement(relatedTarget))
  }
157

Johann-S's avatar
Johann-S committed
158
159
160
161
  hide(event) {
    if (event) {
      event.preventDefault()
    }
162

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

XhmikosR's avatar
XhmikosR committed
167
    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)
168

Johann-S's avatar
Johann-S committed
169
    if (hideEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
170
171
      return
    }
172

Johann-S's avatar
Johann-S committed
173
    this._isShown = false
XhmikosR's avatar
XhmikosR committed
174
    const transition = this._element.classList.contains(CLASS_NAME_FADE)
175

Johann-S's avatar
Johann-S committed
176
177
178
    if (transition) {
      this._isTransitioning = true
    }
179

Johann-S's avatar
Johann-S committed
180
181
    this._setEscapeEvent()
    this._setResizeEvent()
182

XhmikosR's avatar
XhmikosR committed
183
    EventHandler.off(document, EVENT_FOCUSIN)
184

XhmikosR's avatar
XhmikosR committed
185
    this._element.classList.remove(CLASS_NAME_SHOW)
186

XhmikosR's avatar
XhmikosR committed
187
188
    EventHandler.off(this._element, EVENT_CLICK_DISMISS)
    EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS)
189

Johann-S's avatar
Johann-S committed
190
    if (transition) {
191
      const transitionDuration = getTransitionDurationFromElement(this._element)
192

XhmikosR's avatar
XhmikosR committed
193
      EventHandler.one(this._element, TRANSITION_END, event => this._hideModal(event))
194
      emulateTransitionEnd(this._element, transitionDuration)
Johann-S's avatar
Johann-S committed
195
196
197
198
    } else {
      this._hideModal()
    }
  }
199

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

    /**
XhmikosR's avatar
XhmikosR committed
205
     * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API`
206
     * Do not move `document` in `htmlElements` array
XhmikosR's avatar
XhmikosR committed
207
     * It will remove `EVENT_CLICK_DATA_API` event that should remain
208
     */
XhmikosR's avatar
XhmikosR committed
209
    EventHandler.off(document, EVENT_FOCUSIN)
210

211
    Data.removeData(this._element, DATA_KEY)
212

XhmikosR's avatar
XhmikosR committed
213
214
215
216
217
218
    this._config = null
    this._element = null
    this._dialog = null
    this._backdrop = null
    this._isShown = null
    this._isBodyOverflowing = null
Johann-S's avatar
Johann-S committed
219
    this._ignoreBackdropClick = null
XhmikosR's avatar
XhmikosR committed
220
221
    this._isTransitioning = null
    this._scrollbarWidth = null
Johann-S's avatar
Johann-S committed
222
  }
fat's avatar
fat committed
223

Johann-S's avatar
Johann-S committed
224
225
226
  handleUpdate() {
    this._adjustDialog()
  }
fat's avatar
fat committed
227

Johann-S's avatar
Johann-S committed
228
  // Private
fat's avatar
fat committed
229

Johann-S's avatar
Johann-S committed
230
231
232
233
  _getConfig(config) {
    config = {
      ...Default,
      ...config
234
    }
235
    typeCheckConfig(NAME, config, DefaultType)
Johann-S's avatar
Johann-S committed
236
237
    return config
  }
238

Johann-S's avatar
Johann-S committed
239
  _showElement(relatedTarget) {
XhmikosR's avatar
XhmikosR committed
240
241
    const transition = this._element.classList.contains(CLASS_NAME_FADE)
    const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)
242

Johann-S's avatar
Johann-S committed
243
244
245
246
    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
247
248
    }

Johann-S's avatar
Johann-S committed
249
250
    this._element.style.display = 'block'
    this._element.removeAttribute('aria-hidden')
251
    this._element.setAttribute('aria-modal', true)
Shohei Yoshida's avatar
Shohei Yoshida committed
252

XhmikosR's avatar
XhmikosR committed
253
    if (this._dialog.classList.contains(CLASS_NAME_SCROLLABLE) && modalBody) {
254
      modalBody.scrollTop = 0
Shohei Yoshida's avatar
Shohei Yoshida committed
255
256
257
    } else {
      this._element.scrollTop = 0
    }
258

Johann-S's avatar
Johann-S committed
259
    if (transition) {
260
      reflow(this._element)
Johann-S's avatar
Johann-S committed
261
    }
262

XhmikosR's avatar
XhmikosR committed
263
    this._element.classList.add(CLASS_NAME_SHOW)
264

Johann-S's avatar
Johann-S committed
265
266
267
    if (this._config.focus) {
      this._enforceFocus()
    }
268

Johann-S's avatar
Johann-S committed
269
    const transitionComplete = () => {
Jacob Thornton's avatar
Jacob Thornton committed
270
      if (this._config.focus) {
Johann-S's avatar
Johann-S committed
271
        this._element.focus()
Jacob Thornton's avatar
Jacob Thornton committed
272
      }
XhmikosR's avatar
XhmikosR committed
273

Johann-S's avatar
Johann-S committed
274
      this._isTransitioning = false
XhmikosR's avatar
XhmikosR committed
275
      EventHandler.trigger(this._element, EVENT_SHOWN, {
276
277
        relatedTarget
      })
Johann-S's avatar
Johann-S committed
278
    }
279

Johann-S's avatar
Johann-S committed
280
    if (transition) {
XhmikosR's avatar
XhmikosR committed
281
      const transitionDuration = getTransitionDurationFromElement(this._dialog)
282

283
284
      EventHandler.one(this._dialog, TRANSITION_END, transitionComplete)
      emulateTransitionEnd(this._dialog, transitionDuration)
Johann-S's avatar
Johann-S committed
285
286
287
288
289
290
    } else {
      transitionComplete()
    }
  }

  _enforceFocus() {
XhmikosR's avatar
XhmikosR committed
291
292
    EventHandler.off(document, EVENT_FOCUSIN) // guard against infinite focus loop
    EventHandler.on(document, EVENT_FOCUSIN, event => {
Johann-S's avatar
Johann-S committed
293
294
295
296
297
298
      if (document !== event.target &&
          this._element !== event.target &&
          !this._element.contains(event.target)) {
        this._element.focus()
      }
    })
Johann-S's avatar
Johann-S committed
299
  }
300

Johann-S's avatar
Johann-S committed
301
  _setEscapeEvent() {
302
    if (this._isShown) {
XhmikosR's avatar
XhmikosR committed
303
      EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
304
305
306
307
        if (this._config.keyboard && event.which === ESCAPE_KEYCODE) {
          event.preventDefault()
          this.hide()
        } else if (!this._config.keyboard && event.which === ESCAPE_KEYCODE) {
308
          this._triggerBackdropTransition()
Johann-S's avatar
Johann-S committed
309
310
        }
      })
Johann-S's avatar
Johann-S committed
311
    } else {
XhmikosR's avatar
XhmikosR committed
312
      EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)
313
    }
Johann-S's avatar
Johann-S committed
314
  }
315

Johann-S's avatar
Johann-S committed
316
317
  _setResizeEvent() {
    if (this._isShown) {
XhmikosR's avatar
XhmikosR committed
318
      EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())
Johann-S's avatar
Johann-S committed
319
    } else {
XhmikosR's avatar
XhmikosR committed
320
      EventHandler.off(window, EVENT_RESIZE)
321
    }
Johann-S's avatar
Johann-S committed
322
  }
323

Johann-S's avatar
Johann-S committed
324
325
326
  _hideModal() {
    this._element.style.display = 'none'
    this._element.setAttribute('aria-hidden', true)
327
    this._element.removeAttribute('aria-modal')
Johann-S's avatar
Johann-S committed
328
329
    this._isTransitioning = false
    this._showBackdrop(() => {
XhmikosR's avatar
XhmikosR committed
330
      document.body.classList.remove(CLASS_NAME_OPEN)
Johann-S's avatar
Johann-S committed
331
332
      this._resetAdjustments()
      this._resetScrollbar()
XhmikosR's avatar
XhmikosR committed
333
      EventHandler.trigger(this._element, EVENT_HIDDEN)
Johann-S's avatar
Johann-S committed
334
335
336
337
    })
  }

  _removeBackdrop() {
Johann-S's avatar
Johann-S committed
338
339
    this._backdrop.parentNode.removeChild(this._backdrop)
    this._backdrop = null
Johann-S's avatar
Johann-S committed
340
  }
341

Johann-S's avatar
Johann-S committed
342
  _showBackdrop(callback) {
XhmikosR's avatar
XhmikosR committed
343
344
    const animate = this._element.classList.contains(CLASS_NAME_FADE) ?
      CLASS_NAME_FADE :
XhmikosR's avatar
XhmikosR committed
345
      ''
Johann-S's avatar
Johann-S committed
346
347
348

    if (this._isShown && this._config.backdrop) {
      this._backdrop = document.createElement('div')
XhmikosR's avatar
XhmikosR committed
349
      this._backdrop.className = CLASS_NAME_BACKDROP
Johann-S's avatar
Johann-S committed
350
351
352

      if (animate) {
        this._backdrop.classList.add(animate)
353
354
      }

355
      document.body.appendChild(this._backdrop)
Johann-S's avatar
Johann-S committed
356

XhmikosR's avatar
XhmikosR committed
357
      EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {
Johann-S's avatar
Johann-S committed
358
359
360
361
        if (this._ignoreBackdropClick) {
          this._ignoreBackdropClick = false
          return
        }
XhmikosR's avatar
XhmikosR committed
362

Johann-S's avatar
Johann-S committed
363
364
365
        if (event.target !== event.currentTarget) {
          return
        }
XhmikosR's avatar
XhmikosR committed
366

367
        this._triggerBackdropTransition()
368
369
      })

Johann-S's avatar
Johann-S committed
370
      if (animate) {
371
        reflow(this._backdrop)
372
373
      }

XhmikosR's avatar
XhmikosR committed
374
      this._backdrop.classList.add(CLASS_NAME_SHOW)
375

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

381
      const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop)
382

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

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

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

405
406
  _triggerBackdropTransition() {
    if (this._config.backdrop === 'static') {
XhmikosR's avatar
XhmikosR committed
407
      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)
408
409
410
411
      if (hideEvent.defaultPrevented) {
        return
      }

XhmikosR's avatar
XhmikosR committed
412
      this._element.classList.add(CLASS_NAME_STATIC)
413
414
      const modalTransitionDuration = getTransitionDurationFromElement(this._element)
      EventHandler.one(this._element, TRANSITION_END, () => {
XhmikosR's avatar
XhmikosR committed
415
        this._element.classList.remove(CLASS_NAME_STATIC)
416
417
418
419
420
421
422
423
      })
      emulateTransitionEnd(this._element, modalTransitionDuration)
      this._element.focus()
    } else {
      this.hide()
    }
  }

Johann-S's avatar
Johann-S committed
424
425
426
  // ----------------------------------------------------------------------
  // the following methods are used to handle overflowing modals
  // ----------------------------------------------------------------------
427

Johann-S's avatar
Johann-S committed
428
429
430
  _adjustDialog() {
    const isModalOverflowing =
      this._element.scrollHeight > document.documentElement.clientHeight
431

Johann-S's avatar
Johann-S committed
432
433
    if (!this._isBodyOverflowing && isModalOverflowing) {
      this._element.style.paddingLeft = `${this._scrollbarWidth}px`
434
435
    }

Johann-S's avatar
Johann-S committed
436
437
    if (this._isBodyOverflowing && !isModalOverflowing) {
      this._element.style.paddingRight = `${this._scrollbarWidth}px`
438
    }
Johann-S's avatar
Johann-S committed
439
  }
440

Johann-S's avatar
Johann-S committed
441
442
443
444
  _resetAdjustments() {
    this._element.style.paddingLeft = ''
    this._element.style.paddingRight = ''
  }
445

Johann-S's avatar
Johann-S committed
446
447
448
449
450
  _checkScrollbar() {
    const rect = document.body.getBoundingClientRect()
    this._isBodyOverflowing = rect.left + rect.right < window.innerWidth
    this._scrollbarWidth = this._getScrollbarWidth()
  }
451

Johann-S's avatar
Johann-S committed
452
453
454
455
456
457
  _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
458
      SelectorEngine.find(SELECTOR_FIXED_CONTENT)
XhmikosR's avatar
XhmikosR committed
459
        .forEach(element => {
460
461
462
463
464
          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`
        })
465

Johann-S's avatar
Johann-S committed
466
      // Adjust sticky content margin
467
      SelectorEngine.find(SELECTOR_STICKY_CONTENT)
XhmikosR's avatar
XhmikosR committed
468
        .forEach(element => {
469
470
471
472
473
          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`
        })
474

Johann-S's avatar
Johann-S committed
475
476
      // Adjust body padding
      const actualPadding = document.body.style.paddingRight
477
478
479
480
      const calculatedPadding = window.getComputedStyle(document.body)['padding-right']

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

XhmikosR's avatar
XhmikosR committed
483
    document.body.classList.add(CLASS_NAME_OPEN)
Johann-S's avatar
Johann-S committed
484
  }
485

Johann-S's avatar
Johann-S committed
486
487
  _resetScrollbar() {
    // Restore fixed content padding
488
    SelectorEngine.find(SELECTOR_FIXED_CONTENT)
XhmikosR's avatar
XhmikosR committed
489
      .forEach(element => {
490
        const padding = Manipulator.getDataAttribute(element, 'padding-right')
491
492
493
494
495
        if (typeof padding !== 'undefined') {
          Manipulator.removeDataAttribute(element, 'padding-right')
          element.style.paddingRight = padding
        }
      })
Johann-S's avatar
Johann-S committed
496

497
    // Restore sticky content and navbar-toggler margin
498
    SelectorEngine.find(`${SELECTOR_STICKY_CONTENT}`)
XhmikosR's avatar
XhmikosR committed
499
      .forEach(element => {
500
        const margin = Manipulator.getDataAttribute(element, 'margin-right')
501
502
503
504
505
        if (typeof margin !== 'undefined') {
          Manipulator.removeDataAttribute(element, 'margin-right')
          element.style.marginRight = margin
        }
      })
506

Johann-S's avatar
Johann-S committed
507
    // Restore body padding
508
    const padding = Manipulator.getDataAttribute(document.body, 'padding-right')
XhmikosR's avatar
XhmikosR committed
509
510
511
    if (typeof padding === 'undefined') {
      document.body.style.paddingRight = ''
    } else {
512
513
514
      Manipulator.removeDataAttribute(document.body, 'padding-right')
      document.body.style.paddingRight = padding
    }
Johann-S's avatar
Johann-S committed
515
  }
516

Johann-S's avatar
Johann-S committed
517
518
  _getScrollbarWidth() { // thx d.walsh
    const scrollDiv = document.createElement('div')
XhmikosR's avatar
XhmikosR committed
519
    scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER
Johann-S's avatar
Johann-S committed
520
521
522
523
524
    document.body.appendChild(scrollDiv)
    const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
    document.body.removeChild(scrollDiv)
    return scrollbarWidth
  }
525

Johann-S's avatar
Johann-S committed
526
527
  // Static

528
  static jQueryInterface(config, relatedTarget) {
Johann-S's avatar
Johann-S committed
529
    return this.each(function () {
530
      let data = Data.getData(this, DATA_KEY)
Johann-S's avatar
Johann-S committed
531
532
      const _config = {
        ...Default,
533
        ...Manipulator.getDataAttributes(this),
Johann-S's avatar
Johann-S committed
534
535
536
537
538
539
        ...typeof config === 'object' && config ? config : {}
      }

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

Johann-S's avatar
Johann-S committed
541
542
543
      if (typeof config === 'string') {
        if (typeof data[config] === 'undefined') {
          throw new TypeError(`No method named "${config}"`)
544
        }
XhmikosR's avatar
XhmikosR committed
545

Johann-S's avatar
Johann-S committed
546
547
548
549
550
        data[config](relatedTarget)
      } else if (_config.show) {
        data.show(relatedTarget)
      }
    })
551
  }
552

553
  static getInstance(element) {
554
555
    return Data.getData(element, DATA_KEY)
  }
Johann-S's avatar
Johann-S committed
556
557
558
559
560
561
562
}

/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */
563

XhmikosR's avatar
XhmikosR committed
564
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
565
  const target = getElementFromSelector(this)
566

Johann-S's avatar
Johann-S committed
567
568
569
  if (this.tagName === 'A' || this.tagName === 'AREA') {
    event.preventDefault()
  }
570

XhmikosR's avatar
XhmikosR committed
571
  EventHandler.one(target, EVENT_SHOW, showEvent => {
572
573
    if (showEvent.defaultPrevented) {
      // only register focus restorer if modal will actually get shown
Johann-S's avatar
Johann-S committed
574
      return
575
576
    }

XhmikosR's avatar
XhmikosR committed
577
    EventHandler.one(target, EVENT_HIDDEN, () => {
578
      if (isVisible(this)) {
Johann-S's avatar
Johann-S committed
579
        this.focus()
580
581
582
583
      }
    })
  })

584
585
  let data = Data.getData(target, DATA_KEY)
  if (!data) {
Johann-S's avatar
Johann-S committed
586
587
588
589
590
    const config = {
      ...Manipulator.getDataAttributes(target),
      ...Manipulator.getDataAttributes(this)
    }

591
592
593
594
    data = new Modal(target, config)
  }

  data.show(this)
Johann-S's avatar
Johann-S committed
595
596
})

597
598
const $ = getjQuery()

Johann-S's avatar
Johann-S committed
599
600
601
602
/**
 * ------------------------------------------------------------------------
 * jQuery
 * ------------------------------------------------------------------------
Johann-S's avatar
Johann-S committed
603
 * add .modal to jQuery only if jQuery is present
Johann-S's avatar
Johann-S committed
604
 */
Johann-S's avatar
Johann-S committed
605
/* istanbul ignore if */
606
if ($) {
607
  const JQUERY_NO_CONFLICT = $.fn[NAME]
608
  $.fn[NAME] = Modal.jQueryInterface
XhmikosR's avatar
XhmikosR committed
609
610
  $.fn[NAME].Constructor = Modal
  $.fn[NAME].noConflict = () => {
611
    $.fn[NAME] = JQUERY_NO_CONFLICT
612
    return Modal.jQueryInterface
613
  }
Johann-S's avatar
Johann-S committed
614
}
615
616

export default Modal