diff options
Diffstat (limited to 'node_modules/swup/src')
27 files changed, 0 insertions, 2414 deletions
diff --git a/node_modules/swup/src/Swup.ts b/node_modules/swup/src/Swup.ts deleted file mode 100644 index 7fb76f06..00000000 --- a/node_modules/swup/src/Swup.ts +++ /dev/null @@ -1,382 +0,0 @@ -import { type DelegateEvent } from 'delegate-it'; - -import version from './config/version.js'; - -import { delegateEvent, getCurrentUrl, Location, updateHistoryRecord } from './helpers.js'; -import { type DelegateEventUnsubscribe } from './helpers/delegateEvent.js'; - -import { Cache } from './modules/Cache.js'; -import { Classes } from './modules/Classes.js'; -import { type Visit, createVisit } from './modules/Visit.js'; -import { Hooks, type HookName, type HookInitOptions } from './modules/Hooks.js'; -import { getAnchorElement } from './modules/getAnchorElement.js'; -import { awaitAnimations } from './modules/awaitAnimations.js'; -import { navigate, performNavigation, type NavigationToSelfAction } from './modules/navigate.js'; -import { fetchPage } from './modules/fetchPage.js'; -import { animatePageOut } from './modules/animatePageOut.js'; -import { replaceContent } from './modules/replaceContent.js'; -import { scrollToContent } from './modules/scrollToContent.js'; -import { animatePageIn } from './modules/animatePageIn.js'; -import { renderPage } from './modules/renderPage.js'; -import { use, unuse, findPlugin, type Plugin } from './modules/plugins.js'; -import { isSameResolvedUrl, resolveUrl } from './modules/resolveUrl.js'; -import { nextTick } from './utils.js'; -import { type HistoryState } from './helpers/history.js'; - -/** Options for customizing swup's behavior. */ -export type Options = { - /** Whether history visits are animated. Default: `false` */ - animateHistoryBrowsing: boolean; - /** Selector for detecting animation timing. Default: `[class*="transition-"]` */ - animationSelector: string | false; - /** Elements on which to add animation classes. Default: `html` element */ - animationScope: 'html' | 'containers'; - /** Enable in-memory page cache. Default: `true` */ - cache: boolean; - /** Content containers to be replaced on page visits. Default: `['#swup']` */ - containers: string[]; - /** Callback for ignoring visits. Receives the element and event that triggered the visit. */ - ignoreVisit: (url: string, { el, event }: { el?: Element; event?: Event }) => boolean; - /** Selector for links that trigger visits. Default: `'a[href]'` */ - linkSelector: string; - /** How swup handles links to the same page. Default: `scroll` */ - linkToSelf: NavigationToSelfAction; - /** Enable native animations using the View Transitions API. */ - native: boolean; - /** Hook handlers to register. */ - hooks: Partial<HookInitOptions>; - /** Plugins to register on startup. */ - plugins: Plugin[]; - /** Custom headers sent along with fetch requests. */ - requestHeaders: Record<string, string>; - /** Rewrite URLs before loading them. */ - resolveUrl: (url: string) => string; - /** Callback for telling swup to ignore certain popstate events. */ - skipPopStateHandling: (event: PopStateEvent) => boolean; - /** Request timeout in milliseconds. */ - timeout: number; -}; - -const defaults: Options = { - animateHistoryBrowsing: false, - animationSelector: '[class*="transition-"]', - animationScope: 'html', - cache: true, - containers: ['#swup'], - hooks: {}, - ignoreVisit: (url, { el } = {}) => !!el?.closest('[data-no-swup]'), - linkSelector: 'a[href]', - linkToSelf: 'scroll', - native: false, - plugins: [], - resolveUrl: (url) => url, - requestHeaders: { - 'X-Requested-With': 'swup', - 'Accept': 'text/html, application/xhtml+xml' - }, - skipPopStateHandling: (event) => (event.state as HistoryState)?.source !== 'swup', - timeout: 0 -}; - -/** Swup page transition library. */ -export default class Swup { - /** Library version */ - readonly version: string = version; - /** Options passed into the instance */ - options: Options; - /** Default options before merging user options */ - readonly defaults: Options = defaults; - /** Registered plugin instances */ - plugins: Plugin[] = []; - /** Data about the current visit */ - visit: Visit; - /** Cache instance */ - readonly cache: Cache; - /** Hook registry */ - readonly hooks: Hooks; - /** Animation class manager */ - readonly classes: Classes; - /** Location of the currently visible page */ - location: Location = Location.fromUrl(window.location.href); - /** URL of the currently visible page @deprecated Use swup.location.url instead */ - get currentPageUrl(): string { - return this.location.url; - } - /** Index of the current history entry */ - protected currentHistoryIndex: number; - /** Delegated event subscription handle */ - protected clickDelegate?: DelegateEventUnsubscribe; - /** Navigation status */ - protected navigating: boolean = false; - /** Run anytime a visit ends */ - protected onVisitEnd?: () => Promise<unknown>; - - /** Install a plugin */ - use = use; - /** Uninstall a plugin */ - unuse = unuse; - /** Find a plugin by name or instance */ - findPlugin = findPlugin; - - /** Log a message. Has no effect unless debug plugin is installed */ - log: (message: string, context?: unknown) => void = () => {}; - - /** Navigate to a new URL */ - navigate = navigate; - /** Actually perform a navigation */ - protected performNavigation = performNavigation; - /** Create a new context for this visit */ - protected createVisit = createVisit; - /** Register a delegated event listener */ - delegateEvent = delegateEvent; - /** Fetch a page from the server */ - fetchPage = fetchPage; - /** Resolve when animations on the page finish */ - awaitAnimations = awaitAnimations; - protected renderPage = renderPage; - /** Replace the content after page load */ - replaceContent = replaceContent; - protected animatePageIn = animatePageIn; - protected animatePageOut = animatePageOut; - protected scrollToContent = scrollToContent; - /** Find the anchor element for a given hash */ - getAnchorElement = getAnchorElement; - - /** Get the current page URL */ - getCurrentUrl = getCurrentUrl; - /** Resolve a URL to its final location */ - resolveUrl = resolveUrl; - /** Check if two URLs resolve to the same location */ - protected isSameResolvedUrl = isSameResolvedUrl; - - constructor(options: Partial<Options> = {}) { - // Merge defaults and options - this.options = { ...this.defaults, ...options }; - - this.handleLinkClick = this.handleLinkClick.bind(this); - this.handlePopState = this.handlePopState.bind(this); - - this.cache = new Cache(this); - this.classes = new Classes(this); - this.hooks = new Hooks(this); - this.visit = this.createVisit({ to: '' }); - - this.currentHistoryIndex = (window.history.state as HistoryState)?.index ?? 1; - - this.enable(); - } - - /** Enable this instance, adding listeners and classnames. */ - async enable() { - // Add event listener - const { linkSelector } = this.options; - this.clickDelegate = this.delegateEvent(linkSelector, 'click', this.handleLinkClick); - - window.addEventListener('popstate', this.handlePopState); - - // Set scroll restoration to manual if animating history visits - if (this.options.animateHistoryBrowsing) { - window.history.scrollRestoration = 'manual'; - } - - // Initial save to cache - if (this.options.cache) { - // Disabled to avoid caching modified dom state: logic moved to preload plugin - // https://github.com/swup/swup/issues/475 - } - - // Sanitize/check native option - this.options.native = this.options.native && !!document.startViewTransition; - - // Mount plugins - this.options.plugins.forEach((plugin) => this.use(plugin)); - - // Install user hooks - for (const [key, handler] of Object.entries(this.options.hooks)) { - // Build hook options from modifier suffix: 'content:replace.before' => { before: true } - const [hook, modifiers] = this.hooks.parseName(key as HookName); - // @ts-expect-error: object.entries() does not preserve key/value types - this.hooks.on(hook, handler, modifiers); - } - - // Create initial history record - if ((window.history.state as HistoryState)?.source !== 'swup') { - updateHistoryRecord(null, { index: this.currentHistoryIndex }); - } - - // Give consumers a chance to hook into enable - await nextTick(); - - // Trigger enable hook - await this.hooks.call('enable', undefined, undefined, () => { - const html = document.documentElement; - html.classList.add('swup-enabled'); - html.classList.toggle('swup-native', this.options.native); - }); - } - - /** Disable this instance, removing listeners and classnames. */ - async destroy() { - // remove delegated listener - this.clickDelegate!.destroy(); - - // remove popstate listener - window.removeEventListener('popstate', this.handlePopState); - - // empty cache - this.cache.clear(); - - // unmount plugins - this.options.plugins.forEach((plugin) => this.unuse(plugin)); - - // trigger disable hook - await this.hooks.call('disable', undefined, undefined, () => { - const html = document.documentElement; - html.classList.remove('swup-enabled'); - html.classList.remove('swup-native'); - }); - - // remove handlers - this.hooks.clear(); - } - - /** Determine if a visit should be ignored by swup, based on URL or trigger element. */ - shouldIgnoreVisit(href: string, { el, event }: { el?: Element; event?: Event } = {}) { - const { origin, url, hash } = Location.fromUrl(href); - - // Ignore if the new origin doesn't match the current one - if (origin !== window.location.origin) { - return true; - } - - // Ignore if the link/form would open a new window (or none at all) - if (el && this.triggerWillOpenNewWindow(el)) { - return true; - } - - // Ignore if the visit should be ignored as per user options - if (this.options.ignoreVisit(url + hash, { el, event })) { - return true; - } - - // Finally, allow the visit - return false; - } - - protected handleLinkClick(event: DelegateEvent<MouseEvent>) { - const el = event.delegateTarget as HTMLAnchorElement; - const { href, url, hash } = Location.fromElement(el); - - // Exit early if the link should be ignored - if (this.shouldIgnoreVisit(href, { el, event })) { - return; - } - - // Ignore if swup is currently navigating towards the link's URL - if (this.navigating && url === this.visit.to.url) { - event.preventDefault(); - return; - } - - const visit = this.createVisit({ to: url, hash, el, event }); - - // Exit early if control key pressed - if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) { - this.hooks.callSync('link:newtab', visit, { href }); - return; - } - - // Exit early if other than left mouse button - if (event.button !== 0) { - return; - } - - this.hooks.callSync('link:click', visit, { el, event }, () => { - const from = visit.from.url ?? ''; - - event.preventDefault(); - - // Handle links to the same page - if (!url || url === from) { - if (hash) { - // With hash: scroll to anchor - this.hooks.callSync('link:anchor', visit, { hash }, () => { - updateHistoryRecord(url + hash); - this.scrollToContent(visit); - }); - } else { - // Without hash: scroll to top or load/reload page - this.hooks.callSync('link:self', visit, undefined, () => { - if (this.options.linkToSelf === 'navigate') { - this.performNavigation(visit); - } else { - updateHistoryRecord(url); - this.scrollToContent(visit); - } - }); - } - return; - } - - // Exit early if the resolved path hasn't changed - if (this.isSameResolvedUrl(url, from)) { - return; - } - - // Finally, proceed with loading the page - this.performNavigation(visit); - }); - } - - protected handlePopState(event: PopStateEvent) { - const href: string = (event.state as HistoryState)?.url ?? window.location.href; - - // Exit early if this event should be ignored - if (this.options.skipPopStateHandling(event)) { - return; - } - - // Exit early if the resolved path hasn't changed - if (this.isSameResolvedUrl(getCurrentUrl(), this.location.url)) { - return; - } - - const { url, hash } = Location.fromUrl(href); - - const visit = this.createVisit({ to: url, hash, event }); - - // Mark as history visit - visit.history.popstate = true; - - // Determine direction of history visit - const index = (event.state as HistoryState)?.index ?? 0; - if (index && index !== this.currentHistoryIndex) { - const direction = index - this.currentHistoryIndex > 0 ? 'forwards' : 'backwards'; - visit.history.direction = direction; - this.currentHistoryIndex = index; - } - - // Disable animation & scrolling for history visits - visit.animation.animate = false; - visit.scroll.reset = false; - visit.scroll.target = false; - - // Animated history visit: re-enable animation & scroll reset - if (this.options.animateHistoryBrowsing) { - visit.animation.animate = true; - visit.scroll.reset = true; - } - - this.hooks.callSync('history:popstate', visit, { event }, () => { - this.performNavigation(visit); - }); - } - - /** Determine whether an element will open a new tab when clicking/activating. */ - protected triggerWillOpenNewWindow(triggerEl: Element) { - if (triggerEl.matches('[download], [target="_blank"]')) { - return true; - } - return false; - } -} diff --git a/node_modules/swup/src/config/version.ts b/node_modules/swup/src/config/version.ts deleted file mode 100644 index b59b1a2b..00000000 --- a/node_modules/swup/src/config/version.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Import swup's current version from package.json - */ - -// This will only work in webpack 4 -// export { version as default } from '../../package.json'; - -// This will work in microbundle + webpack 5, but won't treeshake in webpack 4 -// @ts-ignore: package.json is outside of rootDir -import pckg from '../../package.json'; - -export default pckg.version; diff --git a/node_modules/swup/src/helpers.ts b/node_modules/swup/src/helpers.ts deleted file mode 100644 index a4aef87d..00000000 --- a/node_modules/swup/src/helpers.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Re-export all helpers to allow custom package export path -// e.g. import { updateHistoryRecord } from 'swup' - -export { classify } from './helpers/classify.js'; -export { createHistoryRecord, updateHistoryRecord } from './helpers/history.js'; -export { delegateEvent } from './helpers/delegateEvent.js'; -export { getCurrentUrl } from './helpers/getCurrentUrl.js'; -export { Location } from './helpers/Location.js'; -export { matchPath } from './helpers/matchPath.js'; diff --git a/node_modules/swup/src/helpers/Location.ts b/node_modules/swup/src/helpers/Location.ts deleted file mode 100755 index aca9d6b6..00000000 --- a/node_modules/swup/src/helpers/Location.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * A helper for creating a Location from either an element - * or a URL object/string - * - */ -export class Location extends URL { - constructor(url: URL | string, base: string = document.baseURI) { - super(url.toString(), base); - // Fix Safari bug with extending native classes - Object.setPrototypeOf(this, Location.prototype); - } - - /** - * The full local path including query params. - */ - get url(): string { - return this.pathname + this.search; - } - - /** - * Instantiate a Location from an element's href attribute - * @param el - * @returns new Location instance - */ - static fromElement(el: Element): Location { - const href = el.getAttribute('href') || el.getAttribute('xlink:href') || ''; - return new Location(href); - } - - /** - * Instantiate a Location from a URL object or string - * @param url - * @returns new Location instance - */ - static fromUrl(url: URL | string): Location { - return new Location(url); - } -} diff --git a/node_modules/swup/src/helpers/classify.ts b/node_modules/swup/src/helpers/classify.ts deleted file mode 100755 index 3d9ecec6..00000000 --- a/node_modules/swup/src/helpers/classify.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** Turn a string into a slug by lowercasing and replacing whitespace. */ -export const classify = (text: string, fallback?: string): string => { - const output = String(text) - .toLowerCase() - // .normalize('NFD') // split an accented letter in the base letter and the acent - // .replace(/[\u0300-\u036f]/g, '') // remove all previously split accents - .replace(/[\s/_.]+/g, '-') // replace spaces and _./ with '-' - .replace(/[^\w-]+/g, '') // remove all non-word chars - .replace(/--+/g, '-') // replace repeating '-' with single '-' - .replace(/^-+|-+$/g, ''); // trim '-' from edges - return output || fallback || ''; -}; diff --git a/node_modules/swup/src/helpers/delegateEvent.ts b/node_modules/swup/src/helpers/delegateEvent.ts deleted file mode 100755 index eec44f50..00000000 --- a/node_modules/swup/src/helpers/delegateEvent.ts +++ /dev/null @@ -1,27 +0,0 @@ -import delegate, { - type DelegateEventHandler, - type DelegateOptions, - type EventType -} from 'delegate-it'; -import type { ParseSelector } from 'typed-query-selector/parser.js'; - -export type DelegateEventUnsubscribe = { - destroy: () => void; -}; - -/** Register a delegated event listener. */ -export const delegateEvent = < - Selector extends string, - TElement extends Element = ParseSelector<Selector, HTMLElement>, - TEvent extends EventType = EventType ->( - selector: Selector, - type: TEvent, - callback: DelegateEventHandler<GlobalEventHandlersEventMap[TEvent], TElement>, - options?: DelegateOptions -): DelegateEventUnsubscribe => { - const controller = new AbortController(); - options = { ...options, signal: controller.signal }; - delegate<Selector, TElement, TEvent>(selector, type, callback, options); - return { destroy: () => controller.abort() }; -}; diff --git a/node_modules/swup/src/helpers/getCurrentUrl.ts b/node_modules/swup/src/helpers/getCurrentUrl.ts deleted file mode 100755 index ab0f0d57..00000000 --- a/node_modules/swup/src/helpers/getCurrentUrl.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** Get the current page URL: path name + query params. Optionally including hash. */ -export const getCurrentUrl = ({ hash }: { hash?: boolean } = {}): string => { - return window.location.pathname + window.location.search + (hash ? window.location.hash : ''); -}; diff --git a/node_modules/swup/src/helpers/history.ts b/node_modules/swup/src/helpers/history.ts deleted file mode 100755 index e7ea2f6c..00000000 --- a/node_modules/swup/src/helpers/history.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { getCurrentUrl } from './getCurrentUrl.js'; - -export interface HistoryState { - url: string; - source: 'swup'; - random: number; - index?: number; - [key: string]: unknown; -} - -type HistoryData = Record<string, unknown>; - -/** Create a new history record with a custom swup identifier. */ -export const createHistoryRecord = (url: string, data: HistoryData = {}): void => { - url = url || getCurrentUrl({ hash: true }); - const state: HistoryState = { - url, - random: Math.random(), - source: 'swup', - ...data - }; - window.history.pushState(state, '', url); -}; - -/** Update the current history record with a custom swup identifier. */ -export const updateHistoryRecord = (url: string | null = null, data: HistoryData = {}): void => { - url = url || getCurrentUrl({ hash: true }); - const currentState = (window.history.state as HistoryState) || {}; - const state: HistoryState = { - ...currentState, - url, - random: Math.random(), - source: 'swup', - ...data - }; - window.history.replaceState(state, '', url); -}; diff --git a/node_modules/swup/src/helpers/matchPath.ts b/node_modules/swup/src/helpers/matchPath.ts deleted file mode 100644 index e1332911..00000000 --- a/node_modules/swup/src/helpers/matchPath.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { match } from 'path-to-regexp'; - -import type { - Path, - ParseOptions, - TokensToRegexpOptions, - RegexpToFunctionOptions, - MatchFunction -} from 'path-to-regexp'; - -export { type Path }; - -/** Create a match function from a path pattern that checks if a URLs matches it. */ -export const matchPath = <P extends object = object>( - path: Path, - options?: ParseOptions & TokensToRegexpOptions & RegexpToFunctionOptions -): MatchFunction<P> => { - try { - return match<P>(path, options); - } catch (error) { - throw new Error(`[swup] Error parsing path "${String(path)}":\n${String(error)}`); - } -}; diff --git a/node_modules/swup/src/index.ts b/node_modules/swup/src/index.ts deleted file mode 100644 index 29fcee5b..00000000 --- a/node_modules/swup/src/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { Path } from 'path-to-regexp'; -import type { DelegateEventUnsubscribe } from './helpers/delegateEvent.js'; -import type { DelegateEvent, DelegateEventHandler } from 'delegate-it'; -import Swup from './Swup.js'; -import type { Options } from './Swup.js'; -import type { CacheData } from './modules/Cache.js'; -import type { PageData } from './modules/fetchPage.js'; -import type { - Visit, - VisitFrom, - VisitTo, - VisitAnimation, - VisitScroll, - VisitHistory -} from './modules/Visit.js'; -import type { - HookName, - HookDefinitions, - HookArguments, - HookReturnValues, - HookHandler, - HookDefaultHandler, - HookOptions, - HookUnregister, - HookEvent -} from './modules/Hooks.js'; -import type { Plugin } from './modules/plugins.js'; - -export default Swup; -export * from './helpers.js'; -export * from './utils.js'; -export type { - Swup, - Options, - Plugin, - CacheData, - PageData, - Path, - Visit, - VisitFrom, - VisitTo, - VisitAnimation, - VisitScroll, - VisitHistory, - HookName, - HookDefinitions, - HookArguments, - HookReturnValues, - HookHandler, - HookHandler as Handler, // backwards compatibility - HookDefaultHandler, - HookOptions, - HookUnregister, - HookEvent, - DelegateEvent, - DelegateEventHandler, - DelegateEventUnsubscribe -}; diff --git a/node_modules/swup/src/modules/Cache.ts b/node_modules/swup/src/modules/Cache.ts deleted file mode 100755 index 913e10a9..00000000 --- a/node_modules/swup/src/modules/Cache.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type Swup from '../Swup.js'; -import { Location } from '../helpers.js'; -import { type PageData } from './fetchPage.js'; - -export interface CacheData extends PageData {} - -/** - * In-memory page cache. - */ -export class Cache { - /** Swup instance this cache belongs to */ - protected swup: Swup; - - /** Cached pages, indexed by URL */ - protected pages: Map<string, CacheData> = new Map(); - - constructor(swup: Swup) { - this.swup = swup; - } - - /** Number of cached pages in memory. */ - get size(): number { - return this.pages.size; - } - - /** All cached pages. */ - get all() { - const copy = new Map(); - this.pages.forEach((page, key) => { - copy.set(key, { ...page }); - }); - return copy; - } - - /** Check if the given URL has been cached. */ - has(url: string): boolean { - return this.pages.has(this.resolve(url)); - } - - /** Return a shallow copy of the cached page object if available. */ - get(url: string): CacheData | undefined { - const result = this.pages.get(this.resolve(url)); - if (!result) return result; - return { ...result }; - } - - /** Create a cache record for the specified URL. */ - set(url: string, page: CacheData) { - url = this.resolve(url); - page = { ...page, url }; - this.pages.set(url, page); - this.swup.hooks.callSync('cache:set', undefined, { page }); - } - - /** Update a cache record, overwriting or adding custom data. */ - update(url: string, payload: object) { - url = this.resolve(url); - const page = { ...this.get(url), ...payload, url } as CacheData; - this.pages.set(url, page); - } - - /** Delete a cache record. */ - delete(url: string): void { - this.pages.delete(this.resolve(url)); - } - - /** Empty the cache. */ - clear(): void { - this.pages.clear(); - this.swup.hooks.callSync('cache:clear', undefined, undefined); - } - - /** Remove all cache entries that return true for a given predicate function. */ - prune(predicate: (url: string, page: CacheData) => boolean): void { - this.pages.forEach((page, url) => { - if (predicate(url, page)) { - this.delete(url); - } - }); - } - - /** Resolve URLs by making them local and letting swup resolve them. */ - protected resolve(urlToResolve: string): string { - const { url } = Location.fromUrl(urlToResolve); - return this.swup.resolveUrl(url); - } -} diff --git a/node_modules/swup/src/modules/Classes.ts b/node_modules/swup/src/modules/Classes.ts deleted file mode 100644 index 7e034511..00000000 --- a/node_modules/swup/src/modules/Classes.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type Swup from '../Swup.js'; -import { queryAll } from '../utils.js'; - -export class Classes { - protected swup: Swup; - protected swupClasses = [ - 'to-', - 'is-changing', - 'is-rendering', - 'is-popstate', - 'is-animating', - 'is-leaving' - ]; - - constructor(swup: Swup) { - this.swup = swup; - } - - protected get selectors(): string[] { - const { scope } = this.swup.visit.animation; - if (scope === 'containers') return this.swup.visit.containers; - if (scope === 'html') return ['html']; - if (Array.isArray(scope)) return scope; - return []; - } - - protected get selector(): string { - return this.selectors.join(','); - } - - protected get targets(): HTMLElement[] { - if (!this.selector.trim()) return []; - return queryAll(this.selector); - } - - add(...classes: string[]): void { - this.targets.forEach((target) => target.classList.add(...classes)); - } - - remove(...classes: string[]): void { - this.targets.forEach((target) => target.classList.remove(...classes)); - } - - clear(): void { - this.targets.forEach((target) => { - const remove = target.className.split(' ').filter((c) => this.isSwupClass(c)); - target.classList.remove(...remove); - }); - } - - protected isSwupClass(className: string): boolean { - return this.swupClasses.some((c) => className.startsWith(c)); - } -} diff --git a/node_modules/swup/src/modules/Hooks.ts b/node_modules/swup/src/modules/Hooks.ts deleted file mode 100644 index e3fb459c..00000000 --- a/node_modules/swup/src/modules/Hooks.ts +++ /dev/null @@ -1,591 +0,0 @@ -import type { DelegateEvent } from 'delegate-it'; - -import type Swup from '../Swup.js'; -import { isPromise, runAsPromise } from '../utils.js'; -import { Visit } from './Visit.js'; -import type { FetchOptions, PageData } from './fetchPage.js'; - -export interface HookDefinitions { - 'animation:out:start': undefined; - 'animation:out:await': { skip: boolean }; - 'animation:out:end': undefined; - 'animation:in:start': undefined; - 'animation:in:await': { skip: boolean }; - 'animation:in:end': undefined; - 'animation:skip': undefined; - 'cache:clear': undefined; - 'cache:set': { page: PageData }; - 'content:replace': { page: PageData }; - 'content:scroll': undefined; - 'enable': undefined; - 'disable': undefined; - 'fetch:request': { url: string; options: FetchOptions }; - 'fetch:error': { url: string; status: number; response: Response }; - 'fetch:timeout': { url: string }; - 'history:popstate': { event: PopStateEvent }; - 'link:click': { el: HTMLAnchorElement; event: DelegateEvent<MouseEvent> }; - 'link:self': undefined; - 'link:anchor': { hash: string }; - 'link:newtab': { href: string }; - 'page:load': { page?: PageData; cache?: boolean; options: FetchOptions }; - 'page:view': { url: string; title: string }; - 'scroll:top': { options: ScrollIntoViewOptions }; - 'scroll:anchor': { hash: string; options: ScrollIntoViewOptions }; - 'visit:start': undefined; - 'visit:transition': undefined; - 'visit:abort': undefined; - 'visit:end': undefined; -} - -export interface HookReturnValues { - 'content:scroll': Promise<boolean> | boolean; - 'fetch:request': Promise<Response>; - 'page:load': Promise<PageData>; - 'scroll:top': boolean; - 'scroll:anchor': boolean; -} - -export type HookArguments<T extends HookName> = HookDefinitions[T]; - -export type HookName = keyof HookDefinitions; - -export type HookNameWithModifier = `${HookName}.${HookModifier}`; - -type HookModifier = 'once' | 'before' | 'replace'; - -/** A generic hook handler. */ -export type HookHandler<T extends HookName> = ( - /** Context about the current visit. */ - visit: Visit, - /** Local arguments passed into the handler. */ - args: HookArguments<T> -) => Promise<unknown> | unknown; - -/** A default hook handler with an expected return type. */ -export type HookDefaultHandler<T extends HookName> = ( - /** Context about the current visit. */ - visit: Visit, - /** Local arguments passed into the handler. */ - args: HookArguments<T>, - /** Default handler to be executed. Available if replacing an internal hook handler. */ - defaultHandler?: HookDefaultHandler<T> -) => T extends keyof HookReturnValues ? HookReturnValues[T] : Promise<unknown> | unknown; - -export type Handlers = { - [K in HookName]: HookHandler<K>[]; -}; - -export type HookInitOptions = { - [K in HookName as K | `${K}.${HookModifier}`]: HookHandler<K>; -} & { - [K in HookName as K | `${K}.${HookModifier}.${HookModifier}`]: HookHandler<K>; -}; - -/** Unregister a previously registered hook handler. */ -export type HookUnregister = () => void; - -/** Define when and how a hook handler is executed. */ -export type HookOptions = { - /** Execute the hook once, then remove the handler */ - once?: boolean; - /** Execute the hook before the internal default handler */ - before?: boolean; - /** Set a priority for when to execute this hook. Lower numbers execute first. Default: `0` */ - priority?: number; - /** Replace the internal default handler with this hook handler */ - replace?: boolean; -}; - -export type HookRegistration< - T extends HookName, - H extends HookHandler<T> | HookDefaultHandler<T> = HookHandler<T> -> = { - id: number; - hook: T; - handler: H; - defaultHandler?: HookDefaultHandler<T>; -} & HookOptions; - -type HookEventDetail = { - hook: HookName; - args: unknown; - visit: Visit; -}; - -export type HookEvent = CustomEvent<HookEventDetail>; - -type HookLedger<T extends HookName> = Map<HookHandler<T>, HookRegistration<T>>; - -interface HookRegistry extends Map<HookName, HookLedger<HookName>> { - get<K extends HookName>(key: K): HookLedger<K> | undefined; - set<K extends HookName>(key: K, value: HookLedger<K>): this; -} - -/** - * Hook registry. - * - * Create, trigger and handle hooks. - * - */ -export class Hooks { - /** Swup instance this registry belongs to */ - protected swup: Swup; - - /** Map of all registered hook handlers. */ - protected registry: HookRegistry = new Map(); - - // Can we deduplicate this somehow? Or make it error when not in sync with HookDefinitions? - // https://stackoverflow.com/questions/53387838/how-to-ensure-an-arrays-values-the-keys-of-a-typescript-interface/53395649 - protected readonly hooks: HookName[] = [ - 'animation:out:start', - 'animation:out:await', - 'animation:out:end', - 'animation:in:start', - 'animation:in:await', - 'animation:in:end', - 'animation:skip', - 'cache:clear', - 'cache:set', - 'content:replace', - 'content:scroll', - 'enable', - 'disable', - 'fetch:request', - 'fetch:error', - 'fetch:timeout', - 'history:popstate', - 'link:click', - 'link:self', - 'link:anchor', - 'link:newtab', - 'page:load', - 'page:view', - 'scroll:top', - 'scroll:anchor', - 'visit:start', - 'visit:transition', - 'visit:abort', - 'visit:end' - ]; - - constructor(swup: Swup) { - this.swup = swup; - this.init(); - } - - /** - * Create ledgers for all core hooks. - */ - protected init() { - this.hooks.forEach((hook) => this.create(hook)); - } - - /** - * Create a new hook type. - */ - create(hook: string) { - if (!this.registry.has(hook as HookName)) { - this.registry.set(hook as HookName, new Map()); - } - } - - /** - * Check if a hook type exists. - */ - exists(hook: HookName): boolean { - return this.registry.has(hook); - } - - /** - * Get the ledger with all registrations for a hook. - */ - protected get<T extends HookName>(hook: T): HookLedger<T> | undefined { - const ledger = this.registry.get(hook); - if (ledger) { - return ledger; - } - console.error(`Unknown hook '${hook}'`); - } - - /** - * Remove all handlers of all hooks. - */ - clear() { - this.registry.forEach((ledger) => ledger.clear()); - } - - /** - * Register a new hook handler. - * @param hook Name of the hook to listen for - * @param handler The handler function to execute - * @param options Object to specify how and when the handler is executed - * Available options: - * - `once`: Only execute the handler once - * - `before`: Execute the handler before the default handler - * - `priority`: Specify the order in which the handlers are executed - * - `replace`: Replace the default handler with this handler - * @returns A function to unregister the handler - */ - - // Overload: replacing default handler - on<T extends HookName, O extends HookOptions>(hook: T, handler: HookDefaultHandler<T>, options: O & { replace: true }): HookUnregister; // prettier-ignore - // Overload: passed in handler options - on<T extends HookName, O extends HookOptions>(hook: T, handler: HookHandler<T>, options: O): HookUnregister; // prettier-ignore - // Overload: no handler options - on<T extends HookName>(hook: T, handler: HookHandler<T>): HookUnregister; // prettier-ignore - // Implementation - on<T extends HookName, O extends HookOptions>( - hook: T, - handler: O['replace'] extends true ? HookDefaultHandler<T> : HookHandler<T>, - options: Partial<O> = {} - ): HookUnregister { - const ledger = this.get(hook); - if (!ledger) { - console.warn(`Hook '${hook}' not found.`); - return () => {}; - } - - const id = ledger.size + 1; - const registration: HookRegistration<T> = { ...options, id, hook, handler }; - ledger.set(handler, registration); - - return () => this.off(hook, handler); - } - - /** - * Register a new hook handler to run before the default handler. - * Shortcut for `hooks.on(hook, handler, { before: true })`. - * @param hook Name of the hook to listen for - * @param handler The handler function to execute - * @param options Any other event options (see `hooks.on()` for details) - * @returns A function to unregister the handler - * @see on - */ - // Overload: passed in handler options - before<T extends HookName>(hook: T, handler: HookHandler<T>, options: HookOptions): HookUnregister; // prettier-ignore - // Overload: no handler options - before<T extends HookName>(hook: T, handler: HookHandler<T>): HookUnregister; - // Implementation - before<T extends HookName>( - hook: T, - handler: HookHandler<T>, - options: HookOptions = {} - ): HookUnregister { - return this.on(hook, handler, { ...options, before: true }); - } - - /** - * Register a new hook handler to replace the default handler. - * Shortcut for `hooks.on(hook, handler, { replace: true })`. - * @param hook Name of the hook to listen for - * @param handler The handler function to execute instead of the default handler - * @param options Any other event options (see `hooks.on()` for details) - * @returns A function to unregister the handler - * @see on - */ - // Overload: passed in handler options - replace<T extends HookName>(hook: T, handler: HookDefaultHandler<T>, options: HookOptions): HookUnregister; // prettier-ignore - // Overload: no handler options - replace<T extends HookName>(hook: T, handler: HookDefaultHandler<T>): HookUnregister; // prettier-ignore - // Implementation - replace<T extends HookName>( - hook: T, - handler: HookDefaultHandler<T>, - options: HookOptions = {} - ): HookUnregister { - return this.on(hook, handler, { ...options, replace: true }); - } - - /** - * Register a new hook handler to run once. - * Shortcut for `hooks.on(hook, handler, { once: true })`. - * @param hook Name of the hook to listen for - * @param handler The handler function to execute - * @param options Any other event options (see `hooks.on()` for details) - * @see on - */ - // Overload: passed in handler options - once<T extends HookName>(hook: T, handler: HookHandler<T>, options: HookOptions): HookUnregister; // prettier-ignore - // Overload: no handler options - once<T extends HookName>(hook: T, handler: HookHandler<T>): HookUnregister; - // Implementation - once<T extends HookName>( - hook: T, - handler: HookHandler<T>, - options: HookOptions = {} - ): HookUnregister { - return this.on(hook, handler, { ...options, once: true }); - } - - /** - * Unregister a hook handler. - * @param hook Name of the hook the handler is registered for - * @param handler The handler function that was registered. - * If omitted, all handlers for the hook will be removed. - */ - // Overload: unregister a specific handler - off<T extends HookName>(hook: T, handler: HookHandler<T> | HookDefaultHandler<T>): void; - // Overload: unregister all handlers - off<T extends HookName>(hook: T): void; - // Implementation - off<T extends HookName>(hook: T, handler?: HookHandler<T> | HookDefaultHandler<T>): void { - const ledger = this.get(hook); - if (ledger && handler) { - const deleted = ledger.delete(handler); - if (!deleted) { - console.warn(`Handler for hook '${hook}' not found.`); - } - } else if (ledger) { - ledger.clear(); - } - } - - /** - * Trigger a hook asynchronously, executing its default handler and all registered handlers. - * Will execute all handlers in order and `await` any `Promise`s they return. - * @param hook Name of the hook to trigger - * @param visit The visit object this hook belongs to - * @param args Arguments to pass to the handler - * @param defaultHandler A default implementation of this hook to execute - * @returns The resolved return value of the executed default handler - */ - // Overload: default order of arguments - async call<T extends HookName>(hook: T, visit: Visit | undefined, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>>; // prettier-ignore - // Overload: legacy order of arguments, with visit missing - async call<T extends HookName>(hook: T, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>>; // prettier-ignore - // Implementation - async call<T extends HookName>( - hook: T, - arg1: Visit | HookArguments<T>, - arg2: HookArguments<T> | HookDefaultHandler<T>, - arg3?: HookDefaultHandler<T> - ): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>> { - const [visit, args, defaultHandler] = this.parseCallArgs(hook, arg1, arg2, arg3); - - const { before, handler, after } = this.getHandlers(hook, defaultHandler); - await this.run(before, visit, args); - const [result] = await this.run(handler, visit, args, true); - await this.run(after, visit, args); - this.dispatchDomEvent(hook, visit, args); - return result; - } - - /** - * Trigger a hook synchronously, executing its default handler and all registered handlers. - * Will execute all handlers in order, but will **not** `await` any `Promise`s they return. - * @param hook Name of the hook to trigger - * @param visit The visit object this hook belongs to - * @param args Arguments to pass to the handler - * @param defaultHandler A default implementation of this hook to execute - * @returns The (possibly unresolved) return value of the executed default handler - */ - // Overload: default order of arguments - callSync<T extends HookName>(hook: T, visit: Visit | undefined, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): ReturnType<HookDefaultHandler<T>>; // prettier-ignore - // Overload: legacy order of arguments, with visit missing - callSync<T extends HookName>(hook: T, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): ReturnType<HookDefaultHandler<T>>; // prettier-ignore - // Implementation - callSync<T extends HookName>( - hook: T, - arg1: Visit | HookArguments<T>, - arg2: HookArguments<T> | HookDefaultHandler<T>, - arg3?: HookDefaultHandler<T> - ): ReturnType<HookDefaultHandler<T>> { - const [visit, args, defaultHandler] = this.parseCallArgs(hook, arg1, arg2, arg3); - const { before, handler, after } = this.getHandlers(hook, defaultHandler); - this.runSync(before, visit, args); - const [result] = this.runSync(handler, visit, args, true); - this.runSync(after, visit, args); - this.dispatchDomEvent(hook, visit, args); - return result; - } - - /** - * Parse the call arguments for call() and callSync() to allow legacy argument order. - */ - protected parseCallArgs<T extends HookName>( - hook: T, - arg1: Visit | HookArguments<T> | undefined, - arg2: HookArguments<T> | HookDefaultHandler<T>, - arg3?: HookDefaultHandler<T> - ): [Visit | undefined, HookArguments<T>, HookDefaultHandler<T> | undefined] { - const isLegacyOrder = - !(arg1 instanceof Visit) && (typeof arg1 === 'object' || typeof arg2 === 'function'); - if (isLegacyOrder) { - // Legacy positioning: arguments in second or handler passed in third place - return [undefined, arg1 as HookArguments<T>, arg2 as HookDefaultHandler<T>]; - } else { - // Default positioning: visit passed in as first argument - return [arg1, arg2 as HookArguments<T>, arg3]; - } - } - - /** - * Execute the handlers for a hook, in order, as `Promise`s that will be `await`ed. - * @param registrations The registrations (handler + options) to execute - * @param args Arguments to pass to the handler - */ - - // Overload: running HookDefaultHandler: expect HookDefaultHandler return type - protected async run<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], visit: Visit | undefined, args: HookArguments<T>, rethrow: true): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>[]>; // prettier-ignore - // Overload: running user handler: expect no specific type - protected async run<T extends HookName>(registrations: HookRegistration<T>[], visit: Visit | undefined, args: HookArguments<T>): Promise<unknown[]>; // prettier-ignore - // Implementation - protected async run<T extends HookName, R extends HookRegistration<T>[]>( - registrations: R, - visit: Visit | undefined = this.swup.visit, - args: HookArguments<T>, - rethrow: boolean = false - ): Promise<Awaited<ReturnType<HookDefaultHandler<T>>> | unknown[]> { - const results = []; - for (const { hook, handler, defaultHandler, once } of registrations) { - if (visit?.done) continue; - if (once) this.off(hook, handler); - try { - const result = await runAsPromise(handler, [visit, args, defaultHandler]); - results.push(result); - } catch (error) { - if (rethrow) { - throw error; - } else { - console.error(`Error in hook '${hook}':`, error); - } - } - } - return results; - } - - /** - * Execute the handlers for a hook, in order, without `await`ing any returned `Promise`s. - * @param registrations The registrations (handler + options) to execute - * @param args Arguments to pass to the handler - */ - - // Overload: running HookDefaultHandler: expect HookDefaultHandler return type - protected runSync<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], visit: Visit | undefined, args: HookArguments<T>, rethrow: true): ReturnType<HookDefaultHandler<T>>[]; // prettier-ignore - // Overload: running user handler: expect no specific type - protected runSync<T extends HookName>(registrations: HookRegistration<T>[], visit: Visit | undefined, args: HookArguments<T>): unknown[]; // prettier-ignore - // Implementation - protected runSync<T extends HookName, R extends HookRegistration<T>[]>( - registrations: R, - visit: Visit | undefined = this.swup.visit, - args: HookArguments<T>, - rethrow: boolean = false - ): (ReturnType<HookDefaultHandler<T>> | unknown)[] { - const results = []; - for (const { hook, handler, defaultHandler, once } of registrations) { - if (visit?.done) continue; - if (once) this.off(hook, handler); - try { - const result = (handler as HookDefaultHandler<T>)(visit, args, defaultHandler); - results.push(result); - if (isPromise(result)) { - console.warn( - `Swup will not await Promises in handler for synchronous hook '${hook}'.` - ); - } - } catch (error) { - if (rethrow) { - throw error; - } else { - console.error(`Error in hook '${hook}':`, error); - } - } - } - return results; - } - - /** - * Get all registered handlers for a hook, sorted by priority and registration order. - * @param hook Name of the hook - * @param defaultHandler The optional default handler of this hook - * @returns An object with the handlers sorted into `before` and `after` arrays, - * as well as a flag indicating if the original handler was replaced - */ - protected getHandlers<T extends HookName>(hook: T, defaultHandler?: HookDefaultHandler<T>) { - const ledger = this.get(hook); - if (!ledger) { - return { found: false, before: [], handler: [], after: [], replaced: false }; - } - - const registrations = Array.from(ledger.values()); - - // Let TypeScript know that replaced handlers are default handlers by filtering to true - const def = (T: HookRegistration<T>): T is HookRegistration<T, HookDefaultHandler<T>> => true; // prettier-ignore - const sort = this.sortRegistrations; - - // Filter into before, after, and replace handlers - const before = registrations.filter(({ before, replace }) => before && !replace).sort(sort); - const replace = registrations.filter(({ replace }) => replace).filter(def).sort(sort); // prettier-ignore - const after = registrations.filter(({ before, replace }) => !before && !replace).sort(sort); - const replaced = replace.length > 0; - - // Define main handler registration - // Created as HookRegistration[] array to allow passing it into hooks.run() directly - let handler: HookRegistration<T, HookDefaultHandler<T>>[] = []; - if (defaultHandler) { - handler = [{ id: 0, hook, handler: defaultHandler }]; - if (replaced) { - const index = replace.length - 1; - const { handler: replacingHandler, once } = replace[index]; - const createDefaultHandler = (index: number): HookDefaultHandler<T> | undefined => { - const next = replace[index - 1]; - if (next) { - return (visit, args) => - next.handler(visit, args, createDefaultHandler(index - 1)); - } else { - return defaultHandler; - } - }; - const nestedDefaultHandler = createDefaultHandler(index); - handler = [{ id: 0, hook, once, handler: replacingHandler, defaultHandler: nestedDefaultHandler }]; // prettier-ignore - } - } - - return { found: true, before, handler, after, replaced }; - } - - /** - * Sort two hook registrations by priority and registration order. - * @param a The registration object to compare - * @param b The other registration object to compare with - * @returns The sort direction - */ - protected sortRegistrations<T extends HookName>( - a: HookRegistration<T>, - b: HookRegistration<T> - ): number { - const priority = (a.priority ?? 0) - (b.priority ?? 0); - const id = a.id - b.id; - return priority || id || 0; - } - - /** - * Dispatch a custom event on the `document` for a hook. Prefixed with `swup:` - * @param hook Name of the hook. - */ - protected dispatchDomEvent<T extends HookName>( - hook: T, - visit: Visit | undefined, - args?: HookArguments<T> - ): void { - if (visit?.done) return; - - const detail: HookEventDetail = { hook, args, visit: visit || this.swup.visit }; - document.dispatchEvent( - new CustomEvent<HookEventDetail>(`swup:any`, { detail, bubbles: true }) - ); - document.dispatchEvent( - new CustomEvent<HookEventDetail>(`swup:${hook}`, { detail, bubbles: true }) - ); - } - - /** - * Parse a hook name into the name and any modifiers. - * @param hook Name of the hook. - */ - parseName(hook: HookName | HookNameWithModifier): [HookName, Partial<HookOptions>] { - const [name, ...modifiers] = hook.split('.'); - const options = modifiers.reduce((acc, mod) => ({ ...acc, [mod]: true }), {}); - return [name as HookName, options]; - } -} diff --git a/node_modules/swup/src/modules/Visit.ts b/node_modules/swup/src/modules/Visit.ts deleted file mode 100644 index 17ed6c7d..00000000 --- a/node_modules/swup/src/modules/Visit.ts +++ /dev/null @@ -1,174 +0,0 @@ -import type Swup from '../Swup.js'; -import type { Options } from '../Swup.js'; -import type { HistoryAction, HistoryDirection } from './navigate.js'; - -/** See below for the class Visit {} definition */ -// export interface Visit {} - -export interface VisitFrom { - /** The URL of the previous page */ - url: string; - /** The hash of the previous page */ - hash?: string; -} - -export interface VisitTo { - /** The URL of the next page */ - url: string; - /** The hash of the next page */ - hash?: string; - /** The HTML content of the next page */ - html?: string; - /** The parsed document of the next page, available during visit */ - document?: Document; -} - -export interface VisitAnimation { - /** Whether this visit is animated. Default: `true` */ - animate: boolean; - /** Whether to wait for the next page to load before starting the animation. Default: `false` */ - wait: boolean; - /** Name of a custom animation to run. */ - name?: string; - /** Whether this animation uses the native browser ViewTransition API. Default: `false` */ - native: boolean; - /** Elements on which to add animation classes. Default: `html` element */ - scope: 'html' | 'containers' | string[]; - /** Selector for detecting animation timing. Default: `[class*="transition-"]` */ - selector: Options['animationSelector']; -} - -export interface VisitScroll { - /** Whether to reset the scroll position after the visit. Default: `true` */ - reset: boolean; - /** Anchor element to scroll to on the next page. */ - target?: string | false; -} - -export interface VisitTrigger { - /** DOM element that triggered this visit. */ - el?: Element; - /** DOM event that triggered this visit. */ - event?: Event; -} - -export interface VisitCache { - /** Whether this visit will try to load the requested page from cache. */ - read: boolean; - /** Whether this visit will save the loaded page in cache. */ - write: boolean; -} - -export interface VisitHistory { - /** History action to perform: `push` for creating a new history entry, `replace` for replacing the current entry. Default: `push` */ - action: HistoryAction; - /** Whether this visit was triggered by a browser history navigation. */ - popstate: boolean; - /** The direction of travel in case of a browser history navigation: backward or forward. */ - direction: HistoryDirection | undefined; -} - -export interface VisitInitOptions { - to: string; - from?: string; - hash?: string; - el?: Element; - event?: Event; -} - -/** @internal */ -export const VisitState = { - CREATED: 1, - QUEUED: 2, - STARTED: 3, - LEAVING: 4, - LOADED: 5, - ENTERING: 6, - COMPLETED: 7, - ABORTED: 8, - FAILED: 9 -} as const; - -/** @internal */ -export type VisitState = (typeof VisitState)[keyof typeof VisitState]; - -/** An object holding details about the current visit. */ -export class Visit { - /** A unique ID to identify this visit */ - id: number; - /** The current state of this visit @internal */ - state: VisitState; - /** The previous page, about to leave */ - from: VisitFrom; - /** The next page, about to enter */ - to: VisitTo; - /** The content containers, about to be replaced */ - containers: Options['containers']; - /** Information about animated page transitions */ - animation: VisitAnimation; - /** What triggered this visit */ - trigger: VisitTrigger; - /** Cache behavior for this visit */ - cache: VisitCache; - /** Browser history behavior on this visit */ - history: VisitHistory; - /** Scroll behavior on this visit */ - scroll: VisitScroll; - /** User-defined metadata */ - meta: Record<string, unknown>; - - constructor(swup: Swup, options: VisitInitOptions) { - const { to, from, hash, el, event } = options; - - this.id = Math.random(); - this.state = VisitState.CREATED; - this.from = { url: from ?? swup.location.url, hash: swup.location.hash }; - this.to = { url: to, hash }; - this.containers = swup.options.containers; - this.animation = { - animate: true, - wait: false, - name: undefined, - native: swup.options.native, - scope: swup.options.animationScope, - selector: swup.options.animationSelector - }; - this.trigger = { el, event }; - this.cache = { - read: swup.options.cache, - write: swup.options.cache - }; - this.history = { - action: 'push', - popstate: false, - direction: undefined - }; - this.scroll = { - reset: true, - target: undefined - }; - this.meta = {}; - } - - /** @internal */ - advance(state: VisitState) { - if (this.state < state) { - this.state = state; - } - } - - /** @internal */ - abort() { - this.state = VisitState.ABORTED; - } - - /** Is this visit done, i.e. completed, failed, or aborted? */ - get done(): boolean { - return this.state >= VisitState.COMPLETED; - } -} - -/** Create a new visit object. */ -export function createVisit(this: Swup, options: VisitInitOptions): Visit { - return new Visit(this, options); -} diff --git a/node_modules/swup/src/modules/animatePageIn.ts b/node_modules/swup/src/modules/animatePageIn.ts deleted file mode 100755 index fc384b73..00000000 --- a/node_modules/swup/src/modules/animatePageIn.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type Swup from '../Swup.js'; -import { nextTick } from '../utils.js'; -import type { Visit } from './Visit.js'; - -/** - * Perform the in/enter animation of the next page. - * @returns Promise<void> - */ -export const animatePageIn = async function (this: Swup, visit: Visit) { - // Check if failed/aborted in the meantime - if (visit.done) return; - - const animation = this.hooks.call( - 'animation:in:await', - visit, - { skip: false }, - (visit, { skip }) => { - if (skip) return; - return this.awaitAnimations({ selector: visit.animation.selector }); - } - ); - - await nextTick(); - - await this.hooks.call('animation:in:start', visit, undefined, () => { - this.classes.remove('is-animating'); - }); - - await animation; - - await this.hooks.call('animation:in:end', visit, undefined); -}; diff --git a/node_modules/swup/src/modules/animatePageOut.ts b/node_modules/swup/src/modules/animatePageOut.ts deleted file mode 100755 index d42bf57f..00000000 --- a/node_modules/swup/src/modules/animatePageOut.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type Swup from '../Swup.js'; -import type { Visit } from './Visit.js'; - -/** - * Perform the out/leave animation of the current page. - * @returns Promise<void> - */ -export const animatePageOut = async function (this: Swup, visit: Visit) { - await this.hooks.call('animation:out:start', visit, undefined, () => { - this.classes.add('is-changing', 'is-animating', 'is-leaving'); - }); - - await this.hooks.call('animation:out:await', visit, { skip: false }, (visit, { skip }) => { - if (skip) return; - return this.awaitAnimations({ selector: visit.animation.selector }); - }); - - await this.hooks.call('animation:out:end', visit, undefined); -}; diff --git a/node_modules/swup/src/modules/awaitAnimations.ts b/node_modules/swup/src/modules/awaitAnimations.ts deleted file mode 100644 index e4b77e74..00000000 --- a/node_modules/swup/src/modules/awaitAnimations.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { queryAll } from '../utils.js'; -import type Swup from '../Swup.js'; -import type { Options } from '../Swup.js'; - -const TRANSITION = 'transition'; -const ANIMATION = 'animation'; - -type AnimationType = typeof TRANSITION | typeof ANIMATION; -type AnimationEndEvent = `${AnimationType}end`; -type AnimationProperty = 'Delay' | 'Duration'; -type AnimationStyleKey = `${AnimationType}${AnimationProperty}` | 'transitionProperty'; - -export type AnimationDirection = 'in' | 'out'; - -/** - * Return a Promise that resolves when all CSS animations and transitions - * are done on the page. Filters by selector or takes elements directly. - */ -export async function awaitAnimations( - this: Swup, - { - selector, - elements - }: { - selector: Options['animationSelector']; - elements?: NodeListOf<HTMLElement> | HTMLElement[]; - } -): Promise<void> { - // Allow usage of swup without animations: { animationSelector: false } - if (selector === false && !elements) { - return; - } - - // Allow passing in elements - let animatedElements: HTMLElement[] = []; - if (elements) { - animatedElements = Array.from(elements); - } else if (selector) { - animatedElements = queryAll(selector, document.body); - // Warn if no elements match the selector, but keep things going - if (!animatedElements.length) { - console.warn(`[swup] No elements found matching animationSelector \`${selector}\``); - return; - } - } - - const awaitedAnimations = animatedElements.map((el) => awaitAnimationsOnElement(el)); - const hasAnimations = awaitedAnimations.filter(Boolean).length > 0; - if (!hasAnimations) { - if (selector) { - console.warn( - `[swup] No CSS animation duration defined on elements matching \`${selector}\`` - ); - } - return; - } - - await Promise.all(awaitedAnimations); -} - -function awaitAnimationsOnElement(element: HTMLElement): Promise<void> | false { - const { type, timeout, propCount } = getTransitionInfo(element); - - // Resolve immediately if no transition defined - if (!type || !timeout) { - return false; - } - - return new Promise((resolve) => { - const endEvent: AnimationEndEvent = `${type}end`; - const startTime = performance.now(); - let propsTransitioned = 0; - - const end = () => { - element.removeEventListener(endEvent, onEnd); - resolve(); - }; - - const onEnd = (event: TransitionEvent | AnimationEvent) => { - // Skip transitions on child elements - if (event.target !== element) { - return; - } - - // Skip transitions that happened before we started listening - const elapsedTime = (performance.now() - startTime) / 1000; - if (elapsedTime < event.elapsedTime) { - return; - } - - // End if all properties have transitioned - if (++propsTransitioned >= propCount) { - end(); - } - }; - - setTimeout(() => { - if (propsTransitioned < propCount) { - end(); - } - }, timeout + 1); - - element.addEventListener(endEvent, onEnd); - }); -} - -function getTransitionInfo(element: Element) { - const styles = window.getComputedStyle(element); - - const transitionDelays = getStyleProperties(styles, `${TRANSITION}Delay`); - const transitionDurations = getStyleProperties(styles, `${TRANSITION}Duration`); - const transitionTimeout = calculateTimeout(transitionDelays, transitionDurations); - - const animationDelays = getStyleProperties(styles, `${ANIMATION}Delay`); - const animationDurations = getStyleProperties(styles, `${ANIMATION}Duration`); - const animationTimeout = calculateTimeout(animationDelays, animationDurations); - - const timeout = Math.max(transitionTimeout, animationTimeout); - const type: AnimationType | null = - timeout > 0 ? (transitionTimeout > animationTimeout ? TRANSITION : ANIMATION) : null; - const propCount = type - ? type === TRANSITION - ? transitionDurations.length - : animationDurations.length - : 0; - - return { - type, - timeout, - propCount - }; -} - -export function getStyleProperties(styles: CSSStyleDeclaration, key: AnimationStyleKey): string[] { - return (styles[key] || '').split(', '); -} - -export function calculateTimeout(delays: string[], durations: string[]): number { - while (delays.length < durations.length) { - delays = delays.concat(delays); - } - - return Math.max(...durations.map((duration, i) => toMs(duration) + toMs(delays[i]))); -} - -export function toMs(time: string): number { - return parseFloat(time) * 1000; -} diff --git a/node_modules/swup/src/modules/fetchPage.ts b/node_modules/swup/src/modules/fetchPage.ts deleted file mode 100644 index 96275448..00000000 --- a/node_modules/swup/src/modules/fetchPage.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type Swup from '../Swup.js'; -import { Location } from '../helpers.js'; -import type { Visit } from './Visit.js'; - -/** A page object as used by swup and its cache. */ -export interface PageData { - /** The URL of the page */ - url: string; - /** The complete HTML response received from the server */ - html: string; -} - -/** Define how a page is fetched. */ -export interface FetchOptions extends Omit<RequestInit, 'cache'> { - /** The request method. */ - method?: 'GET' | 'POST'; - /** The body of the request: raw string, form data object or URL params. */ - body?: string | FormData | URLSearchParams; - /** The request timeout in milliseconds. */ - timeout?: number; - /** Optional visit object with additional context. @internal */ - visit?: Visit; -} - -export class FetchError extends Error { - url: string; - status?: number; - aborted: boolean; - timedOut: boolean; - constructor( - message: string, - details: { url: string; status?: number; aborted?: boolean; timedOut?: boolean } - ) { - super(message); - this.name = 'FetchError'; - this.url = details.url; - this.status = details.status; - this.aborted = details.aborted || false; - this.timedOut = details.timedOut || false; - } -} - -/** - * Fetch a page from the server, return it and cache it. - */ -export async function fetchPage( - this: Swup, - url: URL | string, - options: FetchOptions = {} -): Promise<PageData> { - url = Location.fromUrl(url).url; - - const { visit = this.visit } = options; - const headers = { ...this.options.requestHeaders, ...options.headers }; - const timeout = options.timeout ?? this.options.timeout; - const controller = new AbortController(); - const { signal } = controller; - options = { ...options, headers, signal }; - - let timedOut = false; - let timeoutId: ReturnType<typeof setTimeout> | null = null; - if (timeout && timeout > 0) { - timeoutId = setTimeout(() => { - timedOut = true; - controller.abort('timeout'); - }, timeout); - } - - // Allow hooking before this and returning a custom response-like object (e.g. custom fetch implementation) - let response: Response; - try { - response = await this.hooks.call( - 'fetch:request', - visit, - { url, options }, - (visit, { url, options }) => fetch(url, options) - ); - if (timeoutId) { - clearTimeout(timeoutId); - } - } catch (error) { - if (timedOut) { - this.hooks.call('fetch:timeout', visit, { url }); - throw new FetchError(`Request timed out: ${url}`, { url, timedOut }); - } - if ((error as Error)?.name === 'AbortError' || signal.aborted) { - throw new FetchError(`Request aborted: ${url}`, { url, aborted: true }); - } - throw error; - } - - const { status, url: responseUrl } = response; - const html = await response.text(); - - if (status === 500) { - this.hooks.call('fetch:error', visit, { status, response, url: responseUrl }); - throw new FetchError(`Server error: ${responseUrl}`, { status, url: responseUrl }); - } - - if (!html) { - throw new FetchError(`Empty response: ${responseUrl}`, { status, url: responseUrl }); - } - - // Resolve real url after potential redirect - const { url: finalUrl } = Location.fromUrl(responseUrl); - const page = { url: finalUrl, html }; - - // Write to cache for safe methods and non-redirects - if (visit.cache.write && (!options.method || options.method === 'GET') && url === finalUrl) { - this.cache.set(page.url, page); - } - - return page; -} diff --git a/node_modules/swup/src/modules/getAnchorElement.ts b/node_modules/swup/src/modules/getAnchorElement.ts deleted file mode 100755 index b25de2bc..00000000 --- a/node_modules/swup/src/modules/getAnchorElement.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { query } from '../utils.js'; - -/** - * Find the anchor element for a given hash. - * - * @param hash Hash with or without leading '#' - * @returns The element, if found, or null. - * - * @see https://html.spec.whatwg.org/#find-a-potential-indicated-element - */ -export const getAnchorElement = (hash?: string): Element | null => { - if (hash && hash.charAt(0) === '#') { - hash = hash.substring(1); - } - - if (!hash) { - return null; - } - - const decoded = decodeURIComponent(hash); - let element = - document.getElementById(hash) || - document.getElementById(decoded) || - query(`a[name='${CSS.escape(hash)}']`) || - query(`a[name='${CSS.escape(decoded)}']`); - - if (!element && hash === 'top') { - element = document.body; - } - - return element; -}; diff --git a/node_modules/swup/src/modules/navigate.ts b/node_modules/swup/src/modules/navigate.ts deleted file mode 100755 index ac88935b..00000000 --- a/node_modules/swup/src/modules/navigate.ts +++ /dev/null @@ -1,239 +0,0 @@ -import type Swup from '../Swup.js'; -import { FetchError, type FetchOptions, type PageData } from './fetchPage.js'; -import { type VisitInitOptions, type Visit, VisitState } from './Visit.js'; -import { createHistoryRecord, updateHistoryRecord, Location, classify } from '../helpers.js'; -import { getContextualAttr } from '../utils.js'; - -export type HistoryAction = 'push' | 'replace'; -export type HistoryDirection = 'forwards' | 'backwards'; -export type NavigationToSelfAction = 'scroll' | 'navigate'; -export type CacheControl = Partial<{ read: boolean; write: boolean }>; - -/** Define how to navigate to a page. */ -type NavigationOptions = { - /** Whether this visit is animated. Default: `true` */ - animate?: boolean; - /** Name of a custom animation to run. */ - animation?: string; - /** History action to perform: `push` for creating a new history entry, `replace` for replacing the current entry. Default: `push` */ - history?: HistoryAction; - /** Whether this visit should read from or write to the cache. */ - cache?: CacheControl; - /** Custom metadata associated with this visit. */ - meta?: Record<string, unknown>; -}; - -/** - * Navigate to a new URL. - * @param url The URL to navigate to. - * @param options Options for how to perform this visit. - * @returns Promise<void> - */ -export function navigate( - this: Swup, - url: string, - options: NavigationOptions & FetchOptions = {}, - init: Omit<VisitInitOptions, 'to'> = {} -) { - if (typeof url !== 'string') { - throw new Error(`swup.navigate() requires a URL parameter`); - } - - // Check if the visit should be ignored - if (this.shouldIgnoreVisit(url, { el: init.el, event: init.event })) { - window.location.assign(url); - return; - } - - const { url: to, hash } = Location.fromUrl(url); - - const visit = this.createVisit({ ...init, to, hash }); - this.performNavigation(visit, options); -} - -/** - * Start a visit to a new URL. - * - * Internal method that assumes the visit context has already been created. - * - * As a user, you should call `swup.navigate(url)` instead. - * - * @param url The URL to navigate to. - * @param options Options for how to perform this visit. - * @returns Promise<void> - */ -export async function performNavigation( - this: Swup, - visit: Visit, - options: NavigationOptions & FetchOptions = {} -): Promise<void> { - if (this.navigating) { - if (this.visit.state >= VisitState.ENTERING) { - // Currently navigating and content already loaded? Finish and queue - visit.state = VisitState.QUEUED; - this.onVisitEnd = () => this.performNavigation(visit, options); - return; - } else { - // Currently navigating and content not loaded? Abort running visit - await this.hooks.call('visit:abort', this.visit, undefined); - delete this.visit.to.document; - this.visit.state = VisitState.ABORTED; - } - } - - this.navigating = true; - this.visit = visit; - - const { el } = visit.trigger; - options.referrer = options.referrer || this.location.url; - - if (options.animate === false) { - visit.animation.animate = false; - } - - // Clean up old animation classes - if (!visit.animation.animate) { - this.classes.clear(); - } - - // Get history action from option or attribute on trigger element - const history = options.history || getContextualAttr(el, 'data-swup-history'); - if (typeof history === 'string' && ['push', 'replace'].includes(history)) { - visit.history.action = history as HistoryAction; - } - - // Get custom animation name from option or attribute on trigger element - const animation = options.animation || getContextualAttr(el, 'data-swup-animation'); - if (typeof animation === 'string') { - visit.animation.name = animation; - } - - // Get custom metadata from option - visit.meta = options.meta || {}; - - // Sanitize cache option - if (typeof options.cache === 'object') { - visit.cache.read = options.cache.read ?? visit.cache.read; - visit.cache.write = options.cache.write ?? visit.cache.write; - } else if (options.cache !== undefined) { - visit.cache = { read: !!options.cache, write: !!options.cache }; - } - // Delete this so that window.fetch doesn't mis-interpret it - delete options.cache; - - try { - await this.hooks.call('visit:start', visit, undefined); - - visit.state = VisitState.STARTED; - - // Begin loading page - const page = this.hooks.call('page:load', visit, { options }, async (visit, args) => { - // Read from cache - let cachedPage: PageData | undefined; - if (visit.cache.read) { - cachedPage = this.cache.get(visit.to.url); - } - - args.page = cachedPage || (await this.fetchPage(visit.to.url, args.options)); - args.cache = !!cachedPage; - - return args.page; - }); - - /** - * When the page is loaded: mark the visit as loaded and save - * the raw html and a parsed document of the received page in the visit object - */ - page.then(({ html }) => { - visit.advance(VisitState.LOADED); - visit.to.html = html; - visit.to.document = new DOMParser().parseFromString(html, 'text/html'); - }); - - // Create/update history record if this is not a popstate call or leads to the same URL - const newUrl = visit.to.url + visit.to.hash; - if (!visit.history.popstate) { - if (visit.history.action === 'replace' || visit.to.url === this.location.url) { - updateHistoryRecord(newUrl); - } else { - this.currentHistoryIndex++; - createHistoryRecord(newUrl, { index: this.currentHistoryIndex }); - } - } - this.location = Location.fromUrl(newUrl); - - // Mark visit type with classes - if (visit.history.popstate) { - this.classes.add('is-popstate'); - } - if (visit.animation.name) { - this.classes.add(`to-${classify(visit.animation.name)}`); - } - - // Wait for page before starting to animate out? - if (visit.animation.wait) { - await page; - } - - // Check if failed/aborted in the meantime - if (visit.done) return; - - // Perform the actual transition: animate and replace content - await this.hooks.call('visit:transition', visit, undefined, async () => { - // No animation? Just await page and render - if (!visit.animation.animate) { - await this.hooks.call('animation:skip', undefined); - await this.renderPage(visit, await page); - return; - } - - // Animate page out, render page, animate page in - visit.advance(VisitState.LEAVING); - await this.animatePageOut(visit); - if (visit.animation.native && document.startViewTransition) { - await document.startViewTransition( - async () => await this.renderPage(visit, await page) - ).finished; - } else { - await this.renderPage(visit, await page); - } - await this.animatePageIn(visit); - }); - - // Check if failed/aborted in the meantime - if (visit.done) return; - - // Finalize visit - await this.hooks.call('visit:end', visit, undefined, () => this.classes.clear()); - visit.state = VisitState.COMPLETED; - this.navigating = false; - - /** Run eventually queued function */ - if (this.onVisitEnd) { - this.onVisitEnd(); - this.onVisitEnd = undefined; - } - } catch (error) { - // Return early if error is undefined or signals an aborted request - if (!error || (error as FetchError)?.aborted) { - visit.state = VisitState.ABORTED; - return; - } - - visit.state = VisitState.FAILED; - - // Log to console - console.error(error); - - // Remove current history entry, then load requested url in browser - this.options.skipPopStateHandling = () => { - window.location.assign(visit.to.url + visit.to.hash); - return true; - }; - - // Go back to the actual page we're still at - window.history.back(); - } finally { - delete visit.to.document; - } -} diff --git a/node_modules/swup/src/modules/plugins.ts b/node_modules/swup/src/modules/plugins.ts deleted file mode 100644 index 299d7cb3..00000000 --- a/node_modules/swup/src/modules/plugins.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type Swup from '../Swup.js'; - -export type Plugin = { - /** Identify as a swup plugin */ - isSwupPlugin: true; - /** Name of this plugin */ - name: string; - /** Version of this plugin. Currently not in use, defined here for backward compatiblity. */ - version?: string; - /** The swup instance that mounted this plugin */ - swup?: Swup; - /** Version requirements of this plugin. Example: `{ swup: '>=4' }` */ - requires?: Record<string, string | string[]>; - /** Run on mount */ - mount: () => void; - /** Run on unmount */ - unmount: () => void; - _beforeMount?: () => void; - _afterUnmount?: () => void; - _checkRequirements?: () => boolean; -}; - -const isSwupPlugin = (maybeInvalidPlugin: unknown): maybeInvalidPlugin is Plugin => { - // @ts-ignore: this might be anything, object or no - return Boolean(maybeInvalidPlugin?.isSwupPlugin); -}; - -/** Install a plugin. */ -export const use = function (this: Swup, plugin: unknown) { - if (!isSwupPlugin(plugin)) { - console.error('Not a swup plugin instance', plugin); - return; - } - - plugin.swup = this; - if (plugin._checkRequirements) { - if (!plugin._checkRequirements()) { - return; - } - } - if (plugin._beforeMount) { - plugin._beforeMount(); - } - plugin.mount(); - - this.plugins.push(plugin); - - return this.plugins; -}; - -/** Uninstall a plugin. */ -export function unuse(this: Swup, pluginOrName: Plugin | string) { - const plugin = this.findPlugin(pluginOrName); - if (!plugin) { - console.error('No such plugin', plugin); - return; - } - - plugin.unmount(); - if (plugin._afterUnmount) { - plugin._afterUnmount(); - } - - this.plugins = this.plugins.filter((p) => p !== plugin); - - return this.plugins; -} - -/** Find a plugin by name or reference. */ -export function findPlugin(this: Swup, pluginOrName: Plugin | string) { - return this.plugins.find( - (plugin) => - plugin === pluginOrName || - plugin.name === pluginOrName || - plugin.name === `Swup${String(pluginOrName)}` - ); -} diff --git a/node_modules/swup/src/modules/renderPage.ts b/node_modules/swup/src/modules/renderPage.ts deleted file mode 100755 index 9dc1f9f5..00000000 --- a/node_modules/swup/src/modules/renderPage.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { updateHistoryRecord, getCurrentUrl, classify, Location } from '../helpers.js'; -import type Swup from '../Swup.js'; -import type { PageData } from './fetchPage.js'; -import { VisitState, type Visit } from './Visit.js'; - -/** - * Render the next page: replace the content and update scroll position. - */ -export const renderPage = async function (this: Swup, visit: Visit, page: PageData): Promise<void> { - // Check if failed/aborted in the meantime - if (visit.done) return; - - visit.advance(VisitState.ENTERING); - - const { url } = page; - - // update state if the url was redirected - if (!this.isSameResolvedUrl(getCurrentUrl(), url)) { - updateHistoryRecord(url); - this.location = Location.fromUrl(url); - visit.to.url = this.location.url; - visit.to.hash = this.location.hash; - } - - // replace content: allow handlers and plugins to overwrite paga data and containers - await this.hooks.call('content:replace', visit, { page }, (visit, { page }) => { - this.classes.remove('is-leaving'); - // only add for animated page loads - if (visit.animation.animate) { - this.classes.add('is-rendering'); - } - const success = this.replaceContent(visit); - if (!success) { - throw new Error('[swup] Container mismatch, aborting'); - } - if (visit.animation.animate) { - // Make sure to add these classes to new containers as well - this.classes.add('is-changing', 'is-animating', 'is-rendering'); - if (visit.animation.name) { - this.classes.add(`to-${classify(visit.animation.name)}`); - } - } - }); - - // scroll into view: either anchor or top of page - await this.hooks.call('content:scroll', visit, undefined, () => { - return this.scrollToContent(visit); - }); - - await this.hooks.call('page:view', visit, { url: this.location.url, title: document.title }); -}; diff --git a/node_modules/swup/src/modules/replaceContent.ts b/node_modules/swup/src/modules/replaceContent.ts deleted file mode 100755 index 62859acd..00000000 --- a/node_modules/swup/src/modules/replaceContent.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type Swup from '../Swup.js'; -import { query, queryAll } from '../utils.js'; -import type { Visit } from './Visit.js'; - -/** - * Perform the replacement of content after loading a page. - * - * @returns Whether all containers were replaced. - */ -export const replaceContent = function (this: Swup, visit: Visit): boolean { - const incomingDocument = visit.to.document; - if (!incomingDocument) return false; - - // Update browser title - const title = incomingDocument.querySelector('title')?.innerText || ''; - document.title = title; - - // Save persisted elements - const persistedElements = queryAll('[data-swup-persist]:not([data-swup-persist=""])'); - - // Update content containers - const replaced = visit.containers - .map((selector) => { - const currentEl = document.querySelector(selector); - const incomingEl = incomingDocument.querySelector(selector); - if (currentEl && incomingEl) { - currentEl.replaceWith(incomingEl.cloneNode(true)); - return true; - } - if (!currentEl) { - console.warn(`[swup] Container missing in current document: ${selector}`); - } - if (!incomingEl) { - console.warn(`[swup] Container missing in incoming document: ${selector}`); - } - return false; - }) - .filter(Boolean); - - // Restore persisted elements - persistedElements.forEach((existing) => { - const key = existing.getAttribute('data-swup-persist'); - const replacement = query(`[data-swup-persist="${key}"]`); - if (replacement && replacement !== existing) { - replacement.replaceWith(existing); - } - }); - - // Return true if all containers were replaced - return replaced.length === visit.containers.length; -}; diff --git a/node_modules/swup/src/modules/resolveUrl.ts b/node_modules/swup/src/modules/resolveUrl.ts deleted file mode 100755 index a687d6fe..00000000 --- a/node_modules/swup/src/modules/resolveUrl.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type Swup from '../Swup.js'; - -/** - * Utility function to validate and run the global option 'resolveUrl' - * @param {string} url - * @returns {string} the resolved url - */ -export function resolveUrl(this: Swup, url: string): string { - if (typeof this.options.resolveUrl !== 'function') { - console.warn(`[swup] options.resolveUrl expects a callback function.`); - return url; - } - const result = this.options.resolveUrl(url); - if (!result || typeof result !== 'string') { - console.warn(`[swup] options.resolveUrl needs to return a url`); - return url; - } - if (result.startsWith('//') || result.startsWith('http')) { - console.warn(`[swup] options.resolveUrl needs to return a relative url`); - return url; - } - return result; -} - -/** - * Compares the resolved version of two paths and returns true if they are the same - * @param {string} url1 - * @param {string} url2 - * @returns {boolean} - */ -export function isSameResolvedUrl(this: Swup, url1: string, url2: string): boolean { - return this.resolveUrl(url1) === this.resolveUrl(url2); -} diff --git a/node_modules/swup/src/modules/scrollToContent.ts b/node_modules/swup/src/modules/scrollToContent.ts deleted file mode 100755 index 0c081a28..00000000 --- a/node_modules/swup/src/modules/scrollToContent.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type Swup from '../Swup.js'; -import type { Visit } from './Visit.js'; - -/** - * Update the scroll position after page render. - * @returns Promise<boolean> - */ -export const scrollToContent = function (this: Swup, visit: Visit): boolean { - const options: ScrollIntoViewOptions = { behavior: 'auto' }; - const { target, reset } = visit.scroll; - const scrollTarget = target ?? visit.to.hash; - - let scrolled = false; - - if (scrollTarget) { - scrolled = this.hooks.callSync( - 'scroll:anchor', - visit, - { hash: scrollTarget, options }, - (visit, { hash, options }) => { - const anchor = this.getAnchorElement(hash); - if (anchor) { - anchor.scrollIntoView(options); - } - return !!anchor; - } - ); - } - - if (reset && !scrolled) { - scrolled = this.hooks.callSync('scroll:top', visit, { options }, (visit, { options }) => { - window.scrollTo({ top: 0, left: 0, ...options }); - return true; - }); - } - - return scrolled; -}; diff --git a/node_modules/swup/src/utils.ts b/node_modules/swup/src/utils.ts deleted file mode 100644 index 7a87b145..00000000 --- a/node_modules/swup/src/utils.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Re-export all utils to allow custom package export path -// e.g. import { queryAll } from 'swup/utils' - -export * from './utils/index.js'; diff --git a/node_modules/swup/src/utils/index.ts b/node_modules/swup/src/utils/index.ts deleted file mode 100644 index 2d0e77a2..00000000 --- a/node_modules/swup/src/utils/index.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** Find an element by selector. */ -export const query = (selector: string, context: Document | Element = document) => { - return context.querySelector<HTMLElement>(selector); -}; - -/** Find a set of elements by selector. */ -export const queryAll = ( - selector: string, - context: Document | Element = document -): HTMLElement[] => { - return Array.from(context.querySelectorAll(selector)); -}; - -/** Return a Promise that resolves after the next event loop. */ -export const nextTick = (): Promise<void> => { - return new Promise((resolve) => { - requestAnimationFrame(() => { - requestAnimationFrame(() => { - resolve(); - }); - }); - }); -}; - -/** Check if an object is a Promise or a Thenable */ -export function isPromise<T>(obj: unknown): obj is PromiseLike<T> { - return ( - !!obj && - (typeof obj === 'object' || typeof obj === 'function') && - typeof (obj as Record<string, unknown>).then === 'function' - ); -} - -/** Call a function as a Promise. Resolves with the returned Promsise or immediately. */ -// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -export function runAsPromise(func: Function, args: unknown[] = []): Promise<unknown> { - return new Promise((resolve, reject) => { - const result: unknown = func(...args); - if (isPromise(result)) { - result.then(resolve, reject); - } else { - resolve(result); - } - }); -} - -/** - * Force a layout reflow, e.g. after adding classnames - * @see https://stackoverflow.com/a/21665117/3759615 - */ -export function forceReflow(element?: HTMLElement): void { - element = element || document.body; - element?.getBoundingClientRect(); -} - -/** - * Read data attribute from closest element with that attribute. - * - * Returns `undefined` if no element is found or attribute is missing. - * Returns `true` if attribute is present without a value. - */ -export function getContextualAttr( - el: Element | undefined, - attr: string -): string | boolean | undefined { - const target = el?.closest(`[${attr}]`); - return target?.hasAttribute(attr) ? target?.getAttribute(attr) || true : undefined; -} |
