PageSourceSearch

https://www.warbirds-eaa.org/wp-content/themes/Divi/js/smoothscroll.js

js warbirds-eaa.org collected 2026-10-02 07:12:14 UTC 14,462 bytes, 552 lines download raw bytes

1/*!
2* SmoothScroll for websites v1.2.1
3* Licensed under the terms of the MIT license.
4*
5* People involved
6* - Balazs Galambosi (maintainer)
7* - Michael Herf     (Pulse Algorithm)
8*/
9
10(function(){
11
12// Scroll Variables (tweakable)
13var defaultOptions = {
14
15    // Scrolling Core
16    frameRate        : 150, // [Hz]
17    animationTime    : 400, // [px]
18    stepSize         : 80, // [px]
19
20    // Pulse (less tweakable)
21    // ratio of "tail" to "acceleration"
22    pulseAlgorithm   : true,
23    pulseScale       : 8,
24    pulseNormalize   : 1,
25
26    // Acceleration
27    accelerationDelta : 20,  // 20
28    accelerationMax   : 1,   // 1
29
30    // Keyboard Settings
31    keyboardSupport   : true,  // option
32    arrowScroll       : 50,     // [px]
33
34    // Other
35    touchpadSupport   : true,
36    fixedBackground   : true,
37    excluded          : ""
38};
39
40var options = defaultOptions;
41
42
43// Other Variables
44var isExcluded = false;
45var isFrame = false;
46var direction = { x: 0, y: 0 };
47var initDone  = false;
48var root = document.documentElement;
49var activeElement;
50var observer;
51var deltaBuffer = [ 120, 120, 120 ];
52
53var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,
54            pageup: 33, pagedown: 34, end: 35, home: 36 };
55
56
57/***********************************************
58 * SETTINGS
59 ***********************************************/
60
61var options = defaultOptions;
62
63
64/***********************************************
65 * INITIALIZE
66 ***********************************************/
67
68/**
69 * Tests if smooth scrolling is allowed. Shuts down everything if not.
70 */
71function initTest() {
72
73    var disableKeyboard = false;
74
75    // disable keyboard support if anything above requested it
76    if (disableKeyboard) {
77        removeEvent("keydown", keydown);
78    }
79
80    if (options.keyboardSupport && !disableKeyboard) {
81        addEvent("keydown", keydown);
82    }
83}
84
85/**
86 * Sets up scrolls array, determines if frames are involved.
87 */
88function init() {
89
90    if (!document.body) return;
91
92    var body = document.body;
93    var html = document.documentElement;
94    var windowHeight = window.innerHeight;
95    var scrollHeight = body.scrollHeight;
96
97    // check compat mode for root element
98    root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
99    activeElement = body;
100
101    initTest();
102    initDone = true;
103
104    // Checks if this script is running in a frame
105    if (top != self) {
106        isFrame = true;
107    }
108
109    /**
110     * This fixes a bug where the areas left and right to
111     * the content does not trigger the onmousewheel event
112     * on some pages. e.g.: html, body { height: 100% }
113     */
114    else if (scrollHeight > windowHeight &&
115            (body.offsetHeight <= windowHeight ||
116             html.offsetHeight <= windowHeight)) {
117
118        // DOMChange (throttle): fix height
119        var pending = false;
120        var refresh = function () {
121            if (!pending && html.scrollHeight != document.height) {
122                pending = true; // add a new pending action
123                setTimeout(function () {
124                    html.style.height = document.height + 'px';
125                    pending = false;
126                }, 500); // act rarely to stay fast
127            }
128        };
129        html.style.height = 'auto';
130        setTimeout(refresh, 10);
131
132        // clearfix
133        if (root.offsetHeight <= windowHeight) {
134            var underlay = document.createElement("div");
135            underlay.style.clear = "both";
136            body.appendChild(underlay);
137        }
138    }
139
140    // disable fixed background
141    if (!options.fixedBackground && !isExcluded) {
142        body.style.backgroundAttachment = "scroll";
143        html.style.backgroundAttachment = "scroll";
144    }
145}
146
147
148/************************************************
149 * SCROLLING
150 ************************************************/
151
152var que = [];
153var pending = false;
154var lastScroll = +new Date;
155
156/**
157 * Pushes scroll actions to the scrolling queue.
158 */
159function scrollArray(elem, left, top, delay) {
160
161    delay || (delay = 1000);
162    directionCheck(left, top);
163
164    if (options.accelerationMax != 1) {
165        var now = +new Date;
166        var elapsed = now - lastScroll;
167        if (elapsed < options.accelerationDelta) {
168            var factor = (1 + (30 / elapsed)) / 2;
169            if (factor > 1) {
170                factor = Math.min(factor, options.accelerationMax);
171                left *= factor;
172                top  *= factor;
173            }
174        }
175        lastScroll = +new Date;
176    }
177
178    // push a scroll command
179    que.push({
180        x: left,
181        y: top,
182        lastX: (left < 0) ? 0.99 : -0.99,
183        lastY: (top  < 0) ? 0.99 : -0.99,
184        start: +new Date
185    });
186
187    // don't act if there's a pending queue
188    if (pending) {
189        return;
190    }
191
192    var scrollWindow = (elem === document.body);
193
194    var step = function (time) {
195
196        var now = +new Date;
197        var scrollX = 0;
198        var scrollY = 0;
199
200        for (var i = 0; i < que.length; i++) {
201
202            var item = que[i];
203            var elapsed  = now - item.start;
204            var finished = (elapsed >= options.animationTime);
205
206            // scroll position: [0, 1]
207            var position = (finished) ? 1 : elapsed / options.animationTime;
208
209            // easing [optional]
210            if (options.pulseAlgorithm) {
211                position = pulse(position);
212            }
213
214            // only need the difference
215            var x = (item.x * position - item.lastX) >> 0;
216            var y = (item.y * position - item.lastY) >> 0;
217
218            // add this to the total scrolling
219            scrollX += x;
220            scrollY += y;
221
222            // update last values
223            item.lastX += x;
224            item.lastY += y;
225
226            // delete and step back if it's over
227            if (finished) {
228                que.splice(i, 1); i--;
229            }
230        }
231
232        // scroll left and top
233        if (scrollWindow) {
234            window.scrollBy(scrollX, scrollY);
235        }
236        else {
237            if (scrollX) elem.scrollLeft += scrollX;
238            if (scrollY) elem.scrollTop  += scrollY;
239        }
240
241        // clean up if there's nothing left to do
242        if (!left && !top) {
243            que = [];
244        }
245
246        if (que.length) {
247            requestFrame(step, elem, (delay / options.frameRate + 1));
248        } else {
249            pending = false;
250        }
251    };
252
253    // start a new queue of actions
254    requestFrame(step, elem, 0);
255    pending = true;
256}
257
258
259/***********************************************
260 * EVENTS
261 ***********************************************/
262
263/**
264 * Mouse wheel handler.
265 * @param {Object} event
266 */
267function wheel(event) {
268
269    if (!initDone) {
270        init();
271    }
272
273    var target = event.target;
274    var overflowing = overflowingAncestor(target);
275
276    // use default if there's no overflowing
277    // element or default action is prevented
278    if (!overflowing || event.defaultPrevented ||
279        isNodeName(activeElement, "embed") ||
280       (isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {
281        return true;
282    }
283
284    var deltaX = event.wheelDeltaX || 0;
285    var deltaY = event.wheelDeltaY || 0;
286
287    // use wheelDelta if deltaX/Y is not available
288    if (!deltaX && !deltaY) {
289        deltaY = event.wheelDelta || 0;
290    }
291
292    // check if it's a touchpad scroll that should be ignored
293    if (!options.touchpadSupport && isTouchpad(deltaY)) {
294        return true;
295    }
296
297    // scale by step size
298    // delta is 120 most of the time
299    // synaptics seems to send 1 sometimes
300    if (Math.abs(deltaX) > 1.2) {
301        deltaX *= options.stepSize / 120;
302    }
303    if (Math.abs(deltaY) > 1.2) {
304        deltaY *= options.stepSize / 120;
305    }
306
307    scrollArray(overflowing, -deltaX, -deltaY);
308    event.preventDefault();
309}
310
311/**
312 * Keydown event handler.
313 * @param {Object} event
314 */
315function keydown(event) {
316
317    var target   = event.target;
318    var modifier = event.ctrlKey || event.altKey || event.metaKey ||
319                  (event.shiftKey && event.keyCode !== key.spacebar);
320
321    // do nothing if user is editing text
322    // or using a modifier key (except shift)
323    // or in a dropdown
324    if ( /input|textarea|select|embed/i.test(target.nodeName) ||
325         target.isContentEditable ||
326         event.defaultPrevented   ||
327         modifier ) {
328      return true;
329    }
330    // spacebar should trigger button press
331    if (isNodeName(target, "button") &&
332        event.keyCode === key.spacebar) {
333      return true;
334    }
335
336    var shift, x = 0, y = 0;
337    var elem = overflowingAncestor(activeElement);
338    var clientHeight = elem.clientHeight;
339
340    if (elem == document.body) {
341        clientHeight = window.innerHeight;
342    }
343
344    switch (event.keyCode) {
345        case key.up:
346            y = -options.arrowScroll;
347            break;
348        case key.down:
349            y = options.arrowScroll;
350            break;
351        case key.spacebar: // (+ shift)
352            shift = event.shiftKey ? 1 : -1;
353            y = -shift * clientHeight * 0.9;
354            break;
355        case key.pageup:
356            y = -clientHeight * 0.9;
357            break;
358        case key.pagedown:
359            y = clientHeight * 0.9;
360            break;
361        case key.home:
362            y = -elem.scrollTop;
363            break;
364        case key.end:
365            var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
366            y = (damt > 0) ? damt+10 : 0;
367            break;
368        case key.left:
369            x = -options.arrowScroll;
370            break;
371        case key.right:
372            x = options.arrowScroll;
373            break;
374        default:
375            return true; // a key we don't care about
376    }
377
378    scrollArray(elem, x, y);
379    event.preventDefault();
380}
381
382/**
383 * Mousedown event only for updating activeElement
384 */
385function mousedown(event) {
386    activeElement = event.target;
387}
388
389
390/***********************************************
391 * OVERFLOW
392 ***********************************************/
393
394var cache = {}; // cleared out every once in while
395setInterval(function () { cache = {}; }, 10 * 1000);
396
397var uniqueID = (function () {
398    var i = 0;
399    return function (el) {
400        return el.uniqueID || (el.uniqueID = i++);
401    };
402})();
403
404function setCache(elems, overflowing) {
405    for (var i = elems.length; i--;)
406        cache[uniqueID(elems[i])] = overflowing;
407    return overflowing;
408}
409
410function overflowingAncestor(el) {
411    var elems = [];
412    var rootScrollHeight = root.scrollHeight;
413    do {
414        var cached = cache[uniqueID(el)];
415        if (cached) {
416            return setCache(elems, cached);
417        }
418        elems.push(el);
419        if (rootScrollHeight === el.scrollHeight) {
420            if (!isFrame || root.clientHeight + 10 < rootScrollHeight) {
421                return setCache(elems, document.body); // scrolling root in WebKit
422            }
423        } else if (el.clientHeight + 10 < el.scrollHeight) {
424            overflow = getComputedStyle(el, "").getPropertyValue("overflow-y");
425            if (overflow === "scroll" || overflow === "auto") {
426                return setCache(elems, el);
427            }
428        }
429    } while (el = el.parentNode);
430}
431
432
433/***********************************************
434 * HELPERS
435 ***********************************************/
436
437function addEvent(type, fn, bubble) {
438    window.addEventListener(type, fn, (bubble||false));
439}
440
441function removeEvent(type, fn, bubble) {
442    window.removeEventListener(type, fn, (bubble||false));
443}
444
445function isNodeName(el, tag) {
446    return (el.nodeName||"").toLowerCase() === tag.toLowerCase();
447}
448
449function directionCheck(x, y) {
450    x = (x > 0) ? 1 : -1;
451    y = (y > 0) ? 1 : -1;
452    if (direction.x !== x || direction.y !== y) {
453        direction.x = x;
454        direction.y = y;
455        que = [];
456        lastScroll = 0;
457    }
458}
459
460var deltaBufferTimer;
461
462function isTouchpad(deltaY) {
463    if (!deltaY) return;
464    deltaY = Math.abs(deltaY)
465    deltaBuffer.push(deltaY);
466    deltaBuffer.shift();
467    clearTimeout(deltaBufferTimer);
468    var allDivisable = (isDivisible(deltaBuffer[0], 120) &&
469                        isDivisible(deltaBuffer[1], 120) &&
470                        isDivisible(deltaBuffer[2], 120));
471    return !allDivisable;
472}
473
474function isDivisible(n, divisor) {
475    return (Math.floor(n / divisor) == n / divisor);
476}
477
478var requestFrame = (function () {
479      return  window.requestAnimationFrame       ||
480              window.webkitRequestAnimationFrame ||
481              function (callback, element, delay) {
482                  window.setTimeout(callback, delay || (1000/60));
483              };
484})();
485
486
487/***********************************************
488 * PULSE
489 ***********************************************/
490
491/**
492 * Viscous fluid with a pulse for part and decay for the rest.
493 * - Applies a fixed force over an interval (a damped acceleration), and
494 * - Lets the exponential bleed away the velocity over a longer interval
495 * - Michael Herf, http://stereopsis.com/stopping/
496 */
497function pulse_(x) {
498    var val, start, expx;
499    // test
500    x = x * options.pulseScale;
501    if (x < 1) { // acceleartion
502        val = x - (1 - Math.exp(-x));
503    } else {     // tail
504        // the previous animation ended here:
505        start = Math.exp(-1);
506        // simple viscous drag
507        x -= 1;
508        expx = 1 - Math.exp(-x);
509        val = start + (expx * (1 - start));
510    }
511    return val * options.pulseNormalize;
512}
513
514function pulse(x) {
515    if (x >= 1) return 1;
516    if (x <= 0) return 0;
517
518    if (options.pulseNormalize == 1) {
519        options.pulseNormalize /= pulse_(1);
520    }
521    return pulse_(x);
522}
523
524var isChrome = /chrome/i.test(window.navigator.userAgent);
525var wheelEvent = null;
526if ("onwheel" in document.createElement("div"))
527	wheelEvent = "wheel";
528else if ("onmousewheel" in document.createElement("div"))
529	wheelEvent = "mousewheel";
530
531var isSmoothScrollActive = document.body.className.split(' ').filter( function( className ) { return className === 'et_smooth_scroll' } ).length > 0;
532
533if (wheelEvent && isChrome && isSmoothScrollActive) {
534	addEvent(wheelEvent, wheel);
535	addEvent("mousedown", mousedown);
536	addEvent("load", init);
537}
538
539/***********************************************
540 * Interface for Divi Visual Builder
541 ***********************************************/
542window.ET_SmoothScroll = {
543    toggleKeydown: function(enable) {
544        if (enable) {
545            addEvent("keydown", keydown);
546        } else {
547            removeEvent("keydown", keydown);
548        }
549    },
550};
551
552})();

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.