PageSourceSearch

https://soilslab.ai/wp-content/themes/qempo/js/smooth-scroll.js?ver=7.1.2

js soilslab.ai collected 2026-10-05 15:51:10 UTC 22,953 bytes, 775 lines download raw bytes

vendor: 10,113 bytes, lines 1-355
1(function () {
2  
3// Scroll Variables (tweakable)
4var defaultOptions = {
5
6    // Scrolling Core
7    frameRate        : 150, // [Hz]
8    animationTime    : 400, // [ms]
9    stepSize         : 100, // [px]
10
11    // Pulse (less tweakable)
12    // ratio of "tail" to "acceleration"
13    pulseAlgorithm   : true,
14    pulseScale       : 4,
15    pulseNormalize   : 1,
16
17    // Acceleration
18    accelerationDelta : 50,  // 50
19    accelerationMax   : 3,   // 3
20
21    // Keyboard Settings
22    keyboardSupport   : true,  // option
23    arrowScroll       : 50,    // [px]
24
25    // Other
26    fixedBackground   : true, 
27    excluded          : ''    
28};
29
30var options = defaultOptions;
31
32
33// Other Variables
34var isExcluded = false;
35var isFrame = false;
36var direction = { x: 0, y: 0 };
37var initDone  = false;
38var root = document.documentElement;
39var activeElement;
40var observer;
41var refreshSize;
42var deltaBuffer = [];
43var deltaBufferTimer;
44var isMac = /^Mac/.test(navigator.platform);
45
46var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32, 
47            pageup: 33, pagedown: 34, end: 35, home: 36 };
48var arrowKeys = { 37: 1, 38: 1, 39: 1, 40: 1 };
49
50/***********************************************
51 * INITIALIZE
52 ***********************************************/
53
54/**
55 * Tests if smooth scrolling is allowed. Shuts down everything if not.
56 */
57function initTest() {
58    if (options.keyboardSupport) {
59        addEvent('keydown', keydown);
60    }
61}
62
63/**
64 * Sets up scrolls array, determines if frames are involved.
65 */
66function init() {
67  
68    if (initDone || !document.body) return;
69
70    initDone = true;
71
72    var body = document.body;
73    var html = document.documentElement;
74    var windowHeight = window.innerHeight; 
75    var scrollHeight = body.scrollHeight;
76    
77    // check compat mode for root element
78    root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
79    activeElement = body;
80    
81    initTest();
82
83    // Checks if this script is running in a frame
84    if (top != self) {
85        isFrame = true;
86    }
87
88    /**
89     * Safari 10 fixed it, Chrome fixed it in v45:
90     * This fixes a bug where the areas left and right to 
91     * the content does not trigger the onmousewheel event
92     * on some pages. e.g.: html, body { height: 100% }
93     */
94    else if (isOldSafari &&
95             scrollHeight > windowHeight &&
96            (body.offsetHeight <= windowHeight || 
97             html.offsetHeight <= windowHeight)) {
98
99        var fullPageElem = document.createElement('div');
100        fullPageElem.style.cssText = 'position:absolute; z-index:-10000; ' +
101                                     'top:0; left:0; right:0; height:' + 
102                                      root.scrollHeight + 'px';
103        document.body.appendChild(fullPageElem);
104        
105        // DOM changed (throttled) to fix height
106        var pendingRefresh;
107        refreshSize = function () {
108            if (pendingRefresh) return; // could also be: clearTimeout(pendingRefresh);
109            pendingRefresh = setTimeout(function () {
110                if (isExcluded) return; // could be running after cleanup
111                fullPageElem.style.height = '0';
112                fullPageElem.style.height = root.scrollHeight + 'px';
113                pendingRefresh = null;
114            }, 500); // act rarely to stay fast
115        };
116  
117        setTimeout(refreshSize, 10);
118
119        addEvent('resize', refreshSize);
120
121        // TODO: attributeFilter?
122        var config = {
123            attributes: true, 
124            childList: true, 
125            characterData: false 
126            // subtree: true
127        };
128
129        observer = new MutationObserver(refreshSize);
130        observer.observe(body, config);
131
132        if (root.offsetHeight <= windowHeight) {
133            var clearfix = document.createElement('div');   
134            clearfix.style.clear = 'both';
135            body.appendChild(clearfix);
136        }
137    }
138
139    // disable fixed background
140    if (!options.fixedBackground && !isExcluded) {
141        body.style.backgroundAttachment = 'scroll';
142        html.style.backgroundAttachment = 'scroll';
143    }
144}
145
146/**
147 * Removes event listeners and other traces left on the page.
148 */
149function cleanup() {
150    observer && observer.disconnect();
151    removeEvent(wheelEvent, wheel);
152    removeEvent('mousedown', mousedown);
153    removeEvent('keydown', keydown);
154    removeEvent('resize', refreshSize);
155    removeEvent('load', init);
156}
157
158
159/************************************************
160 * SCROLLING 
161 ************************************************/
162 
163var que = [];
164var pending = false;
165var lastScroll = Date.now();
166
167/**
168 * Pushes scroll actions to the scrolling queue.
169 */
170function scrollArray(elem, left, top) {
171    
172    directionCheck(left, top);
173
174    if (options.accelerationMax != 1) {
175        var now = Date.now();
176        var elapsed = now - lastScroll;
177        if (elapsed < options.accelerationDelta) {
178            var factor = (1 + (50 / elapsed)) / 2;
179            if (factor > 1) {
180                factor = Math.min(factor, options.accelerationMax);
181                left *= factor;
182                top  *= factor;
183            }
184        }
185        lastScroll = Date.now();
186    }          
187    
188    // push a scroll command
189    que.push({
190        x: left, 
191        y: top, 
192        lastX: (left < 0) ? 0.99 : -0.99,
193        lastY: (top  < 0) ? 0.99 : -0.99, 
194        start: Date.now()
195    });
196        
197    // don't act if there's a pending queue
198    if (pending) {
199        return;
200    }  
201
202    var scrollRoot = getScrollRoot();
203    var isWindowScroll = (elem === scrollRoot || elem === document.body);
204    
205    // if we haven't already fixed the behavior, 
206    // and it needs fixing for this sesh
207    if (elem.$scrollBehavior == null && isScrollBehaviorSmooth(elem)) {
208        elem.$scrollBehavior = elem.style.scrollBehavior;
209        elem.style.scrollBehavior = 'auto';
210    }
211
212    var step = function (time) {
213        
214        var now = Date.now();
215        var scrollX = 0;
216        var scrollY = 0; 
217    
218        for (var i = 0; i < que.length; i++) {
219            
220            var item = que[i];
221            var elapsed  = now - item.start;
222            var finished = (elapsed >= options.animationTime);
223            
224            // scroll position: [0, 1]
225            var position = (finished) ? 1 : elapsed / options.animationTime;
226            
227            // easing [optional]
228            if (options.pulseAlgorithm) {
229                position = pulse(position);
230            }
231            
232            // only need the difference
233            var x = (item.x * position - item.lastX) >> 0;
234            var y = (item.y * position - item.lastY) >> 0;
235            
236            // add this to the total scrolling
237            scrollX += x;
238            scrollY += y;            
239            
240            // update last values
241            item.lastX += x;
242            item.lastY += y;
243        
244            // delete and step back if it's over
245            if (finished) {
246                que.splice(i, 1); i--;
247            }           
248        }
249
250        // scroll left and top
251        if (isWindowScroll) {
252            window.scrollBy(scrollX, scrollY);
253        } 
254        else {
255            if (scrollX) elem.scrollLeft += scrollX;
256            if (scrollY) elem.scrollTop  += scrollY;                    
257        }
258        
259        // clean up if there's nothing left to do
260        if (!left && !top) {
261            que = [];
262        }
263        
264        if (que.length) { 
265            requestFrame(step, elem, (1000 / options.frameRate + 1)); 
266        } else { 
267            pending = false;
268            // restore default behavior at the end of scrolling sesh
269            if (elem.$scrollBehavior != null) {
270                elem.style.scrollBehavior = elem.$scrollBehavior;
271                elem.$scrollBehavior = null;
272            }
273        }
274    };
275    
276    // start a new queue of actions
277    requestFrame(step, elem, 0);
278    pending = true;
279}
280
281
282/***********************************************
283 * EVENTS
284 ***********************************************/
285
286/**
287 * Mouse wheel handler.
288 * @param {Object} event
289 */
290function wheel(event) {
291
292    if (!initDone) {
293        init();
294    }
295    
296    var target = event.target;
297
298    // leave early if default action is prevented   
299    // or it's a zooming event with CTRL 
300    if (event.defaultPrevented || event.ctrlKey) {
301        return true;
302    }
303    
304    // leave embedded content alone (flash & pdf)
305    if (isNodeName(activeElement, 'embed') || 
306       (isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) ||
307        isNodeName(activeElement, 'object') ||
308        target.shadowRoot) {
309        return true;
310    }
311
312    var deltaX = -event.wheelDeltaX || event.deltaX || 0;
313    var deltaY = -event.wheelDeltaY || event.deltaY || 0;
314    
315    if (isMac) {
316        if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) {
317            deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX));
318        }
319        if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) {
320            deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY));
321        }
322    }
323    
324    // use wheelDelta if deltaX/Y is not available
325    if (!deltaX && !deltaY) {
326        deltaY = -event.wheelDelta || 0;
327    }
328
329    // line based scrolling (Firefox mostly)
330    if (event.deltaMode === 1) {
331        deltaX *= 40;
332        deltaY *= 40;
333    }
334
335    var overflowing = overflowingAncestor(target);
336
337    // nothing to do if there's no element that's scrollable
338    if (!overflowing) {
339        // except Chrome iframes seem to eat wheel events, which we need to 
340        // propagate up, if the iframe has nothing overflowing to scroll
341        if (isFrame && isChrome)  {
342            // change target to iframe element itself for the parent frame
343            Object.defineProperty(event, "target", {value: window.frameElement});
344            return parent.wheel(event);
345        }
346        return true;
347    }
348    
349    // check if it's a touchpad scroll that should be ignored
350    if (isTouchpad(deltaY)) {
351        return true;
352    }
353
354    // scale by step size
355    // delta is 120 most of the time
356    // synaptics seems to send 1 sometimes
357    if (Math.abs(deltaX) > 1.2) {
358        deltaX *= options.stepSize / 120;
359    }
360    if (Math.abs(deltaY) > 1.2) {
361        deltaY *= options.stepSize / 120;
362    }
363    
364    scrollArray(overflowing, deltaX, deltaY);
365    event.preventDefault();
366    scheduleClearCache();
367}
368
369/**
370 * Keydown event handler.
371 * @param {Object} event
372 */
373function keydown(event) {
374
375    var target   = event.target;
376    var modifier = event.ctrlKey || event.altKey || event.metaKey || 
377                  (event.shiftKey && event.keyCode !== key.spacebar);
378    
379    // our own tracked active element could've been removed from the DOM
380    if (!document.body.contains(activeElement)) {
381        activeElement = document.activeElement;
382    }
383
384    // do nothing if user is editing text
385    // or using a modifier key (except shift)
386    // or in a dropdown
387    // or inside interactive elements
388    var inputNodeNames = /^(textarea|select|embed|object)$/i;
389    var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i;
390    if ( event.defaultPrevented ||
391         inputNodeNames.test(target.nodeName) ||
392         isNodeName(target, 'input') && !buttonTypes.test(target.type) ||
393         isNodeName(activeElement, 'video') ||
394         isInsideYoutubeVideo(event) ||
395         target.isContentEditable || 
396         modifier ) {
397      return true;
398    }
399
400    // [spacebar] should trigger button press, leave it alone
401    if ((isNodeName(target, 'button') ||
402         isNodeName(target, 'input') && buttonTypes.test(target.type)) &&
403        event.keyCode === key.spacebar) {
404      return true;
405    }
406
407    // [arrwow keys] on radio buttons should be left alone
408    if (isNodeName(target, 'input') && target.type == 'radio' &&
409        arrowKeys[event.keyCode])  {
410      return true;
411    }
412    
413    var shift, x = 0, y = 0;
414    var overflowing = overflowingAncestor(activeElement);
415
416    if (!overflowing) {
417        // Chrome iframes seem to eat key events, which we need to 
418        // propagate up, if the iframe has nothing overflowing to scroll
419        return (isFrame && isChrome) ? parent.keydown(event) : true;
420    }
421
422    var clientHeight = overflowing.clientHeight; 
423
424    if (overflowing == document.body) {
425        clientHeight = window.innerHeight;
426    }
427
428    switch (event.keyCode) {
429        case key.up:
430            y = -options.arrowScroll;
431            break;
432        case key.down:
433            y = options.arrowScroll;
434            break;         
435        case key.spacebar: // (+ shift)
436            shift = event.shiftKey ? 1 : -1;
437            y = -shift * clientHeight * 0.9;
438            break;
439        case key.pageup:
440            y = -clientHeight * 0.9;
441            break;
442        case key.pagedown:
443            y = clientHeight * 0.9;
444            break;
445        case key.home:
446            if (overflowing == document.body && document.scrollingElement)
447                overflowing = document.scrollingElement;
448            y = -overflowing.scrollTop;
449            break;
450        case key.end:
451            var scroll = overflowing.scrollHeight - overflowing.scrollTop;
452            var scrollRemaining = scroll - clientHeight;
453            y = (scrollRemaining > 0) ? scrollRemaining + 10 : 0;
454            break;
455        case key.left:
456            x = -options.arrowScroll;
457            break;
458        case key.right:
459            x = options.arrowScroll;
460            break;            
461        default:
462            return true; // a key we don't care about
463    }
464
465    scrollArray(overflowing, x, y);
466    event.preventDefault();
467    scheduleClearCache();
468}
469
470/**
471 * Mousedown event only for updating activeElement
472 */
473function mousedown(event) {
474    activeElement = event.target;
475}
476
477
478/***********************************************
479 * OVERFLOW
480 ***********************************************/
481
482var uniqueID = (function () {
483    var i = 0;
484    return function (el) {
485        return el.uniqueID || (el.uniqueID = i++);
486    };
487})();
488
489var cacheX = {}; // cleared out after a scrolling session
490var cacheY = {}; // cleared out after a scrolling session
491var clearCacheTimer;
492var smoothBehaviorForElement = {};
493
494//setInterval(function () { cache = {}; }, 10 * 1000);
495
496function scheduleClearCache() {
497    clearTimeout(clearCacheTimer);
498    clearCacheTimer = setInterval(function () { 
499        cacheX = cacheY = smoothBehaviorForElement = {}; 
500    }, 1*1000);
501}
502
vendor: 8,459 bytes, lines 503-775
503function setCache(elems, overflowing, x) {
504    var cache = x ? cacheX : cacheY;
505    for (var i = elems.length; i--;)
506        cache[uniqueID(elems[i])] = overflowing;
507    return overflowing;
508}
509
510function getCache(el, x) {
511    return (x ? cacheX : cacheY)[uniqueID(el)];
512}
513
514//  (body)                (root)
515//         | hidden | visible | scroll |  auto  |
516// hidden  |   no   |    no   |   YES  |   YES  |
517// visible |   no   |   YES   |   YES  |   YES  |
518// scroll  |   no   |   YES   |   YES  |   YES  |
519// auto    |   no   |   YES   |   YES  |   YES  |
520
521function overflowingAncestor(el) {
522    var elems = [];
523    var body = document.body;
524    var rootScrollHeight = root.scrollHeight;
525    do {
526        var cached = getCache(el, false);
527        if (cached) {
528            return setCache(elems, cached);
529        }
530        elems.push(el);
531        if (rootScrollHeight === el.scrollHeight) {
532            var topOverflowsNotHidden = overflowNotHidden(root) && overflowNotHidden(body);
533            var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root);
534            if (isFrame && isContentOverflowing(root) || 
535               !isFrame && isOverflowCSS) {
536                return setCache(elems, getScrollRoot()); 
537            }
538        } else if (isContentOverflowing(el) && overflowAutoOrScroll(el)) {
539            return setCache(elems, el);
540        }
541    } while ((el = el.parentElement));
542}
543
544function isContentOverflowing(el) {
545    return (el.clientHeight + 10 < el.scrollHeight);
546}
547
548// typically for <body> and <html>
549function overflowNotHidden(el) {
550    var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
551    return (overflow !== 'hidden');
552}
553
554// for all other elements
555function overflowAutoOrScroll(el) {
556    var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
557    return (overflow === 'scroll' || overflow === 'auto');
558}
559
560// for all other elements
561function isScrollBehaviorSmooth(el) {
562    var id = uniqueID(el);
563    if (smoothBehaviorForElement[id] == null) {
564        var scrollBehavior = getComputedStyle(el, '')['scroll-behavior'];
565        smoothBehaviorForElement[id] = ('smooth' == scrollBehavior);
566    }
567    return smoothBehaviorForElement[id];
568}
569
570
571/***********************************************
572 * HELPERS
573 ***********************************************/
574
575function addEvent(type, fn, arg) {
576    window.addEventListener(type, fn, arg || false);
577}
578
579function removeEvent(type, fn, arg) {
580    window.removeEventListener(type, fn, arg || false);  
581}
582
583function isNodeName(el, tag) {
584    return el && (el.nodeName||'').toLowerCase() === tag.toLowerCase();
585}
586
587function directionCheck(x, y) {
588    x = (x > 0) ? 1 : -1;
589    y = (y > 0) ? 1 : -1;
590    if (direction.x !== x || direction.y !== y) {
591        direction.x = x;
592        direction.y = y;
593        que = [];
594        lastScroll = 0;
595    }
596}
597
598if (window.localStorage && localStorage.SS_deltaBuffer) {
599    try { // #46 Safari throws in private browsing for localStorage 
600        deltaBuffer = localStorage.SS_deltaBuffer.split(',');
601    } catch (e) { } 
602}
603
604function isTouchpad(deltaY) {
605    if (!deltaY) return;
606    if (!deltaBuffer.length) {
607        deltaBuffer = [deltaY, deltaY, deltaY];
608    }
609    deltaY = Math.abs(deltaY);
610    deltaBuffer.push(deltaY);
611    deltaBuffer.shift();
612    clearTimeout(deltaBufferTimer);
613    deltaBufferTimer = setTimeout(function () {
614        try { // #46 Safari throws in private browsing for localStorage
615            localStorage.SS_deltaBuffer = deltaBuffer.join(',');
616        } catch (e) { }  
617    }, 1000);
618    var dpiScaledWheelDelta = deltaY > 120 && allDeltasDivisableBy(deltaY); // win64 
619    var tp = !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100) && !dpiScaledWheelDelta;
620    if (deltaY < 50) return true;
621    return tp;
622} 
623
624function isDivisible(n, divisor) {
625    return (Math.floor(n / divisor) == n / divisor);
626}
627
628function allDeltasDivisableBy(divisor) {
629    return (isDivisible(deltaBuffer[0], divisor) &&
630            isDivisible(deltaBuffer[1], divisor) &&
631            isDivisible(deltaBuffer[2], divisor));
632}
633
634function isInsideYoutubeVideo(event) {
635    var elem = event.target;
636    var isControl = false;
637    if (document.URL.indexOf ('www.youtube.com/watch') != -1) {
638        do {
639            isControl = (elem.classList && 
640                         elem.classList.contains('html5-video-controls'));
641            if (isControl) break;
642        } while ((elem = elem.parentNode));
643    }
644    return isControl;
645}
646
647var requestFrame = (function () {
648      return (window.requestAnimationFrame       || 
649              window.webkitRequestAnimationFrame || 
650              window.mozRequestAnimationFrame    ||
651              function (callback, element, delay) {
652                 window.setTimeout(callback, delay || (1000/60));
653             });
654})();
655
656var MutationObserver = (window.MutationObserver || 
657                        window.WebKitMutationObserver ||
658                        window.MozMutationObserver);  
659
660var getScrollRoot = (function() {
661  var SCROLL_ROOT = document.scrollingElement;
662  return function() {
663    if (!SCROLL_ROOT) {
664      var dummy = document.createElement('div');
665      dummy.style.cssText = 'height:10000px;width:1px;';
666      document.body.appendChild(dummy);
667      var bodyScrollTop  = document.body.scrollTop;
668      var docElScrollTop = document.documentElement.scrollTop;
669      window.scrollBy(0, 3);
670      if (document.body.scrollTop != bodyScrollTop)
671        (SCROLL_ROOT = document.body);
672      else 
673        (SCROLL_ROOT = document.documentElement);
674      window.scrollBy(0, -3);
675      document.body.removeChild(dummy);
676    }
677    return SCROLL_ROOT;
678  };
679})();
680
681
682/***********************************************
683 * PULSE (by Michael Herf)
684 ***********************************************/
685 
686/**
687 * Viscous fluid with a pulse for part and decay for the rest.
688 * - Applies a fixed force over an interval (a damped acceleration), and
689 * - Lets the exponential bleed away the velocity over a longer interval
690 * - Michael Herf, http://stereopsis.com/stopping/
691 */
692function pulse_(x) {
693    var val, start, expx;
694    // test
695    x = x * options.pulseScale;
696    if (x < 1) { // acceleartion
697        val = x - (1 - Math.exp(-x));
698    } else {     // tail
699        // the previous animation ended here:
700        start = Math.exp(-1);
701        // simple viscous drag
702        x -= 1;
703        expx = 1 - Math.exp(-x);
704        val = start + (expx * (1 - start));
705    }
706    return val * options.pulseNormalize;
707}
708
709function pulse(x) {
710    if (x >= 1) return 1;
711    if (x <= 0) return 0;
712
713    if (options.pulseNormalize == 1) {
714        options.pulseNormalize /= pulse_(1);
715    }
716    return pulse_(x);
717}
718
719
720/***********************************************
721 * FIRST RUN
722 ***********************************************/
723
724var userAgent = window.navigator.userAgent;
725var isEdge    = /Edge/.test(userAgent); // thank you MS
726var isChrome  = /chrome/i.test(userAgent) && !isEdge; 
727var isSafari  = /safari/i.test(userAgent) && !isEdge; 
728var isMobile  = /mobile/i.test(userAgent);
729var isIEWin7  = /Windows NT 6.1/i.test(userAgent) && /rv:11/i.test(userAgent);
730var isOldSafari = isSafari && (/Version\/8/i.test(userAgent) || /Version\/9/i.test(userAgent));
731var isEnabledForBrowser = (isChrome || isSafari || isIEWin7) && !isMobile;
732
733var supportsPassive = false;
734try {
735  window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
736    get: function () {
737            supportsPassive = true;
738        } 
739    }));
740} catch(e) {}
741
742var wheelOpt = supportsPassive ? { passive: false } : false;
743var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel'; 
744
745if (wheelEvent && isEnabledForBrowser) {
746    addEvent(wheelEvent, wheel, wheelOpt);
747    addEvent('mousedown', mousedown);
748    addEvent('load', init);
749}
750
751
752/***********************************************
753 * PUBLIC INTERFACE
754 ***********************************************/
755
756function SmoothScroll(optionsToSet) {
757    for (var key in optionsToSet)
758        if (defaultOptions.hasOwnProperty(key)) 
759            options[key] = optionsToSet[key];
760}
761SmoothScroll.destroy = cleanup;
762
763if (window.SmoothScrollOptions) // async API
764    SmoothScroll(window.SmoothScrollOptions);
765
766if (typeof define === 'function' && define.amd)
767    define(function() {
768        return SmoothScroll;
769    });
770else if ('object' == typeof exports)
771    module.exports = SmoothScroll;
772else
773    window.SmoothScroll = SmoothScroll;
774
775})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.