scroll-behavior-polyfill
Advanced tools
Comparing version 2.0.5 to 2.0.6
@@ -0,1 +1,3 @@ | ||
## [2.0.6](https://github.com/wessberg/scroll-behavior-polyfill/compare/v2.0.5...v2.0.6) (2019-02-09) | ||
## [2.0.5](https://github.com/wessberg/scroll-behavior-polyfill/compare/v2.0.4...v2.0.5) (2019-02-07) | ||
@@ -2,0 +4,0 @@ |
1729
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 | ||
/*! ***************************************************************************** | ||
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. | ||
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. | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
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 __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); | ||
}; | ||
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; | ||
} | ||
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 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; | ||
} | ||
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; | ||
} | ||
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 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)); | ||
} | ||
/** | ||
* 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); | ||
} | ||
}); | ||
} | ||
/** | ||
* 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(); | ||
} | ||
/** | ||
* 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 = Element.prototype.scroll; | ||
var ELEMENT_ORIGINAL_SCROLL = Element.prototype.scroll; | ||
var WINDOW_ORIGINAL_SCROLL = window.scroll; | ||
var WINDOW_ORIGINAL_SCROLL = window.scroll; | ||
var ELEMENT_ORIGINAL_SCROLL_BY = Element.prototype.scrollBy; | ||
var ELEMENT_ORIGINAL_SCROLL_BY = Element.prototype.scrollBy; | ||
var WINDOW_ORIGINAL_SCROLL_BY = window.scrollBy; | ||
var WINDOW_ORIGINAL_SCROLL_BY = window.scrollBy; | ||
var ELEMENT_ORIGINAL_SCROLL_TO = Element.prototype.scrollTo; | ||
var ELEMENT_ORIGINAL_SCROLL_TO = Element.prototype.scrollTo; | ||
var WINDOW_ORIGINAL_SCROLL_TO = window.scrollTo; | ||
var WINDOW_ORIGINAL_SCROLL_TO = window.scrollTo; | ||
/** | ||
* 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; | ||
} | ||
} | ||
} | ||
/** | ||
* 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; | ||
} | ||
} | ||
} | ||
/** | ||
* 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) | ||
}; | ||
} | ||
} | ||
/** | ||
* 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) | ||
}; | ||
} | ||
} | ||
/** | ||
* 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; | ||
} | ||
} | ||
/** | ||
* 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; | ||
} | ||
} | ||
/** | ||
* 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"; | ||
} | ||
/** | ||
* 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"; | ||
} | ||
/** | ||
* 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 }); | ||
} | ||
} | ||
/** | ||
* 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 Element prototype | ||
*/ | ||
function patchElementScroll() { | ||
Element.prototype.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 Element prototype | ||
*/ | ||
function patchElementScrollBy() { | ||
Element.prototype.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 Element prototype | ||
*/ | ||
function patchElementScrollTo() { | ||
Element.prototype.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); | ||
}; | ||
} | ||
/** | ||
* 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 Window prototype | ||
*/ | ||
function patchWindowScroll() { | ||
window.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 Window prototype | ||
*/ | ||
function patchWindowScrollBy() { | ||
window.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 Window prototype | ||
*/ | ||
function patchWindowScrollTo() { | ||
window.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; | ||
} | ||
// 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 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"]; | ||
} | ||
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"]; | ||
} | ||
// 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; | ||
} | ||
// 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; | ||
} | ||
/** | ||
* 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 | ||
}); | ||
}); | ||
} | ||
/** | ||
* 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_INTO_VIEW = Element.prototype.scrollIntoView; | ||
var ELEMENT_ORIGINAL_SCROLL_INTO_VIEW = Element.prototype.scrollIntoView; | ||
/** | ||
* 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 | ||
}; | ||
} | ||
/** | ||
* 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 | ||
}; | ||
} | ||
/** | ||
* 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 '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))); | ||
}; | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL_TOP_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollTop").set; | ||
var ELEMENT_ORIGINAL_SCROLL_TOP_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollTop").set; | ||
/** | ||
* 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; | ||
} | ||
}); | ||
} | ||
/** | ||
* 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; | ||
} | ||
}); | ||
} | ||
var ELEMENT_ORIGINAL_SCROLL_LEFT_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollLeft").set; | ||
var ELEMENT_ORIGINAL_SCROLL_LEFT_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollLeft").set; | ||
/** | ||
* 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; | ||
} | ||
}); | ||
} | ||
/** | ||
* 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(); | ||
} | ||
/** | ||
* 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; | ||
/** | ||
* 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(); | ||
} | ||
if (!SUPPORTS_SCROLL_BEHAVIOR || !SUPPORTS_ELEMENT_PROTOTYPE_SCROLL_METHODS) { | ||
patch(); | ||
} | ||
}()); | ||
//# sourceMappingURL=index.js.map |
162
package.json
{ | ||
"name": "scroll-behavior-polyfill", | ||
"version": "2.0.5", | ||
"description": "A polyfill for the 'scroll-behavior' CSS-property", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/wessberg/scroll-behavior-polyfill.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/wessberg/scroll-behavior-polyfill/issues" | ||
}, | ||
"scripts": { | ||
"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", | ||
"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", | ||
"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", | ||
"preversion": "npm run lint && NODE_ENV=production npm run build", | ||
"version": "npm run generate:all && git add .", | ||
"release": "np --no-cleanup --no-yarn" | ||
}, | ||
"files": [ | ||
"dist/**/*.*" | ||
], | ||
"keywords": [ | ||
"scroll-behavior", | ||
"smooth-scrolling", | ||
"polyfill", | ||
"css", | ||
"smooth", | ||
"scroll behavior" | ||
], | ||
"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.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.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" | ||
}, | ||
"dependencies": {}, | ||
"main": "./dist/index.js", | ||
"module": "./dist/index.js", | ||
"browser": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"typings": "./dist/index.d.ts", | ||
"es2015": "./dist/index.js", | ||
"engines": { | ||
"node": ">=4.0.0" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "pretty-quick --staged" | ||
} | ||
} | ||
"name": "scroll-behavior-polyfill", | ||
"version": "2.0.6", | ||
"description": "A polyfill for the 'scroll-behavior' CSS-property", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/wessberg/scroll-behavior-polyfill.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/wessberg/scroll-behavior-polyfill/issues" | ||
}, | ||
"scripts": { | ||
"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", | ||
"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", | ||
"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", | ||
"preversion": "npm run lint && NODE_ENV=production npm run build", | ||
"version": "npm run generate:all && git add .", | ||
"release": "np --no-cleanup --no-yarn" | ||
}, | ||
"files": [ | ||
"dist/**/*.*" | ||
], | ||
"keywords": [ | ||
"scroll-behavior", | ||
"smooth-scrolling", | ||
"polyfill", | ||
"css", | ||
"smooth", | ||
"scroll behavior" | ||
], | ||
"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.28", | ||
"@wessberg/scaffold": "1.0.17", | ||
"@wessberg/ts-config": "^0.0.39", | ||
"rollup": "^1.1.2", | ||
"rollup-plugin-node-resolve": "^4.0.0", | ||
"tslib": "^1.9.3", | ||
"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" | ||
}, | ||
"dependencies": {}, | ||
"main": "./dist/index.js", | ||
"module": "./dist/index.js", | ||
"browser": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"typings": "./dist/index.d.ts", | ||
"es2015": "./dist/index.js", | ||
"engines": { | ||
"node": ">=4.0.0" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "pretty-quick --staged" | ||
} | ||
} | ||
} |
<!-- SHADOW_SECTION_LOGO_START --> | ||
<div><img alt="Logo" src="https://raw.githubusercontent.com/wessberg/scroll-behavior-polyfill/master/documentation/asset/logo.png" height="80" /></div> | ||
<div><img alt="Logo" src="https://raw.githubusercontent.com/wessberg/scroll-behavior-polyfill/master/documentation/asset/logo.png" height="60" /></div> | ||
@@ -16,2 +16,3 @@ <!-- SHADOW_SECTION_LOGO_END --> | ||
<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://www.npmjs.com/package/scroll-behavior-polyfill"><img alt="NPM version" src="https://badge.fury.io/js/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> | ||
@@ -73,3 +74,3 @@ <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> | ||
- [Patreon](#patreon) | ||
- [FAQ](#faq) | ||
- [FAQ](#faq) | ||
- [Are there any known quirks?](#are-there-any-known-quirks) | ||
@@ -112,3 +113,3 @@ - [License](#license) | ||
if (!("scrollBehavior" in document.documentElement.style)) { | ||
await import("scroll-behavior-polyfill"); | ||
await import("scroll-behavior-polyfill"); | ||
} | ||
@@ -142,4 +143,4 @@ ``` | ||
<script> | ||
// Works jut fine when given as a style property | ||
element.style.scrollBehavior = "smooth"; | ||
// Works jut fine when given as a style property | ||
element.style.scrollBehavior = "smooth"; | ||
</script> | ||
@@ -159,5 +160,5 @@ ``` | ||
window.scroll({ | ||
behavior: "smooth", | ||
top: 100, | ||
left: 0 | ||
behavior: "smooth", | ||
top: 100, | ||
left: 0 | ||
}); | ||
@@ -169,5 +170,5 @@ | ||
myElement.scrollBy({ | ||
behavior: "smooth", | ||
top: 50, | ||
left: 0 | ||
behavior: "smooth", | ||
top: 50, | ||
left: 0 | ||
}); | ||
@@ -226,3 +227,3 @@ ``` | ||
### FAQ | ||
## FAQ | ||
@@ -229,0 +230,0 @@ <!-- SHADOW_SECTION_FAQ_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
236
120775
856