swup 3.1.0 → 4.0.0-rc.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/Swup.cjs +1 -1
  2. package/dist/Swup.cjs.map +1 -1
  3. package/dist/Swup.modern.js +1 -1
  4. package/dist/Swup.modern.js.map +1 -1
  5. package/dist/Swup.module.js +1 -1
  6. package/dist/Swup.module.js.map +1 -1
  7. package/dist/Swup.umd.js +1 -1
  8. package/dist/Swup.umd.js.map +1 -1
  9. package/dist/types/Swup.d.ts +20 -20
  10. package/dist/types/helpers/delegateEvent.d.ts +4 -2
  11. package/dist/types/helpers/fetch.d.ts +2 -2
  12. package/dist/types/helpers.d.ts +10 -10
  13. package/dist/types/index.d.ts +5 -5
  14. package/dist/types/modules/Cache.d.ts +2 -2
  15. package/dist/types/modules/__test__/fetchPage.test.d.ts +1 -0
  16. package/dist/types/modules/destroy.d.ts +2 -0
  17. package/dist/types/modules/enable.d.ts +2 -0
  18. package/dist/types/modules/enterPage.d.ts +2 -2
  19. package/dist/types/modules/events.d.ts +6 -6
  20. package/dist/types/modules/fetchPage.d.ts +3 -3
  21. package/dist/types/modules/getAnchorElement.d.ts +0 -7
  22. package/dist/types/modules/getAnimationPromises.d.ts +1 -1
  23. package/dist/types/modules/getPageData.d.ts +2 -2
  24. package/dist/types/modules/handleLinkToSamePage.d.ts +2 -0
  25. package/dist/types/modules/isSameResolvedUrl.d.ts +8 -0
  26. package/dist/types/modules/leavePage.d.ts +2 -2
  27. package/dist/types/modules/linkClickHandler.d.ts +3 -0
  28. package/dist/types/modules/loadPage.d.ts +2 -5
  29. package/dist/types/modules/off.d.ts +3 -0
  30. package/dist/types/modules/on.d.ts +5 -0
  31. package/dist/types/modules/plugins.d.ts +1 -1
  32. package/dist/types/modules/popStateHandler.d.ts +2 -0
  33. package/dist/types/modules/renderPage.d.ts +2 -2
  34. package/dist/types/modules/resolveUrl.d.ts +7 -0
  35. package/dist/types/modules/shouldIgnoreVisit.d.ts +4 -0
  36. package/dist/types/modules/transitions.d.ts +1 -1
  37. package/dist/types/modules/triggerEvent.d.ts +3 -0
  38. package/dist/types/modules/triggerWillOpenNewWindow.d.ts +2 -0
  39. package/dist/types/modules/updateTransition.d.ts +2 -0
  40. package/dist/types/utils.d.ts +1 -1
  41. package/package.json +8 -5
  42. package/readme.md +30 -12
  43. package/src/Swup.ts +115 -143
  44. package/src/__test__/index.test.ts +6 -1
  45. package/src/helpers/Location.ts +10 -7
  46. package/src/helpers/__test__/matchPath.test.ts +54 -0
  47. package/src/helpers/delegateEvent.ts +1 -0
  48. package/src/helpers/matchPath.ts +22 -0
  49. package/src/helpers.ts +2 -4
  50. package/src/index.ts +7 -4
  51. package/src/modules/Cache.ts +43 -33
  52. package/src/modules/Context.ts +91 -0
  53. package/src/modules/Hooks.ts +393 -0
  54. package/src/modules/__test__/cache.test.ts +142 -0
  55. package/src/modules/__test__/hooks.test.ts +192 -0
  56. package/src/modules/enterPage.ts +19 -17
  57. package/src/modules/fetchPage.ts +74 -29
  58. package/src/modules/getAnchorElement.ts +8 -4
  59. package/src/modules/getAnimationPromises.ts +66 -75
  60. package/src/modules/leavePage.ts +22 -20
  61. package/src/modules/loadPage.ts +72 -54
  62. package/src/modules/renderPage.ts +41 -32
  63. package/src/modules/replaceContent.ts +26 -17
  64. package/src/utils/index.ts +24 -3
  65. package/src/helpers/fetch.ts +0 -33
  66. package/src/helpers/getDataFromHtml.ts +0 -39
  67. package/src/helpers/markSwupElements.ts +0 -16
  68. package/src/modules/__test__/events.test.ts +0 -72
  69. package/src/modules/events.ts +0 -92
  70. package/src/modules/getPageData.ts +0 -24
  71. package/src/modules/transitions.ts +0 -10
