aberdeen 1.17.0 → 2.0.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 (139) hide show
  1. package/dist/src/aberdeen.d.ts +928 -944
  2. package/dist/src/aberdeen.js +1693 -1704
  3. package/dist/src/aberdeen.js.map +3 -3
  4. package/dist/src/index.d.ts +12 -0
  5. package/dist/src/index.js +11 -0
  6. package/dist/src/index.js.map +10 -0
  7. package/dist/src/prediction.d.ts +7 -4
  8. package/dist/src/prediction.js +18 -23
  9. package/dist/src/prediction.js.map +3 -3
  10. package/dist/src/route.d.ts +112 -99
  11. package/dist/src/route.js +254 -254
  12. package/dist/src/route.js.map +3 -3
  13. package/dist/src/server-dom.d.ts +158 -0
  14. package/dist/src/server-dom.js +458 -0
  15. package/dist/src/server-dom.js.map +10 -0
  16. package/dist/src/server.d.ts +55 -0
  17. package/dist/src/server.js +25 -0
  18. package/dist/src/server.js.map +10 -0
  19. package/dist/src/transitions.js +7 -4
  20. package/dist/src/transitions.js.map +3 -3
  21. package/dist/tests/environment.d.ts +24 -0
  22. package/dist/tests/{fakedom.js → environment.js} +239 -288
  23. package/dist/tests/environment.js.map +11 -0
  24. package/dist/tests/helpers.d.ts +15 -3
  25. package/dist/tests/helpers.js +277 -306
  26. package/dist/tests/helpers.js.map +6 -5
  27. package/dist-docs/Tutorial/index.html +30 -30
  28. package/dist-docs/aberdeen/Aberdeen/index.html +350 -0
  29. package/dist-docs/aberdeen/InitOptions/index.html +4 -0
  30. package/dist-docs/aberdeen/KeyToString/index.html +2 -0
  31. package/dist-docs/aberdeen/PromiseProxy/index.html +6 -6
  32. package/dist-docs/aberdeen/SortKeyType/index.html +2 -0
  33. package/dist-docs/aberdeen/TargetType/index.html +2 -0
  34. package/dist-docs/aberdeen/ValueRef/index.html +2 -0
  35. package/dist-docs/aberdeen/WindowLike/index.html +5 -0
  36. package/dist-docs/aberdeen/{clone → createA}/index.html +10 -5
  37. package/dist-docs/aberdeen/index.html +13 -54
  38. package/dist-docs/assets/aberdeen/aberdeen.d.ts +928 -944
  39. package/dist-docs/assets/aberdeen/aberdeen.js +1693 -1704
  40. package/dist-docs/assets/aberdeen/aberdeen.js.map +3 -3
  41. package/dist-docs/assets/aberdeen/index.d.ts +12 -0
  42. package/dist-docs/assets/aberdeen/index.js +11 -0
  43. package/dist-docs/assets/aberdeen/index.js.map +10 -0
  44. package/dist-docs/assets/aberdeen/prediction.d.ts +7 -4
  45. package/dist-docs/assets/aberdeen/prediction.js +18 -23
  46. package/dist-docs/assets/aberdeen/prediction.js.map +3 -3
  47. package/dist-docs/assets/aberdeen/route.d.ts +112 -99
  48. package/dist-docs/assets/aberdeen/route.js +254 -254
  49. package/dist-docs/assets/aberdeen/route.js.map +3 -3
  50. package/dist-docs/assets/aberdeen/server-dom.d.ts +158 -0
  51. package/dist-docs/assets/aberdeen/server-dom.js +458 -0
  52. package/dist-docs/assets/aberdeen/server-dom.js.map +10 -0
  53. package/dist-docs/assets/aberdeen/server.d.ts +55 -0
  54. package/dist-docs/assets/aberdeen/server.js +25 -0
  55. package/dist-docs/assets/aberdeen/server.js.map +10 -0
  56. package/dist-docs/assets/aberdeen/transitions.js +7 -4
  57. package/dist-docs/assets/aberdeen/transitions.js.map +3 -3
  58. package/dist-docs/assets/navigation.js +1 -1
  59. package/dist-docs/assets/search.js +1 -1
  60. package/dist-docs/dispatcher/Dispatcher/index.html +5 -6
  61. package/dist-docs/dispatcher/MATCH_FAILED/index.html +2 -2
  62. package/dist-docs/dispatcher/MATCH_REST/index.html +2 -2
  63. package/dist-docs/dispatcher/index.html +2 -2
  64. package/dist-docs/hierarchy.html +1 -1
  65. package/dist-docs/index-1/index.html +2 -0
  66. package/dist-docs/index.html +2 -2
  67. package/dist-docs/media/CHANGELOG.md +14 -2
  68. package/dist-docs/modules.html +1 -1
  69. package/dist-docs/prediction/applyCanon/index.html +4 -3
  70. package/dist-docs/prediction/applyPrediction/index.html +4 -3
  71. package/dist-docs/prediction/index.html +2 -2
  72. package/dist-docs/route/Route/index.html +9 -9
  73. package/dist-docs/route/RouteApi/index.html +60 -0
  74. package/dist-docs/route/index.html +6 -15
  75. package/dist-docs/route/init/index.html +5 -0
  76. package/dist-docs/server/RenderToStringOptions/index.html +6 -0
  77. package/dist-docs/server/RenderToStringResult/index.html +6 -0
  78. package/dist-docs/server/index.html +4 -0
  79. package/dist-docs/server/renderToString/index.html +6 -0
  80. package/dist-docs/sitemap.xml +51 -155
  81. package/dist-docs/transitions/grow/index.html +2 -2
  82. package/dist-docs/transitions/index.html +2 -2
  83. package/dist-docs/transitions/shrink/index.html +2 -2
  84. package/package.json +17 -7
  85. package/skill/SKILL.md +28 -28
  86. package/skill/aberdeen.md +471 -3448
  87. package/skill/dispatcher.md +6 -16
  88. package/skill/prediction.md +17 -5
  89. package/skill/route.md +95 -77
  90. package/skill/transitions.md +3 -3
  91. package/src/aberdeen.ts +1226 -740
  92. package/src/index.ts +13 -0
  93. package/src/prediction.ts +25 -24
  94. package/src/route.ts +192 -142
  95. package/src/server-dom.ts +508 -0
  96. package/src/server.ts +74 -0
  97. package/src/transitions.ts +9 -3
  98. package/dist/tests/fakedom.d.ts +0 -9
  99. package/dist/tests/fakedom.js.map +0 -10
  100. package/dist-docs/aberdeen/A/index.html +0 -115
  101. package/dist-docs/aberdeen/CUSTOM_DUMP/index.html +0 -7
  102. package/dist-docs/aberdeen/NO_COPY/index.html +0 -2
  103. package/dist-docs/aberdeen/OPAQUE/index.html +0 -12
  104. package/dist-docs/aberdeen/clean/index.html +0 -11
  105. package/dist-docs/aberdeen/copy/index.html +0 -22
  106. package/dist-docs/aberdeen/count/index.html +0 -7
  107. package/dist-docs/aberdeen/cssVars/index.html +0 -11
  108. package/dist-docs/aberdeen/darkMode/index.html +0 -9
  109. package/dist-docs/aberdeen/default/index.html +0 -341
  110. package/dist-docs/aberdeen/derive/index.html +0 -18
  111. package/dist-docs/aberdeen/disableCreateDestroy/index.html +0 -6
  112. package/dist-docs/aberdeen/dump/index.html +0 -11
  113. package/dist-docs/aberdeen/insertCss/index.html +0 -30
  114. package/dist-docs/aberdeen/insertGlobalCss/index.html +0 -19
  115. package/dist-docs/aberdeen/invertString/index.html +0 -13
  116. package/dist-docs/aberdeen/isEmpty/index.html +0 -12
  117. package/dist-docs/aberdeen/map/index.html +0 -26
  118. package/dist-docs/aberdeen/merge/index.html +0 -11
  119. package/dist-docs/aberdeen/mount/index.html +0 -17
  120. package/dist-docs/aberdeen/multiMap/index.html +0 -28
  121. package/dist-docs/aberdeen/onEach/index.html +0 -19
  122. package/dist-docs/aberdeen/partition/index.html +0 -36
  123. package/dist-docs/aberdeen/peek/index.html +0 -40
  124. package/dist-docs/aberdeen/proxy/index.html +0 -35
  125. package/dist-docs/aberdeen/ref/index.html +0 -13
  126. package/dist-docs/aberdeen/runQueue/index.html +0 -13
  127. package/dist-docs/aberdeen/setErrorHandler/index.html +0 -16
  128. package/dist-docs/aberdeen/setSpacingCssVars/index.html +0 -8
  129. package/dist-docs/aberdeen/unmountAll/index.html +0 -5
  130. package/dist-docs/aberdeen/unproxy/index.html +0 -11
  131. package/dist-docs/route/back/index.html +0 -4
  132. package/dist-docs/route/current/index.html +0 -2
  133. package/dist-docs/route/go/index.html +0 -14
  134. package/dist-docs/route/interceptLinks/index.html +0 -8
  135. package/dist-docs/route/matchCurrent/index.html +0 -10
  136. package/dist-docs/route/persistScroll/index.html +0 -6
  137. package/dist-docs/route/push/index.html +0 -6
  138. package/dist-docs/route/setLog/index.html +0 -3
  139. package/dist-docs/route/up/index.html +0 -5
