1/** 2 * smooth-scroll v7.0.2 3 * Animate scrolling to anchor links, by Chris Ferdinandi. 4 * http://github.com/cferdinandi/smooth-scroll 5 * 6 * Free to use under the MIT License. 7 * http://gomakethings.com/mit/ 8 */ 9 10(function (root, factory) { 11 if ( typeof define === 'function' && define.amd ) { 12 define([], factory(root)); 13 } else if ( typeof exports === 'object' ) { 14 module.exports = factory(root); 15 } else { 16 root.smoothScroll = factory(root); 17 } 18})(typeof global !== 'undefined' ? global : this.window || this.global, function (root) { 19 20 'use strict'; 21 22 // 23 // Variables 24 // 25 26 var smoothScroll = {}; // Object for public APIs 27 var supports = !!root.document.querySelector && !!root.addEventListener; // Feature test 28 var settings, eventTimeout, fixedHeader, headerHeight; 29 30 // Default settings 31 var defaults = { 32 speed: 500, 33 easing: 'easeInOutCubic', 34 offset: 0, 35 updateURL: true, 36 callback: function () {} 37 }; 38 39 40 // 41 // Methods 42 // 43 44 /** 45 * Merge two or more objects. Returns a new object. 46 * @private 47 * @param {Boolean} deep If true, do a deep (or recursive) merge [optional] 48 * @param {Object} objects The objects to merge together 49 * @returns {Object} Merged values of defaults and options 50 */ 51 var extend = function () { 52 53 // Variables 54 var extended = {}; 55 var deep = false; 56 var i = 0; 57 var length = arguments.length; 58 59 // Check if a deep merge 60 if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) { 61 deep = arguments[0]; 62 i++; 63 } 64 65 // Merge the object into the extended object 66 var merge = function (obj) { 67 for ( var prop in obj ) { 68 if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) { 69 // If deep merge and property is an object, merge properties 70 if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) { 71 extended[prop] = extend( true, extended[prop], obj[prop] ); 72 } else { 73 extended[prop] = obj[prop]; 74 } 75 } 76 } 77 }; 78 79 // Loop through each object and conduct a merge 80 for ( ; i < length; i++ ) { 81 var obj = arguments[i]; 82 merge(obj); 83 } 84 85 return extended; 86 87 }; 88 89 /** 90 * Get the height of an element. 91 * @private 92 * @param {Node} elem The element to get the height of 93 * @return {Number} The element's height in pixels 94 */ 95 var getHeight = function ( elem ) { 96 return Math.max( elem.scrollHeight, elem.offsetHeight, elem.clientHeight ); 97 }; 98 99 /** 100 * Get the closest matching element up the DOM tree. 101 * @private 102 * @param {Element} elem Starting element 103 * @param {String} selector Selector to match against (class, ID, data attribute, or tag) 104 * @return {Boolean|Element} Returns null if not match found 105 */ 106 var getClosest = function ( elem, selector ) { 107 108 // Variables 109 var firstChar = selector.charAt(0); 110 var supports = 'classList' in document.documentElement; 111 var attribute, value; 112 113 // If selector is a data attribute, split attribute from value 114 if ( firstChar === '[' ) { 115 selector = selector.substr(1, selector.length - 2); 116 attribute = selector.split( '=' ); 117 118 if ( attribute.length > 1 ) { 119 value = true; 120 attribute[1] = attribute[1].replace( /"/g, '' ).replace( /'/g, '' ); 121 } 122 } 123 124 // Get closest match 125 for ( ; elem && elem !== document; elem = elem.parentNode ) { 126 127 // If selector is a class 128 if ( firstChar === '.' ) { 129 if ( supports ) { 130 if ( elem.classList.contains( selector.substr(1) ) ) { 131 return elem; 132 } 133 } else { 134 if ( new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)').test( elem.className ) ) { 135 return elem; 136 } 137 } 138 } 139 140 // If selector is an ID 141 if ( firstChar === '#' ) { 142 if ( elem.id === selector.substr(1) ) { 143 return elem; 144 } 145 } 146 147 // If selector is a data attribute 148 if ( firstChar === '[' ) { 149 if ( elem.hasAttribute( attribute[0] ) ) { 150 if ( value ) { 151 if ( elem.getAttribute( attribute[0] ) === attribute[1] ) { 152 return elem; 153 } 154 } else { 155 return elem; 156 } 157 } 158 } 159 160 // If selector is a tag 161 if ( elem.tagName.toLowerCase() === selector ) { 162 return elem; 163 } 164 165 } 166 167 return null; 168 169 }; 170 171 /** 172 * Escape special characters for use with querySelector 173 * @private 174 * @param {String} id The anchor ID to escape 175 * @author Mathias Bynens 176 * @link https://github.com/mathiasbynens/CSS.escape 177 */ 178 var escapeCharacters = function ( id ) { 179 var string = String(id); 180 var length = string.length; 181 var index = -1; 182 var codeUnit; 183 var result = ''; 184 var firstCodeUnit = string.charCodeAt(0); 185 while (++index < length) { 186 codeUnit = string.charCodeAt(index); 187 // Note: thereâs no need to special-case astral symbols, surrogate 188 // pairs, or lone surrogates. 189 190 // If the character is NULL (U+0000), then throw an 191 // `InvalidCharacterError` exception and terminate these steps. 192 if (codeUnit === 0x0000) { 193 throw new InvalidCharacterError( 194 'Invalid character: the input contains U+0000.' 195 ); 196 } 197 198 if ( 199 // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is 200 // U+007F, [â¦] 201 (codeUnit >
201= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F || 202 // If the character is the first character and is in the range [0-9] 203 // (U+0030 to U+0039), [â¦] 204 (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || 205 // If the character is the second character and is in the range [0-9] 206 // (U+0030 to U+0039) and the first character is a `-` (U+002D), [â¦] 207 ( 208 index === 1 && 209 codeUnit >= 0x0030 && codeUnit <= 0x0039 && 210 firstCodeUnit === 0x002D 211 ) 212 ) { 213 // http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point 214 result += '\\' + codeUnit.toString(16) + ' '; 215 continue; 216 } 217 218 // If the character is not handled by one of the above rules and is 219 // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or 220 // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to 221 // U+005A), or [a-z] (U+0061 to U+007A), [â¦] 222 if ( 223 codeUnit >= 0x0080 || 224 codeUnit === 0x002D || 225 codeUnit === 0x005F || 226 codeUnit >= 0x0030 && codeUnit <= 0x0039 || 227 codeUnit >= 0x0041 && codeUnit <= 0x005A || 228 codeUnit >= 0x0061 && codeUnit <= 0x007A 229 ) { 230 // the character itself 231 result += string.charAt(index); 232 continue; 233 } 234 235 // Otherwise, the escaped character. 236 // http://dev.w3.org/csswg/cssom/#escape-a-character 237 result += '\\' + string.charAt(index); 238 239 } 240 return result; 241 }; 242 243 /** 244 * Calculate the easing pattern 245 * @private 246 * @link https://gist.github.com/gre/1650294 247 * @param {String} type Easing pattern 248 * @param {Number} time Time animation should take to complete 249 * @returns {Number} 250 */ 251 var easingPattern = function ( type, time ) { 252 var pattern; 253 if ( type === 'easeInQuad' ) pattern = time * time; // accelerating from zero velocity 254 if ( type === 'easeOutQuad' ) pattern = time * (2 - time); // decelerating to zero velocity 255 if ( type === 'easeInOutQuad' ) pattern = time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration 256 if ( type === 'easeInCubic' ) pattern = time * time * time; // accelerating from zero velocity 257 if ( type === 'easeOutCubic' ) pattern = (--time) * time * time + 1; // decelerating to zero velocity 258 if ( type === 'easeInOutCubic' ) pattern = time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration 259 if ( type === 'easeInQuart' ) pattern = time * time * time * time; // accelerating from zero velocity 260 if ( type === 'easeOutQuart' ) pattern = 1 - (--time) * time * time * time; // decelerating to zero velocity 261 if ( type === 'easeInOutQuart' ) pattern = time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration 262 if ( type === 'easeInQuint' ) pattern = time * time * time * time * time; // accelerating from zero velocity 263 if ( type === 'easeOutQuint' ) pattern = 1 + (--time) * time * time * time * time; // decelerating to zero velocity 264 if ( type === 'easeInOutQuint' ) pattern = time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration 265 return pattern || time; // no easing, no acceleration 266 }; 267 268 /** 269 * Calculate how far to scroll 270 * @private 271 * @param {Element} anchor The anchor element to scroll to 272 * @param {Number} headerHeight Height of a fixed header, if any 273 * @param {Number} offset Number of pixels by which to offset scroll 274 * @returns {Number} 275 */ 276 var getEndLocation = function ( anchor, headerHeight, offset ) { 277 var location = 0; 278 if (anchor.offsetParent) { 279 do { 280 location += anchor.offsetTop; 281 anchor = anchor.offsetParent; 282 } while (anchor); 283 } 284 location = location - headerHeight - offset; 285 return location >= 0 ? location : 0; 286 }; 287 288 /** 289 * Determine the document's height 290 * @private 291 * @returns {Number} 292 */ 293 var getDocumentHeight = function () { 294 return Math.max( 295 root.document.body.scrollHeight, root.document.documentElement.scrollHeight, 296 root.document.body.offsetHeight, root.document.documentElement.offsetHeight, 297 root.document.body.clientHeight, root.document.documentElement.clientHeight 298 ); 299 }; 300 301 /**
302 * Convert data-options attribute into an object of key/value pairs 303 * @private 304 * @param {String} options Link-specific options as a data attribute string 305 * @returns {Object} 306 */ 307 var getDataOptions = function ( options ) { 308 return !options || !(typeof JSON === 'object' && typeof JSON.parse === 'function') ? {} : JSON.parse( options ); 309 }; 310 311 /** 312 * Update the URL 313 * @private 314 * @param {Element} anchor The element to scroll to 315 * @param {Boolean} url Whether or not to update the URL history 316 */ 317 var updateUrl = function ( anchor, url ) { 318 if ( root.history.pushState && (url || url === 'true') ) { 319 root.history.pushState( null, null, [root.location.protocol, '//', root.location.host, root.location.pathname, root.location.search, anchor].join('') ); 320 } 321 }; 322 323 var getHeaderHeight = function ( header ) { 324 return header === null ? 0 : ( getHeight( header ) + header.offsetTop ); 325 }; 326 327 /** 328 * Start/stop the scrolling animation 329 * @public 330 * @param {Element} toggle The element that toggled the scroll event 331 * @param {Element} anchor The element to scroll to 332 * @param {Object} options 333 */ 334 smoothScroll.animateScroll = function ( toggle, anchor, options ) { 335 336 // Options and overrides 337 var overrides = getDataOptions( toggle ? toggle.getAttribute('data-options') : null ); 338 var settings = extend( settings || defaults, options || {}, overrides ); // Merge user options with defaults 339 anchor = '#' + escapeCharacters(anchor.substr(1)); // Escape special characters and leading numbers 340 341 // Selectors and variables 342 var anchorElem = anchor === '#' ? root.document.documentElement : root.document.querySelector(anchor); 343 var startLocation = root.pageYOffset; // Current location on the page 344 if ( !fixedHeader ) { fixedHeader = root.document.querySelector('[data-scroll-header]'); } // Get the fixed header if not already set 345 if ( !headerHeight ) { headerHeight = getHeaderHeight( fixedHeader ); } // Get the height of a fixed header if one exists and not already set 346 var wasSticky = $("header").hasClass("sticky"); 347 var endLocation = getEndLocation( anchorElem, headerHeight, parseInt(settings.offset, 10) ); // Scroll to location 348 if(!$(toggle).hasClass("inline-scroll")){ 349 if(!wasSticky) { 350 if($(".showhide").is(":visible")) 351 endLocation -=40; 352 else 353 endLocation -=100; 354 } 355 } 356 var animationInterval; // interval timer 357 var distance = endLocation - startLocation; // distance to travel 358 var documentHeight = getDocumentHeight(); 359 var timeLapsed = 0; 360 var percentage, position; 361 362 // Update URL 363 updateUrl(anchor, settings.updateURL); 364 365 /** 366 * Stop the scroll animation when it reaches its target (or the bottom/top of page) 367 * @private 368 * @param {Number} position Current position on the page 369 * @param {Number} endLocation Scroll to location 370 * @param {Number} animationInterval How much to scroll on this loop 371 */ 372 var stopAnimateScroll = function (position, endLocation, animationInterval) { 373 var currentLocation = root.pageYOffset; 374 if ( position == endLocation || currentLocation == endLocation || ( (root.innerHeight + currentLocation) >= documentHeight ) ) { 375 clearInterval(animationInterval); 376 anchorElem.focus(); 377 settings.callback( toggle, anchor ); // Run callbacks after animation complete 378 } 379 }; 380 381 /** 382 * Loop scrolling animation 383 * @private 384 */ 385 var loopAnimateScroll = function () { 386 timeLapsed += 16; 387 percentage = ( timeLapsed / parseInt(settings.speed, 10) ); 388 percentage = ( percentage > 1 ) ? 1 : percentage; 389 position = startLocation + ( distance * easingPattern(settings.easing, percentage) ); 390 root.scrollTo( 0, Math.floor(position) ); 391 stopAnimateScroll(position, endLocation, animationInterval); 392 }; 393 394 /** 395 * Set interval timer 396 * @private 397 */ 398 var startAnimateScroll = function () { 399 animationInterval = setInterval(loopAnimateScroll, 16); 400 }; 401 402 /** 403 * Reset position to fix weird iOS bug 404 * @link https://github.com/cferdinandi/smooth-scroll/issues/45 405 */ 406 if ( root.pageYOffset === 0 ) { 407 root.scrollTo( 0, 0 ); 408 } 409 410 // Start scrolling animation 411 startAnimateScroll(); 412 413 }; 414 415 /** 416 * If smooth scroll element clicked, animate scroll 417 * @private 418 */ 419 var eventHandler = function (event) { 420 var toggle = getClosest(event.target, '[data-scroll]'); 421 if ( toggle && toggle.tagName.toLowerCase() === 'a' ) { 422 event.preventDefault(); // Prevent default click event 423 smoothScroll.animateScroll( toggle, toggle.hash, settings); // Animate scroll 424 } 425 }; 426 427 /** 428 * On window scroll and resize, only run events at a rate of 15fps for better performance 429 * @private 430 * @param {Function} eventTimeout Timeout function 431 * @param {Object} settings 432 */ 433 var eventThrottler = function (event) { 434 if ( !eventTimeout ) { 435 eventTimeout = setTimeout(function() { 436 eventTimeout = null; // Reset timeout 437 headerHeight = getHeaderHeight( fixedHeader ); // Get the height of a fixed header if one exists 438 }, 66); 439 } 440 }; 441 442 /** 443 * Destroy the current initialization. 444 * @public 445 */ 446 smoothScroll.destroy = function () { 447 448 // If plugin isn't already initialized, stop 449 if ( !settings ) return; 450 451 // Remove event listeners 452 root.document.removeEventListener( 'click', eventHandler, false ); 453 root.removeEventListener( 'resize', eventThrottler, false ); 454 455 // Reset varaibles 456 settings = null; 457 eventTimeout = null; 458 fixedHeader = null; 459 headerHeight = null; 460 }; 461 462 /** 463 * Initialize Smooth Scroll 464 * @public 465 * @param {Object} options User settings 466 */ 467 smoothScroll.init = function ( options ) { 468 469 // feature test 470 if ( !supports ) return; 471 472 // Destroy any existing initializations 473 smoothScroll.destroy(); 474 475 // Selectors and variables 476 settings = extend( defaults, options || {} ); // Merge user options with defaults 477 fixedHeader = root.document.querySelector('[data-scroll-header]'); // Get the fixed header 478 headerHeight = getHeaderHeight( fixedHeader ); 479 480 // When a toggle is clicked, run the click handler 481 root.document.addEventListener('click', eventHandler, false ); 482 if ( fixedHeader ) { root.addEventListener( 'resize', eventThrottler, false ); } 483 484 }; 485 486 487 // 488 // Public APIs 489 // 490 491 return smoothScroll; 492 493});
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.