1/*! 2 * smooth-scroll v16.1.0 3 * Animate scrolling to anchor links 4 * (c) 2019
vendor: 11,715 bytes, lines 4-375
4 Chris Ferdinandi 5 * MIT License 6 * http://github.com/cferdinandi/smooth-scroll 7 */ 8 9/** 10 * closest() polyfill 11 * @link https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill 12 */ 13if (window.Element && !Element.prototype.closest) { 14 Element.prototype.closest = function(s) { 15 var matches = (this.document || this.ownerDocument).querySelectorAll(s), 16 i, 17 el = this; 18 do { 19 i = matches.length; 20 while (--i >= 0 && matches.item(i) !== el) {} 21 } while ((i < 0) && (el = el.parentElement)); 22 return el; 23 }; 24} 25 26/** 27 * CustomEvent() polyfill 28 * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill 29 */ 30(function () { 31 32 if (typeof window.CustomEvent === "function") return false; 33 34 function CustomEvent(event, params) { 35 params = params || { bubbles: false, cancelable: false, detail: undefined }; 36 var evt = document.createEvent('CustomEvent'); 37 evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); 38 return evt; 39 } 40 41 CustomEvent.prototype = window.Event.prototype; 42 43 window.CustomEvent = CustomEvent; 44})(); 45/** 46 * requestAnimationFrame() polyfill 47 * By Erik Möller. Fixes from Paul Irish and Tino Zijdel. 48 * @link http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 49 * @link http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 50 * @license MIT 51 */ 52(function() { 53 var lastTime = 0; 54 var vendors = ['ms', 'moz', 'webkit', 'o']; 55 for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 56 window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 57 window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || 58 window[vendors[x]+'CancelRequestAnimationFrame']; 59 } 60 61 if (!window.requestAnimationFrame) { 62 window.requestAnimationFrame = function(callback, element) { 63 var currTime = new Date().getTime(); 64 var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 65 var id = window.setTimeout((function() { callback(currTime + timeToCall); }), 66 timeToCall); 67 lastTime = currTime + timeToCall; 68 return id; 69 }; 70 } 71 72 if (!window.cancelAnimationFrame) { 73 window.cancelAnimationFrame = function(id) { 74 clearTimeout(id); 75 }; 76 } 77}()); 78 79(function (root, factory) { 80 if (typeof define === 'function' && define.amd) { 81 define([], (function () { 82 return factory(root); 83 })); 84 } else if (typeof exports === 'object') { 85 module.exports = factory(root); 86 } else { 87 root.SmoothScroll = factory(root); 88 } 89})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this, (function (window) { 90 91 'use strict'; 92 93 // 94 // Default settings 95 // 96 97 var defaults = { 98 99 // Selectors 100 ignore: '[data-scroll-ignore]', 101 header: null, 102 topOnEmptyHash: true, 103 104 // Speed & Duration 105 speed: 500, 106 speedAsDuration: false, 107 durationMax: null, 108 durationMin: null, 109 clip: true, 110 offset: 0, 111 112 // Easing 113 easing: 'easeInOutCubic', 114 customEasing: null, 115 116 // History 117 updateURL: true, 118 popstate: true, 119 120 // Custom Events 121 emitEvents: true 122 123 }; 124 125 126 // 127 // Utility Methods 128 // 129 130 /** 131 * Check if browser supports required methods 132 * @return {Boolean} Returns true if all required methods are supported 133 */ 134 var supports = function () { 135 return ( 136 'querySelector' in document && 137 'addEventListener' in window && 138 'requestAnimationFrame' in window && 139 'closest' in window.Element.prototype 140 ); 141 }; 142 143 /** 144 * Merge two or more objects together. 145 * @param {Object} objects The objects to merge together 146 * @returns {Object} Merged values of defaults and options 147 */ 148 var extend = function () { 149 var merged = {}; 150 Array.prototype.forEach.call(arguments, (function (obj) { 151 for (var key in obj) { 152 if (!obj.hasOwnProperty(key)) return; 153 merged[key] = obj[key]; 154 } 155 })); 156 return merged; 157 }; 158 159 /** 160 * Check to see if user prefers reduced motion 161 * @param {Object} settings Script settings 162 */ 163 var reduceMotion = function () { 164 if ('matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches) { 165 return true; 166 } 167 return false; 168 }; 169 170 /** 171 * Get the height of an element. 172 * @param {Node} elem The element to get the height of 173 * @return {Number} The element's height in pixels 174 */ 175 var getHeight = function (elem) { 176 return parseInt(window.getComputedStyle(elem).height, 10); 177 }; 178 179 /** 180 * Escape special characters for use with querySelector 181 * @author Mathias Bynens 182 * @link https://github.com/mathiasbynens/CSS.escape 183 * @param {String} id The anchor ID to escape 184 */ 185 var escapeCharacters = function (id) { 186 187 // Remove leading hash 188 if (id.charAt(0) === '#') { 189 id = id.substr(1); 190 } 191 192 var string = String(id); 193 var length = string.length; 194 var index = -1; 195 var codeUnit; 196 var result = ''; 197 var firstCodeUnit = string.charCodeAt(0); 198 while (++index < length) { 199 codeUnit = string.charCodeAt(index); 200 // Note: thereâs no need to special-case astral symbols, surrogate 201 // pairs, or lone surrogates. 202 203 // If the character is NULL (U+0000), then throw an 204 // `InvalidCharacterError` exception and terminate these steps. 205 if (codeUnit === 0x0000) { 206 throw new InvalidCharacterError( 207 'Invalid character: the input contains U+0000.' 208 ); 209 } 210 211 if ( 212 // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is 213 // U+007F, [â¦] 214 (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F || 215 // If the character is the first character and is in the range [0-9] 216 // (U+0030 to U+0039), [â¦] 217 (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || 218 // If the character is the second character and is in the range [0-9] 219 // (U+0030 to U+0039) and the first character is a `-` (U+002D), [â¦] 220 ( 221 index === 1 && 222 codeUnit >= 0x0030 && codeUnit <= 0x0039 && 223 firstCodeUnit === 0x002D 224 ) 225 ) { 226 // http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point 227 result += '\\' + codeUnit.toString(16) + ' '; 228 continue; 229 } 230 231 // If the character is not handled by one of the above rules and is 232 // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or 233 // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to 234 // U+005A), or [a-z] (U+0061 to U+007A), [â¦] 235 if ( 236 codeUnit >= 0x0080 || 237 codeUnit === 0x002D || 238 codeUnit === 0x005F || 239 codeUnit >= 0x0030 && codeUnit <= 0x0039 || 240 codeUnit >= 0x0041 && codeUnit <= 0x005A || 241 codeUnit >= 0x0061 && codeUnit <= 0x007A 242 ) { 243 // the character itself 244 result += string.charAt(index); 245 continue; 246 } 247 248 // Otherwise, the escaped character. 249 // http://dev.w3.org/csswg/cssom/#escape-a-character 250 result += '\\' + string.charAt(index); 251 252 } 253 254 // Return sanitized hash 255 return '#' + result; 256 257 }; 258 259 /** 260 * Calculate the easing pattern 261 * @link https://gist.github.com/gre/1650294 262 * @param {String} type Easing pattern 263 * @param {Number} time Time animation should take to complete 264 * @returns {Number} 265 */ 266 var easingPattern = function (settings, time) { 267 var pattern; 268 269 // Default Easing Patterns 270 if (settings.easing === 'easeInQuad') pattern = time * time; // accelerating from zero velocity 271 if (settings.easing === 'easeOutQuad') pattern = time * (2 - time); // decelerating to zero velocity 272 if (settings.easing === 'easeInOutQuad') pattern = time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration 273 if (settings.easing === 'easeInCubic') pattern = time * time * time; // accelerating from zero velocity 274 if (settings.easing === 'easeOutCubic') pattern = (--time) * time * time + 1; // decelerating to zero velocity 275 if (settings.easing === 'easeInOutCubic') pattern = time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration 276 if (settings.easing === 'easeInQuart') pattern = time * time * time * time; // accelerating from zero velocity 277 if (settings.easing === 'easeOutQuart') pattern = 1 - (--time) * time * time * time; // decelerating to zero velocity 278 if (settings.easing === 'easeInOutQuart') pattern = time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration 279 if (settings.easing === 'easeInQuint') pattern = time * time * time * time * time; // accelerating from zero velocity 280 if (settings.easing === 'easeOutQuint') pattern = 1 + (--time) * time * time * time * time; // decelerating to zero velocity 281 if (settings.easing === 'easeInOutQuint') pattern = time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration 282 283 // Custom Easing Patterns 284 if (!!settings.customEasing) pattern = settings.customEasing(time); 285 286 return pattern || time; // no easing, no acceleration 287 }; 288 289 /** 290 * Determine the document's height 291 * @returns {Number} 292 */ 293 var getDocumentHeight = function () { 294 return Math.max( 295 document.body.scrollHeight, document.documentElement.scrollHeight, 296 document.body.offsetHeight, document.documentElement.offsetHeight, 297 document.body.clientHeight, document.documentElement.clientHeight 298 ); 299 }; 300 301 /** 302 * Calculate how far to scroll 303 * Clip support added by robjtede - https://github.com/cferdinandi/smooth-scroll/issues/405 304 * @param {Element} anchor The anchor element to scroll to 305 * @param {Number} headerHeight Height of a fixed header, if any 306 * @param {Number} offset Number of pixels by which to offset scroll 307 * @param {Boolean} clip If true, adjust scroll distance to prevent abrupt stops near the bottom of the page 308 * @returns {Number} 309 */ 310 var getEndLocation = function (anchor, headerHeight, offset, clip) { 311 var location = 0; 312 if (anchor.offsetParent) { 313 do { 314 location += anchor.offsetTop; 315 anchor = anchor.offsetParent; 316 } while (anchor); 317 } 318 location = Math.max(location - headerHeight - offset, 0); 319 if (clip) { 320 location = Math.min(location, getDocumentHeight() - window.innerHeight); 321 } 322 return location; 323 }; 324 325 /** 326 * Get the height of the fixed header 327 * @param {Node} header The header 328 * @return {Number} The height of the header 329 */ 330 var getHeaderHeight = function (header) { 331 return !header ? 0 : (getHeight(header) + header.offsetTop); 332 }; 333 334 /** 335 * Calculate the speed to use for the animation 336 * @param {Number} distance The distance to travel 337 * @param {Object} settings The plugin settings 338 * @return {Number} How fast to animate 339 */ 340 var getSpeed = function (distance, settings) { 341 var speed = settings.speedAsDuration ? settings.speed : Math.abs(distance / 1000 * settings.speed); 342 if (settings.durationMax && speed > settings.durationMax) return settings.durationMax; 343 if (settings.durationMin && speed < settings.durationMin) return settings.durationMin; 344 return parseInt(speed, 10); 345 }; 346 347 var setHistory = function (options) { 348 349 // Make sure this should run 350 if (!history.replaceState || !options.updateURL || history.state) return; 351 352 // Get the hash to use 353 var hash = window.location.hash; 354 hash = hash ? hash : ''; 355 356 // Set a default history 357 history.replaceState( 358 { 359 smoothScroll: JSON.stringify(options), 360 anchor: hash ? hash : window.pageYOffset 361 }, 362 document.title, 363 hash ? hash : window.location.href 364 ); 365 366 }; 367 368 /** 369 * Update the URL 370 * @param {Node} anchor The anchor that was scrolled to 371 * @param {Boolean} isNum If true, anchor is a number 372 * @param {Object} options Settings for Smooth Scroll 373 */ 374 var updateURL = function (anchor, isNum, options) { 375
vendor: 5,566 bytes, lines 376-561
376 // Bail if the anchor is a number 377 if (isNum) return; 378 379 // Verify that pushState is supported and the updateURL option is enabled 380 if (!history.pushState || !options.updateURL) return; 381 382 // Update URL 383 history.pushState( 384 { 385 smoothScroll: JSON.stringify(options), 386 anchor: anchor.id 387 }, 388 document.title, 389 anchor === document.documentElement ? '#top' : '#' + anchor.id 390 ); 391 392 }; 393 394 /** 395 * Bring the anchored element into focus 396 * @param {Node} anchor The anchor element 397 * @param {Number} endLocation The end location to scroll to 398 * @param {Boolean} isNum If true, scroll is to a position rather than an element 399 */ 400 var adjustFocus = function (anchor, endLocation, isNum) { 401 402 // Is scrolling to top of page, blur 403 if (anchor === 0) { 404 document.body.focus(); 405 } 406 407 // Don't run if scrolling to a number on the page 408 if (isNum) return; 409 410 // Otherwise, bring anchor element into focus 411 anchor.focus(); 412 if (document.activeElement !== anchor) { 413 anchor.setAttribute('tabindex', '-1'); 414 anchor.focus(); 415 anchor.style.outline = 'none'; 416 } 417 window.scrollTo(0 , endLocation); 418 419 }; 420 421 /** 422 * Emit a custom event 423 * @param {String} type The event type 424 * @param {Object} options The settings object 425 * @param {Node} anchor The anchor element 426 * @param {Node} toggle The toggle element 427 */ 428 var emitEvent = function (type, options, anchor, toggle) { 429 if (!options.emitEvents || typeof window.CustomEvent !== 'function') return; 430 var event = new CustomEvent(type, { 431 bubbles: true, 432 detail: { 433 anchor: anchor, 434 toggle: toggle 435 } 436 }); 437 document.dispatchEvent(event); 438 }; 439 440 441 // 442 // SmoothScroll Constructor 443 // 444 445 var SmoothScroll = function (selector, options) { 446 447 // 448 // Variables 449 // 450 451 var smoothScroll = {}; // Object for public APIs 452 var settings, anchor, toggle, fixedHeader, eventTimeout, animationInterval; 453 454 455 // 456 // Methods 457 // 458 459 /** 460 * Cancel a scroll-in-progress 461 */ 462 smoothScroll.cancelScroll = function (noEvent) { 463 cancelAnimationFrame(animationInterval); 464 animationInterval = null; 465 if (noEvent) return; 466 emitEvent('scrollCancel', settings); 467 }; 468 469 /** 470 * Start/stop the scrolling animation 471 * @param {Node|Number} anchor The element or position to scroll to 472 * @param {Element} toggle The element that toggled the scroll event 473 * @param {Object} options 474 */ 475 smoothScroll.animateScroll = function (anchor, toggle, options) { 476 477 // Cancel any in progress scrolls 478 smoothScroll.cancelScroll(); 479 480 // Local settings 481 var _settings = extend(settings || defaults, options || {}); // Merge user options with defaults 482 483 // Selectors and variables 484 var isNum = Object.prototype.toString.call(anchor) === '[object Number]' ? true : false; 485 var anchorElem = isNum || !anchor.tagName ? null : anchor; 486 if (!isNum && !anchorElem) return; 487 var startLocation = window.pageYOffset; // Current location on the page 488 if (_settings.header && !fixedHeader) { 489 // Get the fixed header if not already set 490 fixedHeader = document.querySelector(_settings.header); 491 } 492 var headerHeight = getHeaderHeight(fixedHeader); 493 var endLocation = isNum ? anchor : getEndLocation(anchorElem, headerHeight, parseInt((typeof _settings.offset === 'function' ? _settings.offset(anchor, toggle) : _settings.offset), 10), _settings.clip); // Location to scroll to 494 var distance = endLocation - startLocation; // distance to travel 495 var documentHeight = getDocumentHeight(); 496 var timeLapsed = 0; 497 var speed = getSpeed(distance, _settings); 498 var start, percentage, position; 499 500 /** 501 * Stop the scroll animation when it reaches its target (or the bottom/top of page) 502 * @param {Number} position Current position on the page 503 * @param {Number} endLocation Scroll to location 504 * @param {Number} animationInterval How much to scroll on this loop 505 */ 506 var stopAnimateScroll = function (position, endLocation) { 507 508 // Get the current location 509 var currentLocation = window.pageYOffset; 510 511 // Check if the end location has been reached yet (or we've hit the end of the document) 512 if (position == endLocation || currentLocation == endLocation || ((startLocation < endLocation && window.innerHeight + currentLocation) >= documentHeight)) { 513 514 // Clear the animation timer 515 smoothScroll.cancelScroll(true); 516 517 // Bring the anchored element into focus 518 adjustFocus(anchor, endLocation, isNum); 519 520 // Emit a custom event 521 emitEvent('scrollStop', _settings, anchor, toggle); 522 523 // Reset start 524 start = null; 525 animationInterval = null; 526 527 return true; 528 529 } 530 }; 531 532 /** 533 * Loop scrolling animation 534 */ 535 var loopAnimateScroll = function (timestamp) { 536 if (!start) { start = timestamp; } 537 timeLapsed += timestamp - start; 538 percentage = speed === 0 ? 0 : (timeLapsed / speed); 539 percentage = (percentage > 1) ? 1 : percentage; 540 position = startLocation + (distance * easingPattern(_settings, percentage)); 541 window.scrollTo(0, Math.floor(position)); 542 if (!stopAnimateScroll(position, endLocation)) { 543 animationInterval = window.requestAnimationFrame(loopAnimateScroll); 544 start = timestamp; 545 } 546 }; 547 548 /** 549 * Reset position to fix weird iOS bug 550 * @link https://github.com/cferdinandi/smooth-scroll/issues/45 551 */ 552 if (window.pageYOffset === 0) { 553 window.scrollTo(0, 0); 554 } 555 556 // Update the URL 557 updateURL(anchor, isNum, _settings); 558 559 // If the user prefers reduced motion, jump to location 560 if (reduceMotion()) { 561
vendor: 4,408 bytes, lines 561-720
561window.scrollTo(0, Math.floor(endLocation)); 562 return; 563 } 564 565 // Emit a custom event 566 emitEvent('scrollStart', _settings, anchor, toggle); 567 568 // Start scrolling animation 569 smoothScroll.cancelScroll(true); 570 window.requestAnimationFrame(loopAnimateScroll); 571 572 }; 573 574 /** 575 * If smooth scroll element clicked, animate scroll 576 */ 577 var clickHandler = function (event) { 578 579 // Don't run if event was canceled but still bubbled up 580 // By @mgreter - https://github.com/cferdinandi/smooth-scroll/pull/462/ 581 if (event.defaultPrevented) return; 582 583 // Don't run if right-click or command/control + click or shift + click 584 if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey) return; 585 586 // Check if event.target has closest() method 587 // By @totegi - https://github.com/cferdinandi/smooth-scroll/pull/401/ 588 if (!('closest' in event.target)) return; 589 590 // Check if a smooth scroll link was clicked 591 toggle = event.target.closest(selector); 592 if (!toggle || toggle.tagName.toLowerCase() !== 'a' || event.target.closest(settings.ignore)) return; 593 594 // Only run if link is an anchor and points to the current page 595 if (toggle.hostname !== window.location.hostname || toggle.pathname !== window.location.pathname || !/#/.test(toggle.href)) return; 596 597 // Get an escaped version of the hash 598 var hash = escapeCharacters(toggle.hash); 599 600 // Get the anchored element 601 var anchor; 602 if (hash === '#') { 603 if (!settings.topOnEmptyHash) return; 604 anchor = document.documentElement; 605 } else { 606 anchor = document.querySelector(hash); 607 } 608 anchor = !anchor && hash === '#top' ? document.documentElement : anchor; 609 610 // If anchored element exists, scroll to it 611 if (!anchor) return; 612 event.preventDefault(); 613 setHistory(settings); 614 smoothScroll.animateScroll(anchor, toggle); 615 616 }; 617 618 /** 619 * Animate scroll on popstate events 620 */ 621 var popstateHandler = function (event) { 622 623 // Stop if history.state doesn't exist (ex. if clicking on a broken anchor link). 624 // fixes `Cannot read property 'smoothScroll' of null` error getting thrown. 625 if (history.state === null) return; 626 627 // Only run if state is a popstate record for this instantiation 628 if (!history.state.smoothScroll || history.state.smoothScroll !== JSON.stringify(settings)) return; 629 630 // Only run if state includes an anchor 631 632 // if (!history.state.anchor && history.state.anchor !== 0) return; 633 634 // Get the anchor 635 var anchor = history.state.anchor; 636 if (typeof anchor === 'string' && anchor) { 637 anchor = document.querySelector(escapeCharacters(history.state.anchor)); 638 if (!anchor) return; 639 } 640 641 // Animate scroll to anchor link 642 smoothScroll.animateScroll(anchor, null, {updateURL: false}); 643 644 }; 645 646 /** 647 * Destroy the current initialization. 648 */ 649 smoothScroll.destroy = function () { 650 651 // If plugin isn't already initialized, stop 652 if (!settings) return; 653 654 // Remove event listeners 655 document.removeEventListener('click', clickHandler, false); 656 window.removeEventListener('popstate', popstateHandler, false); 657 658 // Cancel any scrolls-in-progress 659 smoothScroll.cancelScroll(); 660 661 // Reset variables 662 settings = null; 663 anchor = null; 664 toggle = null; 665 fixedHeader = null; 666 eventTimeout = null; 667 animationInterval = null; 668 669 }; 670 671 /** 672 * Initialize Smooth Scroll 673 * @param {Object} options User settings 674 */ 675 var init = function () { 676 677 // feature test 678 if (!supports()) throw 'Smooth Scroll: This browser does not support the required JavaScript methods and browser APIs.'; 679 680 // Destroy any existing initializations 681 smoothScroll.destroy(); 682 683 // Selectors and variables 684 settings = extend(defaults, options || {}); // Merge user options with defaults 685 fixedHeader = settings.header ? document.querySelector(settings.header) : null; // Get the fixed header 686 687 // When a toggle is clicked, run the click handler 688 document.addEventListener('click', clickHandler, false); 689 690 // If updateURL and popState are enabled, listen for pop events 691 if (settings.updateURL && settings.popstate) { 692 window.addEventListener('popstate', popstateHandler, false); 693 } 694 695 }; 696 697 698 // 699 // Initialize plugin 700 // 701 702 init(); 703 704 705 // 706 // Public APIs 707 // 708 709 return smoothScroll; 710 711 }; 712 713 return SmoothScroll; 714 715})); 716var scroll = new SmoothScroll('a[href*="#"]', { 717 Â header: '.logo', 718 Â speed: 300, 719 easing: 'easeOutCubic', 720});
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.