@glitchr/stickyjs 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js ADDED
@@ -0,0 +1,1628 @@
1
+ (function(namespace) {
2
+
3
+ namespace.replaceHash = function(newHash, triggerHashChange = true, skipIfEmptyIdentifier = true) {
4
+
5
+ if(!newHash) newHash = "";
6
+ if (newHash !== "" && (''+newHash).charAt(0) !== '#')
7
+ newHash = '#' + newHash;
8
+
9
+ var oldURL = location.origin+location.pathname+location.hash;
10
+ var newURL = location.origin+location.pathname+newHash;
11
+
12
+ var fallback = $(newHash).length === 0;
13
+
14
+ var hashElement = $(newHash)[0] ?? undefined;
15
+ if (hashElement !== undefined) // Update hash only if element is displayed
16
+ fallback |= window.getComputedStyle(hashElement)["display"] == "none";
17
+
18
+ if((skipIfEmptyIdentifier && !newHash) || fallback){
19
+
20
+ dispatchEvent(new HashChangeEvent("hashfallback", {oldURL:oldURL, newURL:newURL}));
21
+ newHash = "";
22
+
23
+ oldURL = location.origin+location.pathname+location.hash;
24
+ newURL = location.origin+location.pathname+newHash;
25
+ }
26
+
27
+ if(oldURL == newURL) return false;
28
+
29
+ var state = Object.assign({}, history.state, {href: newURL});
30
+ history.replaceState(state, '', newURL);
31
+
32
+ if(triggerHashChange)
33
+ dispatchEvent(new HashChangeEvent("hashchange", {oldURL:oldURL, newURL:newURL}));
34
+
35
+ return true;
36
+ }
37
+
38
+ })(window);
39
+
40
+ $.fn.serializeObject = function() {
41
+ var o = {};
42
+ var a = this.serializeArray();
43
+ $.each(a, function() {
44
+ if (o[this.name]) {
45
+ if (!o[this.name].push) {
46
+ o[this.name] = [o[this.name]];
47
+ }
48
+ o[this.name].push(this.value || '');
49
+ } else {
50
+ o[this.name] = this.value || '';
51
+ }
52
+ });
53
+ return o;
54
+ };
55
+
56
+ $.fn.isScrollable = function()
57
+ {
58
+ for (let el of $(this).isScrollableX())
59
+ if(el) return true;
60
+
61
+ for (let el of $(this).isScrollableY())
62
+ if(el) return true;
63
+
64
+ return false;
65
+ }
66
+
67
+ $.fn.isScrollableX = function() {
68
+
69
+ return $(this).map(function(i) {
70
+
71
+ var el = this[i] === window ? document.documentElement : this[i];
72
+ var isDom = el == document.documentElement;
73
+
74
+ var hasScrollableContent = el.scrollWidth > el.clientWidth;
75
+
76
+ var overflowStyle = window.getComputedStyle(el).overflowX;
77
+ var isOverflowScroll = overflowStyle.indexOf('scroll') !== -1;
78
+
79
+ return hasScrollableContent && (isOverflowScroll || isDom);
80
+
81
+ }.bind(this));
82
+ }
83
+ $.fn.isScrollableY = function() {
84
+
85
+ return $(this).map(function(i) {
86
+
87
+ var el = this[i] === window ? document.documentElement : this[i];
88
+ var isDom = el == document.documentElement;
89
+
90
+ var hasScrollableContent = el.scrollHeight > el.clientHeight;
91
+
92
+ var overflowStyle = window.getComputedStyle(el).overflowY;
93
+ var isOverflowScroll = overflowStyle.indexOf('scroll') !== -1;
94
+
95
+ return hasScrollableContent && (isOverflowScroll || isDom);
96
+
97
+ }.bind(this));
98
+ }
99
+
100
+ $.fn.closestScrollable = function()
101
+ {
102
+ return $(this).map((i) => {
103
+
104
+ var target = this[i] === window ? document.documentElement : this[i];
105
+ if (target === undefined) target = document.documentElement;
106
+
107
+ while (target !== document.documentElement) {
108
+
109
+ if($(target).isScrollable()) return target;
110
+
111
+ if(target.parentElement === undefined) return undefined;
112
+ if(target.parentElement === null) return null;
113
+
114
+ target = target.parentElement;
115
+ }
116
+
117
+ return $(target).isScrollable() ? target : undefined;
118
+ });
119
+ }
120
+
121
+ $.fn.closestScrollableWindow = function()
122
+ {
123
+ return $(this).closestScrollable().map(function(i) {
124
+
125
+ if (this === document.documentElement)
126
+ return window;
127
+ if (this === document)
128
+ return window;
129
+ if (this === undefined )
130
+ return window;
131
+
132
+ return this;
133
+
134
+ });
135
+ }
136
+
137
+ /* Internal event */
138
+ jQuery.event.special.scrolldelta = {
139
+
140
+ delegateType: "scroll",
141
+ bindType: "scroll",
142
+ handle: function (event) {
143
+
144
+ var handleObj = event.handleObj;
145
+
146
+ event = Sticky.compute(event);
147
+ event.type = handleObj.origType;
148
+
149
+ var ret = handleObj.handler.apply(this, arguments);
150
+ event.type = handleObj.type;
151
+ return ret;
152
+ }
153
+ };
154
+
155
+ $.fn.serializeObject = function () {
156
+
157
+ var o = {};
158
+ var a = this.serializeArray();
159
+ $.each(a, function () {
160
+ if (o[this.name]) {
161
+ if (!o[this.name].push) {
162
+ o[this.name] = [o[this.name]];
163
+ }
164
+ o[this.name].push(this.value || '');
165
+ } else {
166
+ o[this.name] = this.value || '';
167
+ }
168
+ });
169
+ return o;
170
+ };
171
+
172
+
173
+ (function (root, factory) {
174
+
175
+ if (typeof define === 'function' && define.amd) {
176
+ define(factory);
177
+ } else if (typeof exports === 'object') {
178
+ module.exports = factory();
179
+ } else {
180
+ root.Sticky = factory();
181
+ }
182
+
183
+ })(this, function () {
184
+
185
+ var Sticky = {};
186
+ Sticky.version = '0.1.0';
187
+
188
+ var Settings = Sticky.settings = {
189
+
190
+ //
191
+ // Time control
192
+ "tick" : "1ms" ,
193
+ "debounce": "500ms" ,
194
+ "threshold": "4000ms",
195
+
196
+ "scrollcatch": false,
197
+ "scrolllock" : true,
198
+ "scrollhide" : true,
199
+ "scrollhint" : 0.5,
200
+
201
+ //
202
+ // Manual overscroll detection (browser compatibility, e.g. scroll event missing with Firefox)
203
+ "overscroll": {
204
+ "top":false ,
205
+ "bottom":false,
206
+ "left":false ,
207
+ "right":false ,
208
+ },
209
+
210
+ "scrollsnap" : true ,
211
+ "scrollsnap_start" : "start", //start, center, end
212
+ "scrollsnap_proximity" : 0.01 ,
213
+
214
+ "swipehint" : true,
215
+ "swipehint_delay" : "1s",
216
+ "swipehint_debounce" : 0,
217
+
218
+ "autoscroll": true,
219
+ "autoscroll_bouncing": true,
220
+ "autoscroll_speed": 100, // pixel/s
221
+ "autoscroll_delay": "2s", // pixel/s
222
+ "autoscroll_easing": "linear",
223
+ "autoscroll_minwidth": 200,
224
+ "autoscroll_minheight": 400,
225
+ "autoscroll_startover": true,
226
+ "autoscroll_delay_reverse": "3s",
227
+ "autoscroll_reverse": false,
228
+ "autoscroll_mouse_action": true,
229
+
230
+ "smoothscroll_duration": "250ms",
231
+ "smoothscroll_speed": 0, // pixel/s
232
+ "smoothscroll_easing": "swing",
233
+
234
+ // Ease in/out related variables
235
+ // NB: if easein|easeout > 0 => additional margin
236
+ // else it enters into the element (equiv. negative margin..)
237
+ "easein" : "100px",
238
+ "easeout" : "50px" ,
239
+ "easetime" : "250ms",
240
+ "easedelay" : "250ms",
241
+ "easethrottle": "0ms",
242
+
243
+ "ready" : true,
244
+ "debug" : false,
245
+ "disable" : false,
246
+ "replacehash" : true
247
+ };
248
+
249
+ Sticky.parseDuration = function(str) {
250
+
251
+ var array = String(str).split(", ");
252
+ array = array.map(function(t) {
253
+
254
+ if(String(t).endsWith("ms")) return parseFloat(String(t))/1000;
255
+
256
+ return parseFloat(String(t));
257
+ });
258
+
259
+ return Math.max(...array);
260
+ }
261
+
262
+ Sticky.remToPixel = function(rem) { return parseFloat(rem) * parseFloat(getComputedStyle(document.documentElement).fontSize); }
263
+ Sticky.emToPixel = function(em, el) { return parseFloat(em ) * parseFloat(getComputedStyle(el.parentElement).fontSize); }
264
+ Sticky.percentToPixel = function(p , el) { return parseFloat(p ) * el.outerWidth(); }
265
+ Sticky.parseToPixel = function(str, el) {
266
+
267
+ if(str === undefined) return undefined;
268
+
269
+ var array = String(str).split(", ");
270
+ array = array.map(function(s) {
271
+
272
+ if(s.endsWith("rem")) return Sticky.remToPixel (s);
273
+ else if(s.endsWith("em") ) return Sticky.emToPixel (s, el);
274
+ else if(s.endsWith("%") ) return Sticky.percentToPixel(s, el);
275
+ return parseFloat(s);
276
+ });
277
+
278
+ return Math.max(...array);
279
+ }
280
+
281
+ Sticky.getScrollPadding = function(el = document.documentElement) {
282
+
283
+ var scroller = $(el).closestScrollable()[0];
284
+ var style = window.getComputedStyle(scroller);
285
+
286
+ var dict = {};
287
+ dict["top" ] = Sticky.parseToPixel(style["scroll-padding-top" ] || 0, scroller);
288
+ dict["left" ] = Sticky.parseToPixel(style["scroll-padding-left" ] || 0, scroller);
289
+ dict["right" ] = Sticky.parseToPixel(style["scroll-padding-right" ] || 0, scroller);
290
+ dict["bottom"] = Sticky.parseToPixel(style["scroll-padding-bottom"] || 0, scroller);
291
+
292
+ if(isNaN(dict["top" ])) dict["top"] = 0;
293
+ if(isNaN(dict["left" ])) dict["left"] = 0;
294
+ if(isNaN(dict["right" ])) dict["right"] = 0;
295
+ if(isNaN(dict["bottom"])) dict["bottom"] = 0;
296
+
297
+ return dict;
298
+ }
299
+
300
+ Sticky.epsilon = function(x1, x0) { return Math.abs(x1-x0) < 1; }
301
+ Sticky.reset = function(el = window) {
302
+
303
+ el = el === window ? document.documentElement : el;
304
+ el = $(el).length ? $(el)[0] : undefined;
305
+
306
+ var targetData = jQuery.data(el || document.documentElement);
307
+ Object.keys(targetData).forEach((key) => delete targetData[key]);
308
+
309
+ return this;
310
+ }
311
+
312
+ Sticky.debounce = function(func, wait, immediate) {
313
+
314
+ var timeout;
315
+ return function() {
316
+
317
+ var context = this, args = arguments;
318
+ var later = function() {
319
+
320
+ timeout = null;
321
+ if (!immediate) func.apply(context, args);
322
+ };
323
+
324
+ var callNow = immediate && !timeout;
325
+ clearTimeout(timeout);
326
+
327
+ timeout = setTimeout(later, wait);
328
+ if (callNow) func.apply(context, args);
329
+ };
330
+ };
331
+
332
+ Sticky.ready = function (options = {}) {
333
+
334
+ if("debug" in options)
335
+ Settings.debug = options["debug"];
336
+
337
+ Sticky.configure(options);
338
+ Settings.ready = true;
339
+
340
+ if (Settings.debug) console.log("Sticky is ready.");
341
+ if (Settings.debug) console.log("(padding = ", Sticky.getScrollPadding(), ")");
342
+ dispatchEvent(new Event('sticky:ready'));
343
+
344
+ return this;
345
+ };
346
+
347
+ Sticky.get = function(key) {
348
+
349
+ if(key in Sticky.settings)
350
+ return Sticky.settings[key];
351
+
352
+ return null;
353
+ };
354
+
355
+ Sticky.set = function(key, value) {
356
+
357
+ Sticky.settings[key] = value;
358
+ return this;
359
+ };
360
+
361
+ Sticky.add = function(key, value) {
362
+
363
+ if(! (key in Sticky.settings))
364
+ Sticky.settings[key] = [];
365
+
366
+ if (Sticky.settings[key].indexOf(value) === -1)
367
+ Sticky.settings[key].push(value);
368
+
369
+ return this;
370
+ };
371
+
372
+ Sticky.remove = function(key, value) {
373
+
374
+ if(key in Sticky.settings) {
375
+
376
+ Sticky.settings[key] = Sticky.settings[key].filter(function(setting, index, arr){
377
+ return value != setting;
378
+ });
379
+
380
+ return Sticky.settings[key];
381
+ }
382
+
383
+ return null;
384
+ };
385
+
386
+ Sticky.configure = function (options) {
387
+
388
+ var key, value;
389
+ for (key in options) {
390
+ value = options[key];
391
+ if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value;
392
+ }
393
+
394
+ if (Settings.debug) console.log("Sticky configuration: ", Settings);
395
+
396
+ return this;
397
+ }
398
+
399
+ Sticky.compute = function(event) {
400
+
401
+ if (event.target === undefined )
402
+ event.target = document.documentElement;
403
+ if (event.target === window )
404
+ event.target = document.documentElement;
405
+ if (event.target === document)
406
+ event.target = document.documentElement;
407
+
408
+ event.target = $(event.target);
409
+ event.target = $(event.target).isScrollable() ? event.target : $(event.target).closestScrollable();
410
+ if(event.target.length == 0) return event;
411
+
412
+ if ($(event.target).prop("user-scroll") === undefined)
413
+ $(event.target).prop("user-scroll", true);
414
+
415
+ var targetData = jQuery.data(event.target);
416
+ var first = (Object.keys(targetData).length === 0);
417
+
418
+ var top = targetData.top || 0;
419
+ var left = targetData.left || 0;
420
+ var bottom = targetData.bottom || 0;
421
+ var right = targetData.right || 0;
422
+
423
+ // Screen & viewport positioning
424
+ targetData.first = first;
425
+ targetData.elastic = targetData.elastic || false;
426
+ targetData.vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
427
+ targetData.vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
428
+ targetData.sw = event.target[0].scrollWidth || 0;
429
+ targetData.sh = event.target[0].scrollHeight || 0;
430
+ targetData.width = event.target[0].clientWidth || 0;
431
+ targetData.height = event.target[0].clientHeight || 0;
432
+
433
+ // Scrolling information
434
+ targetData.top = event.target.scrollTop();
435
+ targetData.bottom = Math.round(targetData.sh - event.target.scrollTop() - targetData.height);
436
+ targetData.left = event.target.scrollLeft();
437
+ targetData.right = Math.round(targetData.sw - event.target.scrollLeft() - targetData.width);
438
+
439
+ if(first) {
440
+
441
+ top = targetData.top;
442
+ bottom = targetData.bottom;
443
+ left = targetData.left;
444
+ right = targetData.right;
445
+ }
446
+
447
+ targetData.topCounter = targetData.topCounter || 0;
448
+ if (targetData.top == 0 && targetData.top < top)
449
+ targetData.topCounter = (targetData.top == 0 && top > 0 ? targetData.topCounter + 1 : targetData.topCounter ) || 0;
450
+
451
+ targetData.bottomCounter = targetData.bottomCounter || 0;
452
+ if (targetData.bottom == 0 && targetData.bottom < bottom)
453
+ targetData.bottomCounter = (targetData.bottom == 0 && bottom > 0 ? targetData.bottomCounter + 1 : targetData.bottomCounter) || 0;
454
+
455
+ targetData.leftCounter = targetData.leftCounter || 0;
456
+ if (targetData.left == 0 && targetData.left < left)
457
+ targetData.leftCounter = (targetData.left == 0 && left > 0 ? targetData.leftCounter + 1 : targetData.leftCounter ) || 0;
458
+
459
+ targetData.rightCounter = targetData.rightCounter || 0;
460
+ if (targetData.right == 0 && targetData.right < right)
461
+ targetData.rightCounter = (targetData.right == 0 && right > 0 ? targetData.rightCounter + 1 : targetData.rightCounter ) || 0;
462
+
463
+ targetData.bottomElastic = targetData.bottom < 0;
464
+ if(Sticky.get("overscroll").bottom) {
465
+ targetData.bottomElastic = true;
466
+ targetData.bottom = -1;
467
+ }
468
+
469
+ targetData.topElastic = targetData.top < 0;
470
+ if(Sticky.get("overscroll").top) {
471
+ targetData.topElastic = true;
472
+ targetData.top = -1;
473
+ }
474
+
475
+ targetData.leftElastic = targetData.left < 0;
476
+ if(Sticky.get("overscroll").left) {
477
+ targetData.leftElastic = true;
478
+ targetData.left = -1;
479
+ }
480
+
481
+ targetData.rightElastic = targetData.right < 0;
482
+ if(Sticky.get("overscroll").right) {
483
+ targetData.rightElastic = true;
484
+ targetData.right = -1;
485
+ }
486
+
487
+ targetData.elastic = targetData.topElastic || targetData.bottomElastic || targetData.leftElastic || targetData.rightElastic;
488
+
489
+ // Timing information
490
+ if (targetData.time0 === undefined || !targetData.elastic || targetData.first) {
491
+ targetData.time0 = {};
492
+ targetData.time = {};
493
+ }
494
+
495
+ if(!targetData.topElastic ) {
496
+ targetData.time0.top = null;
497
+ targetData.time .top = null;
498
+ }
499
+
500
+ if(!targetData.bottomElastic) {
501
+ targetData.time0.bottom = null;
502
+ targetData.time .bottom = null;
503
+ }
504
+
505
+ if(!targetData.leftElastic ) {
506
+ targetData.time0.left = null;
507
+ targetData.time .left = null;
508
+ }
509
+
510
+ if(!targetData.rightElastic ) {
511
+ targetData.time0.right = null;
512
+ targetData.time .right = null;
513
+ }
514
+
515
+ if(targetData.topElastic ) targetData.time0.top = targetData.time0.top || new Date().getTime();
516
+ if(targetData.bottomElastic) targetData.time0.bottom = targetData.time0.bottom || new Date().getTime();
517
+ if(targetData.leftElastic ) targetData.time0.left = targetData.time0.left || new Date().getTime();
518
+ if(targetData.rightElastic ) targetData.time0.right = targetData.time0.right || new Date().getTime();
519
+
520
+ if(targetData.topElastic ) targetData.time.top = new Date().getTime();
521
+ if(targetData.bottomElastic) targetData.time.bottom = new Date().getTime();
522
+ if(targetData.leftElastic ) targetData.time.left = new Date().getTime();
523
+ if(targetData.rightElastic ) targetData.time.right = new Date().getTime();
524
+
525
+ var dX = targetData.left - left;
526
+ var dY = targetData.top - top;
527
+ var dT = {
528
+ top: Math.abs(targetData.time0.top - targetData.time.top),
529
+ bottom: Math.abs(targetData.time0.bottom - targetData.time.bottom),
530
+ left: Math.abs(targetData.time0.left - targetData.time.left),
531
+ right: Math.abs(targetData.time0.right - targetData.time.right)
532
+ };
533
+
534
+ // Event summary information
535
+ event.deltaX = dX;
536
+ event.deltaY = dY;
537
+ event.deltaT = dT;
538
+
539
+ event.first = first;
540
+ event.last = event.last || 0;
541
+ event.last = (
542
+ dX == 0 && dY == 0 &&
543
+ dT.top == 0 && dT.left == 0 &&
544
+ dT.bottom == 0 && dT.right == 0
545
+ ) * (event.last + 1);
546
+
547
+ event.reset = event.last < 1;
548
+ event.scrollT = {
549
+ "delta" : dT,
550
+ "t0" : targetData.time0,
551
+ "elastic" : targetData.elastic
552
+ }
553
+
554
+ event.screen = {
555
+ "height" : targetData.height,
556
+ "width" : targetData.width,
557
+ "vh" : targetData.vh,
558
+ "vw" : targetData.vw,
559
+ "userScroll": Sticky.userScroll(event.target) && event.reset
560
+ };
561
+
562
+ event.scrollX = {
563
+ "delta" : dX,
564
+ "left" : targetData.left,
565
+ "leftCounter" : targetData.leftCounter,
566
+ "leftElastic" : targetData.leftElastic,
567
+ "right" : targetData.right,
568
+ "rightCounter" : targetData.rightCounter,
569
+ "rightElastic" : targetData.rightElastic
570
+ };
571
+
572
+ event.scrollY = {
573
+ "delta" : dY,
574
+ "top" : targetData.top,
575
+ "topCounter" : targetData.topCounter,
576
+ "topElastic" : targetData.topElastic,
577
+ "bottom" : targetData.bottom,
578
+ "bottomCounter" : targetData.bottomCounter,
579
+ "bottomElastic" : targetData.bottomElastic,
580
+ };
581
+
582
+ return event;
583
+ };
584
+
585
+ Sticky.overscrollTop = function(event, el = window) {
586
+ el = el === window ? document.documentElement : el;
587
+
588
+ var deltaY = (event.deltaY !== undefined ? event.deltaY : event.originalEvent.deltaY);
589
+ return $(el).scrollTop() === 0 && deltaY < 0;
590
+ }
591
+ Sticky.overscrollBottom = function(event, el = window) {
592
+ el = el === window ? document.documentElement : el;
593
+
594
+ var deltaY = (event.deltaY !== undefined ? event.deltaY : event.originalEvent.deltaY);
595
+ return $(el).scrollTop() >= $(el)[0].scrollHeight - $(el)[0].clientHeight && deltaY > 0;
596
+ }
597
+ Sticky.overscrollLeft = function(event, el = window) {
598
+ el = el === window ? document.documentElement : el;
599
+
600
+ var deltaX = (event.deltaX !== undefined ? event.deltaX : event.originalEvent.deltaX);
601
+ return $(el).scrollLeft() === 0 && deltaX < 0;
602
+ }
603
+ Sticky.overscrollRight = function(event, el = window) {
604
+ el = el === window ? document.documentElement : el;
605
+
606
+ var deltaX = (event.deltaX !== undefined ? event.deltaX : event.originalEvent.deltaX);
607
+ return $(el).scrollLeft() >= $(el)[0].scrollWidth - $(el)[0].clientWidth && deltaX > 0;
608
+ }
609
+
610
+ var anchorY = 0;
611
+ var currentHash = window.location.hash;
612
+
613
+ Sticky.closestToZero = function(numbers) { return Sticky.closestTo(0, numbers); }
614
+ Sticky.closestTo = function(x, numbers)
615
+ {
616
+ if (numbers.length === 0) return undefined;
617
+
618
+ var min, closest = 0;
619
+ for(let i = 0; i < numbers.length; i++) {
620
+
621
+ min = Math.abs(numbers[closest] - x);
622
+ if (Math.abs(numbers[i] - x) < min)
623
+ closest = i;
624
+ }
625
+
626
+ return parseInt(closest);
627
+ }
628
+
629
+ Sticky.userScroll = function(el = undefined) { return $(el === undefined ? document.documentElement : el).closestScrollable().prop("user-scroll") ?? true; }
630
+ Sticky.scrollTo = function(dict, callback = function() {}, el = window)
631
+ {
632
+ el = $(el).length ? $(el)[0] : window;
633
+ if (el === window )
634
+ el = document.documentElement;
635
+ if (el === document)
636
+ el = document.documentElement;
637
+
638
+ if(Sticky.userScroll(el) == false) return;
639
+ $(el).prop("user-scroll", false);
640
+
641
+ var maxScrollX = $(el).prop("scrollWidth") - Math.round($(el).prop("clientWidth"));
642
+ if (maxScrollX == 0) maxScrollX = Math.round($(el).prop("clientWidth"));
643
+ var maxScrollY = $(el).prop("scrollHeight") - Math.round($(el).prop("clientHeight"));
644
+ if (maxScrollY == 0) maxScrollY = Math.round($(el).prop("clientHeight"));
645
+
646
+ scrollTop = Math.max(0, Math.min(dict["top"] ?? $(el).prop("scrollTop"), maxScrollY));
647
+ scrollLeft = Math.max(0, Math.min(dict["left"] ?? $(el).prop("scrollLeft"), maxScrollX));
648
+
649
+ speed = parseFloat(dict["speed"] ?? 0);
650
+ easing = dict["easing"] ?? "swing";
651
+ debounce = dict["debounce"] ?? 0;
652
+
653
+ duration = 1000*Transparent.parseDuration(dict["duration"] ?? 0);
654
+ durationX = 1000*Transparent.parseDuration(dict["duration-x"] ?? dict["duration"] ?? 0);
655
+ durationY = 1000*Transparent.parseDuration(dict["duration-y"] ?? dict["duration"] ?? 0);
656
+
657
+ if(speed) {
658
+
659
+ var currentScrollX = $(el)[0].scrollLeft;
660
+ if(currentScrollX < scrollLeft || scrollLeft == 0) // Going to the right
661
+ distanceX = Math.abs(scrollLeft - currentScrollX);
662
+ else // Going back to 0 position
663
+ distanceX = currentScrollX;
664
+
665
+ var currentScrollY = $(el)[0].scrollTop;
666
+ if(currentScrollY <= scrollTop || scrollTop == 0) // Going to the right
667
+ distanceY = Math.abs(scrollTop - currentScrollY);
668
+ else // Going back to 0 position
669
+ distanceY = currentScrollY;
670
+
671
+ durationX = speed ? 1000*distanceX/speed : durationX;
672
+ durationY = speed ? 1000*distanceY/speed : durationY;
673
+ duration = durationX+durationY;
674
+ }
675
+
676
+ var callbackWrapper = function() {
677
+
678
+ el.dispatchEvent(new Event('scroll'));
679
+ callback();
680
+
681
+ $(el).prop("user-scroll", true);
682
+ };
683
+
684
+ if(duration == 0) {
685
+
686
+ el.scrollTo(scrollLeft, scrollTop);
687
+ callbackWrapper();
688
+
689
+ } else {
690
+
691
+ $(el).animate({scrollTop: scrollTop}, durationY, easing,
692
+ () => $(el).animate({scrollLeft: scrollLeft}, durationX, easing, Transparent.debounce(callbackWrapper, debounce))
693
+ );
694
+ }
695
+
696
+ return this;
697
+ }
698
+
699
+ Sticky.scrollToFirstSnap = function (el = window, callback = function() {})
700
+ {
701
+ var scroller = $(el).closestScrollable()[0];
702
+ var snap = Sticky.getFirstSnap(scroller);
703
+ if (snap !== undefined) Sticky.scrollTo({top:snap.offsetTop, left:snap.offsetLeft, easing:Settings["smoothscroll_easing"], duration: Settings["smoothscroll_duration"], speed: Settings["smoothscroll_speed"]}, callback, scroller);
704
+ else Sticky.scrollTo({top:0, left:0, easing:Settings["smoothscroll_easing"], duration: Settings["smoothscroll_duration"], speed: Settings["smoothscroll_speed"]}, callback, scroller);
705
+ }
706
+
707
+ Sticky.getFirstSnap = function (el = window)
708
+ {
709
+ var magnets = Sticky.getMagnets(el);
710
+ if(!magnets.length) return undefined;
711
+
712
+ return magnets[0];
713
+ }
714
+
715
+ Sticky.scrollToLastSnap = function (el = window, callback = function() {}) {
716
+ var scroller = $(el).closestScrollable()[0];
717
+ var snap = Sticky.getLastSnap(scroller);
718
+ if (snap !== undefined) Sticky.scrollTo({top:snap.offsetTop, left:snap.offsetLeft, easing:Settings["smoothscroll_easing"], duration: Settings["smoothscroll_duration"], speed: Settings["smoothscroll_speed"]}, callback, scroller);
719
+ else Sticky.scrollTo({top:scroller.scrollHeight || document.documentElement.scrollHeight, left:scroller.scrollWidth || document.documentElement.scrollWidth, easing:Settings["smoothscroll_easing"], duration: Settings["smoothscroll_duration"], speed: Settings["smoothscroll_speed"]}, callback, scroller);
720
+ }
721
+
722
+ Sticky.getLastSnap = function (el = window)
723
+ {
724
+ var magnets = Sticky.getMagnets(el);
725
+ if(!magnets.length) return undefined;
726
+
727
+ return magnets[magnets.length-1];
728
+ }
729
+
730
+ Sticky.scrollToSnap = function (el = window, callback = function() {}) {
731
+ var scroller = $(el).closestScrollable()[0];
732
+ var snap = Sticky.getSnap(el);
733
+ if (snap !== undefined) Sticky.scrollTo({top:snap.offsetTop, left:snap.offsetLeft, easing:Settings["smoothscroll_easing"], duration: Settings["smoothscroll_duration"], speed: Settings["smoothscroll_speed"]}, callback, scroller);
734
+ }
735
+
736
+ Sticky.getSnap = function (el = window)
737
+ {
738
+ var magnets = Sticky.getMagnets(el);
739
+ if(!magnets.length) return undefined;
740
+
741
+ return magnets[Sticky.closestTo(el.scrollTop || el.scrollY, magnets.map(function() { return this.offsetTop; }))];
742
+ }
743
+
744
+ Sticky.scrollToPreviousSnap = function (el = window, callback = function() {}) {
745
+ var scroller = $(el).closestScrollable()[0];
746
+ var snap = Sticky.getPreviousSnap(scroller);
747
+ if (snap !== undefined) Sticky.scrollTo({top:snap.offsetTop, left:snap.offsetLeft, easing:Settings["smoothscroll_easing"], duration: Settings["smoothscroll_duration"], speed: Settings["smoothscroll_speed"]}, callback, scroller);
748
+ }
749
+ Sticky.getPreviousSnap = function (el = window)
750
+ {
751
+ var magnets = Sticky.getMagnets(el);
752
+ if(!magnets.length) return undefined;
753
+
754
+ var current = Sticky.closestTo(el.scrollTop || el.scrollY, magnets.map(function() { return this.offsetTop; }));
755
+ return current > 0 ? magnets[current-1] : magnets[current];
756
+ }
757
+
758
+ Sticky.scrollToNextSnap = function (el = window, callback = function() {}) {
759
+
760
+ var scroller = $(el).closestScrollable()[0];
761
+ var snap = Sticky.getNextSnap(scroller);
762
+ if (snap !== undefined) Sticky.scrollTo({top:snap.offsetTop, left:snap.offsetLeft, easing:Settings["smoothscroll_easing"], duration: Settings["smoothscroll_duration"], speed: Settings["smoothscroll_speed"]}, callback, scroller);
763
+ }
764
+
765
+ scrollState = {};
766
+ Sticky.allowScrolling = function(el = window)
767
+ {
768
+ return el in scrollState ? scrollState[el] || true : true;
769
+ }
770
+
771
+ Sticky.enableScroll = function(el = window) {
772
+
773
+ // left: 37, up: 38, right: 39, down: 40,
774
+ // spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36
775
+ var keys = {37: 1, 38: 1, 39: 1, 40: 1};
776
+
777
+ function preventDefault(e) { e.preventDefault(); }
778
+ function preventDefaultForScrollKeys(e) {
779
+ if (keys[e.keyCode]) {
780
+ preventDefault(e);
781
+ return false;
782
+ }
783
+ }
784
+
785
+ // modern Chrome requires { passive: false } when adding event
786
+ var supportsPassive = false;
787
+ try {
788
+ window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
789
+ get: function () { supportsPassive = true; }
790
+ }));
791
+ } catch(e) {}
792
+
793
+ var wheelOpt = supportsPassive ? { passive: false } : false;
794
+ var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel.preventDefault' : 'mousewheel.preventDefault';
795
+
796
+ el.addEventListener('DOMMouseScroll', preventDefault, false); // older FF
797
+ el.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
798
+ el.addEventListener('touchmove.preventDefault', preventDefault, wheelOpt); // mobile
799
+ el.addEventListener('keydown.preventDefault', preventDefaultForScrollKeys, false);
800
+ scrollState[el] = true;
801
+ }
802
+
803
+ Sticky.disableScroll = function(el = window) {
804
+
805
+ // left: 37, up: 38, right: 39, down: 40,
806
+ // spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36
807
+ var keys = {37: 1, 38: 1, 39: 1, 40: 1};
808
+
809
+ function preventDefault(e) { e.preventDefault(); }
810
+ function preventDefaultForScrollKeys(e) {
811
+ if (keys[e.keyCode]) {
812
+ preventDefault(e);
813
+ return false;
814
+ }
815
+ }
816
+
817
+ // modern Chrome requires { passive: false } when adding event
818
+ var supportsPassive = false;
819
+ try {
820
+ window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
821
+ get: function () { supportsPassive = true; }
822
+ }));
823
+ } catch(e) {}
824
+
825
+ var wheelOpt = supportsPassive ? { passive: false } : false;
826
+ var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel.preventDefault' : 'mousewheel.preventDefault';
827
+
828
+ el.removeEventListener('DOMMouseScroll.preventDefault', preventDefault, false);
829
+ el.removeEventListener(wheelEvent, preventDefault, wheelOpt);
830
+ el.removeEventListener('touchmove.preventDefault', preventDefault, wheelOpt);
831
+ el.removeEventListener('keydown.preventDefault', preventDefaultForScrollKeys, false);
832
+
833
+ scrollState[el] = false;
834
+ }
835
+
836
+ Sticky.getNextSnap = function(el = window)
837
+ {
838
+ var magnets = Sticky.getMagnets(el);
839
+ if(!magnets.length) return undefined;
840
+
841
+ var current = Sticky.closestTo(el.scrollTop || el.scrollY, magnets.map(function() { return this.offsetTop; }));
842
+ return current < magnets.length-1 ? magnets[current+1] : magnets[current];
843
+ }
844
+
845
+ var lastScrollTop = window.pageYOffset;
846
+ var scrollSnapDebounce = false;
847
+ Sticky.onScrollSnap = function (e)
848
+ {
849
+ if (Settings.debug) console.log(show,"Sticky magnetic:", scrollSnap, scrollSnapStart, scrollSnapProximity);
850
+ if(!Settings.ready) return;
851
+
852
+ var scrollSnap = Sticky.get("scrollsnap");
853
+ if(!scrollSnap) return;
854
+
855
+ var scrollSnapStart = Sticky.get("scrollsnap_start");
856
+ var scrollSnapProximity = Sticky.get("scrollsnap_proximity");
857
+
858
+ var magnets = Sticky.getMagnets(e.target);
859
+ if(!magnets.length) return;
860
+
861
+ var currentId = Sticky.closestToZero(magnets.map(function() { return this.offsetTop-window.scrollY; }));
862
+ var currMagnet = magnets[currentId];
863
+ var magnet = currMagnet;
864
+
865
+ var scroller = $(e.target).closestScrollable()[0];
866
+ if(!e.screen.userScroll || !Sticky.allowScrolling()) return;
867
+
868
+ var st = e.scrollY.top;
869
+ var roll = st > lastScrollTop;
870
+ var unroll = st < lastScrollTop;
871
+ lastScrollTop = st <= 0 ? 0 : st;
872
+
873
+ var magnet = currMagnet;
874
+ var closestMagnets = magnets.filter(function() { return this.visible > scrollSnapProximity; });
875
+
876
+ if (roll) closestMagnets = closestMagnets.filter(function() { return this.element.offsetTop >= window.scrollY && this.element.offsetLeft >= window.scrollX; });
877
+ else if(unroll) closestMagnets = closestMagnets.filter(function() { return this.element.offsetTop + this.element.offsetHeight >= window.scrollY && this.element.offsetLeft + this.element.offsetWidth >= window.scrollX; });
878
+
879
+ var scrollTo = closestMagnets.length && (closestMagnets[0]["element"] ?? undefined) !== currMagnet["element"] && !scrollSnapDebounce && (roll || unroll);
880
+ if (scrollTo) {
881
+
882
+ if (closestMagnets.length) magnet = closestMagnets[0];
883
+
884
+ $(scroller).addClass("sticky-prevent-scroll");
885
+ if (Sticky.get("scrolllock"))
886
+ Sticky.disableScroll();
887
+
888
+ scrollSnapDebounce = true;
889
+ Sticky.scrollTo({
890
+
891
+ top:magnet.offsetTop - Sticky.getScrollPadding(scroller).top,
892
+ left:magnet.offsetLeft - Sticky.getScrollPadding(scroller).left,
893
+ easing:Settings["smoothscroll_easing"],
894
+ duration: Settings["smoothscroll_duration"],
895
+ speed: Settings["smoothscroll_speed"]
896
+
897
+ }, Sticky.debounce(function() {
898
+
899
+ scrollSnapDebounce = false;
900
+ $(scroller).removeClass("sticky-prevent-scroll");
901
+ if (Sticky.get("scrolllock"))
902
+ Sticky.enableScroll();
903
+
904
+ }, 1000*Sticky.parseDuration(Sticky.get("debounce"))), scroller);
905
+
906
+ $(magnets).each((e) => $(magnets[e].element).removeClass("magnet closest"));
907
+ $(magnet.element).addClass("magnet");
908
+
909
+ $(closestMagnets).each((e) => $(magnets[e].element).addClass("closest"));
910
+ }
911
+
912
+ return this;
913
+ }
914
+
915
+ Sticky.getMagnets = function (el)
916
+ {
917
+ return $(el).find(".sticky-magnet")
918
+ .sort(function (m1, m2) {
919
+
920
+ return m1.offsetTop > m2.offsetTop ? 1 : (m1.offsetTop < m2.offsetTop ? -1 : 0);
921
+
922
+ }).map(function() {
923
+
924
+ var scroller = $(this).closestScrollable()[0];
925
+ var scrollTop = $(scroller).scrollTop() + Sticky.getScrollPadding(scroller).top;
926
+ var scrollBottom = $(scroller).scrollTop() + Sticky.getScrollPadding(scroller).top + scroller.clientHeight;
927
+ var scrollLeft = $(scroller).scrollLeft() + Sticky.getScrollPadding(scroller).left;
928
+ var scrollRight = $(scroller).scrollLeft() + Sticky.getScrollPadding(scroller).left + scroller.clientWidth;
929
+
930
+ var offsetTop = this.offsetTop;
931
+ var offsetBottom = this.offsetTop + this.clientHeight;
932
+ var offsetLeft = this.offsetLeft;
933
+ var offsetRight = this.offsetLeft + this.clientWidth;
934
+
935
+ var visibleTop = offsetTop < scrollTop ? scrollTop : offsetTop;
936
+ var visibleBottom = offsetBottom > scrollBottom ? scrollBottom : offsetBottom;
937
+ var visibleLeft = offsetLeft < scrollLeft ? scrollLeft : offsetLeft;
938
+ var visibleRight = offsetRight > scrollRight ? scrollRight : offsetRight;
939
+
940
+ var visibleX = Math.min(1, Math.max(0, (visibleRight - visibleLeft) / scroller.clientWidth ));
941
+ var visibleY = Math.min(1, Math.max(0, (visibleBottom - visibleTop) / scroller.clientHeight));
942
+ var visible = visibleX * visibleY;
943
+
944
+ return {element: this, offsetTop: this.offsetTop, offsetLeft: this.offsetLeft, visible:visible };
945
+ });
946
+ }
947
+
948
+ var hasReset = false;
949
+ Sticky.onScrollDelta = function (e) {
950
+
951
+ if (Sticky.get("disable")) return;
952
+ if (Settings.debug) console.log("Sticky delta scrolling.. ", e.scrollY, e.scrollX, e.scrollT, e.screen);
953
+ if(!Settings.ready) return;
954
+
955
+ var magnets = Sticky.getMagnets(e.target);
956
+ if(magnets.length) {
957
+
958
+ var currentId = Sticky.closestToZero(magnets.map(function() { return this.offsetTop-window.scrollY; }));
959
+ if(currentId == 0) $(e.target).find(".sticky-magnet-first").removeClass("show");
960
+ else $(e.target).find(".sticky-magnet-first").addClass("show");
961
+
962
+ if(currentId == magnets.length-1) $(e.target).find(".sticky-magnet-last").removeClass("show");
963
+ else $(e.target).find(".sticky-magnet-last").addClass("show");
964
+
965
+ } else {
966
+
967
+ $(e.target).find(".sticky-magnet-first").hide();
968
+ $(e.target).find(".sticky-magnet-last").hide();
969
+ }
970
+
971
+ var scroller = $(this).closestScrollable();
972
+ $(scroller).find(".sticky-top").attr("aria-scrollhide", false);
973
+
974
+ function PayloadReplaceHash() {
975
+
976
+ if(!Sticky.get("replacehash")) return;
977
+ if(!Settings.ready) return;
978
+
979
+ var currentHashEl = $(window.location.hash);
980
+ var ids = $(".sticky-headlines[id], .sticky-headlines [id], .sticky-magnet[id]");
981
+ if (ids.length == 0) return;
982
+
983
+ var hash = null;
984
+ if(Settings.debug) console.log(show,"Sticky headlines:", $(ids));
985
+
986
+ var elAll = $(ids).filter(function() {
987
+
988
+ if(this === $(Settings.identifier)) return false;
989
+ return this.getBoundingClientRect().top < Sticky.getScrollPadding(scroller).top + 1;
990
+
991
+ }).sort(function (el1, el2) {
992
+
993
+ return el1.offsetTop > el2.offsetTop ? -1
994
+ : (el1.offsetTop < el2.offsetTop ? 1 : 0);
995
+ });
996
+
997
+ var el = elAll.filter(function() {
998
+
999
+ if(this === $(Settings.identifier)) return false;
1000
+ return this.getBoundingClientRect().top + this.scrollHeight > Sticky.getScrollPadding(scroller).top + 1;
1001
+ });
1002
+
1003
+ var currentHashEl = $(window.location.hash)[0];
1004
+ var atTop = $(window).scrollTop() < 2;
1005
+ var atBottom = $(window).scrollTop() + $(window).height() - $(document).height() > -2;
1006
+
1007
+ if((el.length == 0 && !atTop) || (!elAll.has(currentHashEl) && atBottom)) currentHash = window.location.hash;
1008
+ else {
1009
+
1010
+ if(el.length > 0) hash = "#" + el[0].getAttribute("id");
1011
+
1012
+ if(currentHash != hash) {
1013
+
1014
+ $(currentHash).removeClass("highlight");
1015
+ $('a[href^=\''+currentHash+'\']').removeClass("highlight");
1016
+
1017
+ if(hash) {
1018
+ $(hash).addClass("highlight");
1019
+ $('a[href^=\''+hash+'\']').addClass("highlight");
1020
+ }
1021
+
1022
+ if(Sticky.userScroll(el) || $(el).hasClass("sticky-magnet") || (hash == null && elAll.length == 0)) {
1023
+
1024
+ window.replaceHash(hash, false, false);
1025
+ dispatchEvent(new HashChangeEvent("hashchange"))
1026
+ }
1027
+
1028
+ currentHash = hash;
1029
+ }
1030
+ }
1031
+ };
1032
+
1033
+ PayloadReplaceHash();
1034
+
1035
+ $(e.target).find(".sticky-top").each(function() {
1036
+
1037
+ var scrollhide = $(this).attr("aria-scrollhide") || Sticky.get("scrollhide");
1038
+ scrollhide = scrollhide === "false" ? false : scrollhide;
1039
+
1040
+ var that = this;
1041
+ if(scrollhide) {
1042
+
1043
+ var isAnchor = Sticky.epsilon(e.scrollY.top, anchorY);
1044
+ if(e.first || isAnchor) {
1045
+
1046
+ $(this).addClass("show");
1047
+ $(this).removeAttr("style");
1048
+
1049
+ } else if(e.scrollY.top > this.clientHeight || $(this).hasClass("show")) {
1050
+
1051
+ // Prevent element shaking
1052
+ if(Sticky.get("transition") && Sticky.get("transition").indexOf(this) !== -1) return;
1053
+ this.addEventListener("transitionstart", function() { Sticky.add("transition", this); }, {"once": true});
1054
+ this.addEventListener("transitionend", function() { Sticky.remove("transition", this); }, {"once": true});
1055
+
1056
+ // Action element
1057
+ if(e.scrollY.delta < 0 && e.scrollY.bottom > 0) {
1058
+
1059
+ $(this).addClass("show");
1060
+ $(this).removeAttr("style");
1061
+ if(!e.first) $(this).removeClass("skip-transition");
1062
+
1063
+ } else if(e.scrollY.delta > 0){
1064
+
1065
+ var borderThickness = parseInt($(this).css("border-bottom-width"))
1066
+ + parseInt($(this).css("border-top-width"));
1067
+
1068
+ $(this).removeClass("show");
1069
+ $(this).css("top", -this.clientHeight-borderThickness);
1070
+ if(e.scrollY.top == e.scrollY.delta && !e.first)
1071
+ $(this).addClass("skip-transition");
1072
+ }
1073
+
1074
+ } else { // Smooth transition
1075
+
1076
+ Sticky.remove("transition", this);
1077
+
1078
+ $(this).css("top", Math.min(0,-e.scrollY.top));
1079
+ if(e.scrollY.top > 0 && !e.first)
1080
+ $(this).addClass("skip-transition");
1081
+ }
1082
+ }
1083
+
1084
+ var style = window.getComputedStyle(this);
1085
+ var scroller = $(this).closestScrollable()[0];
1086
+ var scrollcatchPos = $(this).attr("aria-scrollcatch-pos");
1087
+ var scrollcatchClone = $(this).attr("aria-scrollcatch-clone");
1088
+
1089
+ if(!e.scrollT.elastic) {
1090
+
1091
+ if(style["position"] !== "fixed" && !scrollcatchClone) {
1092
+
1093
+ var scrollcatch = $(this).attr("aria-scrollcatch") || Sticky.get("scrollcatch");
1094
+ scrollcatch = scrollcatch === true ? style["z-index"] : scrollcatch;
1095
+
1096
+ if (scrollcatch !== false && this.offsetTop <= scroller.scrollTop) {
1097
+
1098
+ var that = $(this).clone().removeAttr("id")
1099
+ .attr("aria-scrollcatch-clone", true);
1100
+
1101
+ $(this).addClass("caught")
1102
+ .attr("aria-scrollcatch-pos", scroller.scrollTop+1)
1103
+ .attr("aria-labelledby", $(that).uniqueId().attr("id"));
1104
+
1105
+ $(that).insertBefore($(this).css("position", "fixed").css("z-index", scrollcatch));
1106
+ }
1107
+
1108
+ } else if(scrollcatchPos > scroller.scrollTop) {
1109
+
1110
+ var that = $("#"+$(this).attr("aria-labelledby"));
1111
+ $(that).remove();
1112
+
1113
+ $(this).removeClass("caught").css("position", "").css("z-index" , "")
1114
+ .removeAttr("aria-scrollcatch-pos");
1115
+ }
1116
+ }
1117
+ });
1118
+
1119
+ $(e.target).find(".sticky-bottom").each(function() {
1120
+
1121
+ if(!Sticky.get("scrollhide")) return;
1122
+ var threshold = 1000*Sticky.parseDuration(Sticky.get("threshold"));
1123
+ var scrollHint = Math.min(1, Math.max(0, parseFloat(Sticky.get("scrollhint"))));
1124
+ var hasHint = $(this).hasClass("hint");
1125
+
1126
+ if(e.reset) hasReset = true;
1127
+ if(scrollHint) {
1128
+
1129
+ if(e.scrollY.delta < 0) {
1130
+ $(this).removeClass("hint");
1131
+ $(this).off("click.hint");
1132
+ hasReset = false;
1133
+ }
1134
+
1135
+ if(e.scrollT.delta.bottom > scrollHint*threshold && !hasHint) {
1136
+ $(this).addClass("hint");
1137
+ $(this).on("click.hint", function() { $(this).removeClass("hint").addClass("show"); });
1138
+ hasReset = false;
1139
+ }
1140
+ }
1141
+
1142
+ threshold = hasReset && hasHint ? (1-scrollHint) * threshold : threshold;
1143
+ if($(this).hasClass("show")) {
1144
+
1145
+ $(this).off("click.hint");
1146
+
1147
+ // Action element
1148
+ if (e.scrollY.bottom > this.clientHeight || e.scrollT.delta.top > threshold) {
1149
+
1150
+ $(this).removeClass("show");
1151
+ $(this).removeClass("hint");
1152
+ $(this).removeClass("skip-transition");
1153
+ $(this).removeAttr("style");
1154
+
1155
+ } else { // Smooth transition
1156
+
1157
+ $(this).css("bottom", Math.min(0, -e.scrollY.bottom));
1158
+ $(this).css("position", Sticky.get("scrollcatch"));
1159
+ if(e.scrollY.bottom > 0) $(this).addClass("skip-transition");
1160
+ }
1161
+
1162
+ } else if(e.scrollT.delta.bottom > threshold) {
1163
+
1164
+ $(this).off("click.hint");
1165
+ $(this).addClass("show").removeClass("hint");
1166
+ $(this).removeAttr("style");
1167
+ }
1168
+ });
1169
+
1170
+ $(e.target).find(".sticky-widget").each(function() {
1171
+
1172
+ //
1173
+ // Initialisation
1174
+ if(!e.first) $(this).addClass("skip-transition");
1175
+ else {
1176
+
1177
+ $(this).one("transitionend animationend", function() {
1178
+ $(this).addClass("skip-transition");
1179
+ });
1180
+ }
1181
+
1182
+ //
1183
+ // Compute offsets
1184
+ var extraOffsetTop = Math.max(Sticky.parseToPixel(this.dataset.stickyOffsetTop, this), Sticky.getScrollPadding().top) || 0;
1185
+ var extraOffsetBottom = Math.max(Sticky.parseToPixel(this.dataset.stickyOffsetBottom, this), Sticky.getScrollPadding().bottom) || 0
1186
+
1187
+ var firstChild = this.firstElementChild;
1188
+ var offsetTop = Math.max(parseInt($(this).css("margin-top")), parseInt($(firstChild).css("margin-top")));
1189
+ offsetTop += extraOffsetTop;
1190
+
1191
+ $(this).css("top", offsetTop);
1192
+ $(this).css("margin-top", offsetTop - extraOffsetTop);
1193
+ $(firstChild).css("margin-top", 0);
1194
+
1195
+ var lastChild = this.lastElementChild;
1196
+ var offsetBottom = Math.max(parseInt($(this).css("margin-bottom")), parseInt($(lastChild).css("margin-bottom")));
1197
+ offsetBottom -= extraOffsetBottom;
1198
+
1199
+ if(firstChild !== lastChild) {
1200
+
1201
+ $(this).css("margin-bottom", offsetBottom + extraOffsetBottom);
1202
+ $(lastChild).css("margin-bottom", 0);
1203
+ }
1204
+ });
1205
+
1206
+ $(e.target).find(".sticky-easein, .sticky-easeout").each(function(i) {
1207
+
1208
+ //
1209
+ // Update transition CSS
1210
+ var extraEaseTime = Sticky.parseDuration(this.dataset.stickyEasetime) || Sticky.parseDuration(Sticky.get("easetime")) || -1;
1211
+ var extraEaseDelay = Sticky.parseDuration(this.dataset.stickyEasedelay) || Sticky.parseDuration(Sticky.get("easedelay")) || -1;
1212
+
1213
+ if(e.first) {
1214
+
1215
+ var transitionProperty = $(this).css("transitionProperty").split(",");
1216
+ var transitionDuration = $(this).css("transitionDuration").split(",");
1217
+ var transitionDelay = $(this).css("transitionDelay").split(",");
1218
+
1219
+ var opacityIndex = transitionProperty.indexOf("opacity");
1220
+ if (opacityIndex < 0) opacityIndex = transitionProperty.indexOf("all");
1221
+
1222
+ var opacityIndex = (opacityIndex < 0 ? transitionDuration.length : opacityIndex);
1223
+ opacityDuration = (opacityIndex < 0 ? 0 : Sticky.parseDuration(transitionDuration[opacityIndex]));
1224
+ opacityDelay = (opacityIndex < 0 ? 0 : Sticky.parseDuration(transitionDelay[opacityIndex]));
1225
+
1226
+ transitionDuration[opacityIndex] = (extraEaseTime < 0 ? opacityDuration : extraEaseTime);
1227
+ transitionDelay [opacityIndex] = (extraEaseDelay < 0 ? opacityDelay : extraEaseDelay) + i*Sticky.parseDuration(Sticky.get("easethrottle"));
1228
+
1229
+ var transition = [];
1230
+ for (var i = 0; i < transitionProperty.length; i++)
1231
+ transition[i] = transitionProperty[i] + " " +
1232
+ Sticky.parseDuration(transitionDuration[i])+"s "+
1233
+ Sticky.parseDuration(transitionDelay[i])+"s";
1234
+
1235
+ $(this).css("transition", transition.join(","));
1236
+ }
1237
+
1238
+ //
1239
+ // Display logic
1240
+ var extraEaseIn = this.dataset.stickyEasein;
1241
+ if (extraEaseIn === undefined) extraEaseIn = this.dataset.stickyEaseinout;
1242
+ if (extraEaseIn === undefined && $(this).hasClass("sticky-easein"))
1243
+ extraEaseIn = Sticky.get("easein");
1244
+
1245
+ if(extraEaseIn !== undefined) {
1246
+ // ease-in should not be bigger than the size of the element
1247
+ extraEaseIn = parseInt(extraEaseIn);
1248
+ if(extraEaseIn < 0) extraEaseIn = Math.max(extraEaseIn, -this.clientHeight);
1249
+ }
1250
+
1251
+ var extraEaseOut = this.dataset.stickyEaseout;
1252
+ if (extraEaseOut === undefined) extraEaseOut = this.dataset.stickyEaseinout;
1253
+ if (extraEaseOut === undefined && $(this).hasClass("sticky-easeout"))
1254
+ extraEaseOut = Sticky.get("easeout");
1255
+
1256
+ if(extraEaseOut !== undefined) {
1257
+
1258
+ // ease-out should not be bigger than the size of the element
1259
+ extraEaseOut = parseInt(extraEaseOut);
1260
+ if(extraEaseOut < 0) extraEaseOut = Math.max(extraEaseOut, -this.clientHeight);
1261
+
1262
+ // ease-in-out should not overlap
1263
+ if(Math.sign(extraEaseIn) == Math.sign(extraEaseIn)) {
1264
+ if(extraEaseIn < 0) extraEaseOut = Math.min(extraEaseOut, extraEaseIn);
1265
+ else extraEaseOut = Math.max(extraEaseOut, extraEaseIn);
1266
+ }
1267
+ }
1268
+
1269
+ // Y = 0 : viewport top = element bottom
1270
+ var top = this.offsetTop - (e.scrollY.top+e.screen.vh);
1271
+
1272
+ // Y = 0 : viewport bottom = element top
1273
+ var bottom = this.offsetTop+this.clientHeight - (e.scrollY.top);
1274
+
1275
+ var isBiggerThanViewport = (e.screen.vh < this.clienHeight);
1276
+ var show = $(this).hasClass("show");
1277
+ var easeIn = $(this).hasClass("sticky-easein") && extraEaseIn !== undefined && !show;
1278
+ var easeOut = $(this).hasClass("sticky-easeout") && extraEaseOut !== undefined && show;
1279
+
1280
+ if (easeIn) {
1281
+
1282
+ var isAbove = top - extraEaseIn > 0;
1283
+ var isBelow = bottom + extraEaseIn < 0;
1284
+ var isBetween = isBiggerThanViewport
1285
+ ? !isAbove && (top + this.clientHeight + extraEaseIn > 0) &&
1286
+ !isBelow && (bottom - this.clientHeight - extraEaseIn < 0)
1287
+ : !isAbove && (top + this.clientHeight + extraEaseIn < 0) &&
1288
+ !isBelow && (bottom - this.clientHeight - extraEaseIn > 0);
1289
+
1290
+ if(Settings.debug) console.log(show,"Sticky ease-in:",isAbove,isBelow,isBetween);
1291
+ show = (!isAbove && !isBelow);
1292
+
1293
+ } else if(e.first) show = true;
1294
+
1295
+ if(easeOut) {
1296
+
1297
+ var isAbove = top - extraEaseOut > 0;
1298
+ var isBelow = bottom + extraEaseOut < 0;
1299
+ var isBetween = isBiggerThanViewport
1300
+ ? !isAbove && (top + this.clientHeight + extraEaseOut > 0) &&
1301
+ !isBelow && (bottom - this.clientHeight - extraEaseOut < 0)
1302
+ : !isAbove && (top + this.clientHeight + extraEaseOut < 0) &&
1303
+ !isBelow && (bottom - this.clientHeight - extraEaseOut > 0);
1304
+
1305
+ if(Settings.debug) console.log(show,"Sticky ease-out:",isAbove,isBelow,isBetween);
1306
+ show = !isAbove && !isBelow;
1307
+
1308
+ }
1309
+
1310
+ if(show) $(this).addClass("show");
1311
+ else $(this).removeClass("show");
1312
+ });
1313
+
1314
+ return this;
1315
+ }
1316
+
1317
+ Sticky.onScrollDebounce = function(e) { Sticky.reset(e.target[0]); }
1318
+ Sticky.onWheel = function (e) {
1319
+
1320
+ if(!Settings.ready) return;
1321
+
1322
+ // Overscroll detection
1323
+ var overscroll = {top:false, right:false, bottom:false, left:false};
1324
+ overscroll.top = Sticky.overscrollTop(e);
1325
+ overscroll.bottom = Sticky.overscrollBottom(e);
1326
+ overscroll.left = Sticky.overscrollLeft(e);
1327
+ overscroll.right = Sticky.overscrollRight(e);
1328
+
1329
+ Sticky.set("overscroll", overscroll);
1330
+ if(overscroll.top || overscroll.bottom || overscroll.left || overscroll.right)
1331
+ $(e.target).trigger('scrolldelta.sticky');
1332
+
1333
+ return this;
1334
+ }
1335
+
1336
+ Sticky.onAutoscroll = function() {
1337
+
1338
+ if ($(this).data("autoscroll-prevent")) {
1339
+
1340
+ $(this).closestScrollableWindow().off("scrolldelta.autoscroll");
1341
+ return;
1342
+ }
1343
+
1344
+ var easing = $(this).data("autoscroll-easing");
1345
+ if (easing == undefined) easing = Sticky.get("autoscroll_easing");
1346
+ var speed = $(this).data("autoscroll-speed");
1347
+ if (speed == undefined) speed = Sticky.get("autoscroll_speed");
1348
+ var bouncing = $(this).data("autoscroll-bouncing");
1349
+ if (bouncing != undefined) bouncing = bouncing ? true : false;
1350
+ if (bouncing == undefined) bouncing = Sticky.get("autoscroll_bouncing");
1351
+
1352
+ var startOver = $(this).data("autoscroll-startover");
1353
+ if (startOver != undefined) startOver = startOver ? true : false;
1354
+ if (startOver == undefined) startOver = Sticky.get("autoscroll_startover");
1355
+
1356
+ var reverse = $(this).data("autoscroll-reverse");
1357
+ if (reverse != undefined) reverse = reverse ? true : false;
1358
+ if (reverse == undefined) reverse = Sticky.get("autoscroll_reverse");
1359
+
1360
+ var autoscrollX = $(this).data("autoscroll-x");
1361
+ if(autoscrollX == undefined) autoscrollX = Sticky.get("autoscroll");
1362
+ var autoscrollY = $(this).data("autoscroll-y");
1363
+ if(autoscrollY == undefined) autoscrollY = Sticky.get("autoscroll");
1364
+
1365
+ var thresholdMinX = $(this).data("autoscroll-minwidth");
1366
+ if(thresholdMinX == undefined) thresholdMinX = Sticky.get("autoscroll_minwidth");
1367
+ var thresholdMinY = $(this).data("autoscroll-minheight");
1368
+ if(thresholdMinY == undefined) thresholdMinY = Sticky.get("autoscroll_minheight");
1369
+
1370
+ var _onAutoscroll = function() {
1371
+
1372
+ var scrollHeight = $(this).prop('scrollHeight') - $(this).innerHeight();
1373
+ var atTop = $(this).scrollTop() < 1;
1374
+ var atBottom = Math.abs($(this).scrollTop() - scrollHeight) < 1;
1375
+
1376
+ var scrollWidth = $(this).prop('scrollWidth') - $(this).innerWidth();
1377
+ var atLeft = $(this).scrollLeft() < 1;
1378
+ var atRight = Math.abs($(this).scrollLeft() - scrollWidth) < 1;
1379
+
1380
+ var noScrollY = (atTop && atBottom) || scrollHeight < thresholdMinY;
1381
+ var noScrollX = (atLeft && atRight) || scrollWidth < thresholdMinX;
1382
+
1383
+ if ((noScrollY && noScrollX ) ||
1384
+ (noScrollY && !autoscrollX) ||
1385
+ (noScrollX && !autoscrollY) ||
1386
+ (!autoscrollX && !autoscrollY)) return;
1387
+
1388
+ if (Settings.debug && !$(this).isScrollable()[0])
1389
+ console.error(this, "is not scrollable: autoscroll canceled");
1390
+
1391
+ if(reverse && $(this).scrollLeft() == 0 && $(this).scrollTop() == 0)
1392
+ reverse = !reverse;
1393
+ else if (!reverse && $(this).scrollLeft() == scrollWidth && $(this).scrollTop() == scrollHeight)
1394
+ reverse = !reverse;
1395
+
1396
+ var scrollOptions = { speed: speed, easing: easing};
1397
+ var scrollBackOptions = { speed: speed, easing: easing};
1398
+
1399
+ if(autoscrollY) {
1400
+ scrollOptions.top = noScrollY ? 0 : (reverse ? 0 : scrollHeight);
1401
+ scrollBackOptions.top = noScrollY ? 0 : (reverse ? scrollHeight : 0);
1402
+ }
1403
+
1404
+ if(autoscrollX) {
1405
+ scrollOptions.left = noScrollX ? 0 : (reverse ? 0 : scrollWidth );
1406
+ scrollBackOptions.left = noScrollX ? 0 : (reverse ? scrollWidth : 0);
1407
+ }
1408
+
1409
+ Sticky.scrollTo(scrollOptions, function() {
1410
+
1411
+ if( !bouncing || $(this).data("autoscroll-prevent") ) return;
1412
+
1413
+ $(this).prop("user-scroll", true);
1414
+ Sticky.scrollTo(scrollBackOptions, function() {
1415
+
1416
+ Sticky.onAutoscroll.call(this);
1417
+ if(reverse) $(this).removeData("autoscroll-reverse");
1418
+ else $(this).data("autoscroll-reverse", true);
1419
+
1420
+ }.bind(this), this)
1421
+
1422
+ }.bind(this), this);
1423
+
1424
+ return this;
1425
+
1426
+ }.bind(this);
1427
+
1428
+ return _onAutoscroll();
1429
+ }
1430
+
1431
+ Sticky.onLoad = function (el = window)
1432
+ {
1433
+ if(Sticky.get("disable") === true) {
1434
+
1435
+ $(".sticky").addClass("sticky-disabled");
1436
+ return;
1437
+ }
1438
+
1439
+ Sticky.reset(el);
1440
+
1441
+ $(el).on('wheel.sticky', Sticky.onWheel);
1442
+ $(el).on('scrolldelta.sticky', Sticky.onScrollDelta);
1443
+ $(el).on('scrolldelta.sticky', Sticky.debounce(Sticky.onScrollDebounce, 1000*Sticky.parseDuration(Sticky.get("debounce"))));
1444
+
1445
+ // Sticky top anchor
1446
+ $(el === window ? "html" : el).find('a[href^="#"]').on('click', function () {
1447
+
1448
+ var split = this.href.split("#");
1449
+ var anchorElem = $(split[1] == "" ? "body" : "#"+split[1]);
1450
+ anchorY = anchorElem.length ? anchorElem[0].offsetTop - Sticky.getScrollPadding().top : 0;
1451
+ });
1452
+
1453
+ if(Sticky.get("swipehint"))
1454
+ {
1455
+ $(".sticky-swipehint-container").each(function() {
1456
+
1457
+ var image = document.createElement("img");
1458
+ image.src = $(this).data("image");
1459
+
1460
+ var span = document.createElement("span");
1461
+ span.append(image);
1462
+
1463
+ $(this).append(span);
1464
+ });
1465
+
1466
+ timeout = setTimeout(() => {
1467
+
1468
+ $(".sticky-swipehint").addClass("sticky-swipehint-reveal");
1469
+ $(".sticky-swipehint").on("scroll", function()
1470
+ {
1471
+ $(this).removeClass("sticky-swipehint-reveal");
1472
+ var debounceTime = 1000*Sticky.parseDuration(Sticky.get("swipehint_debounce"));
1473
+ if(!debounceTime) return;
1474
+
1475
+ $(".sticky-swipehint").on('scroll', Sticky.debounce(function() {
1476
+
1477
+ $(this).addClass("sticky-swipehint-reveal");
1478
+
1479
+ }, debounceTime));
1480
+ });
1481
+
1482
+ }, 1000*Sticky.parseDuration(Sticky.get("swipehint_delay") + 1));
1483
+ }
1484
+
1485
+ // Sticky magnet control
1486
+ if(Sticky.get("scrollsnap"))
1487
+ {
1488
+ $(".sticky-magnet-first").on("click", function() { Sticky.scrollToFirstSnap(); });
1489
+ $(".sticky-magnet-prev" ).on("click", function() { Sticky.scrollToPreviousSnap(); });
1490
+ $(".sticky-magnet-next" ).on("click", function() { Sticky.scrollToNextSnap(); });
1491
+ $(".sticky-magnet-last" ).on("click", function() { Sticky.scrollToLastSnap(); });
1492
+
1493
+ $(el).on('scrolldelta.sticky.snap', Sticky.onScrollSnap);
1494
+ }
1495
+
1496
+ // Sticky autoscroll
1497
+ if(Sticky.get("autoscroll"))
1498
+
1499
+ $(".sticky-autoscroll").each(function() {
1500
+
1501
+ var scroller = $(this).closestScrollable();
1502
+
1503
+ var reverseDelay = $(scroller).data("autoscroll-delay-reverse");
1504
+ if (reverseDelay == undefined) reverseDelay = Sticky.get("autoscroll_delay_reverse");
1505
+ var reverseSpeed = $(scroller).data("autoscroll-speed-reverse");
1506
+ if (reverseSpeed == undefined) reverseSpeed = Sticky.get("autoscroll_speed_reverse");
1507
+ var reverseDuration = $(scroller).data("autoscroll-duration-reverse");
1508
+ if (reverseDuration == undefined) reverseDuration = Sticky.get("autoscroll_duration_reverse");
1509
+
1510
+ var startOver = $(scroller).data("autoscroll-startover");
1511
+ if (startOver == undefined) startOver = Sticky.get("autoscroll_startover");
1512
+
1513
+ var delay = $(scroller).data("autoscroll-delay");
1514
+ if (delay == undefined) delay = Sticky.get("autoscroll_delay");
1515
+
1516
+ var thresholdMinX = $(scroller).data("autoscroll-minwidth");
1517
+ if(thresholdMinX == undefined) thresholdMinX = Sticky.get("autoscroll_minwidth");
1518
+ var thresholdMinY = $(scroller).data("autoscroll-minheight");
1519
+ if(thresholdMinY == undefined) thresholdMinY = Sticky.get("autoscroll_minheight");
1520
+
1521
+ var scrollHeight = $(scroller).prop('scrollHeight') - $(scroller).innerHeight();
1522
+ var atTop = $(scroller).scrollTop() < 1;
1523
+ var atBottom = Math.abs($(scroller).scrollTop() - scrollHeight) < 1;
1524
+
1525
+ var scrollWidth = $(scroller).prop('scrollWidth') - $(scroller).innerWidth();
1526
+ var atLeft = $(scroller).scrollLeft() < 1;
1527
+ var atRight = Math.abs($(scroller).scrollLeft() - scrollWidth) < 1;
1528
+
1529
+ var mouseAction = $(scroller).data("autoscroll-mouse-action");
1530
+ if (mouseAction == undefined) mouseAction = Sticky.get("autoscroll_mouse_action");
1531
+
1532
+ var noScrollY = (atTop && atBottom) || scrollHeight < thresholdMinY;
1533
+ var noScrollX = (atLeft && atRight) || scrollWidth < thresholdMinX;
1534
+
1535
+ $(scroller).data("autoscroll-prevent", false);
1536
+
1537
+ var autoscrollTimeout = undefined;
1538
+ var payloadAutoscroll = function() {
1539
+
1540
+ if(autoscrollTimeout != undefined) clearTimeout(autoscrollTimeout);
1541
+ autoscrollTimeout = setTimeout(() => Sticky.onAutoscroll.call(scroller), 1000*Sticky.parseDuration(delay) + 1);
1542
+ };
1543
+
1544
+ //
1545
+ // Mouse events
1546
+ if (mouseAction) {
1547
+
1548
+ $(scroller).on("mouseenter.autoscroll touchstart.autoscroll", function() {
1549
+
1550
+ if(autoscrollTimeout != undefined) clearTimeout(autoscrollTimeout);
1551
+ $(scroller).prop("user-scroll", true);
1552
+ $(scroller).stop();
1553
+ });
1554
+
1555
+ $(scroller).on("mouseleave.autoscroll touchend.autoscroll", function() {
1556
+
1557
+ if(startOver) payloadAutoscroll();
1558
+ else {
1559
+
1560
+ $(scroller).data("autoscroll-prevent", "true");
1561
+ $(scroller).off("mouseleave.autoscroll touchend.autoscroll");
1562
+ }
1563
+ });
1564
+
1565
+ $(scroller).on("onbeforeunload.autoscroll", function() {
1566
+
1567
+ $(this).off("mousewheel.autoscroll touchstart.autoscroll");
1568
+ $(this).off("mouseenter.autoscroll touchstart.autoscroll");
1569
+ $(this).off("mouseleave.autoscroll touchend.autoscroll");
1570
+ $(this).off("scroll.autoscroll");
1571
+ $(this).off("onbeforeunload.autoscroll");
1572
+
1573
+ var scrollerWindow = $(this).closestScrollableWindow();
1574
+ $(scrollerWindow).off("scroll.autoscroll");
1575
+ });
1576
+ }
1577
+
1578
+ //
1579
+ // Call action
1580
+ if(noScrollY && noScrollX) payloadAutoscroll();
1581
+ else if($(scroller).data("autoscroll-reverse")) {
1582
+
1583
+ var scrollWidth = $(scroller).prop('scrollWidth') - scroller.innerWidth();
1584
+ var scrollHeight = $(scroller).prop('scrollHeight') - scroller.innerHeight();
1585
+
1586
+ $(scroller).on("wheel.autoscroll DOMMouseScroll.autoscroll mousewheel.autoscroll touchstart.autoscroll", function(e) {
1587
+
1588
+ $(this).prop("user-scroll", true);
1589
+ $(this).stop();
1590
+ });
1591
+
1592
+ if (Settings.debug) console.log("Autoscroll reverse delay applied is \"" + reverseDelay + "\".");
1593
+ setTimeout(() => Sticky.scrollTo({
1594
+ left:scrollWidth,
1595
+ top:scrollHeight,
1596
+ duration:reverseDuration,
1597
+ speed:reverseSpeed
1598
+ }, payloadAutoscroll, scroller), 1000*Sticky.parseDuration(reverseDelay + 1));
1599
+
1600
+ } else payloadAutoscroll();
1601
+ }
1602
+ );
1603
+
1604
+ Settings.ready = true;
1605
+
1606
+ return this;
1607
+ }
1608
+
1609
+ $(window).on("onbeforeunload", function() {
1610
+
1611
+ Settings.ready = false;
1612
+ Sticky.reset();
1613
+
1614
+ $(window).off('wheel.sticky');
1615
+ $(window).off('scrolldelta.sticky');
1616
+ $(window).off("hashchange");
1617
+ $('a[href^="#"]').off();
1618
+ });
1619
+
1620
+ $(window).on("DOMContentLoaded", function() {
1621
+
1622
+ Sticky.onLoad();
1623
+ $(window).trigger("scroll.sticky");
1624
+ $(window).on("hashchange", (e) => Sticky.reset());
1625
+ });
1626
+
1627
+ return Sticky;
1628
+ });