OSDN Git Service

Modify the dependency on path
[bytom/vapor.git] / tools / side_chain_tool / web / node_modules / bootstrap / js / src / scrollspy.js
1 import $ from 'jquery'
2 import Util from './util'
3
4 /**
5  * --------------------------------------------------------------------------
6  * Bootstrap (v4.1.3): scrollspy.js
7  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8  * --------------------------------------------------------------------------
9  */
10
11 const ScrollSpy = (($) => {
12   /**
13    * ------------------------------------------------------------------------
14    * Constants
15    * ------------------------------------------------------------------------
16    */
17
18   const NAME               = 'scrollspy'
19   const VERSION            = '4.1.3'
20   const DATA_KEY           = 'bs.scrollspy'
21   const EVENT_KEY          = `.${DATA_KEY}`
22   const DATA_API_KEY       = '.data-api'
23   const JQUERY_NO_CONFLICT = $.fn[NAME]
24
25   const Default = {
26     offset : 10,
27     method : 'auto',
28     target : ''
29   }
30
31   const DefaultType = {
32     offset : 'number',
33     method : 'string',
34     target : '(string|element)'
35   }
36
37   const Event = {
38     ACTIVATE      : `activate${EVENT_KEY}`,
39     SCROLL        : `scroll${EVENT_KEY}`,
40     LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`
41   }
42
43   const ClassName = {
44     DROPDOWN_ITEM : 'dropdown-item',
45     DROPDOWN_MENU : 'dropdown-menu',
46     ACTIVE        : 'active'
47   }
48
49   const Selector = {
50     DATA_SPY        : '[data-spy="scroll"]',
51     ACTIVE          : '.active',
52     NAV_LIST_GROUP  : '.nav, .list-group',
53     NAV_LINKS       : '.nav-link',
54     NAV_ITEMS       : '.nav-item',
55     LIST_ITEMS      : '.list-group-item',
56     DROPDOWN        : '.dropdown',
57     DROPDOWN_ITEMS  : '.dropdown-item',
58     DROPDOWN_TOGGLE : '.dropdown-toggle'
59   }
60
61   const OffsetMethod = {
62     OFFSET   : 'offset',
63     POSITION : 'position'
64   }
65
66   /**
67    * ------------------------------------------------------------------------
68    * Class Definition
69    * ------------------------------------------------------------------------
70    */
71
72   class ScrollSpy {
73     constructor(element, config) {
74       this._element       = element
75       this._scrollElement = element.tagName === 'BODY' ? window : element
76       this._config        = this._getConfig(config)
77       this._selector      = `${this._config.target} ${Selector.NAV_LINKS},` +
78                             `${this._config.target} ${Selector.LIST_ITEMS},` +
79                             `${this._config.target} ${Selector.DROPDOWN_ITEMS}`
80       this._offsets       = []
81       this._targets       = []
82       this._activeTarget  = null
83       this._scrollHeight  = 0
84
85       $(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))
86
87       this.refresh()
88       this._process()
89     }
90
91     // Getters
92
93     static get VERSION() {
94       return VERSION
95     }
96
97     static get Default() {
98       return Default
99     }
100
101     // Public
102
103     refresh() {
104       const autoMethod = this._scrollElement === this._scrollElement.window
105         ? OffsetMethod.OFFSET : OffsetMethod.POSITION
106
107       const offsetMethod = this._config.method === 'auto'
108         ? autoMethod : this._config.method
109
110       const offsetBase = offsetMethod === OffsetMethod.POSITION
111         ? this._getScrollTop() : 0
112
113       this._offsets = []
114       this._targets = []
115
116       this._scrollHeight = this._getScrollHeight()
117
118       const targets = [].slice.call(document.querySelectorAll(this._selector))
119
120       targets
121         .map((element) => {
122           let target
123           const targetSelector = Util.getSelectorFromElement(element)
124
125           if (targetSelector) {
126             target = document.querySelector(targetSelector)
127           }
128
129           if (target) {
130             const targetBCR = target.getBoundingClientRect()
131             if (targetBCR.width || targetBCR.height) {
132               // TODO (fat): remove sketch reliance on jQuery position/offset
133               return [
134                 $(target)[offsetMethod]().top + offsetBase,
135                 targetSelector
136               ]
137             }
138           }
139           return null
140         })
141         .filter((item) => item)
142         .sort((a, b) => a[0] - b[0])
143         .forEach((item) => {
144           this._offsets.push(item[0])
145           this._targets.push(item[1])
146         })
147     }
148
149     dispose() {
150       $.removeData(this._element, DATA_KEY)
151       $(this._scrollElement).off(EVENT_KEY)
152
153       this._element       = null
154       this._scrollElement = null
155       this._config        = null
156       this._selector      = null
157       this._offsets       = null
158       this._targets       = null
159       this._activeTarget  = null
160       this._scrollHeight  = null
161     }
162
163     // Private
164
165     _getConfig(config) {
166       config = {
167         ...Default,
168         ...typeof config === 'object' && config ? config : {}
169       }
170
171       if (typeof config.target !== 'string') {
172         let id = $(config.target).attr('id')
173         if (!id) {
174           id = Util.getUID(NAME)
175           $(config.target).attr('id', id)
176         }
177         config.target = `#${id}`
178       }
179
180       Util.typeCheckConfig(NAME, config, DefaultType)
181
182       return config
183     }
184
185     _getScrollTop() {
186       return this._scrollElement === window
187         ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop
188     }
189
190     _getScrollHeight() {
191       return this._scrollElement.scrollHeight || Math.max(
192         document.body.scrollHeight,
193         document.documentElement.scrollHeight
194       )
195     }
196
197     _getOffsetHeight() {
198       return this._scrollElement === window
199         ? window.innerHeight : this._scrollElement.getBoundingClientRect().height
200     }
201
202     _process() {
203       const scrollTop    = this._getScrollTop() + this._config.offset
204       const scrollHeight = this._getScrollHeight()
205       const maxScroll    = this._config.offset +
206         scrollHeight -
207         this._getOffsetHeight()
208
209       if (this._scrollHeight !== scrollHeight) {
210         this.refresh()
211       }
212
213       if (scrollTop >= maxScroll) {
214         const target = this._targets[this._targets.length - 1]
215
216         if (this._activeTarget !== target) {
217           this._activate(target)
218         }
219         return
220       }
221
222       if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
223         this._activeTarget = null
224         this._clear()
225         return
226       }
227
228       const offsetLength = this._offsets.length
229       for (let i = offsetLength; i--;) {
230         const isActiveTarget = this._activeTarget !== this._targets[i] &&
231             scrollTop >= this._offsets[i] &&
232             (typeof this._offsets[i + 1] === 'undefined' ||
233                 scrollTop < this._offsets[i + 1])
234
235         if (isActiveTarget) {
236           this._activate(this._targets[i])
237         }
238       }
239     }
240
241     _activate(target) {
242       this._activeTarget = target
243
244       this._clear()
245
246       let queries = this._selector.split(',')
247       // eslint-disable-next-line arrow-body-style
248       queries = queries.map((selector) => {
249         return `${selector}[data-target="${target}"],` +
250                `${selector}[href="${target}"]`
251       })
252
253       const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))
254
255       if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {
256         $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
257         $link.addClass(ClassName.ACTIVE)
258       } else {
259         // Set triggered link as active
260         $link.addClass(ClassName.ACTIVE)
261         // Set triggered links parents as active
262         // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
263         $link.parents(Selector.NAV_LIST_GROUP).prev(`${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`).addClass(ClassName.ACTIVE)
264         // Handle special case when .nav-link is inside .nav-item
265         $link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_ITEMS).children(Selector.NAV_LINKS).addClass(ClassName.ACTIVE)
266       }
267
268       $(this._scrollElement).trigger(Event.ACTIVATE, {
269         relatedTarget: target
270       })
271     }
272
273     _clear() {
274       const nodes = [].slice.call(document.querySelectorAll(this._selector))
275       $(nodes).filter(Selector.ACTIVE).removeClass(ClassName.ACTIVE)
276     }
277
278     // Static
279
280     static _jQueryInterface(config) {
281       return this.each(function () {
282         let data = $(this).data(DATA_KEY)
283         const _config = typeof config === 'object' && config
284
285         if (!data) {
286           data = new ScrollSpy(this, _config)
287           $(this).data(DATA_KEY, data)
288         }
289
290         if (typeof config === 'string') {
291           if (typeof data[config] === 'undefined') {
292             throw new TypeError(`No method named "${config}"`)
293           }
294           data[config]()
295         }
296       })
297     }
298   }
299
300   /**
301    * ------------------------------------------------------------------------
302    * Data Api implementation
303    * ------------------------------------------------------------------------
304    */
305
306   $(window).on(Event.LOAD_DATA_API, () => {
307     const scrollSpys = [].slice.call(document.querySelectorAll(Selector.DATA_SPY))
308
309     const scrollSpysLength = scrollSpys.length
310     for (let i = scrollSpysLength; i--;) {
311       const $spy = $(scrollSpys[i])
312       ScrollSpy._jQueryInterface.call($spy, $spy.data())
313     }
314   })
315
316   /**
317    * ------------------------------------------------------------------------
318    * jQuery
319    * ------------------------------------------------------------------------
320    */
321
322   $.fn[NAME] = ScrollSpy._jQueryInterface
323   $.fn[NAME].Constructor = ScrollSpy
324   $.fn[NAME].noConflict = function () {
325     $.fn[NAME] = JQUERY_NO_CONFLICT
326     return ScrollSpy._jQueryInterface
327   }
328
329   return ScrollSpy
330 })($)
331
332 export default ScrollSpy