pageturn 0.1.0 → 0.2.0

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 (75) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +179 -4
  3. package/dist/astro.js +0 -2
  4. package/dist/chunks/chunk-3ABPYO7H.js +43 -0
  5. package/dist/chunks/chunk-6FXXBNAF.js +197 -0
  6. package/dist/chunks/chunk-AEOAXMU3.js +33 -0
  7. package/dist/chunks/chunk-BOM5VXA5.js +167 -0
  8. package/dist/chunks/chunk-C3T7BGNG.js +30 -0
  9. package/dist/chunks/chunk-FYSSNYA4.js +17 -0
  10. package/dist/chunks/chunk-JHM7F4CH.js +172 -0
  11. package/dist/chunks/chunk-LM3XLSCW.js +799 -0
  12. package/dist/chunks/{chunk-QJ7GGFM4.js → chunk-YAL4MNCU.js} +4 -6
  13. package/dist/core.js +4 -3
  14. package/dist/dev/chunks/chunk-3JAWIMHB.js +184 -0
  15. package/dist/dev/chunks/chunk-3SU4CIKL.js +799 -0
  16. package/dist/dev/chunks/chunk-AEOAXMU3.js +33 -0
  17. package/dist/dev/chunks/chunk-C3T7BGNG.js +30 -0
  18. package/dist/dev/chunks/chunk-FYSSNYA4.js +17 -0
  19. package/dist/dev/chunks/chunk-I4SBUABE.js +175 -0
  20. package/dist/dev/chunks/chunk-U3XN54YY.js +219 -0
  21. package/dist/dev/chunks/chunk-Y6TD2T53.js +43 -0
  22. package/dist/dev/chunks/chunk-YAL4MNCU.js +22 -0
  23. package/dist/dev/core.js +19 -0
  24. package/dist/dev/index.js +21 -0
  25. package/dist/dev/morph-markup.js +34 -0
  26. package/dist/dev/morph.js +457 -0
  27. package/dist/dev/native.js +3 -0
  28. package/dist/dev/next.js +79 -0
  29. package/dist/dev/nuxt-plugin.js +24 -0
  30. package/dist/dev/prefetch.js +2 -0
  31. package/dist/dev/react-router.js +46 -0
  32. package/dist/dev/router.js +9 -0
  33. package/dist/dev/sveltekit.js +32 -0
  34. package/dist/dev/testing.js +48 -0
  35. package/dist/dev/turbo.js +49 -0
  36. package/dist/dev/vue.js +11 -0
  37. package/dist/head-core.js +1 -1
  38. package/dist/head-string.js +6 -6
  39. package/dist/head.js +1 -1
  40. package/dist/index.js +6 -5
  41. package/dist/morph-head.js +1 -1
  42. package/dist/morph-markup.js +11 -9
  43. package/dist/morph.js +145 -287
  44. package/dist/native.js +3 -2
  45. package/dist/next.js +79 -0
  46. package/dist/nuxt-plugin.js +24 -0
  47. package/dist/nuxt.js +34 -0
  48. package/dist/pageturn-morph.js +1 -1
  49. package/dist/pageturn.js +1 -1
  50. package/dist/prefetch.js +2 -2
  51. package/dist/react-router.js +46 -0
  52. package/dist/router.js +5 -183
  53. package/dist/sveltekit-server.js +22 -0
  54. package/dist/sveltekit.js +32 -0
  55. package/dist/testing.js +12 -22
  56. package/dist/turbo.js +49 -0
  57. package/dist/types/astro/integration.d.ts +0 -4
  58. package/dist/types/common/dev.d.ts +6 -0
  59. package/dist/types/common/page.d.ts +5 -0
  60. package/dist/types/common/registry.d.ts +3 -1
  61. package/dist/types/next/next.d.ts +27 -0
  62. package/dist/types/nuxt/module.d.ts +32 -0
  63. package/dist/types/react-router/react-router.d.ts +12 -0
  64. package/dist/types/router/router.d.ts +4 -2
  65. package/dist/types/sveltekit/server.d.ts +21 -0
  66. package/dist/types/sveltekit/sveltekit.d.ts +32 -0
  67. package/dist/types/turbo/turbo.d.ts +1 -0
  68. package/dist/types/vue/vue.d.ts +36 -0
  69. package/dist/vue.js +11 -0
  70. package/package.json +102 -23
  71. package/dist/chunks/chunk-237GNQNZ.js +0 -37
  72. package/dist/chunks/chunk-7LMNJ25V.js +0 -244
  73. package/dist/chunks/chunk-QC36TQP3.js +0 -24
  74. package/dist/chunks/chunk-WTHN3UIQ.js +0 -1000
  75. package/dist/chunks/chunk-YBAP3PHO.js +0 -247
