polyfill.js 4.26 KB
Newer Older
1
2
3
4
5
6
7
8
import Util from '../util'

/**
 * --------------------------------------------------------------------------
 * Bootstrap (v4.1.1): dom/polyfill.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */
9

10
/* istanbul ignore next */
11
const Polyfill = (() => {
12
  // defaultPrevented is broken in IE
13
14
15
16
17
18
19
  const workingDefaultPrevented = (() => {
    const e = document.createEvent('CustomEvent')
    e.initEvent('Bootstrap', true, true)
    e.preventDefault()
    return e.defaultPrevented
  })()

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  if (!workingDefaultPrevented) {
    const origPreventDefault = Event.prototype.preventDefault
    Event.prototype.preventDefault = function () {
      if (!this.cancelable) {
        return
      }

      origPreventDefault.call(this)
      Object.defineProperty(this, 'defaultPrevented', {
        get() {
          return true
        },
        configurable: true
      })
    }
  }
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

  // CustomEvent polyfill for IE (see: https://mzl.la/2v76Zvn)
  if (typeof window.CustomEvent !== 'function') {
    window.CustomEvent = (event, params) => {
      params = params || {
        bubbles: false,
        cancelable: false,
        detail: null
      }
      const evt = document.createEvent('CustomEvent')
      evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
      return evt
    }

    window.CustomEvent.prototype = window.Event.prototype
51
  }
52

53
54
55
56
57
  // MSEdge resets defaultPrevented flag upon dispatchEvent call if at least one listener is attached
  const defaultPreventedPreservedOnDispatch = (() => {
    const e = new CustomEvent('Bootstrap', {
      cancelable: true
    })
58

59
60
61
62
63
64
65
    const element = document.createElement('div')
    element.addEventListener('Bootstrap', () => null)

    e.preventDefault()
    element.dispatchEvent(e)
    return e.defaultPrevented
  })()
66
67
68
69
70
71
72
73
74
75
76
77
78

  // Event constructor shim
  if (!window.Event || typeof window.Event !== 'function') {
    const origEvent = window.Event
    window.Event = (inType, params) => {
      params = params || {}
      const e = document.createEvent('Event')
      e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable))
      return e
    }
    window.Event.prototype = origEvent.prototype
  }

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  // matches polyfill (see: https://mzl.la/2ikXneG)
  if (!Element.prototype.matches) {
    Element.prototype.matches =
      Element.prototype.msMatchesSelector ||
      Element.prototype.webkitMatchesSelector
  }

  // closest polyfill (see: https://mzl.la/2vXggaI)
  let closest
  if (!Element.prototype.closest) {
    closest = (element, selector) => {
      let ancestor = element
      do {
        if (ancestor.matches(selector)) {
          return ancestor
        }

        ancestor = ancestor.parentElement
      } while (ancestor !== null && ancestor.nodeType === Node.ELEMENT_NODE)

      return null
    }
  } else {
    closest = (element, selector) => element.closest(selector)
  }

  const supportScopeQuery = (() => {
    const element = document.createElement('div')
    try {
      element.querySelectorAll(':scope *')
    } catch (e) {
      return false
    }

    return true
  })()

  const scopeSelectorRegex = /:scope\b/
  let find = Element.prototype.querySelectorAll
  let findOne = Element.prototype.querySelector

  if (!supportScopeQuery) {
    find = function (selector) {
      if (!scopeSelectorRegex.test(selector)) {
        return this.querySelectorAll(selector)
124
      }
125
126
127
128

      const hasId = Boolean(this.id)
      if (!hasId) {
        this.id = Util.getUID('scope')
129
      }
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

      let nodeList = null
      try {
        selector = selector.replace(scopeSelectorRegex, `#${this.id}`)
        nodeList = this.querySelectorAll(selector)
      } finally {
        if (!hasId) {
          this.removeAttribute('id')
        }
      }

      return nodeList
    }

    findOne = function (selector) {
      if (!scopeSelectorRegex.test(selector)) {
        return this.querySelector(selector)
      }

      const matches = find.call(this, selector)
      if (typeof matches[0] !== 'undefined') {
        return matches[0]
      }

      return null
    }
156
157
158
  }

  return {
159
160
161
162
163
    defaultPreventedPreservedOnDispatch,
    focusIn: typeof window.onfocusin === 'undefined',
    closest,
    find,
    findOne
164
165
166
167
  }
})()

export default Polyfill