PageSourceSearch

https://oneoptimumlifestyles.com/v2/js/smooth-scroll.js

js oneoptimumlifestyles.com collected 2026-10-02 16:03:22 UTC 14,343 bytes, 429 lines download raw bytes

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

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.