@@ -0,0 +1,799 @@
1
+ import {
2
+ arrival,
3
+ defineMove
4
+ } from "./chunk-6FXXBNAF.js";
5
+ import {
6
+ readSession,
7
+ removeSession,
8
+ writeSession
9
+ } from "./chunk-JHM7F4CH.js";
10
+ import {
11
+ afterDomContentLoaded,
12
+ giveFocusTo,
13
+ inBrowser,
14
+ navigationEntry,
15
+ notShownAnew,
16
+ reducesMotion
17
+ } from "./chunk-AEOAXMU3.js";
18
+ import {
19
+ isDeclined,
20
+ registry
21
+ } from "./chunk-YAL4MNCU.js";
22
+
23
+ // src/morph/end.ts
24
+ var wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)), within = (promise, milliseconds) => Promise.race([promise, wait(milliseconds).then(() => null)]), isEnding = (animation) => Number.isFinite(animation.effect?.getComputedTiming().endTime), CORNERS = ["TopLeft", "TopRight", "BottomRight", "BottomLeft"], cornersOf = (radius) => typeof radius == "number" ? [radius, radius, radius, radius] : radius, radiusOf = (corners) => corners.every((corner) => corner === corners[0]) ? corners[0] : corners;
25
+ function ownCorners(style, width, scale = 1) {
26
+ return radiusOf(
27
+ CORNERS.map((corner) => {
28
+ let value = style[`border${corner}Radius`], radius = Number.parseFloat(value) || 0;
29
+ return value.includes("%") ? radius / 100 * width : radius * scale;
30
+ })
31
+ );
32
+ }
33
+ var cornersCss = (radius, scale = 1) => cornersOf(radius).map((corner) => `${corner / scale}px`).join(" ");
34
+ function seenCorners(element, box) {
35
+ let corners = [
36
+ ...cornersOf(ownCorners(getComputedStyle(element), box.width))
37
+ ], near = (one, other) => Math.abs(one - other) < 1;
38
+ for (let clip = element.parentElement; clip && clip !== document.body; clip = clip.parentElement) {
39
+ let style = getComputedStyle(clip);
40
+ if (style.overflow === "visible" && !/paint|strict|content/.test(style.contain))
41
+ continue;
42
+ let edge = clip.getBoundingClientRect(), left = edge.left + clip.clientLeft, top = edge.top + clip.clientTop, right = near(box.right, left + clip.clientWidth), bottom = near(box.bottom, top + clip.clientHeight), atLeft = near(box.left, left), atTop = near(box.top, top), radii = cornersOf(ownCorners(style, edge.width)), border = Math.max(clip.clientLeft, clip.clientTop);
43
+ [
44
+ atLeft && atTop,
45
+ right && atTop,
46
+ right && bottom,
47
+ atLeft && bottom
48
+ ].forEach((meets, index) => {
49
+ meets && (corners[index] = Math.max(
50
+ corners[index] ?? 0,
51
+ (radii[index] ?? 0) - border
52
+ ));
53
+ });
54
+ }
55
+ return radiusOf(corners);
56
+ }
57
+ function endOf(element) {
58
+ let rect = element.getBoundingClientRect(), { left, top, width, height } = rect, end = {
59
+ box: { left, top, width, height },
60
+ radius: seenCorners(element, rect)
61
+ }, picture2 = element.matches("img") ? element : element.querySelector("img");
62
+ return picture2?.sizes && (end.sizes = picture2.sizes), end;
63
+ }
64
+ var shifted = (end, x, y) => ({
65
+ ...end,
66
+ box: { ...end.box, left: end.box.left + x, top: end.box.top + y }
67
+ }), looksSame = (one, other) => {
68
+ let drawn = (end) => [
69
+ end.box.left,
70
+ end.box.top,
71
+ end.box.width,
72
+ end.box.height,
73
+ ...cornersOf(end.radius),
74
+ end.rotate ?? 0,
75
+ end.text?.box.left ?? 0,
76
+ end.text?.box.top ?? 0,
77
+ Number.parseFloat(end.text?.type.fontSize ?? "0")
78
+ ], others = drawn(other);
79
+ return drawn(one).every(
80
+ (value, index) => Math.abs(value - (others[index] ?? 0)) < 0.5
81
+ ) && one.text?.color === other.text?.color && one.background === other.background;
82
+ };
83
+ function atTheirEnd(animations, read) {
84
+ let held = animations.filter(isEnding), times = held.map((animation) => animation.currentTime);
85
+ for (let animation of held)
86
+ animation.currentTime = animation.effect?.getComputedTiming().endTime;
87
+ try {
88
+ return read();
89
+ } finally {
90
+ held.forEach((animation, index) => {
91
+ animation.currentTime = times[index] ?? null;
92
+ });
93
+ }
94
+ }
95
+
96
+ // src/morph/layer.ts
97
+ var LAYER_STYLE = "position:fixed;inset:auto;left:0;top:0;margin:0;border:0;padding:0;overflow:visible;background:none;color:inherit;pointer-events:none;z-index:2147483647;transform-origin:50% 50%;max-width:none;max-height:none", CONTENT_STYLE = "margin:0;max-width:none;max-height:none;box-sizing:border-box;visibility:visible;translate:none;rotate:none;scale:none;transform:none";
98
+ function cloned(element) {
99
+ let copy = element.cloneNode(!0);
100
+ for (let named of [copy, ...copy.querySelectorAll("[id]")])
101
+ named.removeAttribute("id");
102
+ return copy;
103
+ }
104
+ var sizeOf = (layer) => [
105
+ Number.parseFloat(layer.style.width),
106
+ Number.parseFloat(layer.style.height)
107
+ ], centredOn = (box, width, height) => `${box.left + (box.width - width) / 2}px ${box.top + (box.height - height) / 2}px`, moveAt = (pose, end) => ({
108
+ ...pose.move,
109
+ opacity: end.opacity ?? 1
110
+ });
111
+ function standAt(layer, kind, end) {
112
+ let pose = kind.pose(layer)(end);
113
+ Object.assign(layer.style, moveAt(pose, end)), Object.assign(pose.on.style, pose.look), pose.inner && Object.assign(pose.inner.on.style, pose.inner.look);
114
+ }
115
+ function markup(layer) {
116
+ let copy = layer.cloneNode(!0);
117
+ for (let shot of copy.querySelectorAll("[data-turn-shot]")) shot.remove();
118
+ let drawn = layer.querySelectorAll("img");
119
+ copy.querySelectorAll("img").forEach((picture2, index) => {
120
+ let live = drawn[index], file = live?.complete && live.naturalWidth ? live.currentSrc : picture2.dataset.turnFile;
121
+ if (file) {
122
+ picture2.src = file, picture2.removeAttribute("srcset"), picture2.removeAttribute("sizes");
123
+ for (let source of picture2.parentElement?.querySelectorAll("source") ?? [])
124
+ source.remove();
125
+ }
126
+ });
127
+ for (let styled of [
128
+ copy,
129
+ ...copy.querySelectorAll("[style]")
130
+ ])
131
+ styled.dataset.turnStyle = styled.style.cssText, styled.removeAttribute("style");
132
+ return copy.outerHTML;
133
+ }
134
+
135
+ // src/morph/picture.ts
136
+ var PICTURE_WAIT_MS = 250, MEDIA = "img,video,canvas";
137
+ async function whenDrawn(element) {
138
+ let picture2 = element.matches("img") ? element : element.querySelector("img");
139
+ picture2 instanceof HTMLImageElement && await within(picture2.decode(), PICTURE_WAIT_MS).catch(() => {
140
+ });
141
+ }
142
+ function snapshot(shown, width, height, position) {
143
+ let { naturalWidth, naturalHeight } = shown;
144
+ if (!shown.complete || !naturalWidth || !naturalHeight) return null;
145
+ let canvas = document.createElement("canvas"), cover = Math.max(width / naturalWidth, height / naturalHeight), across = width / cover, down = height / cover, [x = 0.5, y = 0.5] = position.split(" ").map(
146
+ (share) => share.endsWith("%") ? Number.parseFloat(share) / 100 : 0.5
147
+ );
148
+ canvas.width = Math.round(across), canvas.height = Math.round(down);
149
+ try {
150
+ canvas.getContext("2d")?.drawImage(
151
+ shown,
152
+ (naturalWidth - across) * x,
153
+ (naturalHeight - down) * y,
154
+ across,
155
+ down,
156
+ 0,
157
+ 0,
158
+ canvas.width,
159
+ canvas.height
160
+ );
161
+ } catch {
162
+ return null;
163
+ }
164
+ return canvas.dataset.turnShot = "", canvas.style.cssText = "position:absolute;left:0;top:0;width:100%;height:100%;z-index:-1", canvas;
165
+ }
166
+ function pictureOf(element) {
167
+ let inside = element.querySelectorAll(MEDIA);
168
+ return element.matches(MEDIA) ? element : inside.length === 1 ? inside[0] : null;
169
+ }
170
+ var picture = {
171
+ read(element, end) {
172
+ let inner = pictureOf(element);
173
+ if (!inner || inner === element) return;
174
+ let style = getComputedStyle(inner), own = new DOMMatrix(style.transform), zoom = Math.hypot(own.a, own.b) * (Number.parseFloat(style.scale) || 1);
175
+ Math.abs(zoom - 1) > 1e-3 && (end.zoom = zoom);
176
+ },
177
+ build(element, from, to) {
178
+ let layer = document.createElement("div"), media = pictureOf(element), content = cloned(
179
+ media ? media.closest("picture") ?? media : element
180
+ ), drawn = content.matches(MEDIA) ? content : (media && content.querySelector(MEDIA)) ?? content;
181
+ drawn !== content && (content.style.cssText = "display:contents"), content.dataset.turnContent = "", drawn.style.cssText += `;display:block;width:100%;height:100%;${CONTENT_STYLE}`, media && (drawn.style.objectFit = "cover", drawn.style.objectPosition = getComputedStyle(media).objectPosition);
182
+ let { width, height } = (to ?? from).box;
183
+ if (layer.style.cssText = `${LAYER_STYLE};width:${width}px;height:${height}px`, layer.append(content), drawn instanceof HTMLImageElement && media instanceof HTMLImageElement) {
184
+ if (drawn.loading = "eager", drawn.decoding = "sync", drawn.dataset.turnFile = media.currentSrc, to) {
185
+ let sizes = to.sizes ?? `${Math.ceil(width)}px`;
186
+ for (let sized of layer.querySelectorAll("img[srcset],source[srcset]"))
187
+ sized.sizes = sizes;
188
+ }
189
+ let shot = snapshot(media, width, height, drawn.style.objectPosition);
190
+ if (shot) {
191
+ layer.append(shot);
192
+ let drew = () => {
193
+ drawn.decode().then(
194
+ () => shot.remove(),
195
+ () => {
196
+ }
197
+ );
198
+ };
199
+ drawn.complete && drawn.naturalWidth ? drew() : drawn.addEventListener("load", drew, { once: !0 });
200
+ }
201
+ }
202
+ return layer;
203
+ },
204
+ pose(layer) {
205
+ let [width, height] = sizeOf(layer), held = layer.querySelector(MEDIA) ?? layer.firstElementChild;
206
+ return ({ box, rotate = 0, radius, zoom = 1 }) => {
207
+ let cover = Math.max(box.width / width, box.height / height);
208
+ return {
209
+ move: {
210
+ translate: centredOn(box, width, height),
211
+ rotate: `${rotate}deg`,
212
+ scale: `${cover}`
213
+ },
214
+ look: {
215
+ clipPath: `inset(${(height - box.height / cover) / 2}px ${(width - box.width / cover) / 2}px round ${cornersCss(radius, cover)})`
216
+ },
217
+ on: layer,
218
+ inner: { look: { scale: `${zoom}` }, on: held }
219
+ };
220
+ };
221
+ },
222
+ fit(layer, to) {
223
+ layer.style.width = `${to.box.width}px`, layer.style.height = `${to.box.height}px`;
224
+ }
225
+ };
226
+
227
+ // src/morph/place.ts
228
+ var MORPH_KEY = "pageturn:morph", outsideLayer = (selector) => `:is(${selector}):not([data-turn-layer] *)`, onPage = (selector) => document.querySelector(outsideLayer(selector));
229
+ function whenParsed(run) {
230
+ document.readyState === "loading" ? document.addEventListener("readystatechange", run, { once: !0 }) : run();
231
+ }
232
+ function putBack(selector, name, reopens = !1, kept = [], link) {
233
+ let root = document.documentElement;
234
+ root.dataset.turnRestoring = "", notShownAnew();
235
+ let around = [];
236
+ whenParsed(() => {
237
+ let origin = null;
238
+ try {
239
+ origin = selector ? onPage(selector) : null;
240
+ } catch {
241
+ }
242
+ origin?.setAttribute("data-turn-origin", name);
243
+ for (let [where, attribute, value] of kept)
244
+ try {
245
+ let element = document.querySelector(where);
246
+ value === null ? element?.removeAttribute(attribute) : element?.setAttribute(attribute, value);
247
+ } catch {
248
+ }
249
+ for (let within2 = origin?.parentElement?.closest("[popover]"); reopens && within2; within2 = within2.parentElement?.closest("[popover]"))
250
+ around.unshift(within2);
251
+ for (let popover of around)
252
+ try {
253
+ popover.showPopover();
254
+ } catch {
255
+ }
256
+ }), afterDomContentLoaded(
257
+ () => requestAnimationFrame(
258
+ () => requestAnimationFrame(() => {
259
+ link && giveFocusBack(link, around), delete root.dataset.turnRestoring;
260
+ })
261
+ )
262
+ );
263
+ }
264
+ var linkAt = (address, rank) => [...document.querySelectorAll("a[href]")].filter(
265
+ (anchor) => anchor.href === address
266
+ )[rank];
267
+ function giveFocusBack([address, rank, isShown], around) {
268
+ let focused = document.activeElement, target = linkAt(address, rank);
269
+ target && (!focused || focused === document.body || around.some((popover) => popover.contains(focused))) && giveFocusTo(target, {
270
+ preventScroll: !0,
271
+ focusVisible: isShown
272
+ });
273
+ }
274
+ function showLayer(layer) {
275
+ layer.setAttribute("aria-hidden", "true"), layer.setAttribute("popover", "manual"), layer.dataset.turnLayer = "", document.documentElement.append(layer);
276
+ try {
277
+ layer.showPopover();
278
+ } catch {
279
+ }
280
+ }
281
+ function placeHandedLayer() {
282
+ if (window.top !== window) {
283
+ try {
284
+ let fromName = JSON.parse(
285
+ window.name.split(`${MORPH_KEY}:`)[1] ?? ""
286
+ );
287
+ whenParsed(() => scrollTo(fromName.x, fromName.y)), putBack(fromName.p, fromName.o, fromName.v, fromName.w), window.pageturn && (window.pageturn.behind = { move: fromName.m, data: fromName.d });
288
+ } catch {
289
+ }
290
+ return null;
291
+ }
292
+ let flight = readSession(MORPH_KEY);
293
+ if (removeSession(MORPH_KEY), !flight || flight.n !== window.pageturn?.arrival?.move) return null;
294
+ let holder = document.createElement("div");
295
+ holder.innerHTML = flight.h;
296
+ let layer = holder.firstElementChild;
297
+ if (!(layer instanceof HTMLElement)) return null;
298
+ let sheet = null;
299
+ if (flight.s)
300
+ try {
301
+ let rule = new CSSStyleSheet();
302
+ rule.insertRule(`${outsideLayer(flight.s)}{visibility:hidden!important}`), document.adoptedStyleSheets = [...document.adoptedStyleSheets, rule], sheet = rule;
303
+ } catch {
304
+ }
305
+ for (let styled of [
306
+ layer,
307
+ ...layer.querySelectorAll("[data-turn-style]")
308
+ ])
309
+ styled.style.cssText = styled.dataset.turnStyle ?? "";
310
+ showLayer(layer);
311
+ let placed = { flight, layer, sheet, taken: !1 };
312
+ flight.b && (document.documentElement.dataset.turnBehind = "");
313
+ let { x = 0, y } = flight;
314
+ return y !== void 0 && whenParsed(() => scrollTo(x, y)), flight.o && putBack(flight.s, flight.o, flight.v, flight.w, flight.l), afterDomContentLoaded(
315
+ () => setTimeout(() => placed.taken || clearLayer(placed))
316
+ ), placed;
317
+ }
318
+ function clearLayer(placed) {
319
+ placed.taken = !0, document.querySelector("[data-turn-origin]")?.removeAttribute("data-turn-origin"), placed.layer.remove(), document.adoptedStyleSheets = document.adoptedStyleSheets.filter(
320
+ (sheet) => sheet !== placed.sheet
321
+ );
322
+ }
323
+
324
+ // src/morph/timing.ts
325
+ var over = (timing, leg = {}) => ({
326
+ ...timing,
327
+ duration: leg.duration ?? timing.duration,
328
+ delay: leg.delay ?? timing.delay,
329
+ easing: leg.easing ?? timing.easing
330
+ });
331
+
332
+ // src/morph/define.ts
333
+ var DURATION_MS = 500, EASING = "cubic-bezier(0.2, 0, 0, 1)", CORRECTION_MS = 300;
334
+ function handedOver() {
335
+ return window.pageturnMorph === void 0 && (window.pageturnMorph = placeHandedLayer()), window.pageturnMorph;
336
+ }
337
+ var isHandingOver = !1, isLeaving = () => isHandingOver;
338
+ function hide(element) {
339
+ let hidden = element;
340
+ return hidden.style.visibility = "hidden", hidden;
341
+ }
342
+ var defined = /* @__PURE__ */ new Set(), isDefined = (name) => defined.has(name);
343
+ function defineMorph(name, morph) {
344
+ if (!inBrowser || window.top !== window || reducesMotion())
345
+ return () => {
346
+ };
347
+ defined.add(name);
348
+ let kind = morph.as ?? picture, finders = [morph.find ?? []].flat(), atRest = (element) => (
349
+ // An element of another document (a page peek() loaded) has none of this one's.
350
+ element.ownerDocument !== document ? stage.read(element) : atTheirEnd(
351
+ document.getAnimations().filter((animation) => {
352
+ let { effect } = animation;
353
+ return effect instanceof KeyframeEffect && effect.target?.contains(element);
354
+ }),
355
+ () => stage.read(element)
356
+ )
357
+ ), stage = {
358
+ name,
359
+ morph,
360
+ live: null,
361
+ endOf,
362
+ onLeave: [],
363
+ onStay: [],
364
+ onPlay: [],
365
+ onRemove: [],
366
+ siteAnimations: [],
367
+ atRest,
368
+ read(element) {
369
+ let end = stage.endOf(element);
370
+ return kind.read?.(element, end, element.matches(morph.to)), end;
371
+ },
372
+ timing: (leg) => over(
373
+ over(
374
+ { duration: DURATION_MS, delay: 0, easing: EASING, fill: "both" },
375
+ morph
376
+ ),
377
+ leg
378
+ ),
379
+ about: (link, back2) => ({
380
+ back: back2,
381
+ link,
382
+ decline: () => registry.f,
383
+ measure(element, at) {
384
+ let end = stage.read(element);
385
+ if (!at) return end;
386
+ let place = stage.read(at), own = end.text?.box ?? end.box, other = place.text?.box ?? place.box, across = other.left - own.left, down = other.top - own.top, there = shifted(end, across, down);
387
+ return there.text && (there.text = {
388
+ ...there.text,
389
+ box: {
390
+ ...there.text.box,
391
+ left: there.text.box.left + across,
392
+ top: there.text.box.top + down
393
+ }
394
+ }), there;
395
+ },
396
+ scroll: { x: scrollX, y: scrollY }
397
+ }),
398
+ layer(element, at, to) {
399
+ let layer = kind.build(element, at, to);
400
+ return standAt(layer, kind, at), layer;
401
+ },
402
+ async put(element, at, to) {
403
+ let layer = stage.layer(element, at, to);
404
+ return stage.live = { layer, hidden: hide(element) }, showLayer(layer), layer;
405
+ },
406
+ async travel(start, stop, about, timing, plays = !0, behind = null) {
407
+ let layer = stage.live?.layer;
408
+ if (!layer) return;
409
+ let pose = kind.pose(layer), first = pose(start), last = pose(stop);
410
+ layer.animate([moveAt(first, start), moveAt(last, stop)], timing), first.on.animate([first.look, last.look], timing), first.inner && last.inner && JSON.stringify(first.inner.look) !== JSON.stringify(last.inner.look) && first.inner.on.animate([first.inner.look, last.inner.look], timing);
411
+ let end = () => {
412
+ }, finished = new Promise((resolve) => {
413
+ end = resolve;
414
+ });
415
+ try {
416
+ kind.play?.(layer, start, stop, timing, about.back);
417
+ for (let play of stage.onPlay)
418
+ play(layer, start, stop, timing, finished);
419
+ plays && morph.during?.({
420
+ ...about,
421
+ layer,
422
+ content: layer.firstElementChild,
423
+ box: layer.querySelector("[data-turn-box]"),
424
+ words: layer.querySelector("[data-turn-words]"),
425
+ animate(element, keyframes, options) {
426
+ let animation = element.animate(keyframes, {
427
+ ...timing,
428
+ ...options
429
+ });
430
+ return stage.siteAnimations.push(animation), animation;
431
+ },
432
+ start,
433
+ stop,
434
+ timing,
435
+ behind,
436
+ finished,
437
+ atEnd: (read) => atTheirEnd(layer.getAnimations({ subtree: !0 }), read)
438
+ });
439
+ } catch (error) {
440
+ reportError(error);
441
+ }
442
+ behind && behind.getAnimations().length === 0 && behind.animate({ opacity: [0, 1] }, over(timing, stage.appear));
443
+ let playing = () => [
444
+ ...layer.getAnimations({ subtree: !0 }),
445
+ ...behind?.getAnimations() ?? []
446
+ ].filter(isEnding);
447
+ await Promise.all(
448
+ [...playing(), ...stage.siteAnimations.filter(isEnding)].map(
449
+ (animation) => animation.finished
450
+ )
451
+ ).catch(() => {
452
+ });
453
+ for (let animation of playing()) {
454
+ try {
455
+ animation.commitStyles();
456
+ } catch {
457
+ }
458
+ animation.cancel();
459
+ }
460
+ end();
461
+ },
462
+ hand(flight) {
463
+ isHandingOver = !0, writeSession(MORPH_KEY, flight);
464
+ },
465
+ async land(placed2, target, told, scroll = { x: scrollX, y: scrollY }, leg) {
466
+ let about = { ...told, scroll };
467
+ placed2.taken = !0;
468
+ let { flight, layer } = placed2;
469
+ try {
470
+ document.readyState === "loading" && await new Promise((parsed) => whenParsed(parsed));
471
+ let element = target();
472
+ if (!element) return;
473
+ stage.live = { layer, hidden: hide(element) };
474
+ let there = shifted(
475
+ atRest(element),
476
+ scrollX - scroll.x,
477
+ scrollY - scroll.y
478
+ );
479
+ if (flight.t) {
480
+ let ends = about.back ? { from: there, to: flight.f ?? null } : { from: flight.f ?? there, to: there };
481
+ flight.f && morph.ends?.(ends, about);
482
+ let stop = (about.back ? ends.from : ends.to) ?? there;
483
+ looksSame(flight.e, stop) || await stage.travel(
484
+ flight.e,
485
+ stop,
486
+ about,
487
+ { ...stage.timing(leg), delay: 0, duration: CORRECTION_MS },
488
+ !1
489
+ );
490
+ } else {
491
+ let ends = about.back ? { from: there, to: flight.e } : { from: flight.e, to: there };
492
+ if (!isDeclined(morph.ends?.(ends, about))) {
493
+ let start = (about.back ? ends.to : ends.from) ?? flight.e, stop = (about.back ? ends.from : ends.to) ?? there;
494
+ kind.fit?.(layer, stop), await stage.travel(start, stop, about, stage.timing(leg));
495
+ }
496
+ }
497
+ await whenDrawn(element);
498
+ } catch (error) {
499
+ reportError(error);
500
+ } finally {
501
+ stage.drop(), clearLayer(placed2);
502
+ }
503
+ },
504
+ drop() {
505
+ for (let animation of stage.siteAnimations.splice(0))
506
+ animation.cancel();
507
+ stage.live?.layer.remove(), stage.live?.hidden.style.removeProperty("visibility"), stage.live = null;
508
+ }
509
+ }, remove = defineMove(name, {
510
+ match: (visit) => morph.match ? morph.match(visit) : !!morph.from?.(visit.link),
511
+ async leave(turn) {
512
+ let element = morph.from?.(turn.link);
513
+ if (!element || !turn.canHandOver || stage.live) return turn.decline();
514
+ let about = stage.about(turn.link, !1), ends = {
515
+ from: stage.read(element),
516
+ to: null
517
+ };
518
+ for (let finder of finders)
519
+ try {
520
+ ends.to ??= await finder(turn.to, morph.to, atRest);
521
+ } catch (error) {
522
+ reportError(error);
523
+ }
524
+ if (isDeclined(morph.ends?.(ends, about))) return turn.decline();
525
+ let { from, to } = ends;
526
+ if (from.box.width === 0 || from.box.height === 0) return turn.decline();
527
+ let layer = await stage.put(element, from, to), leaving = turn.leaving();
528
+ to && await stage.travel(from, to, about, stage.timing()), await leaving, stage.hand({
529
+ n: morph.arriveAs ?? name,
530
+ m: name,
531
+ h: markup(layer),
532
+ e: to ?? from,
533
+ ...to ? { f: from } : {},
534
+ t: !!to,
535
+ s: morph.to
536
+ });
537
+ let handover = {
538
+ arriveAs: morph.arriveAs ?? name,
539
+ data: morph.data?.(turn) ?? null
540
+ };
541
+ for (let add of stage.onLeave)
542
+ handover = {
543
+ ...handover,
544
+ ...add(turn, element, from, handover.data)
545
+ };
546
+ return handover;
547
+ },
548
+ stay(turn, stay) {
549
+ isHandingOver = !1, stage.drop();
550
+ for (let tell of stage.onStay) tell(stay);
551
+ morph.stay?.(turn, stay);
552
+ }
553
+ });
554
+ for (let begin of [
555
+ ...finders.map((finder) => finder.start),
556
+ ...morph.use ?? []
557
+ ])
558
+ begin?.(stage);
559
+ let placed = handedOver();
560
+ return placed && !placed.taken && placed.flight.m === name && stage.land(placed, () => onPage(morph.to), stage.about(null, !1)), () => {
561
+ remove(), defined.delete(name);
562
+ for (let stop of stage.onRemove) stop();
563
+ };
564
+ }
565
+
566
+ // src/morph/way.ts
567
+ var originKey = (name) => `${MORPH_KEY}:from:${name}`;
568
+ function originOf(name) {
569
+ let from = readSession(originKey(name)), entry = navigationEntry();
570
+ return from && window.top === window && new URL(from.u).pathname === location.pathname && entry?.type === "navigate" && window.pageturn?.arrival?.move !== from.a ? (forgetWayOut(name), null) : from;
571
+ }
572
+ function forgetWayOut(name) {
573
+ !inBrowser || window.top !== window || removeSession(originKey(name));
574
+ }
575
+ var IN_VIEW = 0.25;
576
+ function shareOnScreen({ left, top, width, height }) {
577
+ let across = Math.min(left + width, innerWidth) - Math.max(left, 0), down = Math.min(top + height, innerHeight) - Math.max(top, 0);
578
+ return Math.max(across, 0) * Math.max(down, 0) / (width * height || 1);
579
+ }
580
+ function pathOf(element) {
581
+ let path = "";
582
+ for (let node = element; node !== document.body && node.parentElement; node = node.parentElement) {
583
+ let rank = [...node.parentElement.children].indexOf(node) + 1;
584
+ path = `>${node.localName}:nth-child(${rank})${path}`;
585
+ }
586
+ return `body${path}`;
587
+ }
588
+ function elementNamedByAddress() {
589
+ try {
590
+ return document.getElementById(decodeURIComponent(location.hash.slice(1)));
591
+ } catch {
592
+ return null;
593
+ }
594
+ }
595
+ function scrollOfAddress() {
596
+ return elementNamedByAddress()?.scrollIntoView({ behavior: "instant" }), { x: scrollX, y: scrollY };
597
+ }
598
+ function isInAnOpenPopover(element) {
599
+ try {
600
+ return element.parentElement?.closest(":popover-open") != null;
601
+ } catch {
602
+ return !1;
603
+ }
604
+ }
605
+ var links = () => [
606
+ ...document.querySelectorAll("a[href]")
607
+ ], waysBack = /* @__PURE__ */ new WeakMap();
608
+ function readKept(keep) {
609
+ let read = [];
610
+ for (let [selector, names] of Object.entries(keep)) {
611
+ let element = null;
612
+ try {
613
+ element = document.querySelector(selector);
614
+ } catch {
615
+ }
616
+ if (element)
617
+ for (let attribute of [names].flat())
618
+ read.push([selector, attribute, element.getAttribute(attribute)]);
619
+ }
620
+ return read;
621
+ }
622
+ function rankOf(link) {
623
+ let { href } = link, rank = 0;
624
+ for (let other of links()) {
625
+ if (other === link) return rank;
626
+ other.href === href && (rank += 1);
627
+ }
628
+ return -1;
629
+ }
630
+ var backFields = (from, name) => ({
631
+ s: from?.p ?? "",
632
+ ...from ? {
633
+ ...putBackFields(from, name),
634
+ l: [from.u, from.k, !!from.i]
635
+ } : {}
636
+ }), putBackFields = (from, name) => ({
637
+ o: name,
638
+ v: from.v,
639
+ ...from.w?.length ? { w: from.w } : {}
640
+ });
641
+ function wayBackOf(stage) {
642
+ let known = waysBack.get(stage);
643
+ if (known) return known;
644
+ let key = originKey(stage.name), way = {
645
+ name: `${stage.name}-back`,
646
+ origin: () => originOf(stage.name),
647
+ keep: {}
648
+ };
649
+ waysBack.set(stage, way), stage.onLeave.push((turn, element, from, data) => {
650
+ writeSession(key, {
651
+ d: data,
652
+ a: stage.morph.arriveAs ?? stage.name,
653
+ f: location.pathname,
654
+ u: turn.link.href,
655
+ k: rankOf(turn.link),
656
+ p: pathOf(element),
657
+ v: isInAnOpenPopover(element),
658
+ w: readKept(way.keep),
659
+ // A click from the keyboard leaves the link with its ring; a mouse's, without.
660
+ i: turn.link.matches(":focus-visible"),
661
+ x: scrollX,
662
+ y: scrollY,
663
+ e: from
664
+ });
665
+ });
666
+ function startedHere() {
667
+ let from = way.origin();
668
+ return from?.f === location.pathname ? from : null;
669
+ }
670
+ function landBack(placed, isShown) {
671
+ let reached = arrival(), options = reached?.via === "history" ? way.byHistory : way.byLink;
672
+ if (!reached || !options) return;
673
+ let from = startedHere(), leading = links().filter((link2) => link2.pathname === reached.from), clicked = from ? linkAt(from.u, from.k) : void 0, link = clicked && leading.includes(clicked) ? clicked : leading.find((link2) => stage.morph.from?.(link2));
674
+ if (!link) return;
675
+ link !== clicked && (document.adoptedStyleSheets = document.adoptedStyleSheets.filter(
676
+ (sheet) => sheet !== placed.sheet
677
+ ));
678
+ let isByLink = reached.via === "link", { x = 0, y } = placed.flight;
679
+ y !== void 0 && scrollTo(x, y), stage.land(
680
+ placed,
681
+ () => stage.morph.from?.(link) ?? null,
682
+ stage.about(link, !0),
683
+ y !== void 0 ? { x, y } : from && !isByLink && !isShown ? (
684
+ // On a Back the browser brings the scroll back itself, sometimes after this.
685
+ { x: from.x, y: from.y }
686
+ ) : isByLink ? scrollOfAddress() : { x: scrollX, y: scrollY },
687
+ options
688
+ );
689
+ }
690
+ return queueMicrotask(() => {
691
+ let placed = handedOver();
692
+ placed && !placed.taken && placed.flight.m === way.name && landBack(placed, !1);
693
+ }), stage.onStay.push((stay) => {
694
+ if (stay.reason !== "returned" || !arrival()) return;
695
+ let placed = placeHandedLayer();
696
+ placed?.flight.m === way.name && (landBack(placed, !0), placed.taken || clearLayer(placed));
697
+ }), way;
698
+ }
699
+ function wayOut(name) {
700
+ let from = originOf(name);
701
+ return from && {
702
+ page: from.f,
703
+ scroll: { x: from.x, y: from.y },
704
+ data: from.d
705
+ };
706
+ }
707
+
708
+ // src/morph/back.ts
709
+ function back(options = {}) {
710
+ return (stage) => {
711
+ let way = wayBackOf(stage);
712
+ way.byHistory = options, Object.assign(way.keep, options.keep), stage.onLeave.push((_turn, _element, _from, data) => ({
713
+ returnAs: way.name,
714
+ remember: data
715
+ }));
716
+ let said = () => {
717
+ if (isLeaving()) return;
718
+ let from = way.origin(), element = onPage(stage.morph.to), end = element && stage.read(element), store = window.pageturn?.j, report = { from: location.pathname, at: Date.now(), data: null };
719
+ if (!element || !end || shareOnScreen(end.box) < (options.inView ?? IN_VIEW)) {
720
+ from && new URL(from.u).pathname === location.pathname && store?.p({ ...report, none: !0 });
721
+ return;
722
+ }
723
+ store && !store.o() && store.p(report);
724
+ let layer = stage.layer(element, end, null);
725
+ writeSession(MORPH_KEY, {
726
+ n: way.name,
727
+ m: way.name,
728
+ h: markup(layer),
729
+ e: end,
730
+ t: !1,
731
+ ...backFields(from, stage.name)
732
+ });
733
+ };
734
+ addEventListener("pagehide", said), stage.onRemove.push(() => removeEventListener("pagehide", said));
735
+ };
736
+ }
737
+
738
+ // src/morph/finders.ts
739
+ var SEEN_KEY = `${MORPH_KEY}:seen:`;
740
+ function here() {
741
+ return (_to, selector, measure) => {
742
+ let element = onPage(selector);
743
+ return element && shifted(measure(element), scrollX, scrollY);
744
+ };
745
+ }
746
+ var seenKey = (selector) => `${SEEN_KEY}${selector}:${innerWidth}x${innerHeight}`, SEEN_PAGES = 20;
747
+ function seen() {
748
+ let finder = (to, selector) => {
749
+ let pages = readSession(seenKey(selector)) ?? {}, others = Object.keys(pages).filter(
750
+ (path) => path !== location.pathname
751
+ );
752
+ return pages[to.pathname] ?? pages[others[others.length - 1] ?? ""] ?? null;
753
+ };
754
+ return finder.start = ({ morph, atRest }) => {
755
+ let say = () => {
756
+ let element = onPage(morph.to);
757
+ if (!element) return;
758
+ let end = atRest(element);
759
+ if (end.box.width > 0) {
760
+ let key = seenKey(morph.to), { [location.pathname]: _, ...pages } = readSession(key) ?? {}, kept = Object.entries(pages).slice(1 - SEEN_PAGES);
761
+ writeSession(key, {
762
+ ...Object.fromEntries(kept),
763
+ [location.pathname]: shifted(end, scrollX, scrollY)
764
+ });
765
+ }
766
+ };
767
+ document.readyState === "complete" ? say() : addEventListener("load", say, { once: !0 });
768
+ }, finder;
769
+ }
770
+
771
+ export {
772
+ within,
773
+ isEnding,
774
+ ownCorners,
775
+ cornersCss,
776
+ endOf,
777
+ atTheirEnd,
778
+ LAYER_STYLE,
779
+ CONTENT_STYLE,
780
+ cloned,
781
+ sizeOf,
782
+ centredOn,
783
+ markup,
784
+ MORPH_KEY,
785
+ onPage,
786
+ over,
787
+ isDefined,
788
+ defineMorph,
789
+ forgetWayOut,
790
+ IN_VIEW,
791
+ shareOnScreen,
792
+ backFields,
793
+ putBackFields,
794
+ wayBackOf,
795
+ wayOut,
796
+ back,
797
+ here,
798
+ seen
799
+ };