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.
- package/dist/src/aberdeen.d.ts +928 -944
- package/dist/src/aberdeen.js +1693 -1704
- package/dist/src/aberdeen.js.map +3 -3
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +11 -0
- package/dist/src/index.js.map +10 -0
- package/dist/src/prediction.d.ts +7 -4
- package/dist/src/prediction.js +18 -23
- package/dist/src/prediction.js.map +3 -3
- package/dist/src/route.d.ts +112 -99
- package/dist/src/route.js +254 -254
- package/dist/src/route.js.map +3 -3
- package/dist/src/server-dom.d.ts +158 -0
- package/dist/src/server-dom.js +458 -0
- package/dist/src/server-dom.js.map +10 -0
- package/dist/src/server.d.ts +55 -0
- package/dist/src/server.js +25 -0
- package/dist/src/server.js.map +10 -0
- package/dist/src/transitions.js +7 -4
- package/dist/src/transitions.js.map +3 -3
- package/dist/tests/environment.d.ts +24 -0
- package/dist/tests/{fakedom.js → environment.js} +239 -288
- package/dist/tests/environment.js.map +11 -0
- package/dist/tests/helpers.d.ts +15 -3
- package/dist/tests/helpers.js +277 -306
- package/dist/tests/helpers.js.map +6 -5
- package/dist-docs/Tutorial/index.html +30 -30
- package/dist-docs/aberdeen/Aberdeen/index.html +350 -0
- package/dist-docs/aberdeen/InitOptions/index.html +4 -0
- package/dist-docs/aberdeen/KeyToString/index.html +2 -0
- package/dist-docs/aberdeen/PromiseProxy/index.html +6 -6
- package/dist-docs/aberdeen/SortKeyType/index.html +2 -0
- package/dist-docs/aberdeen/TargetType/index.html +2 -0
- package/dist-docs/aberdeen/ValueRef/index.html +2 -0
- package/dist-docs/aberdeen/WindowLike/index.html +5 -0
- package/dist-docs/aberdeen/{clone → createA}/index.html +10 -5
- package/dist-docs/aberdeen/index.html +13 -54
- package/dist-docs/assets/aberdeen/aberdeen.d.ts +928 -944
- package/dist-docs/assets/aberdeen/aberdeen.js +1693 -1704
- package/dist-docs/assets/aberdeen/aberdeen.js.map +3 -3
- package/dist-docs/assets/aberdeen/index.d.ts +12 -0
- package/dist-docs/assets/aberdeen/index.js +11 -0
- package/dist-docs/assets/aberdeen/index.js.map +10 -0
- package/dist-docs/assets/aberdeen/prediction.d.ts +7 -4
- package/dist-docs/assets/aberdeen/prediction.js +18 -23
- package/dist-docs/assets/aberdeen/prediction.js.map +3 -3
- package/dist-docs/assets/aberdeen/route.d.ts +112 -99
- package/dist-docs/assets/aberdeen/route.js +254 -254
- package/dist-docs/assets/aberdeen/route.js.map +3 -3
- package/dist-docs/assets/aberdeen/server-dom.d.ts +158 -0
- package/dist-docs/assets/aberdeen/server-dom.js +458 -0
- package/dist-docs/assets/aberdeen/server-dom.js.map +10 -0
- package/dist-docs/assets/aberdeen/server.d.ts +55 -0
- package/dist-docs/assets/aberdeen/server.js +25 -0
- package/dist-docs/assets/aberdeen/server.js.map +10 -0
- package/dist-docs/assets/aberdeen/transitions.js +7 -4
- package/dist-docs/assets/aberdeen/transitions.js.map +3 -3
- package/dist-docs/assets/navigation.js +1 -1
- package/dist-docs/assets/search.js +1 -1
- package/dist-docs/dispatcher/Dispatcher/index.html +5 -6
- package/dist-docs/dispatcher/MATCH_FAILED/index.html +2 -2
- package/dist-docs/dispatcher/MATCH_REST/index.html +2 -2
- package/dist-docs/dispatcher/index.html +2 -2
- package/dist-docs/hierarchy.html +1 -1
- package/dist-docs/index-1/index.html +2 -0
- package/dist-docs/index.html +2 -2
- package/dist-docs/media/CHANGELOG.md +14 -2
- package/dist-docs/modules.html +1 -1
- package/dist-docs/prediction/applyCanon/index.html +4 -3
- package/dist-docs/prediction/applyPrediction/index.html +4 -3
- package/dist-docs/prediction/index.html +2 -2
- package/dist-docs/route/Route/index.html +9 -9
- package/dist-docs/route/RouteApi/index.html +60 -0
- package/dist-docs/route/index.html +6 -15
- package/dist-docs/route/init/index.html +5 -0
- package/dist-docs/server/RenderToStringOptions/index.html +6 -0
- package/dist-docs/server/RenderToStringResult/index.html +6 -0
- package/dist-docs/server/index.html +4 -0
- package/dist-docs/server/renderToString/index.html +6 -0
- package/dist-docs/sitemap.xml +51 -155
- package/dist-docs/transitions/grow/index.html +2 -2
- package/dist-docs/transitions/index.html +2 -2
- package/dist-docs/transitions/shrink/index.html +2 -2
- package/package.json +17 -7
- package/skill/SKILL.md +28 -28
- package/skill/aberdeen.md +471 -3448
- package/skill/dispatcher.md +6 -16
- package/skill/prediction.md +17 -5
- package/skill/route.md +95 -77
- package/skill/transitions.md +3 -3
- package/src/aberdeen.ts +1226 -740
- package/src/index.ts +13 -0
- package/src/prediction.ts +25 -24
- package/src/route.ts +192 -142
- package/src/server-dom.ts +508 -0
- package/src/server.ts +74 -0
- package/src/transitions.ts +9 -3
- package/dist/tests/fakedom.d.ts +0 -9
- package/dist/tests/fakedom.js.map +0 -10
- package/dist-docs/aberdeen/A/index.html +0 -115
- package/dist-docs/aberdeen/CUSTOM_DUMP/index.html +0 -7
- package/dist-docs/aberdeen/NO_COPY/index.html +0 -2
- package/dist-docs/aberdeen/OPAQUE/index.html +0 -12
- package/dist-docs/aberdeen/clean/index.html +0 -11
- package/dist-docs/aberdeen/copy/index.html +0 -22
- package/dist-docs/aberdeen/count/index.html +0 -7
- package/dist-docs/aberdeen/cssVars/index.html +0 -11
- package/dist-docs/aberdeen/darkMode/index.html +0 -9
- package/dist-docs/aberdeen/default/index.html +0 -341
- package/dist-docs/aberdeen/derive/index.html +0 -18
- package/dist-docs/aberdeen/disableCreateDestroy/index.html +0 -6
- package/dist-docs/aberdeen/dump/index.html +0 -11
- package/dist-docs/aberdeen/insertCss/index.html +0 -30
- package/dist-docs/aberdeen/insertGlobalCss/index.html +0 -19
- package/dist-docs/aberdeen/invertString/index.html +0 -13
- package/dist-docs/aberdeen/isEmpty/index.html +0 -12
- package/dist-docs/aberdeen/map/index.html +0 -26
- package/dist-docs/aberdeen/merge/index.html +0 -11
- package/dist-docs/aberdeen/mount/index.html +0 -17
- package/dist-docs/aberdeen/multiMap/index.html +0 -28
- package/dist-docs/aberdeen/onEach/index.html +0 -19
- package/dist-docs/aberdeen/partition/index.html +0 -36
- package/dist-docs/aberdeen/peek/index.html +0 -40
- package/dist-docs/aberdeen/proxy/index.html +0 -35
- package/dist-docs/aberdeen/ref/index.html +0 -13
- package/dist-docs/aberdeen/runQueue/index.html +0 -13
- package/dist-docs/aberdeen/setErrorHandler/index.html +0 -16
- package/dist-docs/aberdeen/setSpacingCssVars/index.html +0 -8
- package/dist-docs/aberdeen/unmountAll/index.html +0 -5
- package/dist-docs/aberdeen/unproxy/index.html +0 -11
- package/dist-docs/route/back/index.html +0 -4
- package/dist-docs/route/current/index.html +0 -2
- package/dist-docs/route/go/index.html +0 -14
- package/dist-docs/route/interceptLinks/index.html +0 -8
- package/dist-docs/route/matchCurrent/index.html +0 -10
- package/dist-docs/route/persistScroll/index.html +0 -6
- package/dist-docs/route/push/index.html +0 -6
- package/dist-docs/route/setLog/index.html +0 -3
- package/dist-docs/route/up/index.html +0 -5
|
@@ -1,287 +1,287 @@
|
|
|
1
1
|
// src/route.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function setLog(value) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
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=
|
|
286
|
+
//# debugId=CA3A70B43BF6BD7864756E2164756E21
|
|
287
287
|
//# sourceMappingURL=route.js.map
|