@@ -1,81 +1,99 @@
1
- import { classify, createHistoryRecord, updateHistoryRecord, getCurrentUrl } from '../helpers.js';
2
1
  import Swup from '../Swup.js';
3
- import { PageRecord } from './Cache.js';
2
+ import { createHistoryRecord, updateHistoryRecord, getCurrentUrl, Location } from '../helpers.js';
3
+ import { FetchOptions } from '../modules/fetchPage.js';
4
+ import { ContextInitOptions } from './Context.js';
4
5
 
5
6
  export type HistoryAction = 'push' | 'replace';
6
7
 
7
- export type TransitionOptions = {
8
- url: string;
9
- customTransition?: string;
10
- history?: HistoryAction;
11
- };
12
-
13
8
  export type PageLoadOptions = {
14
- url: string;
15
- customTransition?: string;
9
+ animate?: boolean;
10
+ transition?: string;
16
11
  history?: HistoryAction;
17
- event?: PopStateEvent;
18
12
  };
19
13
 
20
- export function loadPage(this: Swup, data: TransitionOptions) {
21
- const { url } = data;
22
-
14
+ export function loadPage(
15
+ this: Swup,
16
+ url: string,
17
+ options: PageLoadOptions & FetchOptions = {},
18
+ context: Omit<ContextInitOptions, 'to'> = {}
19
+ ) {
23
20
  // Check if the visit should be ignored
24
21
  if (this.shouldIgnoreVisit(url)) {
25
22
  window.location.href = url;
26
23
  } else {
27
- this.performPageLoad(data);
24
+ const { url: to, hash } = Location.fromUrl(url);
25
+ this.context = this.createContext({ ...context, to, hash });
26
+ this.performPageLoad(to, options);
28
27
  }
29
28
  }
30
29
 
31
- export function performPageLoad(this: Swup, data: PageLoadOptions) {
32
- const { url, event, customTransition, history: historyAction = 'push' } = data ?? {};
30
+ export async function performPageLoad(
31
+ this: Swup,
32
+ url: string,
33
+ options: PageLoadOptions & FetchOptions = {}
34
+ ) {
35
+ if (typeof url !== 'string') {
36
+ throw new Error(`loadPage requires a URL parameter`);
37
+ }
33
38
 
34
- const isHistoryVisit = event instanceof PopStateEvent;
35
- const skipTransition = this.shouldSkipTransition({ url, event });
39
+ const { url: requestedUrl } = Location.fromUrl(url);
40
+ const { transition, animate, history: historyAction } = options;
41
+ options.referrer = options.referrer || this.currentPageUrl;
36
42
 
37
- this.triggerEvent('transitionStart', event);
43
+ if (animate === false) {
44
+ this.context.transition.animate = false;
45
+ }
46
+ if (historyAction) {
47
+ this.context.history.action = historyAction;
48
+ }
38
49
 
39
- // set transition object
40
- this.updateTransition(getCurrentUrl(), url, customTransition);
41
- if (customTransition != null) {
42
- document.documentElement.classList.add(`to-${classify(customTransition)}`);
50
+ if (!this.context.transition.animate) {
51
+ document.documentElement.classList.remove('is-animating');
52
+ this.cleanupAnimationClasses();
53
+ } else if (transition) {
54
+ this.context.transition.name = transition;
43
55
  }
44
56
 
45
- // start/skip animation
46
- const animationPromises = this.leavePage({ event, skipTransition });
57
+ try {
58
+ await this.hooks.trigger('transitionStart');
59
+ const animationPromise = this.leavePage();
60
+ const pagePromise = this.hooks.trigger(
61
+ 'loadPage',
62
+ { url, options },
63
+ async (context, { url, options, page }) => await (page || this.fetchPage(url, options))
64
+ );
65
+
66
+ // create history record if this is not a popstate call (with or without anchor)
67
+ if (!this.context.history.popstate) {
68
+ const newUrl = url + (this.context.scroll.target || '');
69
+ if (this.context.history.action === 'replace') {
70
+ updateHistoryRecord(newUrl);
71
+ } else {
72
+ createHistoryRecord(newUrl);
73
+ }
74
+ }
47
75
 
48
- // Load page data
49
- const fetchPromise = this.fetchPage(data);
76
+ this.currentPageUrl = getCurrentUrl();
50
77
 
51
- // create history record if this is not a popstate call (with or without anchor)
52
- if (!isHistoryVisit) {
53
- const historyUrl = url + (this.scrollToElement || '');
54
- if (historyAction === 'replace') {
55
- updateHistoryRecord(historyUrl);
56
- } else {
57
- createHistoryRecord(historyUrl);
78
+ // when everything is ready, render the page
79
+ const [page] = await Promise.all([pagePromise, animationPromise]);
80
+ this.renderPage(requestedUrl, page);
81
+ } catch (error: unknown) {
82
+ // Return early if error is undefined (probably aborted preload request)
83
+ if (!error) {
84
+ return;
58
85
  }
59
- }
60
86
 
61
- this.currentPageUrl = getCurrentUrl();
87
+ // Log to console as we swallow almost all hook errors
88
+ console.error(error);
62
89
 
63
- // when everything is ready, render the page
64
- Promise.all<PageRecord | void>([fetchPromise, ...animationPromises])
65
- .then(([pageData]) => {
66
- this.renderPage(pageData as PageRecord, { event, skipTransition });
67
- })
68
- .catch((errorUrl) => {
69
- // Return early if errorUrl is not defined (probably aborted preload request)
70
- if (errorUrl === undefined) return;
90
+ // Rewrite `skipPopStateHandling` to redirect manually when `history.go` is processed
91
+ this.options.skipPopStateHandling = () => {
92
+ window.location.href = requestedUrl;
93
+ return true;
94
+ };
71
95
 
72
- // Rewrite `skipPopStateHandling` to redirect manually when `history.go` is processed
73
- this.options.skipPopStateHandling = () => {
74
- window.location = errorUrl;
75
- return true;
76
- };
77
-
78
- // Go back to the actual page we're still at
79
- history.go(-1);
80
- });
96
+ // Go back to the actual page we're still at
97
+ history.go(-1);
98
+ }
81
99
  }
@@ -1,53 +1,62 @@
1
- import { Location, updateHistoryRecord, getCurrentUrl } from '../helpers.js';
1
+ import { updateHistoryRecord, getCurrentUrl } from '../helpers.js';
2
2
  import Swup from '../Swup.js';
3
- import { PageRecord } from './Cache.js';
3
+ import { PageData } from './fetchPage.js';
4
4
 
5
- export type PageRenderOptions = {
6
- event?: PopStateEvent;
7
- skipTransition?: boolean;
8
- };
5
+ export const renderPage = async function (this: Swup, requestedUrl: string, page: PageData) {
6
+ const { url } = page;
9
7
 
10
- export const renderPage = function (
11
- this: Swup,
12
- page: PageRecord,
13
- { event, skipTransition }: PageRenderOptions = {}
14
- ) {
15
8
  document.documentElement.classList.remove('is-leaving');
16
9
 
17
10
  // do nothing if another page was requested in the meantime
18
- if (!this.isSameResolvedUrl(getCurrentUrl(), page.url)) {
11
+ if (!this.isSameResolvedUrl(getCurrentUrl(), requestedUrl)) {
19
12
  return;
20
13
  }
21
14
 
22
- const { url } = Location.fromUrl(page.responseURL);
23
-
24
- // update cache and state if the url was redirected
15
+ // update state if the url was redirected
25
16
  if (!this.isSameResolvedUrl(getCurrentUrl(), url)) {
26
- this.cache.cacheUrl({ ...page, url });
27
- this.currentPageUrl = getCurrentUrl();
28
17
  updateHistoryRecord(url);
18
+ this.currentPageUrl = getCurrentUrl();
19
+ this.context.to!.url = this.currentPageUrl;
29
20
  }
30
21
 
31
22
  // only add for page loads with transitions
32
- if (!skipTransition) {
23
+ if (this.context.transition.animate) {
33
24
  document.documentElement.classList.add('is-rendering');
34
25
  }
35
26
 
36
- this.triggerEvent('willReplaceContent', event);
37
-
38
- this.replaceContent(page).then(() => {
39
- this.triggerEvent('contentReplaced', event);
40
- this.triggerEvent('pageView', event);
41
-
42
- // empty cache if it's disabled (in case preload plugin filled it)
43
- if (!this.options.cache) {
44
- this.cache.empty();
27
+ // replace content: allow handlers and plugins to overwrite paga data and containers
28
+ await this.hooks.trigger(
29
+ 'replaceContent',
30
+ { page, containers: this.context.containers },
31
+ (context, { page, containers }) => {
32
+ this.replaceContent(page, { containers });
33
+ }
34
+ );
35
+
36
+ await this.hooks.trigger(
37
+ 'scrollToContent',
38
+ { options: { behavior: 'auto' } },
39
+ (context, { options }) => {
40
+ if (this.context.scroll.target) {
41
+ const target = this.getAnchorElement(this.context.scroll.target);
42
+ if (target) {
43
+ target.scrollIntoView(options);
44
+ return;
45
+ }
46
+ }
47
+ if (this.context.scroll.reset) {
48
+ window.scrollTo(0, 0);
49
+ }
45
50
  }
51
+ );
46
52
 
47
- // Perform in transition
48
- this.enterPage({ event, skipTransition });
53
+ await this.hooks.trigger('pageView', { url: this.currentPageUrl, title: document.title });
54
+
55
+ // empty cache if it's disabled (in case preload plugin filled it)
56
+ if (!this.options.cache) {
57
+ this.cache.clear();
58
+ }
49
59
 
50
- // reset scroll-to element
51
- this.scrollToElement = null;
52
- });
60
+ // Perform in transition
61
+ this.enterPage();
53
62
  };
@@ -1,26 +1,35 @@
1
+ import Swup, { Options } from '../Swup.js';
2
+ import { PageData } from './fetchPage.js';
3
+
1
4
  /**
2
5
  * Perform the replacement of content after loading a page.
3
6
  *
4
- * This method can be replaced or augmented by plugins to allow pausing.
5
- *
6
- * It takes an object with the page data as return from `getPageData` and has to
7
- * return a Promise that resolves once all content has been replaced and the
8
- * site is ready to start animating in the new page.
9
- *
10
- * @param {object} page The page object
11
- * @returns Promise
7
+ * It takes an object with the page data as returned from `fetchPage` and a list
8
+ * of container selectors to replace.
12
9
  */
13
- export const replaceContent = function ({ blocks, title }: { blocks: string[]; title: string }) {
14
- // Replace content blocks
15
- blocks.forEach((html, i) => {
16
- // we know the block exists at this point
17
- const block = document.body.querySelector(`[data-swup="${i}"]`)!;
18
- block.outerHTML = html;
19
- });
10
+ export const replaceContent = function (
11
+ this: Swup,
12
+ { html }: PageData,
13
+ { containers }: { containers: Options['containers'] } = this.options
14
+ ): void {
15
+ const doc = new DOMParser().parseFromString(html, 'text/html');
20
16
 
21
17
  // Update browser title
18
+ const title = doc.querySelector('title')?.innerText || '';
22
19
  document.title = title;
23
20
 
24
- // Return a Promise to allow plugins to defer
25
- return Promise.resolve();
21
+ // Update content containers
22
+ containers.forEach((selector) => {
23
+ const currentEl = document.querySelector(selector);
24
+ const incomingEl = doc.querySelector(selector);
25
+ if (!currentEl) {
26
+ console.warn(`[swup] Container missing in current document: ${selector}`);
27
+ return;
28
+ }
29
+ if (!incomingEl) {
30
+ console.warn(`[swup] Container missing in incoming document: ${selector}`);
31
+ return;
32
+ }
33
+ currentEl.replaceWith(incomingEl);
34
+ });
26
35
  };
@@ -9,14 +9,35 @@ export const queryAll = (
9
9
  return Array.from(context.querySelectorAll(selector));
10
10
  };
11
11
 
12
- export const nextTick = (callback: () => void) => {
13
- requestAnimationFrame(() => {
12
+ export const nextTick = (): Promise<void> => {
13
+ return new Promise((resolve) => {
14
14
  requestAnimationFrame(() => {
15
- callback();
15
+ requestAnimationFrame(() => {
16
+ resolve();
17
+ });
16
18
  });
17
19
  });
18
20
  };
19
21
 
22
+ export function isPromise<T>(obj: any): obj is PromiseLike<T> {
23
+ return (
24
+ !!obj &&
25
+ (typeof obj === 'object' || typeof obj === 'function') &&
26
+ typeof obj.then === 'function'
27
+ );
28
+ }
29
+
30
+ export function runAsPromise(func: Function, args: any[] = [], ctx: any = {}): Promise<any> {
31
+ return new Promise((resolve, reject) => {
32
+ const result = func.apply(ctx, args);
33
+ if (isPromise(result)) {
34
+ result.then(resolve, reject);
35
+ } else {
36
+ resolve(result);
37
+ }
38
+ });
39
+ }
40
+
20
41
  export const escapeCssIdentifier = (ident: string) => {
21
42
  // @ts-ignore this is for support check, so it's correct that TS complains
22
43
  if (window.CSS && window.CSS.escape) {
@@ -1,33 +0,0 @@
1
- import { TransitionOptions } from '../modules/loadPage.js';
2
- import { Options } from '../Swup.js';
3
-
4
- export const fetch = (
5
- options: TransitionOptions & { headers: Options['requestHeaders'] },
6
- callback: (request: XMLHttpRequest) => void
7
- ): XMLHttpRequest => {
8
- const defaults = {
9
- url: window.location.pathname + window.location.search,
10
- method: 'GET',
11
- data: null,
12
- headers: {}
13
- };
14
-
15
- const { url, method, headers, data } = { ...defaults, ...options };
16
-
17
- const request = new XMLHttpRequest();
18
-
19
- request.onreadystatechange = function () {
20
- if (request.readyState === 4) {
21
- // if (request.status === 500) {} else {}
22
- callback(request);
23
- }
24
- };
25
-
26
- request.open(method, url, true);
27
- Object.entries(headers).forEach(([key, header]) => {
28
- request.setRequestHeader(key, header);
29
- });
30
- request.send(data);
31
-
32
- return request;
33
- };
@@ -1,39 +0,0 @@
1
- import { query, queryAll } from '../utils.js';
2
-
3
- export type PageHtmlData = {
4
- title: string;
5
- originalContent: string;
6
- blocks: string[];
7
- pageClass?: string;
8
- };
9
-
10
- export const getDataFromHtml = (html: string, containers: string[]): PageHtmlData => {
11
- let fakeDom = document.createElement('html');
12
- fakeDom.innerHTML = html;
13
- let blocks: string[] = [];
14
-
15
- containers.forEach((selector) => {
16
- if (query(selector, fakeDom) == null) {
17
- console.warn(`[swup] Container ${selector} not found on page.`);
18
- return null;
19
- } else {
20
- if (queryAll(selector).length !== queryAll(selector, fakeDom).length) {
21
- console.warn(`[swup] Mismatched number of containers found on new page.`);
22
- }
23
- queryAll(selector).forEach((item, index) => {
24
- queryAll(selector, fakeDom)[index].setAttribute('data-swup', String(blocks.length));
25
- blocks.push(queryAll(selector, fakeDom)[index].outerHTML);
26
- });
27
- }
28
- });
29
-
30
- const title = query('title', fakeDom)?.innerText || '';
31
- const pageClass = query('body', fakeDom)?.className;
32
-
33
- // to prevent memory leaks
34
- fakeDom.innerHTML = '';
35
- // @ts-ignore don't want to type it as possible null, since it's created at the top of the function always
36
- fakeDom = null;
37
-
38
- return { title, pageClass, blocks, originalContent: html };
39
- };
@@ -1,16 +0,0 @@
1
- import { query, queryAll } from '../utils.js';
2
-
3
- export const markSwupElements = (element: Element, containers: string[]): void => {
4
- let blocks = 0;
5
-
6
- containers.forEach((selector) => {
7
- if (query(selector, element) == null) {
8
- console.warn(`[swup] Container ${selector} not found on page.`);
9
- } else {
10
- queryAll(selector).forEach((item: Element, index: number) => {
11
- queryAll(selector, element)[index].setAttribute('data-swup', String(blocks));
12
- blocks++;
13
- });
14
- }
15
- });
16
- };
@@ -1,72 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import Swup from '../../Swup.js';
3
- import { Handler } from '../events.js';
4
-
5
- describe('events', () => {
6
- it('should add event handlers to handlers array', () => {
7
- const swup = new Swup();
8
- const handler = vi.fn();
9
-
10
- swup.on('enabled', handler);
11
-
12
- expect(swup._handlers.enabled.indexOf(handler)).toBe(0);
13
- });
14
-
15
- it('should remove event handlers from handlers array', () => {
16
- const swup = new Swup();
17
- const handler = vi.fn();
18
-
19
- swup.on('enabled', handler);
20
- swup.on('animationInDone', handler);
21
- swup.on('animationInStart', handler);
22
-
23
- expect(swup._handlers.enabled.indexOf(handler)).toBe(0);
24
- expect(swup._handlers.animationInDone.indexOf(handler)).toBe(0);
25
- expect(swup._handlers.animationInStart.indexOf(handler)).toBe(0);
26
-
27
- swup.off('enabled', handler);
28
- expect(swup._handlers.enabled.indexOf(handler)).toBe(-1);
29
-
30
- swup.off('animationInDone');
31
- expect(swup._handlers.animationInDone.indexOf(handler)).toBe(-1);
32
-
33
- swup.off();
34
- expect(swup._handlers.animationInStart.indexOf(handler)).toBe(-1);
35
- });
36
-
37
- it('should trigger event handler', () => {
38
- const swup = new Swup();
39
- const handler = vi.fn();
40
-
41
- swup.on('enabled', handler);
42
-
43
- swup.triggerEvent('enabled');
44
-
45
- expect(handler).toBeCalledTimes(1);
46
- });
47
-
48
- it('should trigger event handler with event', () => {
49
- const swup = new Swup();
50
- const handler: Handler<'popState'> = vi.fn();
51
- const event = new PopStateEvent('');
52
-
53
- swup.on('popState', handler);
54
- swup.triggerEvent('popState', event);
55
-
56
- expect(handler).toBeCalledWith(event);
57
- });
58
-
59
- it('types work and error when necessary', () => {
60
- const swup = new Swup();
61
-
62
- // @ts-expect-no-error
63
- swup.on('popState', (event: PopStateEvent) => {});
64
- // @ts-expect-no-error
65
- swup.triggerEvent('popState', new PopStateEvent(''));
66
-
67
- // @ts-expect-error
68
- swup.on('popState', (event: MouseEvent) => {});
69
- // @ts-expect-error
70
- swup.triggerEvent('popState', new MouseEvent(''));
71
- });
72
- });
@@ -1,92 +0,0 @@
1
- import Swup from '../Swup.js';
2
- import { DelegateEvent } from 'delegate-it';
3
-
4
- type HandlersEventMap = {
5
- animationInDone: undefined;
6
- animationInStart: undefined;
7
- animationOutDone: undefined;
8
- animationOutStart: undefined;
9
- animationSkipped: undefined;
10
- clickLink: DelegateEvent<MouseEvent>;
11
- contentReplaced: PopStateEvent | undefined;
12
- disabled: undefined;
13
- enabled: undefined;
14
- openPageInNewTab: DelegateEvent<MouseEvent>;
15
- pageLoaded: undefined;
16
- pageRetrievedFromCache: undefined;
17
- pageView: PopStateEvent | undefined;
18
- popState: PopStateEvent;
19
- samePage: DelegateEvent<MouseEvent>;
20
- samePageWithHash: DelegateEvent<MouseEvent>;
21
- serverError: undefined;
22
- transitionStart: PopStateEvent | undefined;
23
- transitionEnd: PopStateEvent | undefined;
24
- willReplaceContent: PopStateEvent | undefined;
25
- };
26
- type AvailableEventNames = keyof HandlersEventMap;
27
-
28
- export type Handler<T extends keyof HandlersEventMap> = (event: HandlersEventMap[T]) => void;
29
- export type Handlers = {
30
- [Key in keyof HandlersEventMap]: Handler<Key>[];
31
- };
32
-
33
- export function on<TEventType extends AvailableEventNames>(
34
- this: Swup,
35
- event: TEventType,
36
- handler: Handler<TEventType>
37
- ): void {
38
- const eventHandlers = this._handlers[event] as Handler<TEventType>[];
39
-
40
- if (eventHandlers) {
41
- eventHandlers.push(handler);
42
- } else {
43
- console.warn(`Unsupported event ${event}.`);
44
- }
45
- }
46
-
47
- export function off<TEventType extends AvailableEventNames>(
48
- this: Swup,
49
- event?: TEventType,
50
- handler?: Handler<TEventType>
51
- ) {
52
- if (event && handler) {
53
- const eventHandlers = this._handlers[event] as Handler<TEventType>[];
54
- // Remove specific handler
55
- if (eventHandlers.includes(handler)) {
56
- (this._handlers[event] as Handler<TEventType>[]) = eventHandlers.filter(
57
- (h) => h !== handler
58
- );
59
- } else {
60
- console.warn(`Handler for event '${event}' not found.`);
61
- }
62
- } else if (event) {
63
- // Remove all handlers for specific event
64
- this._handlers[event] = [];
65
- } else {
66
- // Remove all handlers for all events
67
- Object.keys(this._handlers).forEach((event) => {
68
- this._handlers[event as keyof HandlersEventMap] = [];
69
- });
70
- }
71
- }
72
-
73
- export function triggerEvent<TEventType extends AvailableEventNames>(
74
- this: Swup,
75
- eventName: TEventType,
76
- originalEvent?: HandlersEventMap[TEventType]
77
- ): void {
78
- const eventHandlers = this._handlers[eventName] as Handler<TEventType>[];
79
-
80
- // call saved handlers with "on" method and pass originalEvent object if available
81
- eventHandlers.forEach((handler) => {
82
- try {
83
- handler(originalEvent as HandlersEventMap[TEventType]);
84
- } catch (error) {
85
- console.error(error);
86
- }
87
- });
88
-
89
- // trigger event on document with prefix "swup:"
90
- const event = new CustomEvent(`swup:${eventName}`, { detail: eventName });
91
- document.dispatchEvent(event);
92
- }
@@ -1,24 +0,0 @@
1
- import { getDataFromHtml } from '../helpers.js';
2
- import Swup from '../Swup.js';
3
- import { PageHtmlData } from '../helpers/getDataFromHtml.js';
4
-
5
- export type PageData = PageHtmlData & {
6
- responseURL: string;
7
- };
8
- export const getPageData = function (this: Swup, request: XMLHttpRequest): PageData | null {
9
- // this method can be replaced in case other content than html is expected to be received from server
10
- // this function should always return { title, pageClass, originalContent, blocks, responseURL }
11
- // in case page has invalid structure - return null
12
- const html = request.responseText;
13
- const pageHtmlData = getDataFromHtml(html, this.options.containers);
14
-
15
- if (!pageHtmlData) {
16
- console.warn('[swup] Received page is invalid.');
17
- return null;
18
- }
19
-
20
- return {
21
- ...pageHtmlData,
22
- responseURL: request.responseURL || window.location.href
23
- };
24
- };
@@ -1,10 +0,0 @@
1
- import Swup from '../Swup.js';
2
-
3
- export function updateTransition(this: Swup, from: string, to: string, custom?: string): void {
4
- this.transition = { from, to, custom };
5
- }
6
-
7
- export function shouldSkipTransition(this: Swup, { event }: { url?: string; event?: Event }) {
8
- const isHistoryVisit = event instanceof PopStateEvent;
9
- return !!(isHistoryVisit && !this.options.animateHistoryBrowsing);
10
- }