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