PageSourceSearch

https://www.openblade.io/wp-content/themes/oneup/framework/js/jquery.smoothscroll.js?ver=1480499366

js openblade.io collected 2026-10-03 18:47:42 UTC 14,197 bytes, 573 lines download raw bytes

1
2// SmoothScroll v1.2.1
3// Licensed under the terms of the MIT license.
4
5// People involved
6//  - Balazs Galambosi (maintainer)  
7//  - Patrick Brunner  (original idea)
8//  - Michael Herf     (Pulse Algorithm)
9
10// Scroll Variables (tweakable)
11
12(function ($) {
13	var defaultOptions = {
14
15			// Scrolling Core
16			frameRate        : 150, // [Hz]
17			animationTime    : 800, // [px]
18			stepSize         : 100, // [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
40	var options = defaultOptions;
41
42
43	// Other Variables
44	var isExcluded = false;
45	var isFrame = false;
46	var direction = { x: 0, y: 0 };
47	var initDone  = false;
48	var root = document.documentElement;
49	var activeElement;
50	var observer;
51	var deltaBuffer = [ 120, 120, 120 ];
52
53	var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32, 
54				pageup: 33, pagedown: 34, end: 35, home: 36 };
55
56
57	/***********************************************
58	 * INITIALIZE
59	 ***********************************************/
60
61	/**
62	 * Tests if smooth scrolling is allowed. Shuts down everything if not.
63	 */
64	function initTest() {
65
66		var disableKeyboard = false; 
67
68		// disable keys for google reader (spacebar conflict)
69		if (document.URL.indexOf("google.com/reader/view") > -1) {
70			disableKeyboard = true;
71		}
72
73		// disable everything if the page is blacklisted
74		if (options.excluded) {
75			var domains = options.excluded.split(/[,\n] ?/);
76			domains.push("mail.google.com"); // exclude Gmail for now
77			for (var i = domains.length; i--;) {
78				if (document.URL.indexOf(domains[i]) > -1) {
79					observer && observer.disconnect();
80					removeEvent("mousewheel", wheel);
81					disableKeyboard = true;
82					isExcluded = true;
83					break;
84				}
85			}
86		}
87		
88		// disable keyboard support if anything above requested it
89		if (disableKeyboard) {
90			removeEvent("keydown", keydown);
91		}
92
93		if (options.keyboardSupport && !disableKeyboard) {
94			addEvent("keydown", keydown);
95		}
96	}
97
98	/**
99	 * Sets up scrolls array, determines if frames are involved.
100	 */
101	function init() {
102		
103		if (!document.body) return;
104
105		var body = document.body;
106		var html = document.documentElement;
107		var windowHeight = window.innerHeight; 
108		var scrollHeight = body.scrollHeight;
109		
110		// check compat mode for root element
111		root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
112		activeElement = body;
113		
114		initTest();
115		initDone = true;
116
117		// Checks if this script is running in a frame
118		if (top != self) {
119			isFrame = true;
120		}
121
122		/**
123		 * This fixes a bug where the areas left and right to 
124		 * the content does not trigger the onmousewheel event
125		 * on some pages. e.g.: html, body { height: 100% }
126		 */
127		else if (scrollHeight > windowHeight &&
128				 (body.offsetHeight <= windowHeight || 
129				  html.offsetHeight <= windowHeight)) {
130
131			// DOMChange (throttle): fix height
132			var pending = false;
133			var refresh = function () {
134					if (!pending && html.scrollHeight != document.height) {
135						pending = true; // add a new pending action
136						setTimeout(function () {
137							html.style.height = document.height + 'px';
138							pending = false;
139						}, 500); // act rarely to stay fast
140					}
141				};
142			html.style.height = 'auto';
143			setTimeout(refresh, 10);
144
145			var config = {
146					attributes: true, 
147					childList: true, 
148					characterData: false 
149				};
150
151			observer = new MutationObserver(refresh);
152			observer.observe(body, config);
153
154			// clearfix
155			if (root.offsetHeight <= windowHeight) {
156				var underlay = document.createElement("div"); 	
157				underlay.style.clear = "both";
158				body.appendChild(underlay);
159			}
160		}
161		
162		// gmail performance fix
163		if (document.URL.indexOf("mail.google.com") > -1) {
164			var s = document.createElement("style");
165			s.innerHTML = ".iu { visibility: hidden }";
166			(document.getElementsByTagName("head")[0] || html).appendChild(s);
167		}
168		// facebook better home timeline performance
169		// all the HTML resized images make rendering CPU intensive
170		else if (document.URL.indexOf("www.facebook.com") > -1) {
171			var home_stream = document.getElementById("home_stream");
172			home_stream && (home_stream.style.webkitTransform = "translateZ(0)");
173		} 
174		// disable fixed background
175		if (!options.fixedBackground && !isExcluded) {
176			body.style.backgroundAttachment = "scroll";
177			html.style.backgroundAttachment = "scroll";
178		}
179	}
180
181
182	/************************************************
183	 * SCROLLING 
184	 ************************************************/
185
186	var que = [];
187	var pending = false;
188	var lastScroll = +new Date;
189
190	/**
191	 * Pushes scroll actions to the scrolling queue.
192	 */
193	function scrollArray(elem, left, top, delay) {
194		
195		delay || (delay = 1000);
196		directionCheck(left, top);
197
198		if (options.accelerationMax != 1) {
199			var now = +new Date;
200			var elapsed = now - lastScroll;
201			if (elapsed < options.accelerationDelta) {
202				var factor = (1 + (30 / elapsed)) / 2;
203				if (factor > 1) {
204					factor = Math.min(factor, options.accelerationMax);
205					left *= factor;
206					top  *= factor;
207				}
208			}
209			lastScroll = +new Date;
210		}          
211		
212		// push a scroll command
213		que.push({
214			x: left, 
215			y: top, 
216			lastX: (left < 0) ? 0.99 : -0.99,
217			lastY: (top  < 0) ? 0.99 : -0.99, 
218			start: +new Date
219		});
220		
221		// don't act if there's a pending queue
222		if (pending) {
223			return;
224		}  
225
226		var scrollWindow = (elem === document.body);
227		
228		var step = function (time) {
229				
230				var now = +new Date;
231				var scrollX = 0;
232				var scrollY = 0; 
233				
234				for (var i = 0; i < que.length; i++) {
235					
236					var item = que[i];
237					var elapsed  = now - item.start;
238					var finished = (elapsed >= options.animationTime);
239					
240					// scroll position: [0, 1]
241					var position = (finished) ? 1 : elapsed / options.animationTime;
242					
243					// easing [optional]
244					if (options.pulseAlgorithm) {
245						position = pulse(position);
246					}
247					
248					// only need the difference
249					var x = (item.x * position - item.lastX) >> 0;
250					var y = (item.y * position - item.lastY) >> 0;
251					
252					// add this to the total scrolling
253					scrollX += x;
254					scrollY += y;            
255					
256					// update last values
257					item.lastX += x;
258					item.lastY += y;
259					
260					// delete and step back if it's over
261					if (finished) {
262						que.splice(i, 1); i--;
263					}           
264				}
265
266				// scroll left and top
267				if (scrollWindow) {
268					window.scrollBy(scrollX, scrollY);
269				} 
270				else {
271					if (scrollX) elem.scrollLeft += scrollX;
272					if (scrollY) elem.scrollTop  += scrollY;                    
273				}
274				
275				// clean up if there's nothing left to do
276				if (!left && !top) {
277					que = [];
278				}
279				
280				if (que.length) { 
281					requestFrame(step, elem, (delay / options.frameRate + 1)); 
282				} else { 
283					pending = false;
284				}
285			};
286		
287		// start a new queue of actions
288		requestFrame(step, elem, 0);
289		pending = true;
290	}
291
292
293	/***********************************************
294	 * EVENTS
295	 ***********************************************/
296
297	/**
298	 * Mouse wheel handler.
299	 * @param {Object} event
300	 */
301	function wheel(event) {
302		if (!initDone) {
303			init();
304		}
305		
306		var target = event.target;
307		var overflowing = overflowingAncestor(target);
308		
309		// use default if there's no overflowing
310		// element or default action is prevented    
311		if (!overflowing || event.defaultPrevented ||
312			isNodeName(activeElement, "embed") ||
313			(isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {
314			return true;
315		}
316
317		var deltaX = event.wheelDeltaX || 0;
318		var deltaY = event.wheelDeltaY || 0;
319		
320		// use wheelDelta if deltaX/Y is not available
321		if (!deltaX && !deltaY) {
322			deltaY = event.wheelDelta || 0;
323		}
324
325		// check if it's a touchpad scroll that should be ignored
326		if (!options.touchpadSupport && isTouchpad(deltaY)) {
327			return true;
328		}
329
330		// scale by step size
331		// delta is 120 most of the time
332		// synaptics seems to send 1 sometimes
333		if (Math.abs(deltaX) > 1.2) {
334			deltaX *= options.stepSize / 120;
335		}
336		if (Math.abs(deltaY) > 1.2) {
337			deltaY *= options.stepSize / 120;
338		}
339		
340		scrollArray(overflowing, -deltaX, -deltaY);
341		event.preventDefault();
342	}
343
344	/**
345	 * Keydown event handler.
346	 * @param {Object} event
347	 */
348	function keydown(event) {
349
350		var target   = event.target;
351		var modifier = event.ctrlKey || event.altKey || event.metaKey || 
352          (event.shiftKey && event.keyCode !== key.spacebar);
353		
354		// do nothing if user is editing text
355		// or using a modifier key (except shift)
356		// or in a dropdown
357		if ( /input|textarea|select|embed/i.test(target.nodeName) ||
358			 target.isContentEditable || 
359			 event.defaultPrevented   ||
360			 modifier ) {
361				 return true;
362			 }
363		// spacebar should trigger button press
364		if (isNodeName(target, "button") &&
365			event.keyCode === key.spacebar) {
366			return true;
367		}
368		
369		var shift, x = 0, y = 0;
370		var elem = overflowingAncestor(activeElement);
371		
372		if (!elem) {
373			// prevents error when unloading the page
374			return true;
375		}
376		
377		var clientHeight = elem.clientHeight;
378
379		if (elem == document.body) {
380			clientHeight = window.innerHeight;
381		}
382
383		switch (event.keyCode) {
384		case key.up:
385			y = -options.arrowScroll;
386			break;
387		case key.down:
388			y = options.arrowScroll;
389			break;         
390		case key.spacebar: // (+ shift)
391			shift = event.shiftKey ? 1 : -1;
392			y = -shift * clientHeight * 0.9;
393			break;
394		case key.pageup:
395			y = -clientHeight * 0.9;
396			break;
397		case key.pagedown:
398			y = clientHeight * 0.9;
399			break;
400		case key.home:
401			y = -elem.scrollTop;
402			break;
403		case key.end:
404			var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
405			y = (damt > 0) ? damt+10 : 0;
406			break;
407		case key.left:
408			x = -options.arrowScroll;
409			break;
410		case key.right:
411			x = options.arrowScroll;
412			break;            
413		default:
414			return true; // a key we don't care about
415		}
416
417		scrollArray(elem, x, y);
418		event.preventDefault();
419	}
420
421	/**
422	 * Mousedown event only for updating activeElement
423	 */
424	function mousedown(event) {
425		activeElement = event.target;
426	}
427
428
429	/***********************************************
430	 * OVERFLOW
431	 ***********************************************/
432
433	var cache = {}; // cleared out every once in while
434	setInterval(function () { cache = {}; }, 10 * 1000);
435
436	var uniqueID = (function () {
437			var i = 0;
438			return function (el) {
439				return el.uniqueID || (el.uniqueID = i++);
440			};
441		})();
442
443	function setCache(elems, overflowing) {
444		for (var i = elems.length; i--;)
445			cache[uniqueID(elems[i])] = overflowing;
446		return overflowing;
447	}
448
449	function overflowingAncestor(el) {
450		var elems = [];
451		var rootScrollHeight = root.scrollHeight;
452		do {
453			var cached = cache[uniqueID(el)];
454			if (cached) {
455				return setCache(elems, cached);
456			}
457			elems.push(el);
458			if (rootScrollHeight === el.scrollHeight) {
459				if (!isFrame || root.clientHeight + 10 < rootScrollHeight) {
460					return setCache(elems, document.body); // scrolling root in WebKit
461				}
462			} else if (el.clientHeight + 10 < el.scrollHeight) {
463				overflow = getComputedStyle(el, "").getPropertyValue("overflow-y");
464				if (overflow === "scroll" || overflow === "auto") {
465					return setCache(elems, el);
466				}
467			}
468		} while (el = el.parentNode);
469	}
470
471
472	/***********************************************
473	 * HELPERS
474	 ***********************************************/
475
476	function addEvent(type, fn, bubble) {
477		window.addEventListener(type, fn, (bubble||false));
478	}
479
480	function removeEvent(type, fn, bubble) {
481		window.removeEventListener(type, fn, (bubble||false));  
482	}
483
484	function isNodeName(el, tag) {
485		return (el.nodeName||"").toLowerCase() === tag.toLowerCase();
486	}
487
488	function directionCheck(x, y) {
489		x = (x > 0) ? 1 : -1;
490		y = (y > 0) ? 1 : -1;
491		if (direction.x !== x || direction.y !== y) {
492			direction.x = x;
493			direction.y = y;
494			que = [];
495			lastScroll = 0;
496		}
497	}
498
499	var deltaBufferTimer;
500
501	function isTouchpad(deltaY) {
502		if (!deltaY) return;
503		deltaY = Math.abs(deltaY)
504		deltaBuffer.push(deltaY);
505		deltaBuffer.shift();
506		clearTimeout(deltaBufferTimer);
507		var allEquals    = (deltaBuffer[0] == deltaBuffer[1] && 
508							deltaBuffer[1] == deltaBuffer[2]);
509		var allDivisable = (isDivisible(deltaBuffer[0], 120) &&
510							isDivisible(deltaBuffer[1], 120) &&
511							isDivisible(deltaBuffer[2], 120));
512		return !(allEquals || allDivisable);
513	} 
514
515	function isDivisible(n, divisor) {
516		return (Math.floor(n / divisor) == n / divisor);
517	}
518
519	var requestFrame = (function () {
520			return  window.requestAnimationFrame       || 
521				window.webkitRequestAnimationFrame || 
522				function (callback, element, delay) {
523					window.setTimeout(callback, delay || (1000/60));
524				};
525		})();
526
527	var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;  
528
529
530	/***********************************************
531	 * PULSE
532	 ***********************************************/
533
534	/**
535	 * Viscous fluid with a pulse for part and decay for the rest.
536	 * - Applies a fixed force over an interval (a damped acceleration), and
537	 * - Lets the exponential bleed away the velocity over a longer interval
538	 * - Michael Herf, http://stereopsis.com/stopping/
539	 */
540	function pulse_(x) {
541		var val, start, expx;
542		// test
543		x = x * options.pulseScale;
544		if (x < 1) { // acceleartion
545			val = x - (1 - Math.exp(-x));
546		} else {     // tail
547			// the previous animation ended here:
548			start = Math.exp(-1);
549			// simple viscous drag
550			x -= 1;
551			expx = 1 - Math.exp(-x);
552			val = start + (expx * (1 - start));
553		}
554		return val * options.pulseNormalize;
555	}
556
557	function pulse(x) {
558		if (x >= 1) return 1;
559		if (x <= 0) return 0;
560
561		if (options.pulseNormalize == 1) {
562			options.pulseNormalize /= pulse_(1);
563		}
564		return pulse_(x);
565	}
566	
567	if ((window.peSmoothScroll !== false) && !$.pixelentity.browser.mobile && $.browser.webkit && (!$("html").hasClass("safari"))) {
568		addEvent("mousedown", mousedown);
569		addEvent("mousewheel", wheel);
570		addEvent("load", init);
571	}
572	
573}(jQuery));

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.