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