scroll-behavior-polyfill
Advanced tools
Comparing version 2.0.4 to 2.0.5
@@ -1,39 +0,31 @@ | ||
## [2.0.3](https://github.com/wessberg/scroll-behavior-polyfill/compare/2.0.3...v2.0.3) (2019-01-11) | ||
## [2.0.5](https://github.com/wessberg/scroll-behavior-polyfill/compare/v2.0.4...v2.0.5) (2019-02-07) | ||
## [2.0.4](https://github.com/wessberg/scroll-behavior-polyfill/compare/v2.0.3...v2.0.4) (2019-01-24) | ||
### Bug Fixes | ||
- **package.json:** updates 'engine' field to reflect support for older Node.js versions. Fixes [#3](https://github.com/wessberg/scroll-behavior-polyfill/issues/3) ([8b49156](https://github.com/wessberg/scroll-behavior-polyfill/commit/8b49156)) | ||
## [2.0.3](https://github.com/wessberg/scroll-behavior-polyfill/compare/2.0.3...v2.0.3) (2019-01-11) | ||
## [2.0.2](https://github.com/wessberg/scroll-behavior-polyfill/compare/v2.0.1...v2.0.2) (2019-01-11) | ||
### Features | ||
* **improvements:** Bug fixes, support for scrolling via the scrollTop and scrollLeft setters, and more ([4989d15](https://github.com/wessberg/scroll-behavior-polyfill/commit/4989d15)) | ||
- **improvements:** Bug fixes, support for scrolling via the scrollTop and scrollLeft setters, and more ([4989d15](https://github.com/wessberg/scroll-behavior-polyfill/commit/4989d15)) | ||
## [2.0.1](https://github.com/wessberg/scroll-behavior-polyfill/compare/2.0.1...v2.0.1) (2019-01-09) | ||
# [2.0.0](https://github.com/wessberg/scroll-behavior-polyfill/compare/v1.0.2...v2.0.0) (2019-01-09) | ||
### Features | ||
* **release:** new major version and rewritten from scratch. ([5647eb3](https://github.com/wessberg/scroll-behavior-polyfill/commit/5647eb3)) | ||
- **release:** new major version and rewritten from scratch. ([5647eb3](https://github.com/wessberg/scroll-behavior-polyfill/commit/5647eb3)) | ||
### BREAKING CHANGES | ||
* **release:** CSS Stylesheets will no longer be parsed. Instead, you must either set inline styles, an attribute with the same name, or set it imperatively. Of course, you can still use the imperative API. | ||
- **release:** CSS Stylesheets will no longer be parsed. Instead, you must either set inline styles, an attribute with the same name, or set it imperatively. Of course, you can still use the imperative API. | ||
## [1.0.2](https://github.com/wessberg/scroll-behavior-polyfill/compare/v1.0.1...v1.0.2) (2017-09-08) | ||
## 1.0.1 (2017-09-08) | ||
1767
dist/index.js
(function () { | ||
'use strict'; | ||
'use strict'; | ||
/** | ||
* Is true if the browser natively supports the 'scroll-behavior' CSS-property. | ||
* @type {boolean} | ||
*/ | ||
var SUPPORTS_SCROLL_BEHAVIOR = "scrollBehavior" in document.documentElement.style; | ||
/** | ||
* Is true if the browser natively supports the 'scroll-behavior' CSS-property. | ||
* @type {boolean} | ||
*/ | ||
var SUPPORTS_SCROLL_BEHAVIOR = "scrollBehavior" in document.documentElement.style; | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
var __assign = function() { | ||
__assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
function __read(o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
} | ||
var __assign = function() { | ||
__assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var styleDeclarationPropertyName = "scrollBehavior"; | ||
var styleAttributePropertyName = "scroll-behavior"; | ||
var styleAttributePropertyNameRegex = new RegExp(styleAttributePropertyName + ":\\s*([^;]*)"); | ||
/** | ||
* Determines the scroll behavior to use, depending on the given ScrollOptions and the position of the Element | ||
* within the DOM | ||
* @param {Element|HTMLElement|Window} inputTarget | ||
* @param {ScrollOptions} [options] | ||
* @returns {ScrollBehavior} | ||
*/ | ||
function getScrollBehavior(inputTarget, options) { | ||
// If the given 'behavior' is 'smooth', apply smooth scrolling no matter what | ||
if (options != null && options.behavior === "smooth") | ||
return "smooth"; | ||
var target = "style" in inputTarget ? inputTarget : document.scrollingElement != null ? document.scrollingElement : document.documentElement; | ||
var value; | ||
if ("style" in target) { | ||
// Check if scroll-behavior is set as a property on the CSSStyleDeclaration | ||
var scrollBehaviorPropertyValue = target.style[styleDeclarationPropertyName]; | ||
// Return it if it is given and has a proper value | ||
if (scrollBehaviorPropertyValue != null && scrollBehaviorPropertyValue !== "") { | ||
value = scrollBehaviorPropertyValue; | ||
} | ||
} | ||
if (value == null) { | ||
var attributeValue = target.getAttribute("scroll-behavior"); | ||
if (attributeValue != null && attributeValue !== "") { | ||
value = attributeValue; | ||
} | ||
} | ||
if (value == null) { | ||
// Otherwise, check if it is set as an inline style | ||
var styleAttributeValue = target.getAttribute("style"); | ||
if (styleAttributeValue != null && styleAttributeValue.includes(styleAttributePropertyName)) { | ||
var match = styleAttributeValue.match(styleAttributePropertyNameRegex); | ||
if (match != null) { | ||
var _a = __read(match, 2), behavior = _a[1]; | ||
if (behavior != null && behavior !== "") { | ||
value = behavior; | ||
} | ||
} | ||
} | ||
} | ||
if (value == null) { | ||
// Take the computed style for the element and see if it contains a specific 'scroll-behavior' value | ||
var computedStyle = getComputedStyle(target); | ||
var computedStyleValue = computedStyle.getPropertyValue("scrollBehavior"); | ||
if (computedStyleValue != null && computedStyleValue !== "") { | ||
value = computedStyleValue; | ||
} | ||
} | ||
// In all other cases, use the value from the CSSOM | ||
return value; | ||
} | ||
function __read(o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
} | ||
var HALF = 0.5; | ||
/** | ||
* The easing function to use when applying the smooth scrolling | ||
* @param {number} k | ||
* @returns {number} | ||
*/ | ||
function ease(k) { | ||
return HALF * (1 - Math.cos(Math.PI * k)); | ||
} | ||
var styleDeclarationPropertyName = "scrollBehavior"; | ||
var styleAttributePropertyName = "scroll-behavior"; | ||
var styleAttributePropertyNameRegex = new RegExp(styleAttributePropertyName + ":\\s*([^;]*)"); | ||
/** | ||
* Determines the scroll behavior to use, depending on the given ScrollOptions and the position of the Element | ||
* within the DOM | ||
* @param {Element|HTMLElement|Window} inputTarget | ||
* @param {ScrollOptions} [options] | ||
* @returns {ScrollBehavior} | ||
*/ | ||
function getScrollBehavior(inputTarget, options) { | ||
// If the given 'behavior' is 'smooth', apply smooth scrolling no matter what | ||
if (options != null && options.behavior === "smooth") | ||
return "smooth"; | ||
var target = "style" in inputTarget | ||
? inputTarget | ||
: document.scrollingElement != null | ||
? document.scrollingElement | ||
: document.documentElement; | ||
var value; | ||
if ("style" in target) { | ||
// Check if scroll-behavior is set as a property on the CSSStyleDeclaration | ||
var scrollBehaviorPropertyValue = target.style[styleDeclarationPropertyName]; | ||
// Return it if it is given and has a proper value | ||
if (scrollBehaviorPropertyValue != null && | ||
scrollBehaviorPropertyValue !== "") { | ||
value = scrollBehaviorPropertyValue; | ||
} | ||
} | ||
if (value == null) { | ||
var attributeValue = target.getAttribute("scroll-behavior"); | ||
if (attributeValue != null && attributeValue !== "") { | ||
value = attributeValue; | ||
} | ||
} | ||
if (value == null) { | ||
// Otherwise, check if it is set as an inline style | ||
var styleAttributeValue = target.getAttribute("style"); | ||
if (styleAttributeValue != null && | ||
styleAttributeValue.includes(styleAttributePropertyName)) { | ||
var match = styleAttributeValue.match(styleAttributePropertyNameRegex); | ||
if (match != null) { | ||
var _a = __read(match, 2), behavior = _a[1]; | ||
if (behavior != null && behavior !== "") { | ||
value = behavior; | ||
} | ||
} | ||
} | ||
} | ||
if (value == null) { | ||
// Take the computed style for the element and see if it contains a specific 'scroll-behavior' value | ||
var computedStyle = getComputedStyle(target); | ||
var computedStyleValue = computedStyle.getPropertyValue("scrollBehavior"); | ||
if (computedStyleValue != null && computedStyleValue !== "") { | ||
value = computedStyleValue; | ||
} | ||
} | ||
// In all other cases, use the value from the CSSOM | ||
return value; | ||
} | ||
/** | ||
* The duration of a smooth scroll | ||
* @type {number} | ||
*/ | ||
var SCROLL_TIME = 15000; | ||
/** | ||
* Performs a smooth repositioning of the scroll | ||
* @param {ISmoothScrollOptions} options | ||
*/ | ||
function smoothScroll(options) { | ||
var startTime = options.startTime, startX = options.startX, startY = options.startY, endX = options.endX, endY = options.endY, method = options.method; | ||
var timeLapsed = 0; | ||
var distanceX = endX - startX; | ||
var distanceY = endY - startY; | ||
var speed = Math.max(Math.abs(distanceX / 1000 * SCROLL_TIME), Math.abs(distanceY / 1000 * SCROLL_TIME)); | ||
requestAnimationFrame(function animate(timestamp) { | ||
timeLapsed += timestamp - startTime; | ||
var percentage = Math.max(0, Math.min(1, speed === 0 ? 0 : (timeLapsed / speed))); | ||
var positionX = Math.floor(startX + (distanceX * ease(percentage))); | ||
var positionY = Math.floor(startY + (distanceY * ease(percentage))); | ||
method(positionX, positionY); | ||
if (positionX !== endX || positionY !== endY) { | ||
requestAnimationFrame(animate); | ||
} | ||
}); | ||
} | ||
/** | ||
* Returns a High Resolution timestamp if possible, otherwise fallbacks to Date.now() | ||
* @returns {number} | ||
*/ | ||
function now() { | ||
if ("performance" in window) | ||
return performance.now(); | ||
return Date.now(); | ||
} | ||
var HALF = 0.5; | ||
/** | ||
* The easing function to use when applying the smooth scrolling | ||
* @param {number} k | ||
* @returns {number} | ||
*/ | ||
function ease(k) { | ||
return HALF * (1 - Math.cos(Math.PI * k)); | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL = Element.prototype.scroll; | ||
/** | ||
* The duration of a smooth scroll | ||
* @type {number} | ||
*/ | ||
var SCROLL_TIME = 15000; | ||
/** | ||
* Performs a smooth repositioning of the scroll | ||
* @param {ISmoothScrollOptions} options | ||
*/ | ||
function smoothScroll(options) { | ||
var startTime = options.startTime, startX = options.startX, startY = options.startY, endX = options.endX, endY = options.endY, method = options.method; | ||
var timeLapsed = 0; | ||
var distanceX = endX - startX; | ||
var distanceY = endY - startY; | ||
var speed = Math.max(Math.abs((distanceX / 1000) * SCROLL_TIME), Math.abs((distanceY / 1000) * SCROLL_TIME)); | ||
requestAnimationFrame(function animate(timestamp) { | ||
timeLapsed += timestamp - startTime; | ||
var percentage = Math.max(0, Math.min(1, speed === 0 ? 0 : timeLapsed / speed)); | ||
var positionX = Math.floor(startX + distanceX * ease(percentage)); | ||
var positionY = Math.floor(startY + distanceY * ease(percentage)); | ||
method(positionX, positionY); | ||
if (positionX !== endX || positionY !== endY) { | ||
requestAnimationFrame(animate); | ||
} | ||
}); | ||
} | ||
var WINDOW_ORIGINAL_SCROLL = window.scroll; | ||
/** | ||
* Returns a High Resolution timestamp if possible, otherwise fallbacks to Date.now() | ||
* @returns {number} | ||
*/ | ||
function now() { | ||
if ("performance" in window) | ||
return performance.now(); | ||
return Date.now(); | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL_BY = Element.prototype.scrollBy; | ||
var WINDOW_ORIGINAL_SCROLL_BY = window.scrollBy; | ||
var ELEMENT_ORIGINAL_SCROLL = Element.prototype.scroll; | ||
var ELEMENT_ORIGINAL_SCROLL_TO = Element.prototype.scrollTo; | ||
var WINDOW_ORIGINAL_SCROLL = window.scroll; | ||
var WINDOW_ORIGINAL_SCROLL_TO = window.scrollTo; | ||
var ELEMENT_ORIGINAL_SCROLL_BY = Element.prototype.scrollBy; | ||
/** | ||
* A fallback if Element.prototype.scroll is not defined | ||
* @param {number} x | ||
* @param {number} y | ||
*/ | ||
function elementPrototypeScrollFallback(x, y) { | ||
this.__adjustingScrollPosition = true; | ||
this.scrollLeft = x; | ||
this.scrollTop = y; | ||
delete this.__adjustingScrollPosition; | ||
} | ||
/** | ||
* A fallback if Element.prototype.scrollTo is not defined | ||
* @param {number} x | ||
* @param {number} y | ||
*/ | ||
function elementPrototypeScrollToFallback(x, y) { | ||
return elementPrototypeScrollFallback.call(this, x, y); | ||
} | ||
/** | ||
* A fallback if Element.prototype.scrollBy is not defined | ||
* @param {number} x | ||
* @param {number} y | ||
*/ | ||
function elementPrototypeScrollByFallback(x, y) { | ||
this.__adjustingScrollPosition = true; | ||
this.scrollLeft += x; | ||
this.scrollTop += y; | ||
delete this.__adjustingScrollPosition; | ||
} | ||
/** | ||
* Gets the original non-patched prototype method for the given kind | ||
* @param {ScrollMethodName} kind | ||
* @param {Element|Window} element | ||
* @return {Function} | ||
*/ | ||
function getOriginalScrollMethodForKind(kind, element) { | ||
switch (kind) { | ||
case "scroll": | ||
if (element instanceof Element) { | ||
if (ELEMENT_ORIGINAL_SCROLL != null) { | ||
return ELEMENT_ORIGINAL_SCROLL; | ||
} | ||
else { | ||
return elementPrototypeScrollFallback; | ||
} | ||
} | ||
else { | ||
return WINDOW_ORIGINAL_SCROLL; | ||
} | ||
case "scrollBy": | ||
if (element instanceof Element) { | ||
if (ELEMENT_ORIGINAL_SCROLL_BY != null) { | ||
return ELEMENT_ORIGINAL_SCROLL_BY; | ||
} | ||
else { | ||
return elementPrototypeScrollByFallback; | ||
} | ||
} | ||
else { | ||
return WINDOW_ORIGINAL_SCROLL_BY; | ||
} | ||
case "scrollTo": | ||
if (element instanceof Element) { | ||
if (ELEMENT_ORIGINAL_SCROLL_TO != null) { | ||
return ELEMENT_ORIGINAL_SCROLL_TO; | ||
} | ||
else { | ||
return elementPrototypeScrollToFallback; | ||
} | ||
} | ||
else { | ||
return WINDOW_ORIGINAL_SCROLL_TO; | ||
} | ||
} | ||
} | ||
var WINDOW_ORIGINAL_SCROLL_BY = window.scrollBy; | ||
/** | ||
* Gets the Smooth Scroll Options to use for the step function | ||
* @param {Element|Window} element | ||
* @param {number} x | ||
* @param {number} y | ||
* @param {ScrollMethodName} kind | ||
* @returns {ISmoothScrollOptions} | ||
*/ | ||
function getSmoothScrollOptions(element, x, y, kind) { | ||
var startTime = now(); | ||
if (!(element instanceof Element)) { | ||
// Use window as the scroll container | ||
var scrollX_1 = window.scrollX, pageXOffset_1 = window.pageXOffset, scrollY_1 = window.scrollY, pageYOffset_1 = window.pageYOffset; | ||
var startX = scrollX_1 == null || scrollX_1 === 0 ? pageXOffset_1 : scrollX_1; | ||
var startY = scrollY_1 == null || scrollY_1 === 0 ? pageYOffset_1 : scrollY_1; | ||
return { | ||
startTime: startTime, | ||
startX: startX, | ||
startY: startY, | ||
endX: Math.floor(kind === "scrollBy" | ||
? startX + x | ||
: x), | ||
endY: Math.floor(kind === "scrollBy" | ||
? startY + y | ||
: y), | ||
method: getOriginalScrollMethodForKind("scrollTo", window).bind(window) | ||
}; | ||
} | ||
else { | ||
var scrollLeft = element.scrollLeft, scrollTop = element.scrollTop; | ||
var startX = scrollLeft; | ||
var startY = scrollTop; | ||
return { | ||
startTime: startTime, | ||
startX: startX, | ||
startY: startY, | ||
endX: Math.floor(kind === "scrollBy" | ||
? startX + x | ||
: x), | ||
endY: Math.floor(kind === "scrollBy" | ||
? startY + y | ||
: y), | ||
method: getOriginalScrollMethodForKind("scrollTo", element).bind(element) | ||
}; | ||
} | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL_TO = Element.prototype.scrollTo; | ||
/** | ||
* Ensures that the given value is numeric | ||
* @param {number} value | ||
* @return {number} | ||
*/ | ||
function ensureNumeric(value) { | ||
if (value == null) | ||
return 0; | ||
else if (typeof value === "number") { | ||
return value; | ||
} | ||
else if (typeof value === "string") { | ||
return parseFloat(value); | ||
} | ||
else { | ||
return 0; | ||
} | ||
} | ||
var WINDOW_ORIGINAL_SCROLL_TO = window.scrollTo; | ||
/** | ||
* Returns true if the given value is some ScrollToOptions | ||
* @param {number | ScrollToOptions} value | ||
* @return {value is ScrollToOptions} | ||
*/ | ||
function isScrollToOptions(value) { | ||
return value != null && typeof value === "object"; | ||
} | ||
/** | ||
* A fallback if Element.prototype.scroll is not defined | ||
* @param {number} x | ||
* @param {number} y | ||
*/ | ||
function elementPrototypeScrollFallback(x, y) { | ||
this.__adjustingScrollPosition = true; | ||
this.scrollLeft = x; | ||
this.scrollTop = y; | ||
delete this.__adjustingScrollPosition; | ||
} | ||
/** | ||
* A fallback if Element.prototype.scrollTo is not defined | ||
* @param {number} x | ||
* @param {number} y | ||
*/ | ||
function elementPrototypeScrollToFallback(x, y) { | ||
return elementPrototypeScrollFallback.call(this, x, y); | ||
} | ||
/** | ||
* A fallback if Element.prototype.scrollBy is not defined | ||
* @param {number} x | ||
* @param {number} y | ||
*/ | ||
function elementPrototypeScrollByFallback(x, y) { | ||
this.__adjustingScrollPosition = true; | ||
this.scrollLeft += x; | ||
this.scrollTop += y; | ||
delete this.__adjustingScrollPosition; | ||
} | ||
/** | ||
* Gets the original non-patched prototype method for the given kind | ||
* @param {ScrollMethodName} kind | ||
* @param {Element|Window} element | ||
* @return {Function} | ||
*/ | ||
function getOriginalScrollMethodForKind(kind, element) { | ||
switch (kind) { | ||
case "scroll": | ||
if (element instanceof Element) { | ||
if (ELEMENT_ORIGINAL_SCROLL != null) { | ||
return ELEMENT_ORIGINAL_SCROLL; | ||
} | ||
else { | ||
return elementPrototypeScrollFallback; | ||
} | ||
} | ||
else { | ||
return WINDOW_ORIGINAL_SCROLL; | ||
} | ||
case "scrollBy": | ||
if (element instanceof Element) { | ||
if (ELEMENT_ORIGINAL_SCROLL_BY != null) { | ||
return ELEMENT_ORIGINAL_SCROLL_BY; | ||
} | ||
else { | ||
return elementPrototypeScrollByFallback; | ||
} | ||
} | ||
else { | ||
return WINDOW_ORIGINAL_SCROLL_BY; | ||
} | ||
case "scrollTo": | ||
if (element instanceof Element) { | ||
if (ELEMENT_ORIGINAL_SCROLL_TO != null) { | ||
return ELEMENT_ORIGINAL_SCROLL_TO; | ||
} | ||
else { | ||
return elementPrototypeScrollToFallback; | ||
} | ||
} | ||
else { | ||
return WINDOW_ORIGINAL_SCROLL_TO; | ||
} | ||
} | ||
} | ||
/** | ||
* Handles a scroll method | ||
* @param {Element|Window} element | ||
* @param {ScrollMethodName} kind | ||
* @param {number | ScrollToOptions} optionsOrX | ||
* @param {number} y | ||
*/ | ||
function handleScrollMethod(element, kind, optionsOrX, y) { | ||
onScrollWithOptions(getScrollToOptionsWithValidation(optionsOrX, y), element, kind); | ||
} | ||
/** | ||
* Invoked when a 'ScrollToOptions' dict is provided to 'scroll()' as the first argument | ||
* @param {ScrollToOptions} options | ||
* @param {Element|Window} element | ||
* @param {ScrollMethodName} kind | ||
*/ | ||
function onScrollWithOptions(options, element, kind) { | ||
var behavior = getScrollBehavior(element, options); | ||
// If the behavior is 'auto' apply instantaneous scrolling | ||
if (behavior == null || behavior === "auto") { | ||
getOriginalScrollMethodForKind(kind, element).call(element, options.left, options.top); | ||
} | ||
else { | ||
smoothScroll(getSmoothScrollOptions(element, options.left, options.top, kind)); | ||
} | ||
} | ||
/** | ||
* Normalizes the given scroll coordinates | ||
* @param {number?} x | ||
* @param {number?} y | ||
* @return {Required<Pick<ScrollToOptions, "top" | "left">>} | ||
*/ | ||
function normalizeScrollCoordinates(x, y) { | ||
return { | ||
left: ensureNumeric(x), | ||
top: ensureNumeric(y) | ||
}; | ||
} | ||
/** | ||
* Gets ScrollToOptions based on the given arguments. Will throw if validation fails | ||
* @param {number | ScrollToOptions} optionsOrX | ||
* @param {number} y | ||
* @return {Required<ScrollToOptions>} | ||
*/ | ||
function getScrollToOptionsWithValidation(optionsOrX, y) { | ||
// If only one argument is given, and it isn't an options object, throw a TypeError | ||
if (y === undefined && !isScrollToOptions(optionsOrX)) { | ||
throw new TypeError("Failed to execute 'scroll' on 'Element': parameter 1 ('options') is not an object."); | ||
} | ||
// Scroll based on the primitive values given as arguments | ||
if (!isScrollToOptions(optionsOrX)) { | ||
return __assign({}, normalizeScrollCoordinates(optionsOrX, y), { behavior: "auto" }); | ||
} | ||
// Scroll based on the received options object | ||
else { | ||
return __assign({}, normalizeScrollCoordinates(optionsOrX.left, optionsOrX.top), { behavior: optionsOrX.behavior == null ? "auto" : optionsOrX.behavior }); | ||
} | ||
} | ||
/** | ||
* Gets the Smooth Scroll Options to use for the step function | ||
* @param {Element|Window} element | ||
* @param {number} x | ||
* @param {number} y | ||
* @param {ScrollMethodName} kind | ||
* @returns {ISmoothScrollOptions} | ||
*/ | ||
function getSmoothScrollOptions(element, x, y, kind) { | ||
var startTime = now(); | ||
if (!(element instanceof Element)) { | ||
// Use window as the scroll container | ||
var scrollX_1 = window.scrollX, pageXOffset_1 = window.pageXOffset, scrollY_1 = window.scrollY, pageYOffset_1 = window.pageYOffset; | ||
var startX = scrollX_1 == null || scrollX_1 === 0 ? pageXOffset_1 : scrollX_1; | ||
var startY = scrollY_1 == null || scrollY_1 === 0 ? pageYOffset_1 : scrollY_1; | ||
return { | ||
startTime: startTime, | ||
startX: startX, | ||
startY: startY, | ||
endX: Math.floor(kind === "scrollBy" ? startX + x : x), | ||
endY: Math.floor(kind === "scrollBy" ? startY + y : y), | ||
method: getOriginalScrollMethodForKind("scrollTo", window).bind(window) | ||
}; | ||
} | ||
else { | ||
var scrollLeft = element.scrollLeft, scrollTop = element.scrollTop; | ||
var startX = scrollLeft; | ||
var startY = scrollTop; | ||
return { | ||
startTime: startTime, | ||
startX: startX, | ||
startY: startY, | ||
endX: Math.floor(kind === "scrollBy" ? startX + x : x), | ||
endY: Math.floor(kind === "scrollBy" ? startY + y : y), | ||
method: getOriginalScrollMethodForKind("scrollTo", element).bind(element) | ||
}; | ||
} | ||
} | ||
/** | ||
* Patches the 'scroll' method on the Element prototype | ||
*/ | ||
function patchElementScroll() { | ||
Element.prototype.scroll = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scroll", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* Ensures that the given value is numeric | ||
* @param {number} value | ||
* @return {number} | ||
*/ | ||
function ensureNumeric(value) { | ||
if (value == null) | ||
return 0; | ||
else if (typeof value === "number") { | ||
return value; | ||
} | ||
else if (typeof value === "string") { | ||
return parseFloat(value); | ||
} | ||
else { | ||
return 0; | ||
} | ||
} | ||
/** | ||
* Patches the 'scrollBy' method on the Element prototype | ||
*/ | ||
function patchElementScrollBy() { | ||
Element.prototype.scrollBy = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scrollBy", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* Returns true if the given value is some ScrollToOptions | ||
* @param {number | ScrollToOptions} value | ||
* @return {value is ScrollToOptions} | ||
*/ | ||
function isScrollToOptions(value) { | ||
return value != null && typeof value === "object"; | ||
} | ||
/** | ||
* Patches the 'scrollTo' method on the Element prototype | ||
*/ | ||
function patchElementScrollTo() { | ||
Element.prototype.scrollTo = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scrollTo", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* Handles a scroll method | ||
* @param {Element|Window} element | ||
* @param {ScrollMethodName} kind | ||
* @param {number | ScrollToOptions} optionsOrX | ||
* @param {number} y | ||
*/ | ||
function handleScrollMethod(element, kind, optionsOrX, y) { | ||
onScrollWithOptions(getScrollToOptionsWithValidation(optionsOrX, y), element, kind); | ||
} | ||
/** | ||
* Invoked when a 'ScrollToOptions' dict is provided to 'scroll()' as the first argument | ||
* @param {ScrollToOptions} options | ||
* @param {Element|Window} element | ||
* @param {ScrollMethodName} kind | ||
*/ | ||
function onScrollWithOptions(options, element, kind) { | ||
var behavior = getScrollBehavior(element, options); | ||
// If the behavior is 'auto' apply instantaneous scrolling | ||
if (behavior == null || behavior === "auto") { | ||
getOriginalScrollMethodForKind(kind, element).call(element, options.left, options.top); | ||
} | ||
else { | ||
smoothScroll(getSmoothScrollOptions(element, options.left, options.top, kind)); | ||
} | ||
} | ||
/** | ||
* Normalizes the given scroll coordinates | ||
* @param {number?} x | ||
* @param {number?} y | ||
* @return {Required<Pick<ScrollToOptions, "top" | "left">>} | ||
*/ | ||
function normalizeScrollCoordinates(x, y) { | ||
return { | ||
left: ensureNumeric(x), | ||
top: ensureNumeric(y) | ||
}; | ||
} | ||
/** | ||
* Gets ScrollToOptions based on the given arguments. Will throw if validation fails | ||
* @param {number | ScrollToOptions} optionsOrX | ||
* @param {number} y | ||
* @return {Required<ScrollToOptions>} | ||
*/ | ||
function getScrollToOptionsWithValidation(optionsOrX, y) { | ||
// If only one argument is given, and it isn't an options object, throw a TypeError | ||
if (y === undefined && !isScrollToOptions(optionsOrX)) { | ||
throw new TypeError("Failed to execute 'scroll' on 'Element': parameter 1 ('options') is not an object."); | ||
} | ||
// Scroll based on the primitive values given as arguments | ||
if (!isScrollToOptions(optionsOrX)) { | ||
return __assign({}, normalizeScrollCoordinates(optionsOrX, y), { behavior: "auto" }); | ||
} | ||
// Scroll based on the received options object | ||
else { | ||
return __assign({}, normalizeScrollCoordinates(optionsOrX.left, optionsOrX.top), { behavior: optionsOrX.behavior == null ? "auto" : optionsOrX.behavior }); | ||
} | ||
} | ||
/** | ||
* Patches the 'scroll' method on the Window prototype | ||
*/ | ||
function patchWindowScroll() { | ||
window.scroll = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scroll", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* Patches the 'scroll' method on the Element prototype | ||
*/ | ||
function patchElementScroll() { | ||
Element.prototype.scroll = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scroll", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* Patches the 'scrollBy' method on the Window prototype | ||
*/ | ||
function patchWindowScrollBy() { | ||
window.scrollBy = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scrollBy", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* Patches the 'scrollBy' method on the Element prototype | ||
*/ | ||
function patchElementScrollBy() { | ||
Element.prototype.scrollBy = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scrollBy", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* Patches the 'scrollTo' method on the Window prototype | ||
*/ | ||
function patchWindowScrollTo() { | ||
window.scrollTo = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scrollTo", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* Patches the 'scrollTo' method on the Element prototype | ||
*/ | ||
function patchElementScrollTo() { | ||
Element.prototype.scrollTo = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scrollTo", optionsOrX, y); | ||
}; | ||
} | ||
// tslint:disable:no-any | ||
/** | ||
* Gets the parent of an element, taking into account DocumentFragments, ShadowRoots, as well as the root context (window) | ||
* @param {EventTarget} currentElement | ||
* @returns {EventTarget | null} | ||
*/ | ||
function getParent(currentElement) { | ||
if ("nodeType" in currentElement && currentElement.nodeType === 1) { | ||
return currentElement.parentNode; | ||
} | ||
if ("ShadowRoot" in window && (currentElement instanceof window.ShadowRoot)) { | ||
return currentElement.host; | ||
} | ||
else if (currentElement === document) { | ||
return window; | ||
} | ||
else if (currentElement instanceof Node) | ||
return currentElement.parentNode; | ||
return null; | ||
} | ||
/** | ||
* Patches the 'scroll' method on the Window prototype | ||
*/ | ||
function patchWindowScroll() { | ||
window.scroll = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scroll", optionsOrX, y); | ||
}; | ||
} | ||
var scrollingElement = document.scrollingElement != null ? document.scrollingElement : document.documentElement; | ||
/** | ||
* Returns true if the given overflow property represents a scrollable overflow value | ||
* @param {string | null} overflow | ||
* @return {boolean} | ||
*/ | ||
function canOverflow(overflow) { | ||
return overflow !== "visible" && overflow !== "clip"; | ||
} | ||
/** | ||
* Returns true if the given element is scrollable | ||
* @param {Element} element | ||
* @return {boolean} | ||
*/ | ||
function isScrollable(element) { | ||
if (element.clientHeight < element.scrollHeight || element.clientWidth < element.scrollWidth) { | ||
var style = getComputedStyle(element, null); | ||
return (canOverflow(style.overflowY) || | ||
canOverflow(style.overflowX)); | ||
} | ||
return false; | ||
} | ||
/** | ||
* Finds the nearest ancestor of an element that can scroll | ||
* @param {Element} target | ||
* @returns {Element|Window?} | ||
*/ | ||
function findNearestAncestorsWithScrollBehavior(target) { | ||
var currentElement = target; | ||
while (currentElement != null) { | ||
var behavior = getScrollBehavior(currentElement); | ||
if (behavior != null && (currentElement === scrollingElement || isScrollable(currentElement))) { | ||
return [currentElement, behavior]; | ||
} | ||
var parent_1 = getParent(currentElement); | ||
currentElement = parent_1; | ||
} | ||
// No such element could be found. Start over, but this time find the nearest ancestor that can simply scroll | ||
currentElement = target; | ||
while (currentElement != null) { | ||
if (currentElement === scrollingElement || isScrollable(currentElement)) { | ||
return [currentElement, "auto"]; | ||
} | ||
var parent_2 = getParent(currentElement); | ||
currentElement = parent_2; | ||
} | ||
// Default to the scrolling element | ||
return [scrollingElement, "auto"]; | ||
} | ||
/** | ||
* Patches the 'scrollBy' method on the Window prototype | ||
*/ | ||
function patchWindowScrollBy() { | ||
window.scrollBy = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scrollBy", optionsOrX, y); | ||
}; | ||
} | ||
// tslint:disable:no-any | ||
/** | ||
* Finds the nearest root from an element | ||
* @param {Element} target | ||
* @returns {Document|ShadowRoot} | ||
*/ | ||
function findNearestRoot(target) { | ||
var currentElement = target; | ||
while (currentElement != null) { | ||
if ("ShadowRoot" in window && (currentElement instanceof window.ShadowRoot)) { | ||
// Assume this is a ShadowRoot | ||
return currentElement; | ||
} | ||
var parent_1 = getParent(currentElement); | ||
if (parent_1 === currentElement) { | ||
return document; | ||
} | ||
currentElement = parent_1; | ||
} | ||
return document; | ||
} | ||
/** | ||
* Patches the 'scrollTo' method on the Window prototype | ||
*/ | ||
function patchWindowScrollTo() { | ||
window.scrollTo = function (optionsOrX, y) { | ||
handleScrollMethod(this, "scrollTo", optionsOrX, y); | ||
}; | ||
} | ||
/** | ||
* A Regular expression that matches id's of the form "#[digit]" | ||
* @type {RegExp} | ||
*/ | ||
var ID_WITH_LEADING_DIGIT_REGEXP = /^#\d/; | ||
/** | ||
* Catches anchor navigation to IDs within the same root and ensures that they can be smooth-scrolled | ||
* if the scroll behavior is smooth in the first rooter within that context | ||
*/ | ||
function catchNavigation() { | ||
// Listen for 'click' events globally | ||
window.addEventListener("click", function (e) { | ||
// Only work with trusted events on HTMLAnchorElements | ||
if (!e.isTrusted || !(e.target instanceof HTMLAnchorElement)) | ||
return; | ||
var hrefAttributeValue = e.target.getAttribute("href"); | ||
// Only work with HTMLAnchorElements that navigates to a specific ID | ||
if (hrefAttributeValue == null || !hrefAttributeValue.startsWith("#")) | ||
return; | ||
// Find the nearest root, whether it be a ShadowRoot or the document itself | ||
var root = findNearestRoot(e.target); | ||
// Attempt to match the selector from that root. querySelector' doesn't support IDs that start with a digit, so work around that limitation | ||
var elementMatch = hrefAttributeValue.match(ID_WITH_LEADING_DIGIT_REGEXP) != null | ||
? root.getElementById(hrefAttributeValue.slice(1)) | ||
: root.querySelector(hrefAttributeValue); | ||
// If no selector could be found, don't proceed | ||
if (elementMatch == null) | ||
return; | ||
// Find the nearest ancestor that can be scrolled | ||
var _a = __read(findNearestAncestorsWithScrollBehavior(elementMatch), 2), ancestorWithScrollBehavior = _a[0], behavior = _a[1]; | ||
// If the behavior isn't smooth, don't proceed | ||
if (behavior !== "smooth") | ||
return; | ||
// Otherwise, first prevent the default action. | ||
e.preventDefault(); | ||
// Now, scroll to the element with that ID | ||
ancestorWithScrollBehavior.scrollTo({ | ||
behavior: behavior, | ||
top: elementMatch.offsetTop, | ||
left: elementMatch.offsetLeft | ||
}); | ||
}); | ||
} | ||
// tslint:disable:no-any | ||
/** | ||
* Gets the parent of an element, taking into account DocumentFragments, ShadowRoots, as well as the root context (window) | ||
* @param {EventTarget} currentElement | ||
* @returns {EventTarget | null} | ||
*/ | ||
function getParent(currentElement) { | ||
if ("nodeType" in currentElement && currentElement.nodeType === 1) { | ||
return currentElement.parentNode; | ||
} | ||
if ("ShadowRoot" in window && | ||
currentElement instanceof window.ShadowRoot) { | ||
return currentElement.host; | ||
} | ||
else if (currentElement === document) { | ||
return window; | ||
} | ||
else if (currentElement instanceof Node) | ||
return currentElement.parentNode; | ||
return null; | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL_INTO_VIEW = Element.prototype.scrollIntoView; | ||
var scrollingElement = document.scrollingElement != null | ||
? document.scrollingElement | ||
: document.documentElement; | ||
/** | ||
* Returns true if the given overflow property represents a scrollable overflow value | ||
* @param {string | null} overflow | ||
* @return {boolean} | ||
*/ | ||
function canOverflow(overflow) { | ||
return overflow !== "visible" && overflow !== "clip"; | ||
} | ||
/** | ||
* Returns true if the given element is scrollable | ||
* @param {Element} element | ||
* @return {boolean} | ||
*/ | ||
function isScrollable(element) { | ||
if (element.clientHeight < element.scrollHeight || | ||
element.clientWidth < element.scrollWidth) { | ||
var style = getComputedStyle(element, null); | ||
return canOverflow(style.overflowY) || canOverflow(style.overflowX); | ||
} | ||
return false; | ||
} | ||
/** | ||
* Finds the nearest ancestor of an element that can scroll | ||
* @param {Element} target | ||
* @returns {Element|Window?} | ||
*/ | ||
function findNearestAncestorsWithScrollBehavior(target) { | ||
var currentElement = target; | ||
while (currentElement != null) { | ||
var behavior = getScrollBehavior(currentElement); | ||
if (behavior != null && | ||
(currentElement === scrollingElement || isScrollable(currentElement))) { | ||
return [currentElement, behavior]; | ||
} | ||
var parent_1 = getParent(currentElement); | ||
currentElement = parent_1; | ||
} | ||
// No such element could be found. Start over, but this time find the nearest ancestor that can simply scroll | ||
currentElement = target; | ||
while (currentElement != null) { | ||
if (currentElement === scrollingElement || isScrollable(currentElement)) { | ||
return [currentElement, "auto"]; | ||
} | ||
var parent_2 = getParent(currentElement); | ||
currentElement = parent_2; | ||
} | ||
// Default to the scrolling element | ||
return [scrollingElement, "auto"]; | ||
} | ||
/** | ||
* The majority of this file is based on https://github.com/stipsan/compute-scroll-into-view (MIT license), | ||
* but has been rewritten to accept a scroller as an argument. | ||
*/ | ||
/** | ||
* Find out which edge to align against when logical scroll position is "nearest" | ||
* Interesting fact: "nearest" works similarly to "if-needed", if the element is fully visible it will not scroll it | ||
* | ||
* Legends: | ||
* ┌────────┐ ┏ ━ ━ ━ ┓ | ||
* │ target │ frame | ||
* └────────┘ ┗ ━ ━ ━ ┛ | ||
*/ | ||
function alignNearest(scrollingEdgeStart, scrollingEdgeEnd, scrollingSize, scrollingBorderStart, scrollingBorderEnd, elementEdgeStart, elementEdgeEnd, elementSize) { | ||
/** | ||
* If element edge A and element edge B are both outside scrolling box edge A and scrolling box edge B | ||
* | ||
* ┌──┐ | ||
* ┏━│━━│━┓ | ||
* │ │ | ||
* ┃ │ │ ┃ do nothing | ||
* │ │ | ||
* ┗━│━━│━┛ | ||
* └──┘ | ||
* | ||
* If element edge C and element edge D are both outside scrolling box edge C and scrolling box edge D | ||
* | ||
* ┏ ━ ━ ━ ━ ┓ | ||
* ┌───────────┐ | ||
* │┃ ┃│ do nothing | ||
* └───────────┘ | ||
* ┗ ━ ━ ━ ━ ┛ | ||
*/ | ||
if ((elementEdgeStart < scrollingEdgeStart && | ||
elementEdgeEnd > scrollingEdgeEnd) || | ||
(elementEdgeStart > scrollingEdgeStart && elementEdgeEnd < scrollingEdgeEnd)) { | ||
return 0; | ||
} | ||
/** | ||
* If element edge A is outside scrolling box edge A and element height is less than scrolling box height | ||
* | ||
* ┌──┐ | ||
* ┏━│━━│━┓ ┏━┌━━┐━┓ | ||
* └──┘ │ │ | ||
* from ┃ ┃ to ┃ └──┘ ┃ | ||
* | ||
* ┗━ ━━ ━┛ ┗━ ━━ ━┛ | ||
* | ||
* If element edge B is outside scrolling box edge B and element height is greater than scrolling box height | ||
* | ||
* ┏━ ━━ ━┓ ┏━┌━━┐━┓ | ||
* │ │ | ||
* from ┃ ┌──┐ ┃ to ┃ │ │ ┃ | ||
* │ │ │ │ | ||
* ┗━│━━│━┛ ┗━│━━│━┛ | ||
* │ │ └──┘ | ||
* │ │ | ||
* └──┘ | ||
* | ||
* If element edge C is outside scrolling box edge C and element width is less than scrolling box width | ||
* | ||
* from to | ||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓ | ||
* ┌───┐ ┌───┐ | ||
* │ ┃ │ ┃ ┃ │ ┃ | ||
* └───┘ └───┘ | ||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛ | ||
* | ||
* If element edge D is outside scrolling box edge D and element width is greater than scrolling box width | ||
* | ||
* from to | ||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓ | ||
* ┌───────────┐ ┌───────────┐ | ||
* ┃ │ ┃ │ ┃ ┃ │ | ||
* └───────────┘ └───────────┘ | ||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛ | ||
*/ | ||
if ((elementEdgeStart <= scrollingEdgeStart && elementSize <= scrollingSize) || | ||
(elementEdgeEnd >= scrollingEdgeEnd && elementSize >= scrollingSize)) { | ||
return elementEdgeStart - scrollingEdgeStart - scrollingBorderStart; | ||
} | ||
/** | ||
* If element edge B is outside scrolling box edge B and element height is less than scrolling box height | ||
* | ||
* ┏━ ━━ ━┓ ┏━ ━━ ━┓ | ||
* | ||
* from ┃ ┃ to ┃ ┌──┐ ┃ | ||
* ┌──┐ │ │ | ||
* ┗━│━━│━┛ ┗━└━━┘━┛ | ||
* └──┘ | ||
* | ||
* If element edge A is outside scrolling box edge A and element height is greater than scrolling box height | ||
* | ||
* ┌──┐ | ||
* │ │ | ||
* │ │ ┌──┐ | ||
* ┏━│━━│━┓ ┏━│━━│━┓ | ||
* │ │ │ │ | ||
* from ┃ └──┘ ┃ to ┃ │ │ ┃ | ||
* │ │ | ||
* ┗━ ━━ ━┛ ┗━└━━┘━┛ | ||
* | ||
* If element edge C is outside scrolling box edge C and element width is greater than scrolling box width | ||
* | ||
* from to | ||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓ | ||
* ┌───────────┐ ┌───────────┐ | ||
* │ ┃ │ ┃ │ ┃ ┃ | ||
* └───────────┘ └───────────┘ | ||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛ | ||
* | ||
* If element edge D is outside scrolling box edge D and element width is less than scrolling box width | ||
* | ||
* from to | ||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓ | ||
* ┌───┐ ┌───┐ | ||
* ┃ │ ┃ │ ┃ │ ┃ | ||
* └───┘ └───┘ | ||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛ | ||
* | ||
*/ | ||
if ((elementEdgeEnd > scrollingEdgeEnd && elementSize < scrollingSize) || | ||
(elementEdgeStart < scrollingEdgeStart && elementSize > scrollingSize)) { | ||
return elementEdgeEnd - scrollingEdgeEnd + scrollingBorderEnd; | ||
} | ||
return 0; | ||
} | ||
function computeScrollIntoView(target, scroller, options) { | ||
var block = options.block, inline = options.inline; | ||
// Used to handle the top most element that can be scrolled | ||
var scrollingElement = document.scrollingElement || document.documentElement; | ||
// Support pinch-zooming properly, making sure elements scroll into the visual viewport | ||
// Browsers that don't support visualViewport will report the layout viewport dimensions on document.documentElement.clientWidth/Height | ||
// and viewport dimensions on window.innerWidth/Height | ||
// https://www.quirksmode.org/mobile/viewports2.html | ||
// https://bokand.github.io/viewport/index.html | ||
var viewportWidth = window.visualViewport != null | ||
? visualViewport.width | ||
: innerWidth; | ||
var viewportHeight = window.visualViewport != null | ||
? visualViewport.height | ||
: innerHeight; | ||
var viewportX = window.scrollX != null ? window.scrollX : window.pageXOffset; | ||
var viewportY = window.scrollY != null ? window.scrollY : window.pageYOffset; | ||
var _a = target.getBoundingClientRect(), targetHeight = _a.height, targetWidth = _a.width, targetTop = _a.top, targetRight = _a.right, targetBottom = _a.bottom, targetLeft = _a.left; | ||
// These values mutate as we loop through and generate scroll coordinates | ||
var targetBlock = block === "start" || block === "nearest" | ||
? targetTop | ||
: block === "end" | ||
? targetBottom | ||
: targetTop + targetHeight / 2; // block === 'center | ||
var targetInline = inline === "center" | ||
? targetLeft + targetWidth / 2 | ||
: inline === "end" | ||
? targetRight | ||
: targetLeft; // inline === 'start || inline === 'nearest | ||
var _b = scroller.getBoundingClientRect(), height = _b.height, width = _b.width, top = _b.top, right = _b.right, bottom = _b.bottom, left = _b.left; | ||
var frameStyle = getComputedStyle(scroller); | ||
var borderLeft = parseInt(frameStyle.borderLeftWidth, 10); | ||
var borderTop = parseInt(frameStyle.borderTopWidth, 10); | ||
var borderRight = parseInt(frameStyle.borderRightWidth, 10); | ||
var borderBottom = parseInt(frameStyle.borderBottomWidth, 10); | ||
var blockScroll = 0; | ||
var inlineScroll = 0; | ||
// The property existance checks for offset[Width|Height] is because only HTMLElement objects have them, but any Element might pass by here | ||
// @TODO find out if the "as HTMLElement" overrides can be dropped | ||
var scrollbarWidth = "offsetWidth" in scroller | ||
? scroller.offsetWidth - | ||
scroller.clientWidth - | ||
borderLeft - | ||
borderRight | ||
: 0; | ||
var scrollbarHeight = "offsetHeight" in scroller | ||
? scroller.offsetHeight - | ||
scroller.clientHeight - | ||
borderTop - | ||
borderBottom | ||
: 0; | ||
if (scrollingElement === scroller) { | ||
// Handle viewport logic (document.documentElement or document.body) | ||
if (block === "start") { | ||
blockScroll = targetBlock; | ||
} | ||
else if (block === "end") { | ||
blockScroll = targetBlock - viewportHeight; | ||
} | ||
else if (block === "nearest") { | ||
blockScroll = alignNearest(viewportY, viewportY + viewportHeight, viewportHeight, borderTop, borderBottom, viewportY + targetBlock, viewportY + targetBlock + targetHeight, targetHeight); | ||
} | ||
else { | ||
// block === 'center' is the default | ||
blockScroll = targetBlock - viewportHeight / 2; | ||
} | ||
if (inline === "start") { | ||
inlineScroll = targetInline; | ||
} | ||
else if (inline === "center") { | ||
inlineScroll = targetInline - viewportWidth / 2; | ||
} | ||
else if (inline === "end") { | ||
inlineScroll = targetInline - viewportWidth; | ||
} | ||
else { | ||
// inline === 'nearest' is the default | ||
inlineScroll = alignNearest(viewportX, viewportX + viewportWidth, viewportWidth, borderLeft, borderRight, viewportX + targetInline, viewportX + targetInline + targetWidth, targetWidth); | ||
} | ||
// Apply scroll position offsets and ensure they are within bounds | ||
// @TODO add more test cases to cover this 100% | ||
blockScroll = Math.max(0, blockScroll + viewportY); | ||
inlineScroll = Math.max(0, inlineScroll + viewportX); | ||
} | ||
else { | ||
// Handle each scrolling frame that might exist between the target and the viewport | ||
if (block === "start") { | ||
blockScroll = targetBlock - top - borderTop; | ||
} | ||
else if (block === "end") { | ||
blockScroll = targetBlock - bottom + borderBottom + scrollbarHeight; | ||
} | ||
else if (block === "nearest") { | ||
blockScroll = alignNearest(top, bottom, height, borderTop, borderBottom + scrollbarHeight, targetBlock, targetBlock + targetHeight, targetHeight); | ||
} | ||
else { | ||
// block === 'center' is the default | ||
blockScroll = targetBlock - (top + height / 2) + scrollbarHeight / 2; | ||
} | ||
if (inline === "start") { | ||
inlineScroll = targetInline - left - borderLeft; | ||
} | ||
else if (inline === "center") { | ||
inlineScroll = targetInline - (left + width / 2) + scrollbarWidth / 2; | ||
} | ||
else if (inline === "end") { | ||
inlineScroll = targetInline - right + borderRight + scrollbarWidth; | ||
} | ||
else { | ||
// inline === 'nearest' is the default | ||
inlineScroll = alignNearest(left, right, width, borderLeft, borderRight + scrollbarWidth, targetInline, targetInline + targetWidth, targetWidth); | ||
} | ||
var scrollLeft = scroller.scrollLeft, scrollTop = scroller.scrollTop; | ||
// Ensure scroll coordinates are not out of bounds while applying scroll offsets | ||
blockScroll = Math.max(0, Math.min(scrollTop + blockScroll, scroller.scrollHeight - height + scrollbarHeight)); | ||
inlineScroll = Math.max(0, Math.min(scrollLeft + inlineScroll, scroller.scrollWidth - width + scrollbarWidth)); | ||
} | ||
return { | ||
top: blockScroll, | ||
left: inlineScroll | ||
}; | ||
} | ||
// tslint:disable:no-any | ||
/** | ||
* Finds the nearest root from an element | ||
* @param {Element} target | ||
* @returns {Document|ShadowRoot} | ||
*/ | ||
function findNearestRoot(target) { | ||
var currentElement = target; | ||
while (currentElement != null) { | ||
if ("ShadowRoot" in window && | ||
currentElement instanceof window.ShadowRoot) { | ||
// Assume this is a ShadowRoot | ||
return currentElement; | ||
} | ||
var parent_1 = getParent(currentElement); | ||
if (parent_1 === currentElement) { | ||
return document; | ||
} | ||
currentElement = parent_1; | ||
} | ||
return document; | ||
} | ||
/** | ||
* Patches the 'scrollIntoView' method on the Element prototype | ||
*/ | ||
function patchElementScrollIntoView() { | ||
Element.prototype.scrollIntoView = function (arg) { | ||
var normalizedOptions = arg == null || arg === true | ||
? { | ||
block: "start", | ||
inline: "nearest" | ||
} | ||
: arg === false | ||
? { | ||
block: "end", | ||
inline: "nearest" | ||
} | ||
: arg; | ||
// Find the nearest ancestor that can be scrolled | ||
var _a = __read(findNearestAncestorsWithScrollBehavior(this), 2), ancestorWithScroll = _a[0], ancestorWithScrollBehavior = _a[1]; | ||
var behavior = normalizedOptions.behavior != null | ||
? normalizedOptions.behavior | ||
: ancestorWithScrollBehavior; | ||
// If the behavior isn't smooth, simply invoke the original implementation and do no more | ||
if (behavior !== "smooth") { | ||
// Assert that 'scrollIntoView' is actually defined | ||
if (ELEMENT_ORIGINAL_SCROLL_INTO_VIEW != null) { | ||
ELEMENT_ORIGINAL_SCROLL_INTO_VIEW.call(this, normalizedOptions); | ||
} | ||
// Otherwise, invoke 'scrollTo' instead and provide the scroll coordinates | ||
else { | ||
var _b = computeScrollIntoView(this, ancestorWithScroll, normalizedOptions), top_1 = _b.top, left = _b.left; | ||
getOriginalScrollMethodForKind("scrollTo", this).call(this, left, top_1); | ||
} | ||
return; | ||
} | ||
ancestorWithScroll.scrollTo(__assign({ behavior: behavior }, computeScrollIntoView(this, ancestorWithScroll, normalizedOptions))); | ||
}; | ||
} | ||
/** | ||
* A Regular expression that matches id's of the form "#[digit]" | ||
* @type {RegExp} | ||
*/ | ||
var ID_WITH_LEADING_DIGIT_REGEXP = /^#\d/; | ||
/** | ||
* Catches anchor navigation to IDs within the same root and ensures that they can be smooth-scrolled | ||
* if the scroll behavior is smooth in the first rooter within that context | ||
*/ | ||
function catchNavigation() { | ||
// Listen for 'click' events globally | ||
window.addEventListener("click", function (e) { | ||
// Only work with trusted events on HTMLAnchorElements | ||
if (!e.isTrusted || !(e.target instanceof HTMLAnchorElement)) | ||
return; | ||
var hrefAttributeValue = e.target.getAttribute("href"); | ||
// Only work with HTMLAnchorElements that navigates to a specific ID | ||
if (hrefAttributeValue == null || !hrefAttributeValue.startsWith("#")) { | ||
return; | ||
} | ||
// Find the nearest root, whether it be a ShadowRoot or the document itself | ||
var root = findNearestRoot(e.target); | ||
// Attempt to match the selector from that root. querySelector' doesn't support IDs that start with a digit, so work around that limitation | ||
var elementMatch = hrefAttributeValue.match(ID_WITH_LEADING_DIGIT_REGEXP) != null | ||
? root.getElementById(hrefAttributeValue.slice(1)) | ||
: root.querySelector(hrefAttributeValue); | ||
// If no selector could be found, don't proceed | ||
if (elementMatch == null) | ||
return; | ||
// Find the nearest ancestor that can be scrolled | ||
var _a = __read(findNearestAncestorsWithScrollBehavior(elementMatch), 2), ancestorWithScrollBehavior = _a[0], behavior = _a[1]; | ||
// If the behavior isn't smooth, don't proceed | ||
if (behavior !== "smooth") | ||
return; | ||
// Otherwise, first prevent the default action. | ||
e.preventDefault(); | ||
// Now, scroll to the element with that ID | ||
ancestorWithScrollBehavior.scrollTo({ | ||
behavior: behavior, | ||
top: elementMatch.offsetTop, | ||
left: elementMatch.offsetLeft | ||
}); | ||
}); | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL_TOP_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollTop").set; | ||
var ELEMENT_ORIGINAL_SCROLL_INTO_VIEW = Element.prototype.scrollIntoView; | ||
/** | ||
* Patches the 'scrollTop' property descriptor on the Element prototype | ||
*/ | ||
function patchElementScrollTop() { | ||
Object.defineProperty(Element.prototype, "scrollTop", { | ||
set: function (scrollTop) { | ||
if (this.__adjustingScrollPosition) { | ||
return ELEMENT_ORIGINAL_SCROLL_TOP_SET_DESCRIPTOR.call(this, scrollTop); | ||
} | ||
handleScrollMethod(this, "scrollTo", this.scrollLeft, scrollTop); | ||
return scrollTop; | ||
} | ||
}); | ||
} | ||
/** | ||
* The majority of this file is based on https://github.com/stipsan/compute-scroll-into-view (MIT license), | ||
* but has been rewritten to accept a scroller as an argument. | ||
*/ | ||
/** | ||
* Find out which edge to align against when logical scroll position is "nearest" | ||
* Interesting fact: "nearest" works similarly to "if-needed", if the element is fully visible it will not scroll it | ||
* | ||
* Legends: | ||
* ┌────────┐ ┏ ━ ━ ━ ┓ | ||
* │ target │ frame | ||
* └────────┘ ┗ ━ ━ ━ ┛ | ||
*/ | ||
function alignNearest(scrollingEdgeStart, scrollingEdgeEnd, scrollingSize, scrollingBorderStart, scrollingBorderEnd, elementEdgeStart, elementEdgeEnd, elementSize) { | ||
/** | ||
* If element edge A and element edge B are both outside scrolling box edge A and scrolling box edge B | ||
* | ||
* ┌──┐ | ||
* ┏━│━━│━┓ | ||
* │ │ | ||
* ┃ │ │ ┃ do nothing | ||
* │ │ | ||
* ┗━│━━│━┛ | ||
* └──┘ | ||
* | ||
* If element edge C and element edge D are both outside scrolling box edge C and scrolling box edge D | ||
* | ||
* ┏ ━ ━ ━ ━ ┓ | ||
* ┌───────────┐ | ||
* │┃ ┃│ do nothing | ||
* └───────────┘ | ||
* ┗ ━ ━ ━ ━ ┛ | ||
*/ | ||
if ((elementEdgeStart < scrollingEdgeStart && | ||
elementEdgeEnd > scrollingEdgeEnd) || | ||
(elementEdgeStart > scrollingEdgeStart && elementEdgeEnd < scrollingEdgeEnd)) { | ||
return 0; | ||
} | ||
/** | ||
* If element edge A is outside scrolling box edge A and element height is less than scrolling box height | ||
* | ||
* ┌──┐ | ||
* ┏━│━━│━┓ ┏━┌━━┐━┓ | ||
* └──┘ │ │ | ||
* from ┃ ┃ to ┃ └──┘ ┃ | ||
* | ||
* ┗━ ━━ ━┛ ┗━ ━━ ━┛ | ||
* | ||
* If element edge B is outside scrolling box edge B and element height is greater than scrolling box height | ||
* | ||
* ┏━ ━━ ━┓ ┏━┌━━┐━┓ | ||
* │ │ | ||
* from ┃ ┌──┐ ┃ to ┃ │ │ ┃ | ||
* │ │ │ │ | ||
* ┗━│━━│━┛ ┗━│━━│━┛ | ||
* │ │ └──┘ | ||
* │ │ | ||
* └──┘ | ||
* | ||
* If element edge C is outside scrolling box edge C and element width is less than scrolling box width | ||
* | ||
* from to | ||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓ | ||
* ┌───┐ ┌───┐ | ||
* │ ┃ │ ┃ ┃ │ ┃ | ||
* └───┘ └───┘ | ||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛ | ||
* | ||
* If element edge D is outside scrolling box edge D and element width is greater than scrolling box width | ||
* | ||
* from to | ||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓ | ||
* ┌───────────┐ ┌───────────┐ | ||
* ┃ │ ┃ │ ┃ ┃ │ | ||
* └───────────┘ └───────────┘ | ||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛ | ||
*/ | ||
if ((elementEdgeStart <= scrollingEdgeStart && elementSize <= scrollingSize) || | ||
(elementEdgeEnd >= scrollingEdgeEnd && elementSize >= scrollingSize)) { | ||
return elementEdgeStart - scrollingEdgeStart - scrollingBorderStart; | ||
} | ||
/** | ||
* If element edge B is outside scrolling box edge B and element height is less than scrolling box height | ||
* | ||
* ┏━ ━━ ━┓ ┏━ ━━ ━┓ | ||
* | ||
* from ┃ ┃ to ┃ ┌──┐ ┃ | ||
* ┌──┐ │ │ | ||
* ┗━│━━│━┛ ┗━└━━┘━┛ | ||
* └──┘ | ||
* | ||
* If element edge A is outside scrolling box edge A and element height is greater than scrolling box height | ||
* | ||
* ┌──┐ | ||
* │ │ | ||
* │ │ ┌──┐ | ||
* ┏━│━━│━┓ ┏━│━━│━┓ | ||
* │ │ │ │ | ||
* from ┃ └──┘ ┃ to ┃ │ │ ┃ | ||
* │ │ | ||
* ┗━ ━━ ━┛ ┗━└━━┘━┛ | ||
* | ||
* If element edge C is outside scrolling box edge C and element width is greater than scrolling box width | ||
* | ||
* from to | ||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓ | ||
* ┌───────────┐ ┌───────────┐ | ||
* │ ┃ │ ┃ │ ┃ ┃ | ||
* └───────────┘ └───────────┘ | ||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛ | ||
* | ||
* If element edge D is outside scrolling box edge D and element width is less than scrolling box width | ||
* | ||
* from to | ||
* ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓ | ||
* ┌───┐ ┌───┐ | ||
* ┃ │ ┃ │ ┃ │ ┃ | ||
* └───┘ └───┘ | ||
* ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛ | ||
* | ||
*/ | ||
if ((elementEdgeEnd > scrollingEdgeEnd && elementSize < scrollingSize) || | ||
(elementEdgeStart < scrollingEdgeStart && elementSize > scrollingSize)) { | ||
return elementEdgeEnd - scrollingEdgeEnd + scrollingBorderEnd; | ||
} | ||
return 0; | ||
} | ||
function computeScrollIntoView(target, scroller, options) { | ||
var block = options.block, inline = options.inline; | ||
// Used to handle the top most element that can be scrolled | ||
var scrollingElement = document.scrollingElement || document.documentElement; | ||
// Support pinch-zooming properly, making sure elements scroll into the visual viewport | ||
// Browsers that don't support visualViewport will report the layout viewport dimensions on document.documentElement.clientWidth/Height | ||
// and viewport dimensions on window.innerWidth/Height | ||
// https://www.quirksmode.org/mobile/viewports2.html | ||
// https://bokand.github.io/viewport/index.html | ||
var viewportWidth = window.visualViewport != null ? visualViewport.width : innerWidth; | ||
var viewportHeight = window.visualViewport != null ? visualViewport.height : innerHeight; | ||
var viewportX = window.scrollX != null ? window.scrollX : window.pageXOffset; | ||
var viewportY = window.scrollY != null ? window.scrollY : window.pageYOffset; | ||
var _a = target.getBoundingClientRect(), targetHeight = _a.height, targetWidth = _a.width, targetTop = _a.top, targetRight = _a.right, targetBottom = _a.bottom, targetLeft = _a.left; | ||
// These values mutate as we loop through and generate scroll coordinates | ||
var targetBlock = block === "start" || block === "nearest" | ||
? targetTop | ||
: block === "end" | ||
? targetBottom | ||
: targetTop + targetHeight / 2; // block === 'center | ||
var targetInline = inline === "center" | ||
? targetLeft + targetWidth / 2 | ||
: inline === "end" | ||
? targetRight | ||
: targetLeft; // inline === 'start || inline === 'nearest | ||
var _b = scroller.getBoundingClientRect(), height = _b.height, width = _b.width, top = _b.top, right = _b.right, bottom = _b.bottom, left = _b.left; | ||
var frameStyle = getComputedStyle(scroller); | ||
var borderLeft = parseInt(frameStyle.borderLeftWidth, 10); | ||
var borderTop = parseInt(frameStyle.borderTopWidth, 10); | ||
var borderRight = parseInt(frameStyle.borderRightWidth, 10); | ||
var borderBottom = parseInt(frameStyle.borderBottomWidth, 10); | ||
var blockScroll = 0; | ||
var inlineScroll = 0; | ||
// The property existance checks for offset[Width|Height] is because only HTMLElement objects have them, but any Element might pass by here | ||
// @TODO find out if the "as HTMLElement" overrides can be dropped | ||
var scrollbarWidth = "offsetWidth" in scroller | ||
? scroller.offsetWidth - | ||
scroller.clientWidth - | ||
borderLeft - | ||
borderRight | ||
: 0; | ||
var scrollbarHeight = "offsetHeight" in scroller | ||
? scroller.offsetHeight - | ||
scroller.clientHeight - | ||
borderTop - | ||
borderBottom | ||
: 0; | ||
if (scrollingElement === scroller) { | ||
// Handle viewport logic (document.documentElement or document.body) | ||
if (block === "start") { | ||
blockScroll = targetBlock; | ||
} | ||
else if (block === "end") { | ||
blockScroll = targetBlock - viewportHeight; | ||
} | ||
else if (block === "nearest") { | ||
blockScroll = alignNearest(viewportY, viewportY + viewportHeight, viewportHeight, borderTop, borderBottom, viewportY + targetBlock, viewportY + targetBlock + targetHeight, targetHeight); | ||
} | ||
else { | ||
// block === 'center' is the default | ||
blockScroll = targetBlock - viewportHeight / 2; | ||
} | ||
if (inline === "start") { | ||
inlineScroll = targetInline; | ||
} | ||
else if (inline === "center") { | ||
inlineScroll = targetInline - viewportWidth / 2; | ||
} | ||
else if (inline === "end") { | ||
inlineScroll = targetInline - viewportWidth; | ||
} | ||
else { | ||
// inline === 'nearest' is the default | ||
inlineScroll = alignNearest(viewportX, viewportX + viewportWidth, viewportWidth, borderLeft, borderRight, viewportX + targetInline, viewportX + targetInline + targetWidth, targetWidth); | ||
} | ||
// Apply scroll position offsets and ensure they are within bounds | ||
// @TODO add more test cases to cover this 100% | ||
blockScroll = Math.max(0, blockScroll + viewportY); | ||
inlineScroll = Math.max(0, inlineScroll + viewportX); | ||
} | ||
else { | ||
// Handle each scrolling frame that might exist between the target and the viewport | ||
if (block === "start") { | ||
blockScroll = targetBlock - top - borderTop; | ||
} | ||
else if (block === "end") { | ||
blockScroll = targetBlock - bottom + borderBottom + scrollbarHeight; | ||
} | ||
else if (block === "nearest") { | ||
blockScroll = alignNearest(top, bottom, height, borderTop, borderBottom + scrollbarHeight, targetBlock, targetBlock + targetHeight, targetHeight); | ||
} | ||
else { | ||
// block === 'center' is the default | ||
blockScroll = targetBlock - (top + height / 2) + scrollbarHeight / 2; | ||
} | ||
if (inline === "start") { | ||
inlineScroll = targetInline - left - borderLeft; | ||
} | ||
else if (inline === "center") { | ||
inlineScroll = targetInline - (left + width / 2) + scrollbarWidth / 2; | ||
} | ||
else if (inline === "end") { | ||
inlineScroll = targetInline - right + borderRight + scrollbarWidth; | ||
} | ||
else { | ||
// inline === 'nearest' is the default | ||
inlineScroll = alignNearest(left, right, width, borderLeft, borderRight + scrollbarWidth, targetInline, targetInline + targetWidth, targetWidth); | ||
} | ||
var scrollLeft = scroller.scrollLeft, scrollTop = scroller.scrollTop; | ||
// Ensure scroll coordinates are not out of bounds while applying scroll offsets | ||
blockScroll = Math.max(0, Math.min(scrollTop + blockScroll, scroller.scrollHeight - height + scrollbarHeight)); | ||
inlineScroll = Math.max(0, Math.min(scrollLeft + inlineScroll, scroller.scrollWidth - width + scrollbarWidth)); | ||
} | ||
return { | ||
top: blockScroll, | ||
left: inlineScroll | ||
}; | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL_LEFT_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollLeft").set; | ||
/** | ||
* Patches the 'scrollIntoView' method on the Element prototype | ||
*/ | ||
function patchElementScrollIntoView() { | ||
Element.prototype.scrollIntoView = function (arg) { | ||
var normalizedOptions = arg == null || arg === true | ||
? { | ||
block: "start", | ||
inline: "nearest" | ||
} | ||
: arg === false | ||
? { | ||
block: "end", | ||
inline: "nearest" | ||
} | ||
: arg; | ||
// Find the nearest ancestor that can be scrolled | ||
var _a = __read(findNearestAncestorsWithScrollBehavior(this), 2), ancestorWithScroll = _a[0], ancestorWithScrollBehavior = _a[1]; | ||
var behavior = normalizedOptions.behavior != null | ||
? normalizedOptions.behavior | ||
: ancestorWithScrollBehavior; | ||
// If the behavior isn't smooth, simply invoke the original implementation and do no more | ||
if (behavior !== "smooth") { | ||
// Assert that 'scrollIntoView' is actually defined | ||
if (ELEMENT_ORIGINAL_SCROLL_INTO_VIEW != null) { | ||
ELEMENT_ORIGINAL_SCROLL_INTO_VIEW.call(this, normalizedOptions); | ||
} | ||
// Otherwise, invoke 'scrollTo' instead and provide the scroll coordinates | ||
else { | ||
var _b = computeScrollIntoView(this, ancestorWithScroll, normalizedOptions), top_1 = _b.top, left = _b.left; | ||
getOriginalScrollMethodForKind("scrollTo", this).call(this, left, top_1); | ||
} | ||
return; | ||
} | ||
ancestorWithScroll.scrollTo(__assign({ behavior: behavior }, computeScrollIntoView(this, ancestorWithScroll, normalizedOptions))); | ||
}; | ||
} | ||
/** | ||
* Patches the 'scrollLeft' property descriptor on the Element prototype | ||
*/ | ||
function patchElementScrollLeft() { | ||
Object.defineProperty(Element.prototype, "scrollLeft", { | ||
set: function (scrollLeft) { | ||
if (this.__adjustingScrollPosition) { | ||
return ELEMENT_ORIGINAL_SCROLL_LEFT_SET_DESCRIPTOR.call(this, scrollLeft); | ||
} | ||
handleScrollMethod(this, "scrollTo", scrollLeft, this.scrollTop); | ||
return scrollLeft; | ||
} | ||
}); | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL_TOP_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollTop").set; | ||
/** | ||
* Applies the polyfill | ||
*/ | ||
function patch() { | ||
// Element.prototype methods | ||
patchElementScroll(); | ||
patchElementScrollBy(); | ||
patchElementScrollTo(); | ||
patchElementScrollIntoView(); | ||
// Element.prototype descriptors | ||
patchElementScrollLeft(); | ||
patchElementScrollTop(); | ||
// window methods | ||
patchWindowScroll(); | ||
patchWindowScrollBy(); | ||
patchWindowScrollTo(); | ||
// Navigation | ||
catchNavigation(); | ||
} | ||
/** | ||
* Patches the 'scrollTop' property descriptor on the Element prototype | ||
*/ | ||
function patchElementScrollTop() { | ||
Object.defineProperty(Element.prototype, "scrollTop", { | ||
set: function (scrollTop) { | ||
if (this.__adjustingScrollPosition) { | ||
return ELEMENT_ORIGINAL_SCROLL_TOP_SET_DESCRIPTOR.call(this, scrollTop); | ||
} | ||
handleScrollMethod(this, "scrollTo", this.scrollLeft, scrollTop); | ||
return scrollTop; | ||
} | ||
}); | ||
} | ||
/** | ||
* Is true if the browser natively supports the Element.prototype.[scroll|scrollTo|scrollBy|scrollIntoView] methods | ||
* @type {boolean} | ||
*/ | ||
var SUPPORTS_ELEMENT_PROTOTYPE_SCROLL_METHODS = "scroll" in Element.prototype && "scrollTo" in Element.prototype && "scrollBy" in Element.prototype && "scrollIntoView" in Element.prototype; | ||
var ELEMENT_ORIGINAL_SCROLL_LEFT_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollLeft").set; | ||
if (!SUPPORTS_SCROLL_BEHAVIOR || !SUPPORTS_ELEMENT_PROTOTYPE_SCROLL_METHODS) { | ||
patch(); | ||
} | ||
/** | ||
* Patches the 'scrollLeft' property descriptor on the Element prototype | ||
*/ | ||
function patchElementScrollLeft() { | ||
Object.defineProperty(Element.prototype, "scrollLeft", { | ||
set: function (scrollLeft) { | ||
if (this.__adjustingScrollPosition) { | ||
return ELEMENT_ORIGINAL_SCROLL_LEFT_SET_DESCRIPTOR.call(this, scrollLeft); | ||
} | ||
handleScrollMethod(this, "scrollTo", scrollLeft, this.scrollTop); | ||
return scrollLeft; | ||
} | ||
}); | ||
} | ||
/** | ||
* Applies the polyfill | ||
*/ | ||
function patch() { | ||
// Element.prototype methods | ||
patchElementScroll(); | ||
patchElementScrollBy(); | ||
patchElementScrollTo(); | ||
patchElementScrollIntoView(); | ||
// Element.prototype descriptors | ||
patchElementScrollLeft(); | ||
patchElementScrollTop(); | ||
// window methods | ||
patchWindowScroll(); | ||
patchWindowScrollBy(); | ||
patchWindowScrollTo(); | ||
// Navigation | ||
catchNavigation(); | ||
} | ||
/** | ||
* Is true if the browser natively supports the Element.prototype.[scroll|scrollTo|scrollBy|scrollIntoView] methods | ||
* @type {boolean} | ||
*/ | ||
var SUPPORTS_ELEMENT_PROTOTYPE_SCROLL_METHODS = "scroll" in Element.prototype && | ||
"scrollTo" in Element.prototype && | ||
"scrollBy" in Element.prototype && | ||
"scrollIntoView" in Element.prototype; | ||
if (!SUPPORTS_SCROLL_BEHAVIOR || !SUPPORTS_ELEMENT_PROTOTYPE_SCROLL_METHODS) { | ||
patch(); | ||
} | ||
}()); | ||
//# sourceMappingURL=index.js.map |
The MIT License (MIT) | ||
Copyright © 2019 Frederik Wessberg <frederikwessberg@hotmail.com> | ||
Copyright © 2019 [Frederik Wessberg](mailto:frederikwessberg@hotmail.com) ([@FredWessberg](https://twitter.com/FredWessberg)) ([Website](https://github.com/wessberg)) | ||
@@ -21,2 +21,2 @@ Permission is hereby granted, free of charge, to any person obtaining a copy | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE | ||
SOFTWARE |
{ | ||
"name": "scroll-behavior-polyfill", | ||
"version": "2.0.4", | ||
"version": "2.0.5", | ||
"description": "A polyfill for the 'scroll-behavior' CSS-property", | ||
@@ -13,18 +13,19 @@ "repository": { | ||
"scripts": { | ||
"generate:readme": "scaffold readme", | ||
"generate:license": "scaffold license", | ||
"generate:contributing": "scaffold contributing", | ||
"generate:coc": "scaffold coc", | ||
"generate:readme": "scaffold readme --yes", | ||
"generate:license": "scaffold license --yes", | ||
"generate:contributing": "scaffold contributing --yes", | ||
"generate:coc": "scaffold coc --yes", | ||
"generate:changelog": "standard-changelog --first-release", | ||
"generate:all": "npm run generate:license & npm run generate:contributing & npm run generate:coc & npm run generate:readme & npm run generate:changelog", | ||
"update": "ncu -ua && npm update && npm install", | ||
"generate:all": "npm run generate:license & npm run generate:contributing & npm run generate:coc & npm run generate:readme && npm run generate:changelog", | ||
"clean:dist": "rm -rf dist", | ||
"clean:compiled": "rm -rf compiled", | ||
"clean": "npm run clean:dist && npm run clean:compiled", | ||
"lint": "tsc --noEmit && tslint -c tslint.json --project tsconfig.json", | ||
"prerollup": "rm -r -f dist", | ||
"prettier": "prettier --write '{src,documentation}/**/*.{js,ts,json,html,xml,css,md}'", | ||
"prebuild": "npm run clean:dist", | ||
"build": "npm run rollup", | ||
"rollup": "rollup -c rollup.config.js", | ||
"prepare": "npm run rollup", | ||
"publish:before": "NODE_ENV=production npm run lint && NODE_ENV=production npm run prepare && npm run generate:all && git add . && (git commit -am \"Bumped version\" && git push) || true", | ||
"publish:after": "git push && npm publish", | ||
"publish:patch": "npm run publish:before && npm version patch && npm run publish:after", | ||
"publish:minor": "npm run publish:before && npm version minor && npm run publish:after", | ||
"publish:major": "npm run publish:before && npm version major && npm run publish:after" | ||
"preversion": "npm run lint && NODE_ENV=production npm run build", | ||
"version": "npm run generate:all && git add .", | ||
"release": "np --no-cleanup --no-yarn" | ||
}, | ||
@@ -42,19 +43,27 @@ "files": [ | ||
], | ||
"author": { | ||
"name": "Frederik Wessberg", | ||
"email": "frederikwessberg@hotmail.com", | ||
"url": "https://github.com/wessberg" | ||
}, | ||
"contributors": [ | ||
{ | ||
"name": "Frederik Wessberg", | ||
"email": "frederikwessberg@hotmail.com", | ||
"url": "https://github.com/wessberg", | ||
"imageUrl": "https://avatars2.githubusercontent.com/u/20454213?s=460&v=4", | ||
"role": "Lead Developer", | ||
"twitter": "FredWessberg" | ||
} | ||
], | ||
"license": "MIT", | ||
"devDependencies": { | ||
"@wessberg/rollup-plugin-ts": "1.1.17", | ||
"@wessberg/scaffold": "1.0.5", | ||
"@wessberg/ts-config": "^0.0.34", | ||
"npm-check-updates": "^2.15.0", | ||
"rollup": "^1.0.2", | ||
"@wessberg/rollup-plugin-ts": "1.1.28", | ||
"@wessberg/scaffold": "1.0.15", | ||
"@wessberg/ts-config": "^0.0.39", | ||
"rollup": "^1.1.2", | ||
"rollup-plugin-node-resolve": "^4.0.0", | ||
"tslib": "^1.9.3", | ||
"tslint": "^5.12.0", | ||
"typescript": "^3.2.2", | ||
"standard-changelog": "^2.0.6" | ||
"tslint": "^5.12.1", | ||
"typescript": "^3.3.3", | ||
"standard-changelog": "^2.0.6", | ||
"prettier": "^1.16.4", | ||
"pretty-quick": "^1.10.0", | ||
"husky": "^1.3.1", | ||
"np": "^4.0.2" | ||
}, | ||
@@ -71,14 +80,7 @@ "dependencies": {}, | ||
}, | ||
"scaffold": { | ||
"patreonUserId": "11315442", | ||
"contributorMeta": { | ||
"Frederik Wessberg": { | ||
"imageUrl": "https://avatars2.githubusercontent.com/u/20454213?s=460&v=4", | ||
"role": "Maintainer", | ||
"twitterHandle": "FredWessberg", | ||
"isCocEnforcer": true | ||
} | ||
}, | ||
"backers": [] | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "pretty-quick --staged" | ||
} | ||
} | ||
} |
121
README.md
@@ -1,15 +0,31 @@ | ||
<a href="https://npmcharts.com/compare/scroll-behavior-polyfill?minimal=true"><img alt="Downloads per month" src="https://img.shields.io/npm/dm/scroll-behavior-polyfill.svg" height="20"></img></a> | ||
<a href="https://david-dm.org/scroll-behavior-polyfill"><img alt="Dependencies" src="https://img.shields.io/david/scroll-behavior-polyfill.svg" height="20"></img></a> | ||
<a href="https://www.npmjs.com/package/scroll-behavior-polyfill"><img alt="NPM Version" src="https://badge.fury.io/js/scroll-behavior-polyfill.svg" height="20"></img></a> | ||
<a href="https://github.com/wessberg/scroll-behavior-polyfill/graphs/contributors"><img alt="Contributors" src="https://img.shields.io/github/contributors/wessberg%2Fscroll-behavior-polyfill.svg" height="20"></img></a> | ||
<a href="https://opensource.org/licenses/MIT"><img alt="MIT License" src="https://img.shields.io/badge/License-MIT-yellow.svg" height="20"></img></a> | ||
<a href="https://www.patreon.com/bePatron?u=11315442"><img alt="Support on Patreon" src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" height="20"></img></a> | ||
<!-- SHADOW_SECTION_LOGO_START --> | ||
# `scroll-behavior-polyfill` | ||
<div><img alt="Logo" src="https://raw.githubusercontent.com/wessberg/scroll-behavior-polyfill/master/documentation/asset/logo.png" height="80" /></div> | ||
> A polyfill for the [`scroll-behavior`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior) CSS-property as well as the extensions to the Element interface in the [CSSOM View Module](https://drafts.csswg.org/cssom-view/#dom-element-scrollto-options-options) | ||
<!-- SHADOW_SECTION_LOGO_END --> | ||
<!-- SHADOW_SECTION_DESCRIPTION_SHORT_START --> | ||
> A polyfill for the 'scroll-behavior' CSS-property | ||
<!-- SHADOW_SECTION_DESCRIPTION_SHORT_END --> | ||
<!-- SHADOW_SECTION_BADGES_START --> | ||
<a href="https://npmcharts.com/compare/scroll-behavior-polyfill?minimal=true"><img alt="Downloads per month" src="https://img.shields.io/npm/dm/scroll-behavior-polyfill.svg" /></a> | ||
<a href="https://david-dm.org/wessberg/scroll-behavior-polyfill"><img alt="Dependencies" src="https://img.shields.io/david/wessberg%2Fscroll-behavior-polyfill.svg" /></a> | ||
<a href="https://github.com/wessberg/scroll-behavior-polyfill/graphs/contributors"><img alt="Contributors" src="https://img.shields.io/github/contributors/wessberg%2Fscroll-behavior-polyfill.svg" /></a> | ||
<a href="https://github.com/prettier/prettier"><img alt="code style: prettier" src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square" /></a> | ||
<a href="https://opensource.org/licenses/MIT"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg" /></a> | ||
<a href="https://www.patreon.com/bePatron?u=11315442"><img alt="Support on Patreon" src="https://img.shields.io/badge/patreon-donate-green.svg" /></a> | ||
<!-- SHADOW_SECTION_BADGES_END --> | ||
<!-- SHADOW_SECTION_DESCRIPTION_LONG_START --> | ||
## Description | ||
The `scroll-behavior` CSS property sets the behavior for a scrolling box when scrolling is triggered by the navigation or CSSOM scrolling APIs. | ||
<!-- SHADOW_SECTION_DESCRIPTION_LONG_END --> | ||
The [`scroll-behavior`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior) CSS-property as well as the extensions to the Element interface in the [CSSOM View Module](https://drafts.csswg.org/cssom-view/#dom-element-scrollto-options-options) CSS property sets the behavior for a scrolling box when scrolling is triggered by the navigation or CSSOM scrolling APIs. | ||
This polyfill brings this new feature to all browsers. | ||
@@ -21,2 +37,45 @@ | ||
<!-- SHADOW_SECTION_FEATURES_START --> | ||
### Features | ||
<!-- SHADOW_SECTION_FEATURES_END --> | ||
- Spec-compliant | ||
- Tiny | ||
- Efficient | ||
- Works with the latest browser technologies, including Shadow DOM | ||
- Seamless | ||
<!-- SHADOW_SECTION_FEATURE_IMAGE_START --> | ||
<!-- SHADOW_SECTION_FEATURE_IMAGE_END --> | ||
<!-- SHADOW_SECTION_TOC_START --> | ||
## Table of Contents | ||
- [Description](#description) | ||
- [Features](#features) | ||
- [Table of Contents](#table-of-contents) | ||
- [Install](#install) | ||
- [NPM](#npm) | ||
- [Yarn](#yarn) | ||
- [Applying the polyfill](#applying-the-polyfill) | ||
- [Usage](#usage) | ||
- [Declarative API](#declarative-api) | ||
- [Imperative API](#imperative-api) | ||
- [Dependencies & Browser support](#dependencies--browser-support) | ||
- [Contributing](#contributing) | ||
- [Maintainers](#maintainers) | ||
- [Backers](#backers) | ||
- [Patreon](#patreon) | ||
- [FAQ](#faq) | ||
- [Are there any known quirks?](#are-there-any-known-quirks) | ||
- [License](#license) | ||
<!-- SHADOW_SECTION_TOC_END --> | ||
<!-- SHADOW_SECTION_INSTALL_START --> | ||
## Install | ||
@@ -36,2 +95,4 @@ | ||
<!-- SHADOW_SECTION_INSTALL_END --> | ||
## Applying the polyfill | ||
@@ -57,4 +118,8 @@ | ||
<!-- SHADOW_SECTION_USAGE_START --> | ||
## Usage | ||
<!-- SHADOW_SECTION_USAGE_END --> | ||
### Declarative API | ||
@@ -126,2 +191,4 @@ | ||
<!-- SHADOW_SECTION_CONTRIBUTING_START --> | ||
## Contributing | ||
@@ -131,8 +198,32 @@ | ||
<!-- SHADOW_SECTION_CONTRIBUTING_END --> | ||
<!-- SHADOW_SECTION_MAINTAINERS_START --> | ||
## Maintainers | ||
- <a href="https://github.com/wessberg"><img alt="Frederik Wessberg" src="https://avatars2.githubusercontent.com/u/20454213?s=460&v=4" height="11"></img></a> [Frederik Wessberg](https://github.com/wessberg): _Maintainer_ | ||
| <img alt="Frederik Wessberg" src="https://avatars2.githubusercontent.com/u/20454213?s=460&v=4" height="70" /> | | ||
| --------------------------------------------------------------------------------------------------------------------------------- | | ||
| [Frederik Wessberg](mailto:frederikwessberg@hotmail.com)<br>[@FredWessberg](https://twitter.com/FredWessberg)<br>_Lead Developer_ | | ||
## FAQ | ||
<!-- SHADOW_SECTION_MAINTAINERS_END --> | ||
<!-- SHADOW_SECTION_BACKERS_START --> | ||
## Backers | ||
### Patreon | ||
[Become a backer](https://www.patreon.com/bePatron?u=11315442) and get your name, avatar, and Twitter handle listed here. | ||
<a href="https://www.patreon.com/bePatron?u=11315442"><img alt="Backers on Patreon" src="https://patreon-badge.herokuapp.com/11315442.png" width="500" /></a> | ||
<!-- SHADOW_SECTION_BACKERS_END --> | ||
<!-- SHADOW_SECTION_FAQ_START --> | ||
### FAQ | ||
<!-- SHADOW_SECTION_FAQ_END --> | ||
### Are there any known quirks? | ||
@@ -142,8 +233,8 @@ | ||
## Backers 🏅 | ||
<!-- SHADOW_SECTION_LICENSE_START --> | ||
[Become a backer](https://www.patreon.com/bePatron?u=11315442) and get your name, logo, and link to your site listed here. | ||
## License | ||
## License 📄 | ||
MIT © [Frederik Wessberg](mailto:frederikwessberg@hotmail.com) ([@FredWessberg](https://twitter.com/FredWessberg)) ([Website](https://github.com/wessberg)) | ||
MIT © [Frederik Wessberg](https://github.com/wessberg) | ||
<!-- SHADOW_SECTION_LICENSE_END --> |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
121089
897
235
13