{"version":3,"file":"index.module.js","sources":["../src/index.js"],"sourcesContent":["import Plugin from '@swup/plugin';\nimport { getCurrentUrl, Location, queryAll } from 'swup';\nimport Scrl from 'scrl';\n\n/**\n * Class representing a Scroll Plugin.\n * @extends Plugin\n */\nexport default class ScrollPlugin extends Plugin {\n\tname = 'ScrollPlugin';\n\n\t/**\n\t * Constructor\n\t * @param {?object} options the plugin options\n\t */\n\tconstructor(options) {\n\t\tsuper();\n\t\tconst defaultOptions = {\n\t\t\tdoScrollingRightAway: false,\n\t\t\tanimateScroll: {\n\t\t\t\tbetweenPages: true,\n\t\t\t\tsamePageWithHash: true,\n\t\t\t\tsamePage: true\n\t\t\t},\n\t\t\tscrollFriction: 0.3,\n\t\t\tscrollAcceleration: 0.04,\n\t\t\tgetAnchorElement: null,\n\t\t\toffset: 0,\n\t\t\tscrollContainers: `[data-swup-scroll-container]`,\n\t\t\tshouldResetScrollPosition: (htmlAnchorElement) => true\n\t\t};\n\n\t\tthis.options = {\n\t\t\t...defaultOptions,\n\t\t\t...options\n\t\t};\n\t}\n\n\t/**\n\t * Runs if the plugin is mounted\n\t */\n\tmount() {\n\t\tconst swup = this.swup;\n\n\t\t// add empty handlers array for scroll events\n\t\tswup._handlers.scrollDone = [];\n\t\tswup._handlers.scrollStart = [];\n\n\t\t// Initialize Scrl for smooth animations\n\t\tthis.scrl = new Scrl({\n\t\t\tonStart: () => swup.triggerEvent('scrollStart'),\n\t\t\tonEnd: () => swup.triggerEvent('scrollDone'),\n\t\t\tonCancel: () => swup.triggerEvent('scrollDone'),\n\t\t\tfriction: this.options.scrollFriction,\n\t\t\tacceleration: this.options.scrollAcceleration\n\t\t});\n\n\t\t// set scrollTo method of swup and animate based on current animateScroll option\n\t\tswup.scrollTo = (offset, animate = true) => {\n\t\t\tif (animate) {\n\t\t\t\tthis.scrl.scrollTo(offset);\n\t\t\t} else {\n\t\t\t\tswup.triggerEvent('scrollStart');\n\t\t\t\twindow.scrollTo(0, offset);\n\t\t\t\tswup.triggerEvent('scrollDone');\n\t\t\t}\n\t\t};\n\n\t\t// This object will hold all scroll positions\n\t\tthis.scrollPositionsStore = {};\n\t\t// this URL helps with storing the current scroll positions on `willReplaceContent`\n\t\tthis.currentCacheKey = this.getCurrentCacheKey();\n\n\t\t// disable browser scroll control on popstates when\n\t\t// animateHistoryBrowsing option is enabled in swup.\n\t\t// Cache the previous setting to be able to properly restore it on unmount\n\t\tthis.previousScrollRestoration = window.history.scrollRestoration;\n\t\tif (swup.options.animateHistoryBrowsing) {\n\t\t\twindow.history.scrollRestoration = 'manual';\n\t\t}\n\n\t\t// scroll to the top of the page\n\t\tswup.on('samePage', this.onSamePage);\n\n\t\t// scroll to referenced element on the same page\n\t\tswup.on('samePageWithHash', this.onSamePageWithHash);\n\n\t\t// scroll to the referenced element\n\t\tswup.on('transitionStart', this.onTransitionStart);\n\n\t\t// scroll to the referenced element when it's in the page (after render)\n\t\tswup.on('contentReplaced', this.onContentReplaced);\n\n\t\tswup.on('willReplaceContent', this.onWillReplaceContent);\n\t\tswup.on('clickLink', this.onClickLink);\n\t}\n\n\t/**\n\t * Runs when the plugin is unmounted\n\t */\n\tunmount() {\n\t\tconst swup = this.swup;\n\t\tswup.scrollTo = null;\n\n\t\tdelete this.scrl;\n\t\tthis.scrl = null;\n\n\t\tswup.off('samePage', this.onSamePage);\n\t\tswup.off('samePageWithHash', this.onSamePageWithHash);\n\t\tswup.off('transitionStart', this.onTransitionStart);\n\t\tswup.off('contentReplaced', this.onContentReplaced);\n\t\tswup.off('willReplaceContent', this.onWillReplaceContent);\n\t\tswup.off('clickLink', this.onClickLink);\n\n\t\tswup._handlers.scrollDone = null;\n\t\tswup._handlers.scrollStart = null;\n\n\t\twindow.history.scrollRestoration = this.previousScrollRestoration;\n\t}\n\n\t/**\n\t * Detects if a scroll should be animated, based on context\n\t * @param {string} context\n\t * @returns {boolean}\n\t */\n\tshouldAnimate(context) {\n\t\tif (typeof this.options.animateScroll === 'boolean') {\n\t\t\treturn this.options.animateScroll;\n\t\t}\n\t\treturn this.options.animateScroll[context];\n\t}\n\n\t/**\n\t * Get an element based on anchor\n\t * @param {string} hash\n\t * @returns {mixed}\n\t */\n\tgetAnchorElement = (hash = '') => {\n\t\t// Look for a custom function provided via the plugin options\n\t\tif (typeof this.options.getAnchorElement === 'function') {\n\t\t\treturn this.options.getAnchorElement(hash);\n\t\t}\n\t\t// Look for a the built-in function in swup, added in swup 2.0.16\n\t\tif (typeof this.swup.getAnchorElement === 'function') {\n\t\t\treturn this.swup.getAnchorElement(hash);\n\t\t}\n\t\t// Finally, return a native browser query\n\t\treturn document.querySelector(hash);\n\t};\n\n\t/**\n\t * Get the offset for a scroll\n\t * @param {(HtmlELement|null)} element\n\t * @returns {number}\n\t */\n\tgetOffset = (element = null) => {\n\t\t// If options.offset is a function, apply and return it\n\t\tif (typeof this.options.offset === 'function') {\n\t\t\treturn parseInt(this.options.offset(element), 10);\n\t\t}\n\t\t// Otherwise, return the sanitized offset\n\t\treturn parseInt(this.options.offset, 10);\n\t};\n\n\t/**\n\t * Handles `samePage`\n\t */\n\tonSamePage = () => {\n\t\tthis.swup.scrollTo(0, this.shouldAnimate('samePage'));\n\t};\n\n\t/**\n\t * Handles `onSamePageWithHash`\n\t * @param {PointerEvent} event\n\t */\n\tonSamePageWithHash = (event) => {\n\t\tconst link = event.delegateTarget;\n\t\tthis.maybeScrollToAnchor(link.hash, 'samePageWithHash');\n\t};\n\n\t/**\n\t * Attempts to scroll to an anchor\n\t * @param {string} hash\n\t * @param {string} context\n\t * @returns {boolean}\n\t */\n\tmaybeScrollToAnchor(hash, context) {\n\t\t// Bail early if the hash is null\n\t\tif (hash == null) {\n\t\t\treturn false;\n\t\t}\n\t\tconst element = this.getAnchorElement(hash);\n\t\tif (!element) {\n\t\t\tconsole.warn(`Element ${hash} not found`);\n\t\t\treturn false;\n\t\t}\n\t\tif (!(element instanceof Element)) {\n\t\t\tconsole.warn(`Element ${hash} is not a DOM node`);\n\t\t\treturn false;\n\t\t}\n\t\tconst top =\n\t\t\telement.getBoundingClientRect().top + window.pageYOffset - this.getOffset(element);\n\t\tthis.swup.scrollTo(top, this.shouldAnimate(context));\n\t\treturn true;\n\t}\n\n\t/**\n\t * Handles `transitionStart`\n\t * @param {PopStateEvent} popstate\n\t */\n\tonTransitionStart = (popstate) => {\n\t\tif (this.options.doScrollingRightAway && !this.swup.scrollToElement) {\n\t\t\tthis.doScrollingBetweenPages(popstate);\n\t\t}\n\t};\n\n\t/**\n\t * Handles `contentReplaced`\n\t * @param {PopStateEvent} popstate\n\t */\n\tonContentReplaced = (popstate) => {\n\t\tif (!this.options.doScrollingRightAway || this.swup.scrollToElement) {\n\t\t\tthis.doScrollingBetweenPages(popstate);\n\t\t}\n\n\t\tthis.restoreScrollContainers(popstate);\n\t};\n\n\t/**\n\t * Scrolls the window, based on context\n\t * @param {(PopStateEvent|boolean)} popstate\n\t * @returns {void}\n\t */\n\tdoScrollingBetweenPages = (popstate) => {\n\t\tconst swup = this.swup;\n\n\t\t// Bail early on popstate and inactive `animateHistoryBrowsing`\n\t\tif (popstate && !swup.options.animateHistoryBrowsing) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Try scrolling to a given anchor\n\t\tif (this.maybeScrollToAnchor(swup.scrollToElement, 'betweenPages')) {\n\t\t\tswup.scrollToElement = null;\n\t\t\treturn;\n\t\t}\n\n\t\t// Finally, scroll to either the stored scroll position or to the very top of the page\n\t\tconst scrollPositions = this.getStoredScrollPositions(this.getCurrentCacheKey()) || {};\n\t\tconst top = (scrollPositions.window && scrollPositions.window.top) || 0;\n\t\t// Give possible JavaScript time to execute before scrolling\n\t\trequestAnimationFrame(() => swup.scrollTo(top, this.shouldAnimate('betweenPages')));\n\t};\n\n\t/**\n\t * Stores the current scroll positions for the URL we just came from\n\t */\n\tonWillReplaceContent = () => {\n\t\tthis.storeScrollPositions(this.currentCacheKey);\n\t\tthis.currentCacheKey = this.getCurrentCacheKey();\n\t};\n\n\t/**\n\t * Handles `clickLink`\n\t * @param {PointerEvent}\n\t * @returns {void}\n\t */\n\tonClickLink = (event) => {\n\t\tthis.maybeResetScrollPositions(event.delegateTarget);\n\t};\n\n\t/**\n\t * Deletes the scroll positions for the URL a link is pointing to,\n\t * if shouldResetScrollPosition evaluates to true\n\t * @param {HTMLAnchorElement} htmlAnchorElement\n\t * @returns {void}\n\t */\n\tmaybeResetScrollPositions(htmlAnchorElement) {\n\t\tif (!this.options.shouldResetScrollPosition(htmlAnchorElement)) {\n\t\t\treturn;\n\t\t}\n\t\tconst { url } = Location.fromElement(htmlAnchorElement);\n\t\tthis.resetScrollPositions(url);\n\t}\n\n\t/**\n\t * Stores the scroll positions for the current URL\n\t * @param {string} url\n\t * @returns {void}\n\t */\n\tstoreScrollPositions(url) {\n\t\t// retrieve the current scroll position for all containers\n\t\tconst containers = queryAll(this.options.scrollContainers).map((el) => ({\n\t\t\ttop: el.scrollTop,\n\t\t\tleft: el.scrollLeft\n\t\t}));\n\n\t\t// construct the final object entry, with the window scroll positions added\n\t\tthis.scrollPositionsStore[url] = {\n\t\t\twindow: { top: window.scrollY, left: window.scrollX },\n\t\t\tcontainers\n\t\t};\n\t}\n\n\t/**\n\t * Resets stored scroll positions for a given URL\n\t * @param {string} url\n\t */\n\tresetScrollPositions(url) {\n\t\tdelete this.scrollPositionsStore[url];\n\t\tthis.scrollPositionsStore[url] = null;\n\t}\n\n\t/**\n\t * Get the stored scroll positions for a given URL from the cache\n\t * @returns {(object|null)}\n\t */\n\tgetStoredScrollPositions(url) {\n\t\treturn this.scrollPositionsStore[url];\n\t}\n\n\t/**\n\t * Restore the scroll positions for all matching scrollContainers\n\t * @returns void\n\t */\n\trestoreScrollContainers(popstate) {\n\t\t// get the stored scroll positions from the cache\n\t\tconst scrollPositions = this.getStoredScrollPositions(this.getCurrentCacheKey()) || {};\n\t\tif (scrollPositions.containers == null) {\n\t\t\treturn;\n\t\t}\n\n\t\t// cycle through all containers on the current page and restore their scroll positions, if appropriate\n\t\tqueryAll(this.options.scrollContainers).forEach((el, index) => {\n\t\t\tconst scrollPosition = scrollPositions.containers[index];\n\t\t\tif (scrollPosition == null) return;\n\t\t\tel.scrollTop = scrollPosition.top;\n\t\t\tel.scrollLeft = scrollPosition.left;\n\t\t});\n\t}\n\t/**\n\t * Get the current cache key for the scroll positions.\n\t * uses `getCurrentUrl` and applies `swup.resolveUrl` if present\n\t *\n\t * `swup.resolveUrl` will become available in Swup 3\n\t *\n\t * @returns {string}\n\t */\n\tgetCurrentCacheKey() {\n\t\tconst path = getCurrentUrl();\n\t\tif (typeof this.swup.resolveUrl === 'function') {\n\t\t\treturn this.swup.resolveUrl(path);\n\t\t}\n\t\treturn path;\n\t}\n}\n"],"names":["ScrollPlugin","constructor","options","_this","super","this","name","getAnchorElement","hash","swup","document","querySelector","getOffset","element","offset","parseInt","onSamePage","scrollTo","shouldAnimate","onSamePageWithHash","event","maybeScrollToAnchor","delegateTarget","onTransitionStart","popstate","doScrollingRightAway","scrollToElement","doScrollingBetweenPages","onContentReplaced","restoreScrollContainers","animateHistoryBrowsing","scrollPositions","getStoredScrollPositions","getCurrentCacheKey","top","window","requestAnimationFrame","onWillReplaceContent","storeScrollPositions","currentCacheKey","onClickLink","maybeResetScrollPositions","animateScroll","betweenPages","samePageWithHash","samePage","scrollFriction","scrollAcceleration","scrollContainers","shouldResetScrollPosition","htmlAnchorElement","mount","_handlers","scrollDone","scrollStart","scrl","onStart","triggerEvent","onEnd","onCancel","friction","acceleration","animate","_this2","scrollPositionsStore","previousScrollRestoration","history","scrollRestoration","on","unmount","off","context","console","warn","Element","getBoundingClientRect","pageYOffset","url","Location","fromElement","resetScrollPositions","containers","queryAll","map","el","scrollTop","left","scrollLeft","scrollY","scrollX","forEach","index","scrollPosition","path","getCurrentUrl","resolveUrl"],"mappings":"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"}