@@ -1,287 +1,287 @@
1
1
  // src/route.ts
2
- import A, { leakScope } from "./aberdeen.js";
3
- var log = () => {};
4
- function setLog(value) {
5
- if (value === true) {
6
- log = console.log.bind(console, "aberdeen router");
7
- } else if (value === false) {
8
- log = () => {};
9
- } else {
10
- log = value;
2
+ function init(A) {
3
+ let log = () => {};
4
+ function setLog(value) {
5
+ if (value === true) {
6
+ log = console.log.bind(console, "aberdeen router");
7
+ } else if (value === false) {
8
+ log = () => {};
9
+ } else {
10
+ log = value;
11
+ }
11
12
  }
12
- }
13
- var windowE = typeof ABERDEEN_FAKE_WINDOW !== "undefined" ? ABERDEEN_FAKE_WINDOW : window;
14
- var locationE = windowE.location;
15
- var historyE = windowE.history;
16
- function getRouteFromBrowser() {
17
- return toCanonRoute({
18
- path: locationE.pathname,
19
- hash: locationE.hash,
20
- search: Object.fromEntries(new URLSearchParams(locationE.search)),
21
- state: historyE.state?.state || {}
22
- }, "load", (historyE.state?.stack?.length || 0) + 1);
23
- }
24
- function equal(a, b, partial) {
25
- if (a === b)
26
- return true;
27
- if (typeof a !== "object" || !a || typeof b !== "object" || !b)
28
- return false;
29
- if (a.constructor !== b.constructor)
30
- return false;
31
- if (b instanceof Array) {
32
- if (a.length !== b.length)
13
+ const windowE = () => A.window;
14
+ const locationE = () => A.window.location;
15
+ const historyE = () => A.window.history;
16
+ function getRouteFromBrowser() {
17
+ return toCanonRoute({
18
+ path: locationE().pathname,
19
+ hash: locationE().hash,
20
+ search: Object.fromEntries(new URLSearchParams(locationE().search)),
21
+ state: historyE().state?.state || {}
22
+ }, "load", (historyE().state?.stack?.length || 0) + 1);
23
+ }
24
+ function equal(a, b, partial) {
25
+ if (a === b)
26
+ return true;
27
+ if (typeof a !== "object" || !a || typeof b !== "object" || !b)
33
28
  return false;
34
- for (let i = 0;i < b.length; i++) {
35
- if (!equal(a[i], b[i], partial))
36
- return false;
37
- }
38
- } else {
39
- for (const k of Object.keys(b)) {
40
- if (!equal(a[k], b[k], partial))
29
+ if (a.constructor !== b.constructor)
30
+ return false;
31
+ if (b instanceof Array) {
32
+ if (a.length !== b.length)
41
33
  return false;
42
- }
43
- if (!partial) {
44
- for (const k of Object.keys(a)) {
45
- if (!b.hasOwnProperty(k))
34
+ for (let i = 0;i < b.length; i++) {
35
+ if (!equal(a[i], b[i], partial))
46
36
  return false;
47
37
  }
38
+ } else {
39
+ for (const k of Object.keys(b)) {
40
+ if (!equal(a[k], b[k], partial))
41
+ return false;
42
+ }
43
+ if (!partial) {
44
+ for (const k of Object.keys(a)) {
45
+ if (!b.hasOwnProperty(k))
46
+ return false;
47
+ }
48
+ }
48
49
  }
50
+ return true;
49
51
  }
50
- return true;
51
- }
52
- function getUrl(target) {
53
- const search = new URLSearchParams(target.search).toString();
54
- return (search ? `${target.path}?${search}` : target.path) + target.hash;
55
- }
56
- function toCanonRoute(target, nav, depth) {
57
- let path = target.path || (target.p || []).join("/") || "/";
58
- path = ("" + path).replace(/\/+$/, "");
59
- if (!path.startsWith("/"))
60
- path = `/${path}`;
61
- return {
62
- path,
63
- hash: target.hash && target.hash !== "#" ? target.hash.startsWith("#") ? target.hash : "#" + target.hash : "",
64
- p: path.length > 1 ? path.slice(1).replace(/\/+$/, "").split("/") : [],
65
- nav,
66
- search: typeof target.search === "object" && target.search ? A.clone(target.search) : {},
67
- state: typeof target.state === "object" && target.state ? A.clone(target.state) : {},
68
- depth
69
- };
70
- }
71
- function targetToPartial(target, undefinedOnExternal = false) {
72
- if (typeof target === "string") {
73
- const url = new URL(target, locationE.href);
74
- if (url.host !== locationE.host) {
75
- if (undefinedOnExternal)
76
- return;
77
- throw new Error(`Unexpected external URL: ${url.host} != ${locationE.host}`);
78
- }
79
- target = {
80
- path: url.pathname,
81
- search: Object.fromEntries(url.searchParams),
82
- hash: url.hash
83
- };
84
- } else if (target instanceof Array) {
85
- target = { p: target };
86
- }
87
- if (target.p) {
88
- target.p = target.p.map(String);
89
- }
90
- if (target.search) {
91
- for (const key of Object.keys(target.search)) {
92
- target.search[key] = String(target.search[key]);
93
- }
52
+ function getUrl(target) {
53
+ const search = new URLSearchParams(target.search).toString();
54
+ return (search ? `${target.path}?${search}` : target.path) + target.hash;
94
55
  }
95
- return target;
96
- }
97
- function go(target, nav = "go") {
98
- pendingGoOffset = 0;
99
- const stack = historyE.state?.stack || [];
100
- prevStack = stack.concat(JSON.stringify(A.unproxy(current)));
101
- const newRoute = toCanonRoute(targetToPartial(target), nav, prevStack.length + 1);
102
- A.copy(current, newRoute);
103
- log(nav, newRoute);
104
- historyE.pushState({ state: newRoute.state, stack: prevStack }, "", getUrl(newRoute));
105
- A.runQueue();
106
- }
107
- function matchCurrent(target) {
108
- const partial = targetToPartial(target, true);
109
- if (!partial)
110
- return false;
111
- if (partial.path != null || partial.p != null) {
112
- let path = partial.path || (partial.p || []).join("/") || "/";
56
+ function toCanonRoute(target, nav, depth) {
57
+ let path = target.path || (target.p || []).join("/") || "/";
113
58
  path = ("" + path).replace(/\/+$/, "");
114
59
  if (!path.startsWith("/"))
115
60
  path = `/${path}`;
116
- if (!currentRouteParts[path])
117
- return false;
61
+ return {
62
+ path,
63
+ hash: target.hash && target.hash !== "#" ? target.hash.startsWith("#") ? target.hash : "#" + target.hash : "",
64
+ p: path.length > 1 ? path.slice(1).replace(/\/+$/, "").split("/") : [],
65
+ nav,
66
+ search: typeof target.search === "object" && target.search ? A.clone(target.search) : {},
67
+ state: typeof target.state === "object" && target.state ? A.clone(target.state) : {},
68
+ depth
69
+ };
118
70
  }
119
- if (partial.search) {
120
- for (const [k, v] of Object.entries(partial.search)) {
121
- if (!currentRouteParts[`${k}=${v}`])
122
- return false;
71
+ function targetToPartial(target, undefinedOnExternal = false) {
72
+ if (typeof target === "string") {
73
+ const url = new URL(target, locationE().href);
74
+ if (url.host !== locationE().host) {
75
+ if (undefinedOnExternal)
76
+ return;
77
+ throw new Error(`Unexpected external URL: ${url.host} != ${locationE().host}`);
78
+ }
79
+ target = {
80
+ path: url.pathname,
81
+ search: Object.fromEntries(url.searchParams),
82
+ hash: url.hash
83
+ };
84
+ } else if (target instanceof Array) {
85
+ target = { p: target };
123
86
  }
124
- }
125
- return true;
126
- }
127
- function push(target, nav) {
128
- const c = A.clone(A.unproxy(current));
129
- A.merge(c, targetToPartial(target));
130
- go(c, nav || c.nav);
131
- }
132
- function back(target = {}) {
133
- const partial = targetToPartial(target);
134
- const stack = historyE.state?.stack || [];
135
- const effectiveLen = stack.length + pendingGoOffset;
136
- for (let i = effectiveLen - 1;i >= 0; i--) {
137
- const histRoute = JSON.parse(stack[i]);
138
- if (equal(histRoute, partial, true)) {
139
- const pages = i - effectiveLen;
140
- log(`back`, pages, histRoute);
141
- scheduleHistoryGo(pages);
142
- return;
87
+ if (target.p) {
88
+ target.p = target.p.map(String);
143
89
  }
144
- }
145
- const newRoute = toCanonRoute(partial, "back", effectiveLen + 1);
146
- log(`back not found, replacing`, partial);
147
- A.copy(current, newRoute);
148
- }
149
- function up(stripCount = 1) {
150
- const currentP = A.unproxy(current).p;
151
- const stack = historyE.state?.stack || [];
152
- const effectiveLen = stack.length + pendingGoOffset;
153
- for (let i = effectiveLen - 1;i >= 0; i--) {
154
- const histRoute = JSON.parse(stack[i]);
155
- if (histRoute.p.length < currentP.length && equal(histRoute.p, currentP.slice(0, histRoute.p.length), false)) {
156
- log(`up to ${i + 1} / ${effectiveLen}`, histRoute);
157
- scheduleHistoryGo(i - effectiveLen);
158
- return;
90
+ if (target.search) {
91
+ for (const key of Object.keys(target.search)) {
92
+ target.search[key] = String(target.search[key]);
93
+ }
159
94
  }
95
+ return target;
160
96
  }
161
- const newRoute = toCanonRoute({ p: currentP.slice(0, currentP.length - stripCount) }, "back", effectiveLen + 1);
162
- log(`up not found, replacing`, newRoute);
163
- A.copy(current, newRoute);
164
- }
165
- var prevStack;
166
- var pendingGoOffset = 0;
167
- function scheduleHistoryGo(delta) {
168
- pendingGoOffset += delta;
169
- setTimeout(() => {
170
- if (pendingGoOffset) {
171
- const offset = pendingGoOffset;
172
- pendingGoOffset = 0;
173
- historyE.go(offset);
97
+ function go(target, nav = "go") {
98
+ pendingGoOffset = 0;
99
+ const stack = historyE().state?.stack || [];
100
+ prevStack = stack.concat(JSON.stringify(A.unproxy(current)));
101
+ const newRoute = toCanonRoute(targetToPartial(target), nav, prevStack.length + 1);
102
+ A.copy(current, newRoute);
103
+ log(nav, newRoute);
104
+ historyE().pushState({ state: newRoute.state, stack: prevStack }, "", getUrl(newRoute));
105
+ A.runQueue();
106
+ }
107
+ function matchCurrent(target) {
108
+ const partial = targetToPartial(target, true);
109
+ if (!partial)
110
+ return false;
111
+ if (partial.path != null || partial.p != null) {
112
+ let path = partial.path || (partial.p || []).join("/") || "/";
113
+ path = ("" + path).replace(/\/+$/, "");
114
+ if (!path.startsWith("/"))
115
+ path = `/${path}`;
116
+ if (!currentRouteParts[path])
117
+ return false;
174
118
  }
175
- }, 0);
176
- }
177
- var current = A.proxy({});
178
- var currentRouteParts = A.proxy({});
179
- function reset() {
180
- prevStack = historyE.state?.stack || [];
181
- const initRoute = getRouteFromBrowser();
182
- log("initial", initRoute);
183
- A.copy(A.unproxy(current), initRoute);
184
- }
185
- reset();
186
- windowE.addEventListener("popstate", function(event) {
187
- pendingGoOffset = 0;
188
- const newRoute = getRouteFromBrowser();
189
- const stack = historyE.state?.stack || [];
190
- if (stack.length !== prevStack.length) {
191
- const maxIndex = Math.min(prevStack.length, stack.length) - 1;
192
- if (maxIndex < 0 || stack[maxIndex] === prevStack[maxIndex]) {
193
- newRoute.nav = stack.length < prevStack.length ? "back" : "forward";
119
+ if (partial.search) {
120
+ for (const [k, v] of Object.entries(partial.search)) {
121
+ if (!currentRouteParts[`${k}=${v}`])
122
+ return false;
123
+ }
194
124
  }
125
+ return true;
195
126
  }
196
- prevStack = stack;
197
- log("popstate", newRoute);
198
- A.copy(current, newRoute);
199
- A.runQueue();
200
- });
201
- leakScope(() => {
202
- A(() => {
203
- current.path = "/" + Array.from(current.p).join("/");
204
- });
205
- A(() => {
206
- const stack = historyE.state?.stack || [];
207
- const newRoute = toCanonRoute(current, A.unproxy(current).nav, stack.length + 1);
208
- A.copy(current, newRoute);
209
- const state = { state: newRoute.state, stack };
210
- const url = getUrl(newRoute);
211
- if (url !== locationE.pathname + locationE.search + locationE.hash || !equal(historyE.state, state, false)) {
212
- log("replaceState", newRoute, state, url);
213
- historyE.replaceState(state, "", url);
127
+ function push(target, nav) {
128
+ const c = A.clone(A.unproxy(current));
129
+ A.merge(c, targetToPartial(target));
130
+ go(c, nav || c.nav);
131
+ }
132
+ function back(target = {}) {
133
+ const partial = targetToPartial(target);
134
+ const stack = historyE().state?.stack || [];
135
+ const effectiveLen = stack.length + pendingGoOffset;
136
+ for (let i = effectiveLen - 1;i >= 0; i--) {
137
+ const histRoute = JSON.parse(stack[i]);
138
+ if (equal(histRoute, partial, true)) {
139
+ const pages = i - effectiveLen;
140
+ log(`back`, pages, histRoute);
141
+ scheduleHistoryGo(pages);
142
+ return;
143
+ }
214
144
  }
215
- });
216
- A(() => {
217
- const n = {};
218
- n[current.path] = true;
219
- for (const [k, v] of Object.entries(current.search)) {
220
- n[`${k}=${v}`] = true;
145
+ const newRoute = toCanonRoute(partial, "back", effectiveLen + 1);
146
+ log(`back not found, replacing`, partial);
147
+ A.copy(current, newRoute);
148
+ }
149
+ function up(stripCount = 1) {
150
+ const currentP = A.unproxy(current).p;
151
+ const stack = historyE().state?.stack || [];
152
+ const effectiveLen = stack.length + pendingGoOffset;
153
+ for (let i = effectiveLen - 1;i >= 0; i--) {
154
+ const histRoute = JSON.parse(stack[i]);
155
+ if (histRoute.p.length < currentP.length && equal(histRoute.p, currentP.slice(0, histRoute.p.length), false)) {
156
+ log(`up to ${i + 1} / ${effectiveLen}`, histRoute);
157
+ scheduleHistoryGo(i - effectiveLen);
158
+ return;
159
+ }
221
160
  }
222
- A.copy(currentRouteParts, n);
223
- });
224
- });
225
- function persistScroll(name = "main") {
226
- const el = A();
227
- A("scroll=", onScroll);
228
- const restore = A.unproxy(current).state.scroll?.[name];
229
- if (restore) {
230
- log("restoring scroll", name, restore);
231
- Object.assign(el, restore);
161
+ const newRoute = toCanonRoute({ p: currentP.slice(0, currentP.length - stripCount) }, "back", effectiveLen + 1);
162
+ log(`up not found, replacing`, newRoute);
163
+ A.copy(current, newRoute);
232
164
  }
233
- function onScroll() {
234
- (current.state.scroll ||= {})[name] = {
235
- scrollTop: el.scrollTop,
236
- scrollLeft: el.scrollLeft
237
- };
165
+ let prevStack;
166
+ let pendingGoOffset = 0;
167
+ function scheduleHistoryGo(delta) {
168
+ pendingGoOffset += delta;
169
+ (A.window.setTimeout ?? setTimeout)(() => {
170
+ if (pendingGoOffset) {
171
+ const offset = pendingGoOffset;
172
+ pendingGoOffset = 0;
173
+ historyE().go(offset);
174
+ }
175
+ }, 0);
238
176
  }
239
- }
240
- function interceptLinks() {
241
- A({
242
- click: handleEvent,
243
- keydown: handleKeyEvent
244
- });
245
- function handleKeyEvent(e) {
246
- if (e.key === "Enter") {
247
- handleEvent(e);
177
+ const current = A.proxy({});
178
+ const currentRouteParts = A.proxy({});
179
+ function reset() {
180
+ prevStack = historyE().state?.stack || [];
181
+ const initRoute = getRouteFromBrowser();
182
+ log("initial", initRoute);
183
+ A.copy(A.unproxy(current), initRoute);
184
+ }
185
+ let routerInited = false;
186
+ function initRouter() {
187
+ if (routerInited || !A.window)
188
+ return;
189
+ routerInited = true;
190
+ reset();
191
+ windowE().addEventListener("popstate", function(event) {
192
+ pendingGoOffset = 0;
193
+ const newRoute = getRouteFromBrowser();
194
+ const stack = historyE().state?.stack || [];
195
+ if (stack.length !== prevStack.length) {
196
+ const maxIndex = Math.min(prevStack.length, stack.length) - 1;
197
+ if (maxIndex < 0 || stack[maxIndex] === prevStack[maxIndex]) {
198
+ newRoute.nav = stack.length < prevStack.length ? "back" : "forward";
199
+ }
200
+ }
201
+ prevStack = stack;
202
+ log("popstate", newRoute);
203
+ A.copy(current, newRoute);
204
+ A.runQueue();
205
+ });
206
+ A.leakScope(() => {
207
+ A(() => {
208
+ current.path = "/" + Array.from(current.p).join("/");
209
+ });
210
+ A(() => {
211
+ const stack = historyE().state?.stack || [];
212
+ const newRoute = toCanonRoute(current, A.unproxy(current).nav, stack.length + 1);
213
+ A.copy(current, newRoute);
214
+ const state = { state: newRoute.state, stack };
215
+ const url = getUrl(newRoute);
216
+ if (url !== locationE().pathname + locationE().search + locationE().hash || !equal(historyE().state, state, false)) {
217
+ log("replaceState", newRoute, state, url);
218
+ historyE().replaceState(state, "", url);
219
+ }
220
+ });
221
+ A(() => {
222
+ const n = {};
223
+ n[current.path] = true;
224
+ for (const [k, v] of Object.entries(current.search)) {
225
+ n[`${k}=${v}`] = true;
226
+ }
227
+ A.copy(currentRouteParts, n);
228
+ });
229
+ });
230
+ }
231
+ initRouter();
232
+ function persistScroll(name = "main") {
233
+ const el = A();
234
+ A("scroll=", onScroll);
235
+ const restore = A.unproxy(current).state.scroll?.[name];
236
+ if (restore) {
237
+ log("restoring scroll", name, restore);
238
+ Object.assign(el, restore);
239
+ }
240
+ function onScroll() {
241
+ (current.state.scroll ||= {})[name] = {
242
+ scrollTop: el.scrollTop,
243
+ scrollLeft: el.scrollLeft
244
+ };
248
245
  }
249
246
  }
250
- function handleEvent(e) {
251
- let target = e.target;
252
- while (target && target.tagName?.toUpperCase() !== "A") {
253
- target = target.parentElement;
247
+ function interceptLinks() {
248
+ A({
249
+ click: handleEvent,
250
+ keydown: handleKeyEvent
251
+ });
252
+ function handleKeyEvent(e) {
253
+ if (e.key === "Enter") {
254
+ handleEvent(e);
255
+ }
256
+ }
257
+ function handleEvent(e) {
258
+ let target = e.target;
259
+ while (target && target.tagName?.toUpperCase() !== "A") {
260
+ target = target.parentElement;
261
+ }
262
+ if (!target)
263
+ return;
264
+ const anchor = target;
265
+ const href = anchor.getAttribute("href");
266
+ if (!href)
267
+ return;
268
+ if (href.startsWith("#"))
269
+ return;
270
+ if (href.startsWith("//") || /^[^/?#]+:/.test(href))
271
+ return;
272
+ if (anchor.getAttribute("target") || anchor.getAttribute("download"))
273
+ return;
274
+ if (typeof MouseEvent !== "undefined" && e instanceof MouseEvent && (e.ctrlKey || e.metaKey || e.shiftKey))
275
+ return;
276
+ e.preventDefault();
277
+ go(href);
254
278
  }
255
- if (!target)
256
- return;
257
- const anchor = target;
258
- const href = anchor.getAttribute("href");
259
- if (!href)
260
- return;
261
- if (href.startsWith("#"))
262
- return;
263
- if (href.startsWith("//") || /^[^/?#]+:/.test(href))
264
- return;
265
- if (anchor.getAttribute("target") || anchor.getAttribute("download"))
266
- return;
267
- if (typeof MouseEvent !== "undefined" && e instanceof MouseEvent && (e.ctrlKey || e.metaKey || e.shiftKey))
268
- return;
269
- e.preventDefault();
270
- go(href);
271
279
  }
280
+ return { current, setLog, go, matchCurrent, push, back, up, reset, initRouter, persistScroll, interceptLinks };
272
281
  }
273
282
  export {
274
- up,
275
- setLog,
276
- reset,
277
- push,
278
- persistScroll,
279
- matchCurrent,
280
- interceptLinks,
281
- go,
282
- current,
283
- back
283
+ init
284
284
  };
285
285
 
286
- //# debugId=E926F5C48B21197864756E2164756E21
286
+ //# debugId=CA3A70B43BF6BD7864756E2164756E21
287
287
  //# sourceMappingURL=route.js.map