corrade-http-templates – Blame information for rev 61
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
61 | office | 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 |