Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/themefisher/parsa-hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Feng <ayfeng@uci.edu>2020-07-16 21:14:06 +0300
committerAndy Feng <ayfeng@uci.edu>2020-07-16 21:14:06 +0300
commitbf04ca3ebb4e576874eb1e8daa13e4cdb8529ccd (patch)
tree618f8d37254b523c518e64ec4b6826c3437caa6d
parent8120d9227d95d0c0fc78b0a2828c99c01a61c481 (diff)
Update smooth-scroll plugin to v1.4.10
Updated the smooth-scroll plugin to the latest release (v1.4.10). This was done in order to fix jittery scrolling in Chrome 56+.
-rw-r--r--static/plugins/smooth-scroll/smooth-scroll.js1308
1 files changed, 674 insertions, 634 deletions
diff --git a/static/plugins/smooth-scroll/smooth-scroll.js b/static/plugins/smooth-scroll/smooth-scroll.js
index cfeb22a..3aeafa9 100644
--- a/static/plugins/smooth-scroll/smooth-scroll.js
+++ b/static/plugins/smooth-scroll/smooth-scroll.js
@@ -1,750 +1,790 @@
//
-// SmoothScroll for websites v1.4.6 (Balazs Galambosi)
+// SmoothScroll for websites v1.4.10 (Balazs Galambosi)
// http://www.smoothscroll.net/
//
// Licensed under the terms of the MIT license.
//
-// You may use it in your theme if you credit me.
+// You may use it in your theme if you credit me.
// It is also free to use on any individual website.
//
// Exception:
-// The only restriction is to not publish any
+// The only restriction is to not publish any
// extension for browsers or native application
// without getting a written permission first.
//
(function () {
- // Scroll Variables (tweakable)
- var defaultOptions = {
-
- // Scrolling Core
- frameRate: 60, // [Hz]
- animationTime: 1000, // [ms]
- stepSize: 80, // [px]
-
- // Pulse (less tweakable)
- // ratio of "tail" to "acceleration"
- pulseAlgorithm: true,
- pulseScale: 4,
- pulseNormalize: 1,
-
- // Acceleration
- accelerationDelta: 50, // 50
- accelerationMax: 3, // 3
-
- // Keyboard Settings
- keyboardSupport: true, // option
- arrowScroll: 50, // [px]
+// Scroll Variables (tweakable)
+var defaultOptions = {
+
+ // Scrolling Core
+ frameRate : 150, // [Hz]
+ animationTime : 400, // [ms]
+ stepSize : 100, // [px]
+
+ // Pulse (less tweakable)
+ // ratio of "tail" to "acceleration"
+ pulseAlgorithm : true,
+ pulseScale : 4,
+ pulseNormalize : 1,
+
+ // Acceleration
+ accelerationDelta : 50, // 50
+ accelerationMax : 3, // 3
+
+ // Keyboard Settings
+ keyboardSupport : true, // option
+ arrowScroll : 50, // [px]
+
+ // Other
+ fixedBackground : true,
+ excluded : ''
+};
+
+var options = defaultOptions;
+
+
+// Other Variables
+var isExcluded = false;
+var isFrame = false;
+var direction = { x: 0, y: 0 };
+var initDone = false;
+var root = document.documentElement;
+var activeElement;
+var observer;
+var refreshSize;
+var deltaBuffer = [];
+var deltaBufferTimer;
+var isMac = /^Mac/.test(navigator.platform);
+
+var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,
+ pageup: 33, pagedown: 34, end: 35, home: 36 };
+var arrowKeys = { 37: 1, 38: 1, 39: 1, 40: 1 };
+
+/***********************************************
+ * INITIALIZE
+ ***********************************************/
+
+/**
+ * Tests if smooth scrolling is allowed. Shuts down everything if not.
+ */
+function initTest() {
+ if (options.keyboardSupport) {
+ addEvent('keydown', keydown);
+ }
+}
- // Other
- fixedBackground: true,
- excluded: ''
- };
+/**
+ * Sets up scrolls array, determines if frames are involved.
+ */
+function init() {
- var options = defaultOptions;
+ if (initDone || !document.body) return;
+ initDone = true;
- // Other Variables
- var isExcluded = false;
- var isFrame = false;
- var direction = { x: 0, y: 0 };
- var initDone = false;
- var root = document.documentElement;
- var activeElement;
- var observer;
- var refreshSize;
- var deltaBuffer = [];
- var isMac = /^Mac/.test(navigator.platform);
+ var body = document.body;
+ var html = document.documentElement;
+ var windowHeight = window.innerHeight;
+ var scrollHeight = body.scrollHeight;
- var key = {
- left: 37, up: 38, right: 39, down: 40, spacebar: 32,
- pageup: 33, pagedown: 34, end: 35, home: 36
- };
- var arrowKeys = { 37: 1, 38: 1, 39: 1, 40: 1 };
+ // check compat mode for root element
+ root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
+ activeElement = body;
- /***********************************************
- * INITIALIZE
- ***********************************************/
+ initTest();
- /**
- * Tests if smooth scrolling is allowed. Shuts down everything if not.
- */
- function initTest() {
- if (options.keyboardSupport) {
- addEvent('keydown', keydown);
- }
+ // Checks if this script is running in a frame
+ if (top != self) {
+ isFrame = true;
}
/**
- * Sets up scrolls array, determines if frames are involved.
+ * Safari 10 fixed it, Chrome fixed it in v45:
+ * This fixes a bug where the areas left and right to
+ * the content does not trigger the onmousewheel event
+ * on some pages. e.g.: html, body { height: 100% }
*/
- function init() {
-
- if (initDone || !document.body) return;
-
- initDone = true;
-
- var body = document.body;
- var html = document.documentElement;
- var windowHeight = window.innerHeight;
- var scrollHeight = body.scrollHeight;
+ else if (isOldSafari &&
+ scrollHeight > windowHeight &&
+ (body.offsetHeight <= windowHeight ||
+ html.offsetHeight <= windowHeight)) {
+
+ var fullPageElem = document.createElement('div');
+ fullPageElem.style.cssText = 'position:absolute; z-index:-10000; ' +
+ 'top:0; left:0; right:0; height:' +
+ root.scrollHeight + 'px';
+ document.body.appendChild(fullPageElem);
+
+ // DOM changed (throttled) to fix height
+ var pendingRefresh;
+ refreshSize = function () {
+ if (pendingRefresh) return; // could also be: clearTimeout(pendingRefresh);
+ pendingRefresh = setTimeout(function () {
+ if (isExcluded) return; // could be running after cleanup
+ fullPageElem.style.height = '0';
+ fullPageElem.style.height = root.scrollHeight + 'px';
+ pendingRefresh = null;
+ }, 500); // act rarely to stay fast
+ };
- // check compat mode for root element
- root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
- activeElement = body;
+ setTimeout(refreshSize, 10);
- initTest();
+ addEvent('resize', refreshSize);
- // Checks if this script is running in a frame
- if (top != self) {
- isFrame = true;
- }
+ // TODO: attributeFilter?
+ var config = {
+ attributes: true,
+ childList: true,
+ characterData: false
+ // subtree: true
+ };
- /**
- * Safari 10 fixed it, Chrome fixed it in v45:
- * This fixes a bug where the areas left and right to
- * the content does not trigger the onmousewheel event
- * on some pages. e.g.: html, body { height: 100% }
- */
- else if (isOldSafari &&
- scrollHeight > windowHeight &&
- (body.offsetHeight <= windowHeight ||
- html.offsetHeight <= windowHeight)) {
-
- var fullPageElem = document.createElement('div');
- fullPageElem.style.cssText = 'position:absolute; z-index:-10000; ' +
- 'top:0; left:0; right:0; height:' +
- root.scrollHeight + 'px';
- document.body.appendChild(fullPageElem);
-
- // DOM changed (throttled) to fix height
- var pendingRefresh;
- refreshSize = function () {
- if (pendingRefresh) return; // could also be: clearTimeout(pendingRefresh);
- pendingRefresh = setTimeout(function () {
- if (isExcluded) return; // could be running after cleanup
- fullPageElem.style.height = '0';
- fullPageElem.style.height = root.scrollHeight + 'px';
- pendingRefresh = null;
- }, 500); // act rarely to stay fast
- };
-
- setTimeout(refreshSize, 10);
-
- addEvent('resize', refreshSize);
-
- // TODO: attributeFilter?
- var config = {
- attributes: true,
- childList: true,
- characterData: false
- // subtree: true
- };
-
- observer = new MutationObserver(refreshSize);
- observer.observe(body, config);
-
- if (root.offsetHeight <= windowHeight) {
- var clearfix = document.createElement('div');
- clearfix.style.clear = 'both';
- body.appendChild(clearfix);
- }
- }
+ observer = new MutationObserver(refreshSize);
+ observer.observe(body, config);
- // disable fixed background
- if (!options.fixedBackground && !isExcluded) {
- body.style.backgroundAttachment = 'scroll';
- html.style.backgroundAttachment = 'scroll';
+ if (root.offsetHeight <= windowHeight) {
+ var clearfix = document.createElement('div');
+ clearfix.style.clear = 'both';
+ body.appendChild(clearfix);
}
}
- /**
- * Removes event listeners and other traces left on the page.
- */
- function cleanup() {
- observer && observer.disconnect();
- removeEvent(wheelEvent, wheel);
- removeEvent('mousedown', mousedown);
- removeEvent('keydown', keydown);
- removeEvent('resize', refreshSize);
- removeEvent('load', init);
+ // disable fixed background
+ if (!options.fixedBackground && !isExcluded) {
+ body.style.backgroundAttachment = 'scroll';
+ html.style.backgroundAttachment = 'scroll';
}
-
-
- /************************************************
- * SCROLLING
- ************************************************/
-
- var que = [];
- var pending = false;
- var lastScroll = Date.now();
-
- /**
- * Pushes scroll actions to the scrolling queue.
- */
- function scrollArray(elem, left, top) {
-
- directionCheck(left, top);
-
- if (options.accelerationMax != 1) {
- var now = Date.now();
- var elapsed = now - lastScroll;
- if (elapsed < options.accelerationDelta) {
- var factor = (1 + (50 / elapsed)) / 2;
- if (factor > 1) {
- factor = Math.min(factor, options.accelerationMax);
- left *= factor;
- top *= factor;
- }
+}
+
+/**
+ * Removes event listeners and other traces left on the page.
+ */
+function cleanup() {
+ observer && observer.disconnect();
+ removeEvent(wheelEvent, wheel);
+ removeEvent('mousedown', mousedown);
+ removeEvent('keydown', keydown);
+ removeEvent('resize', refreshSize);
+ removeEvent('load', init);
+}
+
+
+/************************************************
+ * SCROLLING
+ ************************************************/
+
+var que = [];
+var pending = false;
+var lastScroll = Date.now();
+
+/**
+ * Pushes scroll actions to the scrolling queue.
+ */
+function scrollArray(elem, left, top) {
+
+ directionCheck(left, top);
+
+ if (options.accelerationMax != 1) {
+ var now = Date.now();
+ var elapsed = now - lastScroll;
+ if (elapsed < options.accelerationDelta) {
+ var factor = (1 + (50 / elapsed)) / 2;
+ if (factor > 1) {
+ factor = Math.min(factor, options.accelerationMax);
+ left *= factor;
+ top *= factor;
}
- lastScroll = Date.now();
}
+ lastScroll = Date.now();
+ }
- // push a scroll command
- que.push({
- x: left,
- y: top,
- lastX: (left < 0) ? 0.99 : -0.99,
- lastY: (top < 0) ? 0.99 : -0.99,
- start: Date.now()
- });
-
- // don't act if there's a pending queue
- if (pending) {
- return;
- }
-
- var scrollWindow = (elem === document.body);
-
- var step = function (time) {
-
- var now = Date.now();
- var scrollX = 0;
- var scrollY = 0;
-
- for (var i = 0; i < que.length; i++) {
-
- var item = que[i];
- var elapsed = now - item.start;
- var finished = (elapsed >= options.animationTime);
-
- // scroll position: [0, 1]
- var position = (finished) ? 1 : elapsed / options.animationTime;
+ // push a scroll command
+ que.push({
+ x: left,
+ y: top,
+ lastX: (left < 0) ? 0.99 : -0.99,
+ lastY: (top < 0) ? 0.99 : -0.99,
+ start: Date.now()
+ });
+
+ // don't act if there's a pending queue
+ if (pending) {
+ return;
+ }
- // easing [optional]
- if (options.pulseAlgorithm) {
- position = pulse(position);
- }
+ var scrollRoot = getScrollRoot();
+ var isWindowScroll = (elem === scrollRoot || elem === document.body);
- // only need the difference
- var x = (item.x * position - item.lastX) >> 0;
- var y = (item.y * position - item.lastY) >> 0;
+ // if we haven't already fixed the behavior,
+ // and it needs fixing for this sesh
+ if (elem.$scrollBehavior == null && isScrollBehaviorSmooth(elem)) {
+ elem.$scrollBehavior = elem.style.scrollBehavior;
+ elem.style.scrollBehavior = 'auto';
+ }
- // add this to the total scrolling
- scrollX += x;
- scrollY += y;
+ var step = function (time) {
- // update last values
- item.lastX += x;
- item.lastY += y;
+ var now = Date.now();
+ var scrollX = 0;
+ var scrollY = 0;
- // delete and step back if it's over
- if (finished) {
- que.splice(i, 1); i--;
- }
- }
+ for (var i = 0; i < que.length; i++) {
- // scroll left and top
- if (scrollWindow) {
- window.scrollBy(scrollX, scrollY);
- }
- else {
- if (scrollX) elem.scrollLeft += scrollX;
- if (scrollY) elem.scrollTop += scrollY;
- }
+ var item = que[i];
+ var elapsed = now - item.start;
+ var finished = (elapsed >= options.animationTime);
- // clean up if there's nothing left to do
- if (!left && !top) {
- que = [];
- }
+ // scroll position: [0, 1]
+ var position = (finished) ? 1 : elapsed / options.animationTime;
- if (que.length) {
- requestFrame(step, elem, (1000 / options.frameRate + 1));
- } else {
- pending = false;
+ // easing [optional]
+ if (options.pulseAlgorithm) {
+ position = pulse(position);
}
- };
-
- // start a new queue of actions
- requestFrame(step, elem, 0);
- pending = true;
- }
+ // only need the difference
+ var x = (item.x * position - item.lastX) >> 0;
+ var y = (item.y * position - item.lastY) >> 0;
- /***********************************************
- * EVENTS
- ***********************************************/
+ // add this to the total scrolling
+ scrollX += x;
+ scrollY += y;
- /**
- * Mouse wheel handler.
- * @param {Object} event
- */
- function wheel(event) {
+ // update last values
+ item.lastX += x;
+ item.lastY += y;
- if (!initDone) {
- init();
- }
-
- var target = event.target;
-
- // leave early if default action is prevented
- // or it's a zooming event with CTRL
- if (event.defaultPrevented || event.ctrlKey) {
- return true;
+ // delete and step back if it's over
+ if (finished) {
+ que.splice(i, 1); i--;
+ }
}
- // leave embedded content alone (flash & pdf)
- if (isNodeName(activeElement, 'embed') ||
- (isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) ||
- isNodeName(activeElement, 'object') ||
- target.shadowRoot) {
- return true;
+ // scroll left and top
+ if (isWindowScroll) {
+ window.scrollBy(scrollX, scrollY);
}
-
- var deltaX = -event.wheelDeltaX || event.deltaX || 0;
- var deltaY = -event.wheelDeltaY || event.deltaY || 0;
-
- if (isMac) {
- if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) {
- deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX));
- }
- if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) {
- deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY));
- }
+ else {
+ if (scrollX) elem.scrollLeft += scrollX;
+ if (scrollY) elem.scrollTop += scrollY;
}
- // use wheelDelta if deltaX/Y is not available
- if (!deltaX && !deltaY) {
- deltaY = -event.wheelDelta || 0;
+ // clean up if there's nothing left to do
+ if (!left && !top) {
+ que = [];
}
- // line based scrolling (Firefox mostly)
- if (event.deltaMode === 1) {
- deltaX *= 40;
- deltaY *= 40;
+ if (que.length) {
+ requestFrame(step, elem, (1000 / options.frameRate + 1));
+ } else {
+ pending = false;
+ // restore default behavior at the end of scrolling sesh
+ if (elem.$scrollBehavior != null) {
+ elem.style.scrollBehavior = elem.$scrollBehavior;
+ elem.$scrollBehavior = null;
+ }
}
+ };
- var overflowing = overflowingAncestor(target);
+ // start a new queue of actions
+ requestFrame(step, elem, 0);
+ pending = true;
+}
- // nothing to do if there's no element that's scrollable
- if (!overflowing) {
- // except Chrome iframes seem to eat wheel events, which we need to
- // propagate up, if the iframe has nothing overflowing to scroll
- if (isFrame && isChrome) {
- // change target to iframe element itself for the parent frame
- Object.defineProperty(event, "target", { value: window.frameElement });
- return parent.wheel(event);
- }
- return true;
- }
- // check if it's a touchpad scroll that should be ignored
- if (isTouchpad(deltaY)) {
- return true;
- }
+/***********************************************
+ * EVENTS
+ ***********************************************/
- // scale by step size
- // delta is 120 most of the time
- // synaptics seems to send 1 sometimes
- if (Math.abs(deltaX) > 1.2) {
- deltaX *= options.stepSize / 120;
- }
- if (Math.abs(deltaY) > 1.2) {
- deltaY *= options.stepSize / 120;
- }
+/**
+ * Mouse wheel handler.
+ * @param {Object} event
+ */
+function wheel(event) {
- scrollArray(overflowing, deltaX, deltaY);
- event.preventDefault();
- scheduleClearCache();
+ if (!initDone) {
+ init();
}
- /**
- * Keydown event handler.
- * @param {Object} event
- */
- function keydown(event) {
+ var target = event.target;
- var target = event.target;
- var modifier = event.ctrlKey || event.altKey || event.metaKey ||
- (event.shiftKey && event.keyCode !== key.spacebar);
+ // leave early if default action is prevented
+ // or it's a zooming event with CTRL
+ if (event.defaultPrevented || event.ctrlKey) {
+ return true;
+ }
- // our own tracked active element could've been removed from the DOM
- if (!document.body.contains(activeElement)) {
- activeElement = document.activeElement;
- }
+ // leave embedded content alone (flash & pdf)
+ if (isNodeName(activeElement, 'embed') ||
+ (isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) ||
+ isNodeName(activeElement, 'object') ||
+ target.shadowRoot) {
+ return true;
+ }
- // do nothing if user is editing text
- // or using a modifier key (except shift)
- // or in a dropdown
- // or inside interactive elements
- var inputNodeNames = /^(textarea|select|embed|object)$/i;
- var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i;
- if (event.defaultPrevented ||
- inputNodeNames.test(target.nodeName) ||
- isNodeName(target, 'input') && !buttonTypes.test(target.type) ||
- isNodeName(activeElement, 'video') ||
- isInsideYoutubeVideo(event) ||
- target.isContentEditable ||
- modifier) {
- return true;
- }
+ var deltaX = -event.wheelDeltaX || event.deltaX || 0;
+ var deltaY = -event.wheelDeltaY || event.deltaY || 0;
- // [spacebar] should trigger button press, leave it alone
- if ((isNodeName(target, 'button') ||
- isNodeName(target, 'input') && buttonTypes.test(target.type)) &&
- event.keyCode === key.spacebar) {
- return true;
+ if (isMac) {
+ if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) {
+ deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX));
}
-
- // [arrwow keys] on radio buttons should be left alone
- if (isNodeName(target, 'input') && target.type == 'radio' &&
- arrowKeys[event.keyCode]) {
- return true;
+ if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) {
+ deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY));
}
+ }
- var shift, x = 0, y = 0;
- var overflowing = overflowingAncestor(activeElement);
-
- if (!overflowing) {
- // Chrome iframes seem to eat key events, which we need to
- // propagate up, if the iframe has nothing overflowing to scroll
- return (isFrame && isChrome) ? parent.keydown(event) : true;
- }
+ // use wheelDelta if deltaX/Y is not available
+ if (!deltaX && !deltaY) {
+ deltaY = -event.wheelDelta || 0;
+ }
- var clientHeight = overflowing.clientHeight;
+ // line based scrolling (Firefox mostly)
+ if (event.deltaMode === 1) {
+ deltaX *= 40;
+ deltaY *= 40;
+ }
- if (overflowing == document.body) {
- clientHeight = window.innerHeight;
- }
+ var overflowing = overflowingAncestor(target);
- switch (event.keyCode) {
- case key.up:
- y = -options.arrowScroll;
- break;
- case key.down:
- y = options.arrowScroll;
- break;
- case key.spacebar: // (+ shift)
- shift = event.shiftKey ? 1 : -1;
- y = -shift * clientHeight * 0.9;
- break;
- case key.pageup:
- y = -clientHeight * 0.9;
- break;
- case key.pagedown:
- y = clientHeight * 0.9;
- break;
- case key.home:
- y = -overflowing.scrollTop;
- break;
- case key.end:
- var scroll = overflowing.scrollHeight - overflowing.scrollTop;
- var scrollRemaining = scroll - clientHeight;
- y = (scrollRemaining > 0) ? scrollRemaining + 10 : 0;
- break;
- case key.left:
- x = -options.arrowScroll;
- break;
- case key.right:
- x = options.arrowScroll;
- break;
- default:
- return true; // a key we don't care about
+ // nothing to do if there's no element that's scrollable
+ if (!overflowing) {
+ // except Chrome iframes seem to eat wheel events, which we need to
+ // propagate up, if the iframe has nothing overflowing to scroll
+ if (isFrame && isChrome) {
+ // change target to iframe element itself for the parent frame
+ Object.defineProperty(event, "target", {value: window.frameElement});
+ return parent.wheel(event);
}
-
- scrollArray(overflowing, x, y);
- event.preventDefault();
- scheduleClearCache();
+ return true;
}
- /**
- * Mousedown event only for updating activeElement
- */
- function mousedown(event) {
- activeElement = event.target;
+ // check if it's a touchpad scroll that should be ignored
+ if (isTouchpad(deltaY)) {
+ return true;
}
+ // scale by step size
+ // delta is 120 most of the time
+ // synaptics seems to send 1 sometimes
+ if (Math.abs(deltaX) > 1.2) {
+ deltaX *= options.stepSize / 120;
+ }
+ if (Math.abs(deltaY) > 1.2) {
+ deltaY *= options.stepSize / 120;
+ }
- /***********************************************
- * OVERFLOW
- ***********************************************/
-
- var uniqueID = (function () {
- var i = 0;
- return function (el) {
- return el.uniqueID || (el.uniqueID = i++);
- };
- })();
+ scrollArray(overflowing, deltaX, deltaY);
+ event.preventDefault();
+ scheduleClearCache();
+}
- var cache = {}; // cleared out after a scrolling session
- var clearCacheTimer;
+/**
+ * Keydown event handler.
+ * @param {Object} event
+ */
+function keydown(event) {
- //setInterval(function () { cache = {}; }, 10 * 1000);
+ var target = event.target;
+ var modifier = event.ctrlKey || event.altKey || event.metaKey ||
+ (event.shiftKey && event.keyCode !== key.spacebar);
- function scheduleClearCache() {
- clearTimeout(clearCacheTimer);
- clearCacheTimer = setInterval(function () { cache = {}; }, 1 * 1000);
+ // our own tracked active element could've been removed from the DOM
+ if (!document.body.contains(activeElement)) {
+ activeElement = document.activeElement;
}
- function setCache(elems, overflowing) {
- for (var i = elems.length; i--;)
- cache[uniqueID(elems[i])] = overflowing;
- return overflowing;
+ // do nothing if user is editing text
+ // or using a modifier key (except shift)
+ // or in a dropdown
+ // or inside interactive elements
+ var inputNodeNames = /^(textarea|select|embed|object)$/i;
+ var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i;
+ if ( event.defaultPrevented ||
+ inputNodeNames.test(target.nodeName) ||
+ isNodeName(target, 'input') && !buttonTypes.test(target.type) ||
+ isNodeName(activeElement, 'video') ||
+ isInsideYoutubeVideo(event) ||
+ target.isContentEditable ||
+ modifier ) {
+ return true;
}
- // (body) (root)
- // | hidden | visible | scroll | auto |
- // hidden | no | no | YES | YES |
- // visible | no | YES | YES | YES |
- // scroll | no | YES | YES | YES |
- // auto | no | YES | YES | YES |
-
- function overflowingAncestor(el) {
- var elems = [];
- var body = document.body;
- var rootScrollHeight = root.scrollHeight;
- do {
- var cached = cache[uniqueID(el)];
- if (cached) {
- return setCache(elems, cached);
- }
- elems.push(el);
- if (rootScrollHeight === el.scrollHeight) {
- var topOverflowsNotHidden = overflowNotHidden(root) && overflowNotHidden(body);
- var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root);
- if (isFrame && isContentOverflowing(root) ||
- !isFrame && isOverflowCSS) {
- return setCache(elems, getScrollRoot());
- }
- } else if (isContentOverflowing(el) && overflowAutoOrScroll(el)) {
- return setCache(elems, el);
- }
- } while (el = el.parentElement);
+ // [spacebar] should trigger button press, leave it alone
+ if ((isNodeName(target, 'button') ||
+ isNodeName(target, 'input') && buttonTypes.test(target.type)) &&
+ event.keyCode === key.spacebar) {
+ return true;
}
- function isContentOverflowing(el) {
- return (el.clientHeight + 10 < el.scrollHeight);
+ // [arrwow keys] on radio buttons should be left alone
+ if (isNodeName(target, 'input') && target.type == 'radio' &&
+ arrowKeys[event.keyCode]) {
+ return true;
}
- // typically for <body> and <html>
- function overflowNotHidden(el) {
- var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
- return (overflow !== 'hidden');
- }
+ var shift, x = 0, y = 0;
+ var overflowing = overflowingAncestor(activeElement);
- // for all other elements
- function overflowAutoOrScroll(el) {
- var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
- return (overflow === 'scroll' || overflow === 'auto');
+ if (!overflowing) {
+ // Chrome iframes seem to eat key events, which we need to
+ // propagate up, if the iframe has nothing overflowing to scroll
+ return (isFrame && isChrome) ? parent.keydown(event) : true;
}
+ var clientHeight = overflowing.clientHeight;
- /***********************************************
- * HELPERS
- ***********************************************/
-
- function addEvent(type, fn) {
- window.addEventListener(type, fn, false);
+ if (overflowing == document.body) {
+ clientHeight = window.innerHeight;
}
- function removeEvent(type, fn) {
- window.removeEventListener(type, fn, false);
+ switch (event.keyCode) {
+ case key.up:
+ y = -options.arrowScroll;
+ break;
+ case key.down:
+ y = options.arrowScroll;
+ break;
+ case key.spacebar: // (+ shift)
+ shift = event.shiftKey ? 1 : -1;
+ y = -shift * clientHeight * 0.9;
+ break;
+ case key.pageup:
+ y = -clientHeight * 0.9;
+ break;
+ case key.pagedown:
+ y = clientHeight * 0.9;
+ break;
+ case key.home:
+ if (overflowing == document.body && document.scrollingElement)
+ overflowing = document.scrollingElement;
+ y = -overflowing.scrollTop;
+ break;
+ case key.end:
+ var scroll = overflowing.scrollHeight - overflowing.scrollTop;
+ var scrollRemaining = scroll - clientHeight;
+ y = (scrollRemaining > 0) ? scrollRemaining + 10 : 0;
+ break;
+ case key.left:
+ x = -options.arrowScroll;
+ break;
+ case key.right:
+ x = options.arrowScroll;
+ break;
+ default:
+ return true; // a key we don't care about
}
- function isNodeName(el, tag) {
- return (el.nodeName || '').toLowerCase() === tag.toLowerCase();
- }
+ scrollArray(overflowing, x, y);
+ event.preventDefault();
+ scheduleClearCache();
+}
- function directionCheck(x, y) {
- x = (x > 0) ? 1 : -1;
- y = (y > 0) ? 1 : -1;
- if (direction.x !== x || direction.y !== y) {
- direction.x = x;
- direction.y = y;
- que = [];
- lastScroll = 0;
- }
- }
+/**
+ * Mousedown event only for updating activeElement
+ */
+function mousedown(event) {
+ activeElement = event.target;
+}
- var deltaBufferTimer;
- if (window.localStorage && localStorage.SS_deltaBuffer) {
- try { // #46 Safari throws in private browsing for localStorage
- deltaBuffer = localStorage.SS_deltaBuffer.split(',');
- } catch (e) { }
- }
+/***********************************************
+ * OVERFLOW
+ ***********************************************/
- function isTouchpad(deltaY) {
- if (!deltaY) return;
- if (!deltaBuffer.length) {
- deltaBuffer = [deltaY, deltaY, deltaY];
+var uniqueID = (function () {
+ var i = 0;
+ return function (el) {
+ return el.uniqueID || (el.uniqueID = i++);
+ };
+})();
+
+var cacheX = {}; // cleared out after a scrolling session
+var cacheY = {}; // cleared out after a scrolling session
+var clearCacheTimer;
+var smoothBehaviorForElement = {};
+
+//setInterval(function () { cache = {}; }, 10 * 1000);
+
+function scheduleClearCache() {
+ clearTimeout(clearCacheTimer);
+ clearCacheTimer = setInterval(function () {
+ cacheX = cacheY = smoothBehaviorForElement = {};
+ }, 1*1000);
+}
+
+function setCache(elems, overflowing, x) {
+ var cache = x ? cacheX : cacheY;
+ for (var i = elems.length; i--;)
+ cache[uniqueID(elems[i])] = overflowing;
+ return overflowing;
+}
+
+function getCache(el, x) {
+ return (x ? cacheX : cacheY)[uniqueID(el)];
+}
+
+// (body) (root)
+// | hidden | visible | scroll | auto |
+// hidden | no | no | YES | YES |
+// visible | no | YES | YES | YES |
+// scroll | no | YES | YES | YES |
+// auto | no | YES | YES | YES |
+
+function overflowingAncestor(el) {
+ var elems = [];
+ var body = document.body;
+ var rootScrollHeight = root.scrollHeight;
+ do {
+ var cached = getCache(el, false);
+ if (cached) {
+ return setCache(elems, cached);
+ }
+ elems.push(el);
+ if (rootScrollHeight === el.scrollHeight) {
+ var topOverflowsNotHidden = overflowNotHidden(root) && overflowNotHidden(body);
+ var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root);
+ if (isFrame && isContentOverflowing(root) ||
+ !isFrame && isOverflowCSS) {
+ return setCache(elems, getScrollRoot());
+ }
+ } else if (isContentOverflowing(el) && overflowAutoOrScroll(el)) {
+ return setCache(elems, el);
}
- deltaY = Math.abs(deltaY);
- deltaBuffer.push(deltaY);
- deltaBuffer.shift();
- clearTimeout(deltaBufferTimer);
- deltaBufferTimer = setTimeout(function () {
- try { // #46 Safari throws in private browsing for localStorage
- localStorage.SS_deltaBuffer = deltaBuffer.join(',');
- } catch (e) { }
- }, 1000);
- return !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100);
+ } while ((el = el.parentElement));
+}
+
+function isContentOverflowing(el) {
+ return (el.clientHeight + 10 < el.scrollHeight);
+}
+
+// typically for <body> and <html>
+function overflowNotHidden(el) {
+ var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
+ return (overflow !== 'hidden');
+}
+
+// for all other elements
+function overflowAutoOrScroll(el) {
+ var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
+ return (overflow === 'scroll' || overflow === 'auto');
+}
+
+// for all other elements
+function isScrollBehaviorSmooth(el) {
+ var id = uniqueID(el);
+ if (smoothBehaviorForElement[id] == null) {
+ var scrollBehavior = getComputedStyle(el, '')['scroll-behavior'];
+ smoothBehaviorForElement[id] = ('smooth' == scrollBehavior);
}
-
- function isDivisible(n, divisor) {
- return (Math.floor(n / divisor) == n / divisor);
+ return smoothBehaviorForElement[id];
+}
+
+
+/***********************************************
+ * HELPERS
+ ***********************************************/
+
+function addEvent(type, fn, arg) {
+ window.addEventListener(type, fn, arg || false);
+}
+
+function removeEvent(type, fn, arg) {
+ window.removeEventListener(type, fn, arg || false);
+}
+
+function isNodeName(el, tag) {
+ return el && (el.nodeName||'').toLowerCase() === tag.toLowerCase();
+}
+
+function directionCheck(x, y) {
+ x = (x > 0) ? 1 : -1;
+ y = (y > 0) ? 1 : -1;
+ if (direction.x !== x || direction.y !== y) {
+ direction.x = x;
+ direction.y = y;
+ que = [];
+ lastScroll = 0;
}
-
- function allDeltasDivisableBy(divisor) {
- return (isDivisible(deltaBuffer[0], divisor) &&
+}
+
+if (window.localStorage && localStorage.SS_deltaBuffer) {
+ try { // #46 Safari throws in private browsing for localStorage
+ deltaBuffer = localStorage.SS_deltaBuffer.split(',');
+ } catch (e) { }
+}
+
+function isTouchpad(deltaY) {
+ if (!deltaY) return;
+ if (!deltaBuffer.length) {
+ deltaBuffer = [deltaY, deltaY, deltaY];
+ }
+ deltaY = Math.abs(deltaY);
+ deltaBuffer.push(deltaY);
+ deltaBuffer.shift();
+ clearTimeout(deltaBufferTimer);
+ deltaBufferTimer = setTimeout(function () {
+ try { // #46 Safari throws in private browsing for localStorage
+ localStorage.SS_deltaBuffer = deltaBuffer.join(',');
+ } catch (e) { }
+ }, 1000);
+ var dpiScaledWheelDelta = deltaY > 120 && allDeltasDivisableBy(deltaY); // win64
+ var tp = !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100) && !dpiScaledWheelDelta;
+ if (deltaY < 50) return true;
+ return tp;
+}
+
+function isDivisible(n, divisor) {
+ return (Math.floor(n / divisor) == n / divisor);
+}
+
+function allDeltasDivisableBy(divisor) {
+ return (isDivisible(deltaBuffer[0], divisor) &&
isDivisible(deltaBuffer[1], divisor) &&
isDivisible(deltaBuffer[2], divisor));
- }
-
- function isInsideYoutubeVideo(event) {
- var elem = event.target;
- var isControl = false;
- if (document.URL.indexOf('www.youtube.com/watch') != -1) {
- do {
- isControl = (elem.classList &&
- elem.classList.contains('html5-video-controls'));
- if (isControl) break;
- } while (elem = elem.parentNode);
- }
- return isControl;
- }
-
- var requestFrame = (function () {
- return (window.requestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame ||
- function (callback, element, delay) {
- window.setTimeout(callback, delay || (1000 / 60));
- });
- })();
-
- var MutationObserver = (window.MutationObserver ||
- window.WebKitMutationObserver ||
- window.MozMutationObserver);
-
- var getScrollRoot = (function () {
- var SCROLL_ROOT;
- return function () {
- if (!SCROLL_ROOT) {
- var dummy = document.createElement('div');
- dummy.style.cssText = 'height:10000px;width:1px;';
- document.body.appendChild(dummy);
- var bodyScrollTop = document.body.scrollTop;
- var docElScrollTop = document.documentElement.scrollTop;
- window.scrollBy(0, 3);
- if (document.body.scrollTop != bodyScrollTop)
- (SCROLL_ROOT = document.body);
- else
- (SCROLL_ROOT = document.documentElement);
- window.scrollBy(0, -3);
- document.body.removeChild(dummy);
- }
- return SCROLL_ROOT;
- };
- })();
-
+}
- /***********************************************
- * PULSE (by Michael Herf)
- ***********************************************/
-
- /**
- * Viscous fluid with a pulse for part and decay for the rest.
- * - Applies a fixed force over an interval (a damped acceleration), and
- * - Lets the exponential bleed away the velocity over a longer interval
- * - Michael Herf, http://stereopsis.com/stopping/
- */
- function pulse_(x) {
- var val, start, expx;
- // test
- x = x * options.pulseScale;
- if (x < 1) { // acceleartion
- val = x - (1 - Math.exp(-x));
- } else { // tail
- // the previous animation ended here:
- start = Math.exp(-1);
- // simple viscous drag
- x -= 1;
- expx = 1 - Math.exp(-x);
- val = start + (expx * (1 - start));
- }
- return val * options.pulseNormalize;
+function isInsideYoutubeVideo(event) {
+ var elem = event.target;
+ var isControl = false;
+ if (document.URL.indexOf ('www.youtube.com/watch') != -1) {
+ do {
+ isControl = (elem.classList &&
+ elem.classList.contains('html5-video-controls'));
+ if (isControl) break;
+ } while ((elem = elem.parentNode));
}
-
- function pulse(x) {
- if (x >= 1) return 1;
- if (x <= 0) return 0;
-
- if (options.pulseNormalize == 1) {
- options.pulseNormalize /= pulse_(1);
- }
- return pulse_(x);
+ return isControl;
+}
+
+var requestFrame = (function () {
+ return (window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ function (callback, element, delay) {
+ window.setTimeout(callback, delay || (1000/60));
+ });
+})();
+
+var MutationObserver = (window.MutationObserver ||
+ window.WebKitMutationObserver ||
+ window.MozMutationObserver);
+
+var getScrollRoot = (function() {
+ var SCROLL_ROOT = document.scrollingElement;
+ return function() {
+ if (!SCROLL_ROOT) {
+ var dummy = document.createElement('div');
+ dummy.style.cssText = 'height:10000px;width:1px;';
+ document.body.appendChild(dummy);
+ var bodyScrollTop = document.body.scrollTop;
+ var docElScrollTop = document.documentElement.scrollTop;
+ window.scrollBy(0, 3);
+ if (document.body.scrollTop != bodyScrollTop)
+ (SCROLL_ROOT = document.body);
+ else
+ (SCROLL_ROOT = document.documentElement);
+ window.scrollBy(0, -3);
+ document.body.removeChild(dummy);
}
-
-
- /***********************************************
- * FIRST RUN
- ***********************************************/
-
- var userAgent = window.navigator.userAgent;
- var isEdge = /Edge/.test(userAgent); // thank you MS
- var isChrome = /chrome/i.test(userAgent) && !isEdge;
- var isSafari = /safari/i.test(userAgent) && !isEdge;
- var isMobile = /mobile/i.test(userAgent);
- var isIEWin7 = /Windows NT 6.1/i.test(userAgent) && /rv:11/i.test(userAgent);
- var isOldSafari = isSafari && (/Version\/8/i.test(userAgent) || /Version\/9/i.test(userAgent));
- var isEnabledForBrowser = (isChrome || isSafari || isIEWin7) && !isMobile;
-
- var wheelEvent;
- if ('onwheel' in document.createElement('div'))
- wheelEvent = 'wheel';
- else if ('onmousewheel' in document.createElement('div'))
- wheelEvent = 'mousewheel';
-
- if (wheelEvent && isEnabledForBrowser) {
- addEvent(wheelEvent, wheel);
- addEvent('mousedown', mousedown);
- addEvent('load', init);
+ return SCROLL_ROOT;
+ };
+})();
+
+
+/***********************************************
+ * PULSE (by Michael Herf)
+ ***********************************************/
+
+/**
+ * Viscous fluid with a pulse for part and decay for the rest.
+ * - Applies a fixed force over an interval (a damped acceleration), and
+ * - Lets the exponential bleed away the velocity over a longer interval
+ * - Michael Herf, http://stereopsis.com/stopping/
+ */
+function pulse_(x) {
+ var val, start, expx;
+ // test
+ x = x * options.pulseScale;
+ if (x < 1) { // acceleartion
+ val = x - (1 - Math.exp(-x));
+ } else { // tail
+ // the previous animation ended here:
+ start = Math.exp(-1);
+ // simple viscous drag
+ x -= 1;
+ expx = 1 - Math.exp(-x);
+ val = start + (expx * (1 - start));
}
+ return val * options.pulseNormalize;
+}
+function pulse(x) {
+ if (x >= 1) return 1;
+ if (x <= 0) return 0;
- /***********************************************
- * PUBLIC INTERFACE
- ***********************************************/
-
- function SmoothScroll(optionsToSet) {
- for (var key in optionsToSet)
- if (defaultOptions.hasOwnProperty(key))
- options[key] = optionsToSet[key];
+ if (options.pulseNormalize == 1) {
+ options.pulseNormalize /= pulse_(1);
}
- SmoothScroll.destroy = cleanup;
-
- if (window.SmoothScrollOptions) // async API
- SmoothScroll(window.SmoothScrollOptions);
-
- if (typeof define === 'function' && define.amd)
- define(function () {
- return SmoothScroll;
- });
- else if ('object' == typeof exports)
- module.exports = SmoothScroll;
- else
- window.SmoothScroll = SmoothScroll;
-
-})(); \ No newline at end of file
+ return pulse_(x);
+}
+
+
+/***********************************************
+ * FIRST RUN
+ ***********************************************/
+
+var userAgent = window.navigator.userAgent;
+var isEdge = /Edge/.test(userAgent); // thank you MS
+var isChrome = /chrome/i.test(userAgent) && !isEdge;
+var isSafari = /safari/i.test(userAgent) && !isEdge;
+var isMobile = /mobile/i.test(userAgent);
+var isIEWin7 = /Windows NT 6.1/i.test(userAgent) && /rv:11/i.test(userAgent);
+var isOldSafari = isSafari && (/Version\/8/i.test(userAgent) || /Version\/9/i.test(userAgent));
+var isEnabledForBrowser = (isChrome || isSafari || isIEWin7) && !isMobile;
+
+var supportsPassive = false;
+try {
+ window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
+ get: function () {
+ supportsPassive = true;
+ }
+ }));
+} catch(e) {}
+
+var wheelOpt = supportsPassive ? { passive: false } : false;
+var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel';
+
+if (wheelEvent && isEnabledForBrowser) {
+ addEvent(wheelEvent, wheel, wheelOpt);
+ addEvent('mousedown', mousedown);
+ addEvent('load', init);
+}
+
+
+/***********************************************
+ * PUBLIC INTERFACE
+ ***********************************************/
+
+function SmoothScroll(optionsToSet) {
+ for (var key in optionsToSet)
+ if (defaultOptions.hasOwnProperty(key))
+ options[key] = optionsToSet[key];
+}
+SmoothScroll.destroy = cleanup;
+
+if (window.SmoothScrollOptions) // async API
+ SmoothScroll(window.SmoothScrollOptions);
+
+if (typeof define === 'function' && define.amd)
+ define(function() {
+ return SmoothScroll;
+ });
+else if ('object' == typeof exports)
+ module.exports = SmoothScroll;
+else
+ window.SmoothScroll = SmoothScroll;
+
+})();