Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

scroll-behavior-polyfill.js 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. // https://www.jsdelivr.com/package/npm/scroll-behavior-polyfill?path=dist
  2. /*!
  3. scroll-behavior-polyfill 2.0.6
  4. license: MIT (https://github.com/wessberg/scroll-behavior-polyfill/blob/master/LICENSE.md)
  5. Copyright © 2019 Frederik Wessberg <frederikwessberg@hotmail.com>
  6. */
  7. (function () {
  8. 'use strict';
  9. /**
  10. * Is true if the browser natively supports the 'scroll-behavior' CSS-property.
  11. * @type {boolean}
  12. */
  13. var SUPPORTS_SCROLL_BEHAVIOR = "scrollBehavior" in document.documentElement.style;
  14. /*! *****************************************************************************
  15. Copyright (c) Microsoft Corporation. All rights reserved.
  16. Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  17. this file except in compliance with the License. You may obtain a copy of the
  18. License at http://www.apache.org/licenses/LICENSE-2.0
  19. THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  20. KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  21. WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  22. MERCHANTABLITY OR NON-INFRINGEMENT.
  23. See the Apache Version 2.0 License for specific language governing permissions
  24. and limitations under the License.
  25. ***************************************************************************** */
  26. var __assign = function() {
  27. __assign = Object.assign || function __assign(t) {
  28. for (var s, i = 1, n = arguments.length; i < n; i++) {
  29. s = arguments[i];
  30. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
  31. }
  32. return t;
  33. };
  34. return __assign.apply(this, arguments);
  35. };
  36. function __read(o, n) {
  37. var m = typeof Symbol === "function" && o[Symbol.iterator];
  38. if (!m) return o;
  39. var i = m.call(o), r, ar = [], e;
  40. try {
  41. while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
  42. }
  43. catch (error) { e = { error: error }; }
  44. finally {
  45. try {
  46. if (r && !r.done && (m = i["return"])) m.call(i);
  47. }
  48. finally { if (e) throw e.error; }
  49. }
  50. return ar;
  51. }
  52. var styleDeclarationPropertyName = "scrollBehavior";
  53. var styleAttributePropertyName = "scroll-behavior";
  54. var styleAttributePropertyNameRegex = new RegExp(styleAttributePropertyName + ":\\s*([^;]*)");
  55. /**
  56. * Determines the scroll behavior to use, depending on the given ScrollOptions and the position of the Element
  57. * within the DOM
  58. * @param {Element|HTMLElement|Window} inputTarget
  59. * @param {ScrollOptions} [options]
  60. * @returns {ScrollBehavior}
  61. */
  62. function getScrollBehavior(inputTarget, options) {
  63. // If the given 'behavior' is 'smooth', apply smooth scrolling no matter what
  64. if (options != null && options.behavior === "smooth")
  65. return "smooth";
  66. var target = "style" in inputTarget ? inputTarget : document.scrollingElement != null ? document.scrollingElement : document.documentElement;
  67. var value;
  68. if ("style" in target) {
  69. // Check if scroll-behavior is set as a property on the CSSStyleDeclaration
  70. var scrollBehaviorPropertyValue = target.style[styleDeclarationPropertyName];
  71. // Return it if it is given and has a proper value
  72. if (scrollBehaviorPropertyValue != null && scrollBehaviorPropertyValue !== "") {
  73. value = scrollBehaviorPropertyValue;
  74. }
  75. }
  76. if (value == null) {
  77. var attributeValue = target.getAttribute("scroll-behavior");
  78. if (attributeValue != null && attributeValue !== "") {
  79. value = attributeValue;
  80. }
  81. }
  82. if (value == null) {
  83. // Otherwise, check if it is set as an inline style
  84. var styleAttributeValue = target.getAttribute("style");
  85. if (styleAttributeValue != null && styleAttributeValue.includes(styleAttributePropertyName)) {
  86. var match = styleAttributeValue.match(styleAttributePropertyNameRegex);
  87. if (match != null) {
  88. var _a = __read(match, 2), behavior = _a[1];
  89. if (behavior != null && behavior !== "") {
  90. value = behavior;
  91. }
  92. }
  93. }
  94. }
  95. if (value == null) {
  96. // Take the computed style for the element and see if it contains a specific 'scroll-behavior' value
  97. var computedStyle = getComputedStyle(target);
  98. var computedStyleValue = computedStyle.getPropertyValue("scrollBehavior");
  99. if (computedStyleValue != null && computedStyleValue !== "") {
  100. value = computedStyleValue;
  101. }
  102. }
  103. // In all other cases, use the value from the CSSOM
  104. return value;
  105. }
  106. var HALF = 0.5;
  107. /**
  108. * The easing function to use when applying the smooth scrolling
  109. * @param {number} k
  110. * @returns {number}
  111. */
  112. function ease(k) {
  113. return HALF * (1 - Math.cos(Math.PI * k));
  114. }
  115. /**
  116. * The duration of a smooth scroll
  117. * @type {number}
  118. */
  119. var SCROLL_TIME = 15000;
  120. /**
  121. * Performs a smooth repositioning of the scroll
  122. * @param {ISmoothScrollOptions} options
  123. */
  124. function smoothScroll(options) {
  125. var startTime = options.startTime, startX = options.startX, startY = options.startY, endX = options.endX, endY = options.endY, method = options.method;
  126. var timeLapsed = 0;
  127. var distanceX = endX - startX;
  128. var distanceY = endY - startY;
  129. var speed = Math.max(Math.abs((distanceX / 1000) * SCROLL_TIME), Math.abs((distanceY / 1000) * SCROLL_TIME));
  130. requestAnimationFrame(function animate(timestamp) {
  131. timeLapsed += timestamp - startTime;
  132. var percentage = Math.max(0, Math.min(1, speed === 0 ? 0 : timeLapsed / speed));
  133. var positionX = Math.floor(startX + distanceX * ease(percentage));
  134. var positionY = Math.floor(startY + distanceY * ease(percentage));
  135. method(positionX, positionY);
  136. if (positionX !== endX || positionY !== endY) {
  137. requestAnimationFrame(animate);
  138. }
  139. });
  140. }
  141. /**
  142. * Returns a High Resolution timestamp if possible, otherwise fallbacks to Date.now()
  143. * @returns {number}
  144. */
  145. function now() {
  146. if ("performance" in window)
  147. return performance.now();
  148. return Date.now();
  149. }
  150. var ELEMENT_ORIGINAL_SCROLL = Element.prototype.scroll;
  151. var WINDOW_ORIGINAL_SCROLL = window.scroll;
  152. var ELEMENT_ORIGINAL_SCROLL_BY = Element.prototype.scrollBy;
  153. var WINDOW_ORIGINAL_SCROLL_BY = window.scrollBy;
  154. var ELEMENT_ORIGINAL_SCROLL_TO = Element.prototype.scrollTo;
  155. var WINDOW_ORIGINAL_SCROLL_TO = window.scrollTo;
  156. /**
  157. * A fallback if Element.prototype.scroll is not defined
  158. * @param {number} x
  159. * @param {number} y
  160. */
  161. function elementPrototypeScrollFallback(x, y) {
  162. this.__adjustingScrollPosition = true;
  163. this.scrollLeft = x;
  164. this.scrollTop = y;
  165. delete this.__adjustingScrollPosition;
  166. }
  167. /**
  168. * A fallback if Element.prototype.scrollTo is not defined
  169. * @param {number} x
  170. * @param {number} y
  171. */
  172. function elementPrototypeScrollToFallback(x, y) {
  173. return elementPrototypeScrollFallback.call(this, x, y);
  174. }
  175. /**
  176. * A fallback if Element.prototype.scrollBy is not defined
  177. * @param {number} x
  178. * @param {number} y
  179. */
  180. function elementPrototypeScrollByFallback(x, y) {
  181. this.__adjustingScrollPosition = true;
  182. this.scrollLeft += x;
  183. this.scrollTop += y;
  184. delete this.__adjustingScrollPosition;
  185. }
  186. /**
  187. * Gets the original non-patched prototype method for the given kind
  188. * @param {ScrollMethodName} kind
  189. * @param {Element|Window} element
  190. * @return {Function}
  191. */
  192. function getOriginalScrollMethodForKind(kind, element) {
  193. switch (kind) {
  194. case "scroll":
  195. if (element instanceof Element) {
  196. if (ELEMENT_ORIGINAL_SCROLL != null) {
  197. return ELEMENT_ORIGINAL_SCROLL;
  198. }
  199. else {
  200. return elementPrototypeScrollFallback;
  201. }
  202. }
  203. else {
  204. return WINDOW_ORIGINAL_SCROLL;
  205. }
  206. case "scrollBy":
  207. if (element instanceof Element) {
  208. if (ELEMENT_ORIGINAL_SCROLL_BY != null) {
  209. return ELEMENT_ORIGINAL_SCROLL_BY;
  210. }
  211. else {
  212. return elementPrototypeScrollByFallback;
  213. }
  214. }
  215. else {
  216. return WINDOW_ORIGINAL_SCROLL_BY;
  217. }
  218. case "scrollTo":
  219. if (element instanceof Element) {
  220. if (ELEMENT_ORIGINAL_SCROLL_TO != null) {
  221. return ELEMENT_ORIGINAL_SCROLL_TO;
  222. }
  223. else {
  224. return elementPrototypeScrollToFallback;
  225. }
  226. }
  227. else {
  228. return WINDOW_ORIGINAL_SCROLL_TO;
  229. }
  230. }
  231. }
  232. /**
  233. * Gets the Smooth Scroll Options to use for the step function
  234. * @param {Element|Window} element
  235. * @param {number} x
  236. * @param {number} y
  237. * @param {ScrollMethodName} kind
  238. * @returns {ISmoothScrollOptions}
  239. */
  240. function getSmoothScrollOptions(element, x, y, kind) {
  241. var startTime = now();
  242. if (!(element instanceof Element)) {
  243. // Use window as the scroll container
  244. var scrollX_1 = window.scrollX, pageXOffset_1 = window.pageXOffset, scrollY_1 = window.scrollY, pageYOffset_1 = window.pageYOffset;
  245. var startX = scrollX_1 == null || scrollX_1 === 0 ? pageXOffset_1 : scrollX_1;
  246. var startY = scrollY_1 == null || scrollY_1 === 0 ? pageYOffset_1 : scrollY_1;
  247. return {
  248. startTime: startTime,
  249. startX: startX,
  250. startY: startY,
  251. endX: Math.floor(kind === "scrollBy" ? startX + x : x),
  252. endY: Math.floor(kind === "scrollBy" ? startY + y : y),
  253. method: getOriginalScrollMethodForKind("scrollTo", window).bind(window)
  254. };
  255. }
  256. else {
  257. var scrollLeft = element.scrollLeft, scrollTop = element.scrollTop;
  258. var startX = scrollLeft;
  259. var startY = scrollTop;
  260. return {
  261. startTime: startTime,
  262. startX: startX,
  263. startY: startY,
  264. endX: Math.floor(kind === "scrollBy" ? startX + x : x),
  265. endY: Math.floor(kind === "scrollBy" ? startY + y : y),
  266. method: getOriginalScrollMethodForKind("scrollTo", element).bind(element)
  267. };
  268. }
  269. }
  270. /**
  271. * Ensures that the given value is numeric
  272. * @param {number} value
  273. * @return {number}
  274. */
  275. function ensureNumeric(value) {
  276. if (value == null)
  277. return 0;
  278. else if (typeof value === "number") {
  279. return value;
  280. }
  281. else if (typeof value === "string") {
  282. return parseFloat(value);
  283. }
  284. else {
  285. return 0;
  286. }
  287. }
  288. /**
  289. * Returns true if the given value is some ScrollToOptions
  290. * @param {number | ScrollToOptions} value
  291. * @return {value is ScrollToOptions}
  292. */
  293. function isScrollToOptions(value) {
  294. return value != null && typeof value === "object";
  295. }
  296. /**
  297. * Handles a scroll method
  298. * @param {Element|Window} element
  299. * @param {ScrollMethodName} kind
  300. * @param {number | ScrollToOptions} optionsOrX
  301. * @param {number} y
  302. */
  303. function handleScrollMethod(element, kind, optionsOrX, y) {
  304. onScrollWithOptions(getScrollToOptionsWithValidation(optionsOrX, y), element, kind);
  305. }
  306. /**
  307. * Invoked when a 'ScrollToOptions' dict is provided to 'scroll()' as the first argument
  308. * @param {ScrollToOptions} options
  309. * @param {Element|Window} element
  310. * @param {ScrollMethodName} kind
  311. */
  312. function onScrollWithOptions(options, element, kind) {
  313. var behavior = getScrollBehavior(element, options);
  314. // If the behavior is 'auto' apply instantaneous scrolling
  315. if (behavior == null || behavior === "auto") {
  316. getOriginalScrollMethodForKind(kind, element).call(element, options.left, options.top);
  317. }
  318. else {
  319. smoothScroll(getSmoothScrollOptions(element, options.left, options.top, kind));
  320. }
  321. }
  322. /**
  323. * Normalizes the given scroll coordinates
  324. * @param {number?} x
  325. * @param {number?} y
  326. * @return {Required<Pick<ScrollToOptions, "top" | "left">>}
  327. */
  328. function normalizeScrollCoordinates(x, y) {
  329. return {
  330. left: ensureNumeric(x),
  331. top: ensureNumeric(y)
  332. };
  333. }
  334. /**
  335. * Gets ScrollToOptions based on the given arguments. Will throw if validation fails
  336. * @param {number | ScrollToOptions} optionsOrX
  337. * @param {number} y
  338. * @return {Required<ScrollToOptions>}
  339. */
  340. function getScrollToOptionsWithValidation(optionsOrX, y) {
  341. // If only one argument is given, and it isn't an options object, throw a TypeError
  342. if (y === undefined && !isScrollToOptions(optionsOrX)) {
  343. throw new TypeError("Failed to execute 'scroll' on 'Element': parameter 1 ('options') is not an object.");
  344. }
  345. // Scroll based on the primitive values given as arguments
  346. if (!isScrollToOptions(optionsOrX)) {
  347. return __assign({}, normalizeScrollCoordinates(optionsOrX, y), { behavior: "auto" });
  348. }
  349. // Scroll based on the received options object
  350. else {
  351. return __assign({}, normalizeScrollCoordinates(optionsOrX.left, optionsOrX.top), { behavior: optionsOrX.behavior == null ? "auto" : optionsOrX.behavior });
  352. }
  353. }
  354. /**
  355. * Patches the 'scroll' method on the Element prototype
  356. */
  357. function patchElementScroll() {
  358. Element.prototype.scroll = function (optionsOrX, y) {
  359. handleScrollMethod(this, "scroll", optionsOrX, y);
  360. };
  361. }
  362. /**
  363. * Patches the 'scrollBy' method on the Element prototype
  364. */
  365. function patchElementScrollBy() {
  366. Element.prototype.scrollBy = function (optionsOrX, y) {
  367. handleScrollMethod(this, "scrollBy", optionsOrX, y);
  368. };
  369. }
  370. /**
  371. * Patches the 'scrollTo' method on the Element prototype
  372. */
  373. function patchElementScrollTo() {
  374. Element.prototype.scrollTo = function (optionsOrX, y) {
  375. handleScrollMethod(this, "scrollTo", optionsOrX, y);
  376. };
  377. }
  378. /**
  379. * Patches the 'scroll' method on the Window prototype
  380. */
  381. function patchWindowScroll() {
  382. window.scroll = function (optionsOrX, y) {
  383. handleScrollMethod(this, "scroll", optionsOrX, y);
  384. };
  385. }
  386. /**
  387. * Patches the 'scrollBy' method on the Window prototype
  388. */
  389. function patchWindowScrollBy() {
  390. window.scrollBy = function (optionsOrX, y) {
  391. handleScrollMethod(this, "scrollBy", optionsOrX, y);
  392. };
  393. }
  394. /**
  395. * Patches the 'scrollTo' method on the Window prototype
  396. */
  397. function patchWindowScrollTo() {
  398. window.scrollTo = function (optionsOrX, y) {
  399. handleScrollMethod(this, "scrollTo", optionsOrX, y);
  400. };
  401. }
  402. // tslint:disable:no-any
  403. /**
  404. * Gets the parent of an element, taking into account DocumentFragments, ShadowRoots, as well as the root context (window)
  405. * @param {EventTarget} currentElement
  406. * @returns {EventTarget | null}
  407. */
  408. function getParent(currentElement) {
  409. if ("nodeType" in currentElement && currentElement.nodeType === 1) {
  410. return currentElement.parentNode;
  411. }
  412. if ("ShadowRoot" in window && currentElement instanceof window.ShadowRoot) {
  413. return currentElement.host;
  414. }
  415. else if (currentElement === document) {
  416. return window;
  417. }
  418. else if (currentElement instanceof Node)
  419. return currentElement.parentNode;
  420. return null;
  421. }
  422. var scrollingElement = document.scrollingElement != null ? document.scrollingElement : document.documentElement;
  423. /**
  424. * Returns true if the given overflow property represents a scrollable overflow value
  425. * @param {string | null} overflow
  426. * @return {boolean}
  427. */
  428. function canOverflow(overflow) {
  429. return overflow !== "visible" && overflow !== "clip";
  430. }
  431. /**
  432. * Returns true if the given element is scrollable
  433. * @param {Element} element
  434. * @return {boolean}
  435. */
  436. function isScrollable(element) {
  437. if (element.clientHeight < element.scrollHeight || element.clientWidth < element.scrollWidth) {
  438. var style = getComputedStyle(element, null);
  439. return canOverflow(style.overflowY) || canOverflow(style.overflowX);
  440. }
  441. return false;
  442. }
  443. /**
  444. * Finds the nearest ancestor of an element that can scroll
  445. * @param {Element} target
  446. * @returns {Element|Window?}
  447. */
  448. function findNearestAncestorsWithScrollBehavior(target) {
  449. var currentElement = target;
  450. while (currentElement != null) {
  451. var behavior = getScrollBehavior(currentElement);
  452. if (behavior != null && (currentElement === scrollingElement || isScrollable(currentElement))) {
  453. return [currentElement, behavior];
  454. }
  455. var parent_1 = getParent(currentElement);
  456. currentElement = parent_1;
  457. }
  458. // No such element could be found. Start over, but this time find the nearest ancestor that can simply scroll
  459. currentElement = target;
  460. while (currentElement != null) {
  461. if (currentElement === scrollingElement || isScrollable(currentElement)) {
  462. return [currentElement, "auto"];
  463. }
  464. var parent_2 = getParent(currentElement);
  465. currentElement = parent_2;
  466. }
  467. // Default to the scrolling element
  468. return [scrollingElement, "auto"];
  469. }
  470. // tslint:disable:no-any
  471. /**
  472. * Finds the nearest root from an element
  473. * @param {Element} target
  474. * @returns {Document|ShadowRoot}
  475. */
  476. function findNearestRoot(target) {
  477. var currentElement = target;
  478. while (currentElement != null) {
  479. if ("ShadowRoot" in window && currentElement instanceof window.ShadowRoot) {
  480. // Assume this is a ShadowRoot
  481. return currentElement;
  482. }
  483. var parent_1 = getParent(currentElement);
  484. if (parent_1 === currentElement) {
  485. return document;
  486. }
  487. currentElement = parent_1;
  488. }
  489. return document;
  490. }
  491. /**
  492. * A Regular expression that matches id's of the form "#[digit]"
  493. * @type {RegExp}
  494. */
  495. var ID_WITH_LEADING_DIGIT_REGEXP = /^#\d/;
  496. /**
  497. * Catches anchor navigation to IDs within the same root and ensures that they can be smooth-scrolled
  498. * if the scroll behavior is smooth in the first rooter within that context
  499. */
  500. function catchNavigation() {
  501. // Listen for 'click' events globally
  502. window.addEventListener("click", function (e) {
  503. // Only work with trusted events on HTMLAnchorElements
  504. if (!e.isTrusted || !(e.target instanceof HTMLAnchorElement))
  505. return;
  506. var hrefAttributeValue = e.target.getAttribute("href");
  507. // Only work with HTMLAnchorElements that navigates to a specific ID
  508. if (hrefAttributeValue == null || !hrefAttributeValue.startsWith("#")) {
  509. return;
  510. }
  511. // Find the nearest root, whether it be a ShadowRoot or the document itself
  512. var root = findNearestRoot(e.target);
  513. // Attempt to match the selector from that root. querySelector' doesn't support IDs that start with a digit, so work around that limitation
  514. var elementMatch = hrefAttributeValue.match(ID_WITH_LEADING_DIGIT_REGEXP) != null ? root.getElementById(hrefAttributeValue.slice(1)) : root.querySelector(hrefAttributeValue);
  515. // If no selector could be found, don't proceed
  516. if (elementMatch == null)
  517. return;
  518. // Find the nearest ancestor that can be scrolled
  519. var _a = __read(findNearestAncestorsWithScrollBehavior(elementMatch), 2), ancestorWithScrollBehavior = _a[0], behavior = _a[1];
  520. // If the behavior isn't smooth, don't proceed
  521. if (behavior !== "smooth")
  522. return;
  523. // Otherwise, first prevent the default action.
  524. e.preventDefault();
  525. // Now, scroll to the element with that ID
  526. ancestorWithScrollBehavior.scrollTo({
  527. behavior: behavior,
  528. top: elementMatch.offsetTop,
  529. left: elementMatch.offsetLeft
  530. });
  531. });
  532. }
  533. var ELEMENT_ORIGINAL_SCROLL_INTO_VIEW = Element.prototype.scrollIntoView;
  534. /**
  535. * The majority of this file is based on https://github.com/stipsan/compute-scroll-into-view (MIT license),
  536. * but has been rewritten to accept a scroller as an argument.
  537. */
  538. /**
  539. * Find out which edge to align against when logical scroll position is "nearest"
  540. * Interesting fact: "nearest" works similarly to "if-needed", if the element is fully visible it will not scroll it
  541. *
  542. * Legends:
  543. * ┌────────┐ ┏ ━ ━ ━ ┓
  544. * │ target │ frame
  545. * └────────┘ ┗ ━ ━ ━ ┛
  546. */
  547. function alignNearest(scrollingEdgeStart, scrollingEdgeEnd, scrollingSize, scrollingBorderStart, scrollingBorderEnd, elementEdgeStart, elementEdgeEnd, elementSize) {
  548. /**
  549. * If element edge A and element edge B are both outside scrolling box edge A and scrolling box edge B
  550. *
  551. * ┌──┐
  552. * ┏━│━━│━┓
  553. * │ │
  554. * ┃ │ │ ┃ do nothing
  555. * │ │
  556. * ┗━│━━│━┛
  557. * └──┘
  558. *
  559. * If element edge C and element edge D are both outside scrolling box edge C and scrolling box edge D
  560. *
  561. * ┏ ━ ━ ━ ━ ┓
  562. * ┌───────────┐
  563. * │┃ ┃│ do nothing
  564. * └───────────┘
  565. * ┗ ━ ━ ━ ━ ┛
  566. */
  567. if ((elementEdgeStart < scrollingEdgeStart && elementEdgeEnd > scrollingEdgeEnd) || (elementEdgeStart > scrollingEdgeStart && elementEdgeEnd < scrollingEdgeEnd)) {
  568. return 0;
  569. }
  570. /**
  571. * If element edge A is outside scrolling box edge A and element height is less than scrolling box height
  572. *
  573. * ┌──┐
  574. * ┏━│━━│━┓ ┏━┌━━┐━┓
  575. * └──┘ │ │
  576. * from ┃ ┃ to ┃ └──┘ ┃
  577. *
  578. * ┗━ ━━ ━┛ ┗━ ━━ ━┛
  579. *
  580. * If element edge B is outside scrolling box edge B and element height is greater than scrolling box height
  581. *
  582. * ┏━ ━━ ━┓ ┏━┌━━┐━┓
  583. * │ │
  584. * from ┃ ┌──┐ ┃ to ┃ │ │ ┃
  585. * │ │ │ │
  586. * ┗━│━━│━┛ ┗━│━━│━┛
  587. * │ │ └──┘
  588. * │ │
  589. * └──┘
  590. *
  591. * If element edge C is outside scrolling box edge C and element width is less than scrolling box width
  592. *
  593. * from to
  594. * ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓
  595. * ┌───┐ ┌───┐
  596. * │ ┃ │ ┃ ┃ │ ┃
  597. * └───┘ └───┘
  598. * ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛
  599. *
  600. * If element edge D is outside scrolling box edge D and element width is greater than scrolling box width
  601. *
  602. * from to
  603. * ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓
  604. * ┌───────────┐ ┌───────────┐
  605. * ┃ │ ┃ │ ┃ ┃ │
  606. * └───────────┘ └───────────┘
  607. * ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛
  608. */
  609. if ((elementEdgeStart <= scrollingEdgeStart && elementSize <= scrollingSize) || (elementEdgeEnd >= scrollingEdgeEnd && elementSize >= scrollingSize)) {
  610. return elementEdgeStart - scrollingEdgeStart - scrollingBorderStart;
  611. }
  612. /**
  613. * If element edge B is outside scrolling box edge B and element height is less than scrolling box height
  614. *
  615. * ┏━ ━━ ━┓ ┏━ ━━ ━┓
  616. *
  617. * from ┃ ┃ to ┃ ┌──┐ ┃
  618. * ┌──┐ │ │
  619. * ┗━│━━│━┛ ┗━└━━┘━┛
  620. * └──┘
  621. *
  622. * If element edge A is outside scrolling box edge A and element height is greater than scrolling box height
  623. *
  624. * ┌──┐
  625. * │ │
  626. * │ │ ┌──┐
  627. * ┏━│━━│━┓ ┏━│━━│━┓
  628. * │ │ │ │
  629. * from ┃ └──┘ ┃ to ┃ │ │ ┃
  630. * │ │
  631. * ┗━ ━━ ━┛ ┗━└━━┘━┛
  632. *
  633. * If element edge C is outside scrolling box edge C and element width is greater than scrolling box width
  634. *
  635. * from to
  636. * ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓
  637. * ┌───────────┐ ┌───────────┐
  638. * │ ┃ │ ┃ │ ┃ ┃
  639. * └───────────┘ └───────────┘
  640. * ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛
  641. *
  642. * If element edge D is outside scrolling box edge D and element width is less than scrolling box width
  643. *
  644. * from to
  645. * ┏ ━ ━ ━ ━ ┓ ┏ ━ ━ ━ ━ ┓
  646. * ┌───┐ ┌───┐
  647. * ┃ │ ┃ │ ┃ │ ┃
  648. * └───┘ └───┘
  649. * ┗ ━ ━ ━ ━ ┛ ┗ ━ ━ ━ ━ ┛
  650. *
  651. */
  652. if ((elementEdgeEnd > scrollingEdgeEnd && elementSize < scrollingSize) || (elementEdgeStart < scrollingEdgeStart && elementSize > scrollingSize)) {
  653. return elementEdgeEnd - scrollingEdgeEnd + scrollingBorderEnd;
  654. }
  655. return 0;
  656. }
  657. function computeScrollIntoView(target, scroller, options) {
  658. var block = options.block, inline = options.inline;
  659. // Used to handle the top most element that can be scrolled
  660. var scrollingElement = document.scrollingElement || document.documentElement;
  661. // Support pinch-zooming properly, making sure elements scroll into the visual viewport
  662. // Browsers that don't support visualViewport will report the layout viewport dimensions on document.documentElement.clientWidth/Height
  663. // and viewport dimensions on window.innerWidth/Height
  664. // https://www.quirksmode.org/mobile/viewports2.html
  665. // https://bokand.github.io/viewport/index.html
  666. var viewportWidth = window.visualViewport != null ? visualViewport.width : innerWidth;
  667. var viewportHeight = window.visualViewport != null ? visualViewport.height : innerHeight;
  668. var viewportX = window.scrollX != null ? window.scrollX : window.pageXOffset;
  669. var viewportY = window.scrollY != null ? window.scrollY : window.pageYOffset;
  670. var _a = target.getBoundingClientRect(), targetHeight = _a.height, targetWidth = _a.width, targetTop = _a.top, targetRight = _a.right, targetBottom = _a.bottom, targetLeft = _a.left;
  671. // These values mutate as we loop through and generate scroll coordinates
  672. var targetBlock = block === "start" || block === "nearest" ? targetTop : block === "end" ? targetBottom : targetTop + targetHeight / 2; // block === 'center
  673. var targetInline = inline === "center" ? targetLeft + targetWidth / 2 : inline === "end" ? targetRight : targetLeft; // inline === 'start || inline === 'nearest
  674. var _b = scroller.getBoundingClientRect(), height = _b.height, width = _b.width, top = _b.top, right = _b.right, bottom = _b.bottom, left = _b.left;
  675. var frameStyle = getComputedStyle(scroller);
  676. var borderLeft = parseInt(frameStyle.borderLeftWidth, 10);
  677. var borderTop = parseInt(frameStyle.borderTopWidth, 10);
  678. var borderRight = parseInt(frameStyle.borderRightWidth, 10);
  679. var borderBottom = parseInt(frameStyle.borderBottomWidth, 10);
  680. var blockScroll = 0;
  681. var inlineScroll = 0;
  682. // The property existance checks for offset[Width|Height] is because only HTMLElement objects have them, but any Element might pass by here
  683. // @TODO find out if the "as HTMLElement" overrides can be dropped
  684. var scrollbarWidth = "offsetWidth" in scroller ? scroller.offsetWidth - scroller.clientWidth - borderLeft - borderRight : 0;
  685. var scrollbarHeight = "offsetHeight" in scroller ? scroller.offsetHeight - scroller.clientHeight - borderTop - borderBottom : 0;
  686. if (scrollingElement === scroller) {
  687. // Handle viewport logic (document.documentElement or document.body)
  688. if (block === "start") {
  689. blockScroll = targetBlock;
  690. }
  691. else if (block === "end") {
  692. blockScroll = targetBlock - viewportHeight;
  693. }
  694. else if (block === "nearest") {
  695. blockScroll = alignNearest(viewportY, viewportY + viewportHeight, viewportHeight, borderTop, borderBottom, viewportY + targetBlock, viewportY + targetBlock + targetHeight, targetHeight);
  696. }
  697. else {
  698. // block === 'center' is the default
  699. blockScroll = targetBlock - viewportHeight / 2;
  700. }
  701. if (inline === "start") {
  702. inlineScroll = targetInline;
  703. }
  704. else if (inline === "center") {
  705. inlineScroll = targetInline - viewportWidth / 2;
  706. }
  707. else if (inline === "end") {
  708. inlineScroll = targetInline - viewportWidth;
  709. }
  710. else {
  711. // inline === 'nearest' is the default
  712. inlineScroll = alignNearest(viewportX, viewportX + viewportWidth, viewportWidth, borderLeft, borderRight, viewportX + targetInline, viewportX + targetInline + targetWidth, targetWidth);
  713. }
  714. // Apply scroll position offsets and ensure they are within bounds
  715. // @TODO add more test cases to cover this 100%
  716. blockScroll = Math.max(0, blockScroll + viewportY);
  717. inlineScroll = Math.max(0, inlineScroll + viewportX);
  718. }
  719. else {
  720. // Handle each scrolling frame that might exist between the target and the viewport
  721. if (block === "start") {
  722. blockScroll = targetBlock - top - borderTop;
  723. }
  724. else if (block === "end") {
  725. blockScroll = targetBlock - bottom + borderBottom + scrollbarHeight;
  726. }
  727. else if (block === "nearest") {
  728. blockScroll = alignNearest(top, bottom, height, borderTop, borderBottom + scrollbarHeight, targetBlock, targetBlock + targetHeight, targetHeight);
  729. }
  730. else {
  731. // block === 'center' is the default
  732. blockScroll = targetBlock - (top + height / 2) + scrollbarHeight / 2;
  733. }
  734. if (inline === "start") {
  735. inlineScroll = targetInline - left - borderLeft;
  736. }
  737. else if (inline === "center") {
  738. inlineScroll = targetInline - (left + width / 2) + scrollbarWidth / 2;
  739. }
  740. else if (inline === "end") {
  741. inlineScroll = targetInline - right + borderRight + scrollbarWidth;
  742. }
  743. else {
  744. // inline === 'nearest' is the default
  745. inlineScroll = alignNearest(left, right, width, borderLeft, borderRight + scrollbarWidth, targetInline, targetInline + targetWidth, targetWidth);
  746. }
  747. var scrollLeft = scroller.scrollLeft, scrollTop = scroller.scrollTop;
  748. // Ensure scroll coordinates are not out of bounds while applying scroll offsets
  749. blockScroll = Math.max(0, Math.min(scrollTop + blockScroll, scroller.scrollHeight - height + scrollbarHeight));
  750. inlineScroll = Math.max(0, Math.min(scrollLeft + inlineScroll, scroller.scrollWidth - width + scrollbarWidth));
  751. }
  752. return {
  753. top: blockScroll,
  754. left: inlineScroll
  755. };
  756. }
  757. /**
  758. * Patches the 'scrollIntoView' method on the Element prototype
  759. */
  760. function patchElementScrollIntoView() {
  761. Element.prototype.scrollIntoView = function (arg) {
  762. var normalizedOptions = arg == null || arg === true
  763. ? {
  764. block: "start",
  765. inline: "nearest"
  766. }
  767. : arg === false
  768. ? {
  769. block: "end",
  770. inline: "nearest"
  771. }
  772. : arg;
  773. // Find the nearest ancestor that can be scrolled
  774. var _a = __read(findNearestAncestorsWithScrollBehavior(this), 2), ancestorWithScroll = _a[0], ancestorWithScrollBehavior = _a[1];
  775. var behavior = normalizedOptions.behavior != null ? normalizedOptions.behavior : ancestorWithScrollBehavior;
  776. // If the behavior isn't smooth, simply invoke the original implementation and do no more
  777. if (behavior !== "smooth") {
  778. // Assert that 'scrollIntoView' is actually defined
  779. if (ELEMENT_ORIGINAL_SCROLL_INTO_VIEW != null) {
  780. ELEMENT_ORIGINAL_SCROLL_INTO_VIEW.call(this, normalizedOptions);
  781. }
  782. // Otherwise, invoke 'scrollTo' instead and provide the scroll coordinates
  783. else {
  784. var _b = computeScrollIntoView(this, ancestorWithScroll, normalizedOptions), top_1 = _b.top, left = _b.left;
  785. getOriginalScrollMethodForKind("scrollTo", this).call(this, left, top_1);
  786. }
  787. return;
  788. }
  789. ancestorWithScroll.scrollTo(__assign({ behavior: behavior }, computeScrollIntoView(this, ancestorWithScroll, normalizedOptions)));
  790. };
  791. }
  792. var ELEMENT_ORIGINAL_SCROLL_TOP_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollTop").set;
  793. /**
  794. * Patches the 'scrollTop' property descriptor on the Element prototype
  795. */
  796. function patchElementScrollTop() {
  797. Object.defineProperty(Element.prototype, "scrollTop", {
  798. set: function (scrollTop) {
  799. if (this.__adjustingScrollPosition) {
  800. return ELEMENT_ORIGINAL_SCROLL_TOP_SET_DESCRIPTOR.call(this, scrollTop);
  801. }
  802. handleScrollMethod(this, "scrollTo", this.scrollLeft, scrollTop);
  803. return scrollTop;
  804. }
  805. });
  806. }
  807. var ELEMENT_ORIGINAL_SCROLL_LEFT_SET_DESCRIPTOR = Object.getOwnPropertyDescriptor(Element.prototype, "scrollLeft").set;
  808. /**
  809. * Patches the 'scrollLeft' property descriptor on the Element prototype
  810. */
  811. function patchElementScrollLeft() {
  812. Object.defineProperty(Element.prototype, "scrollLeft", {
  813. set: function (scrollLeft) {
  814. if (this.__adjustingScrollPosition) {
  815. return ELEMENT_ORIGINAL_SCROLL_LEFT_SET_DESCRIPTOR.call(this, scrollLeft);
  816. }
  817. handleScrollMethod(this, "scrollTo", scrollLeft, this.scrollTop);
  818. return scrollLeft;
  819. }
  820. });
  821. }
  822. /**
  823. * Applies the polyfill
  824. */
  825. function patch() {
  826. // Element.prototype methods
  827. patchElementScroll();
  828. patchElementScrollBy();
  829. patchElementScrollTo();
  830. patchElementScrollIntoView();
  831. // Element.prototype descriptors
  832. patchElementScrollLeft();
  833. patchElementScrollTop();
  834. // window methods
  835. patchWindowScroll();
  836. patchWindowScrollBy();
  837. patchWindowScrollTo();
  838. // Navigation
  839. catchNavigation();
  840. }
  841. /**
  842. * Is true if the browser natively supports the Element.prototype.[scroll|scrollTo|scrollBy|scrollIntoView] methods
  843. * @type {boolean}
  844. */
  845. var SUPPORTS_ELEMENT_PROTOTYPE_SCROLL_METHODS = "scroll" in Element.prototype && "scrollTo" in Element.prototype && "scrollBy" in Element.prototype && "scrollIntoView" in Element.prototype;
  846. if (!SUPPORTS_SCROLL_BEHAVIOR || !SUPPORTS_ELEMENT_PROTOTYPE_SCROLL_METHODS) {
  847. patch();
  848. }
  849. }());