lightview 2.3.8 → 2.4.7
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/.gemini/CODE_ANALYSIS_AND_IMPROVEMENT_PLAN.md +56 -0
- package/AI-GUIDANCE.md +274 -0
- package/README.md +35 -0
- package/build_tmp/lightview-cdom.js +3934 -0
- package/build_tmp/lightview-router.js +185 -0
- package/build_tmp/lightview-x.js +1739 -0
- package/build_tmp/lightview.js +740 -0
- package/components/data-display/diff.js +36 -4
- package/docs/api/hypermedia.html +75 -5
- package/docs/api/index.html +3 -3
- package/docs/api/nav.html +0 -16
- package/docs/articles/html-vs-json-partials.md +102 -0
- package/docs/articles/lightview-vs-htmx.md +610 -0
- package/docs/assets/styles/site.css +16 -7
- package/docs/benchmarks/bau-tagged-fragment.js +41 -0
- package/docs/benchmarks/tagged-fragment.js +36 -0
- package/docs/cdom.html +127 -88
- package/docs/components/chart.html +157 -210
- package/docs/components/component-nav.html +1 -1
- package/docs/components/diff.html +33 -21
- package/docs/components/gallery.html +107 -4
- package/docs/components/index.css +18 -3
- package/docs/components/index.html +20 -9
- package/docs/dom-benchmark.html +771 -0
- package/docs/getting-started/index.html +42 -2
- package/docs/hypermedia/index.html +391 -0
- package/docs/hypermedia/nav.html +17 -0
- package/docs/index.html +136 -17
- package/index.html +59 -10
- package/lightview-all.js +223 -67
- package/lightview-cdom.js +1 -2
- package/lightview-x.js +144 -13
- package/lightview.js +85 -277
- package/package.json +2 -2
- package/src/lightview-cdom.js +1 -5
- package/src/lightview-x.js +158 -27
- package/src/lightview.js +94 -60
- package/docs/articles/calculator-no-javascript-hackernoon.md +0 -283
- package/docs/articles/calculator-no-javascript.md +0 -290
- package/docs/articles/part1-reference.md +0 -236
- package/lightview.js.bak +0 -1
- package/test-xpath.html +0 -63
- package/test_error.txt +0 -0
- package/test_output.txt +0 -0
- package/test_output_full.txt +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
"use strict";
|
|
3
|
+
(() => {
|
|
4
|
+
const base = (shellPath) => {
|
|
5
|
+
if (typeof window === "undefined" || document.getElementById("content")) return;
|
|
6
|
+
const url = new URL(shellPath, globalThis.location.href);
|
|
7
|
+
url.searchParams.set("load", globalThis.location.pathname);
|
|
8
|
+
globalThis.location.href = url.toString();
|
|
9
|
+
};
|
|
10
|
+
const router = (options = {}) => {
|
|
11
|
+
const { base: base2 = "", contentEl, notFound, debug, onResponse, onStart } = options;
|
|
12
|
+
const chains = [];
|
|
13
|
+
const normalizePath = (p) => {
|
|
14
|
+
if (!p) return "/";
|
|
15
|
+
let hash = "";
|
|
16
|
+
if (p.includes("#")) {
|
|
17
|
+
[p, hash] = p.split("#");
|
|
18
|
+
hash = "#" + hash;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
if (p.startsWith("http") || p.startsWith("//")) p = new URL(p, globalThis.location.origin).pathname;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
}
|
|
24
|
+
if (base2 && p.startsWith(base2)) p = p.slice(base2.length);
|
|
25
|
+
return (p.replace(/\/+$/, "").replace(/^([^/])/, "/$1") || "/") + hash;
|
|
26
|
+
};
|
|
27
|
+
const createMatcher = (pattern) => {
|
|
28
|
+
if (typeof pattern === "function") return pattern;
|
|
29
|
+
return (ctx) => {
|
|
30
|
+
const { path } = ctx;
|
|
31
|
+
const pathOnly = path.split("#")[0];
|
|
32
|
+
if (pattern instanceof RegExp) {
|
|
33
|
+
const m2 = pathOnly.match(pattern);
|
|
34
|
+
return m2 ? { ...ctx, match: m2 } : null;
|
|
35
|
+
}
|
|
36
|
+
if (pattern === "*" || pattern === pathOnly) return { ...ctx, wildcard: pathOnly };
|
|
37
|
+
const keys = [];
|
|
38
|
+
const regexStr = "^" + pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, "(.*)").replace(/:([^/]+)/g, (_, k) => (keys.push(k), "([^/]+)")) + "$";
|
|
39
|
+
const m = pathOnly.match(new RegExp(regexStr));
|
|
40
|
+
if (m) {
|
|
41
|
+
const params = {};
|
|
42
|
+
keys.forEach((k, i) => params[k] = m[i + 1]);
|
|
43
|
+
return { ...ctx, params, wildcard: m[1] };
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const createReplacer = (pat) => (ctx) => {
|
|
49
|
+
const [path, hash] = ctx.path.split("#");
|
|
50
|
+
return {
|
|
51
|
+
...ctx,
|
|
52
|
+
path: pat.replace(/\*|:([^/]+)/g, (m, k) => {
|
|
53
|
+
var _a;
|
|
54
|
+
return (k ? (_a = ctx.params) == null ? void 0 : _a[k] : ctx.wildcard) || m;
|
|
55
|
+
}) + (hash ? "#" + hash : "")
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const fetchHandler = async (ctx) => {
|
|
59
|
+
try {
|
|
60
|
+
const pathOnly = ctx.path.split("#")[0];
|
|
61
|
+
const res = await fetch(pathOnly);
|
|
62
|
+
if (res.ok) return res;
|
|
63
|
+
} catch (e) {
|
|
64
|
+
if (debug) console.error("[Router] Fetch error:", e);
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
};
|
|
68
|
+
const use = (...args) => {
|
|
69
|
+
const chain = args.map((arg, i) => i === 0 && typeof arg !== "function" ? createMatcher(arg) : typeof arg === "string" ? createReplacer(arg) : arg);
|
|
70
|
+
if (contentEl && !chain.some((f) => f.name === "fetchHandler" || args.some((a) => typeof a === "function"))) chain.push(fetchHandler);
|
|
71
|
+
chains.push(chain);
|
|
72
|
+
return routerInstance;
|
|
73
|
+
};
|
|
74
|
+
const route = async (raw) => {
|
|
75
|
+
let ctx = { path: normalizePath(raw), contentEl };
|
|
76
|
+
if (debug) console.log(`[Router] Routing: ${ctx.path}`);
|
|
77
|
+
for (const chain of chains) {
|
|
78
|
+
let res = ctx, failed = false;
|
|
79
|
+
for (const fn of chain) {
|
|
80
|
+
try {
|
|
81
|
+
res = await fn(res);
|
|
82
|
+
if (res instanceof Response) return res;
|
|
83
|
+
if (!res) {
|
|
84
|
+
failed = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.error("[Router] Chain error:", e);
|
|
89
|
+
failed = true;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!failed) ctx = typeof res === "string" ? { ...ctx, path: res } : { ...ctx, ...res };
|
|
94
|
+
}
|
|
95
|
+
return notFound ? notFound(ctx) : null;
|
|
96
|
+
};
|
|
97
|
+
const handleRequest = async (path) => {
|
|
98
|
+
var _a, _b;
|
|
99
|
+
if (onStart) onStart(path);
|
|
100
|
+
const internals = (_a = globalThis.Lightview) == null ? void 0 : _a.internals;
|
|
101
|
+
const scrollMap = (_b = internals == null ? void 0 : internals.saveScrolls) == null ? void 0 : _b.call(internals);
|
|
102
|
+
const res = await route(path);
|
|
103
|
+
if (!res) return console.warn(`[Router] No route: ${path}`);
|
|
104
|
+
if (res.ok && contentEl) {
|
|
105
|
+
contentEl.innerHTML = await res.text();
|
|
106
|
+
contentEl.querySelectorAll("script").forEach((s) => {
|
|
107
|
+
const n = document.createElement("script");
|
|
108
|
+
[...s.attributes].forEach((a) => n.setAttribute(a.name, a.value));
|
|
109
|
+
n.textContent = s.textContent;
|
|
110
|
+
s.replaceWith(n);
|
|
111
|
+
});
|
|
112
|
+
if ((internals == null ? void 0 : internals.restoreScrolls) && scrollMap) {
|
|
113
|
+
internals.restoreScrolls(scrollMap);
|
|
114
|
+
}
|
|
115
|
+
const urlParts = path.split("#");
|
|
116
|
+
const hash = urlParts.length > 1 ? "#" + urlParts[1] : "";
|
|
117
|
+
if (hash) {
|
|
118
|
+
requestAnimationFrame(() => {
|
|
119
|
+
requestAnimationFrame(() => {
|
|
120
|
+
const id = hash.slice(1);
|
|
121
|
+
const target = document.getElementById(id);
|
|
122
|
+
if (target) {
|
|
123
|
+
target.style.scrollMarginTop = "calc(var(--site-nav-height, 0px) + 2rem)";
|
|
124
|
+
target.scrollIntoView({ behavior: "smooth", block: "start", inline: "start" });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (onResponse) await onResponse(res, path);
|
|
131
|
+
return res;
|
|
132
|
+
};
|
|
133
|
+
const navigate = (path) => {
|
|
134
|
+
const p = normalizePath(path);
|
|
135
|
+
return handleRequest(base2 + p).then((r) => {
|
|
136
|
+
let dest = (r == null ? void 0 : r.url) ? new URL(r.url, globalThis.location.origin).pathname : base2 + p;
|
|
137
|
+
if (p.includes("#") && !dest.includes("#")) {
|
|
138
|
+
dest += "#" + p.split("#")[1];
|
|
139
|
+
}
|
|
140
|
+
globalThis.history.pushState({ path: dest }, "", dest);
|
|
141
|
+
}).catch((e) => console.error("[Router] Nav error:", e));
|
|
142
|
+
};
|
|
143
|
+
const start = async () => {
|
|
144
|
+
const load = new URLSearchParams(globalThis.location.search).get("load");
|
|
145
|
+
globalThis.onpopstate = (e) => {
|
|
146
|
+
var _a;
|
|
147
|
+
return handleRequest(((_a = e.state) == null ? void 0 : _a.path) || normalizePath(globalThis.location.pathname + globalThis.location.hash));
|
|
148
|
+
};
|
|
149
|
+
document.onclick = (e) => {
|
|
150
|
+
const path = e.composedPath();
|
|
151
|
+
const a = path.find((el) => {
|
|
152
|
+
var _a;
|
|
153
|
+
return el.tagName === "A" && ((_a = el.hasAttribute) == null ? void 0 : _a.call(el, "href"));
|
|
154
|
+
});
|
|
155
|
+
if (!a || a.target === "_blank" || /^(http|#|mailto|tel)/.test(a.getAttribute("href"))) return;
|
|
156
|
+
const url = new URL(a.href, document.baseURI);
|
|
157
|
+
if (url.origin === globalThis.location.origin) {
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
const fullPath = url.pathname + url.search + url.hash;
|
|
160
|
+
navigate(normalizePath(fullPath));
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const init = load || normalizePath(globalThis.location.pathname + globalThis.location.hash);
|
|
164
|
+
globalThis.history.replaceState({ path: init }, "", base2 + init);
|
|
165
|
+
return handleRequest(init).then(() => routerInstance);
|
|
166
|
+
};
|
|
167
|
+
const routerInstance = { use, navigate, start };
|
|
168
|
+
return routerInstance;
|
|
169
|
+
};
|
|
170
|
+
const LightviewRouter = { base, router };
|
|
171
|
+
if (typeof module !== "undefined" && module.exports) module.exports = LightviewRouter;
|
|
172
|
+
else if (typeof window !== "undefined") {
|
|
173
|
+
globalThis.LightviewRouter = LightviewRouter;
|
|
174
|
+
try {
|
|
175
|
+
const script = document.currentScript;
|
|
176
|
+
if (script && script.src.includes("?")) {
|
|
177
|
+
const params = new URL(script.src).searchParams;
|
|
178
|
+
const b = params.get("base");
|
|
179
|
+
if (b) LightviewRouter.base(b);
|
|
180
|
+
}
|
|
181
|
+
} catch (e) {
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
})();
|
|
185
|
+
})();
|