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.