diff options
Diffstat (limited to 'node_modules/swup/src/modules')
| -rwxr-xr-x | node_modules/swup/src/modules/Cache.ts | 87 | ||||
| -rw-r--r-- | node_modules/swup/src/modules/Classes.ts | 54 | ||||
| -rw-r--r-- | node_modules/swup/src/modules/Hooks.ts | 591 | ||||
| -rw-r--r-- | node_modules/swup/src/modules/Visit.ts | 174 | ||||
| -rwxr-xr-x | node_modules/swup/src/modules/animatePageIn.ts | 32 | ||||
| -rwxr-xr-x | node_modules/swup/src/modules/animatePageOut.ts | 19 | ||||
| -rw-r--r-- | node_modules/swup/src/modules/awaitAnimations.ts | 148 | ||||
| -rw-r--r-- | node_modules/swup/src/modules/fetchPage.ts | 114 | ||||
| -rwxr-xr-x | node_modules/swup/src/modules/getAnchorElement.ts | 32 | ||||
| -rwxr-xr-x | node_modules/swup/src/modules/navigate.ts | 239 | ||||
| -rw-r--r-- | node_modules/swup/src/modules/plugins.ts | 77 | ||||
| -rwxr-xr-x | node_modules/swup/src/modules/renderPage.ts | 51 | ||||
| -rwxr-xr-x | node_modules/swup/src/modules/replaceContent.ts | 51 | ||||
| -rwxr-xr-x | node_modules/swup/src/modules/resolveUrl.ts | 33 | ||||
| -rwxr-xr-x | node_modules/swup/src/modules/scrollToContent.ts | 38 |
15 files changed, 0 insertions, 1740 deletions
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; -}; |
