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