lightview 2.2.2 → 2.3.4
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/cDOMIntro.md +279 -0
- package/docs/about.html +15 -12
- package/docs/api/computed.html +1 -1
- package/docs/api/effects.html +1 -1
- package/docs/api/elements.html +56 -25
- package/docs/api/enhance.html +1 -1
- package/docs/api/hypermedia.html +1 -1
- package/docs/api/index.html +1 -1
- package/docs/api/nav.html +28 -3
- package/docs/api/signals.html +1 -1
- package/docs/api/state.html +283 -85
- package/docs/assets/js/examplify.js +2 -1
- package/docs/cdom-nav.html +3 -2
- package/docs/cdom.html +383 -114
- package/jprx/README.md +112 -71
- package/jprx/helpers/state.js +21 -0
- package/jprx/package.json +1 -1
- package/jprx/parser.js +136 -86
- package/jprx/specs/expressions.json +71 -0
- package/jprx/specs/helpers.json +150 -0
- package/lightview-all.js +618 -431
- package/lightview-cdom.js +311 -605
- package/lightview-router.js +6 -0
- package/lightview-x.js +226 -54
- package/lightview.js +351 -42
- package/package.json +2 -1
- package/src/lightview-cdom.js +211 -315
- package/src/lightview-router.js +10 -0
- package/src/lightview-x.js +121 -1
- package/src/lightview.js +88 -16
- package/src/reactivity/signal.js +73 -29
- package/src/reactivity/state.js +84 -21
- package/tests/cdom/fixtures/helpers.cdomc +24 -24
- package/tests/cdom/helpers.test.js +28 -28
- package/tests/cdom/parser.test.js +39 -114
- package/tests/cdom/reactivity.test.js +32 -29
- package/tests/jprx/spec.test.js +99 -0
- package/tests/cdom/loader.test.js +0 -125
package/lightview-router.js
CHANGED
|
@@ -95,7 +95,10 @@
|
|
|
95
95
|
return notFound ? notFound(ctx) : null;
|
|
96
96
|
};
|
|
97
97
|
const handleRequest = async (path) => {
|
|
98
|
+
var _a, _b;
|
|
98
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);
|
|
99
102
|
const res = await route(path);
|
|
100
103
|
if (!res) return console.warn(`[Router] No route: ${path}`);
|
|
101
104
|
if (res.ok && contentEl) {
|
|
@@ -106,6 +109,9 @@
|
|
|
106
109
|
n.textContent = s.textContent;
|
|
107
110
|
s.replaceWith(n);
|
|
108
111
|
});
|
|
112
|
+
if ((internals == null ? void 0 : internals.restoreScrolls) && scrollMap) {
|
|
113
|
+
internals.restoreScrolls(scrollMap);
|
|
114
|
+
}
|
|
109
115
|
const urlParts = path.split("#");
|
|
110
116
|
const hash = urlParts.length > 1 ? "#" + urlParts[1] : "";
|
|
111
117
|
if (hash) {
|
package/lightview-x.js
CHANGED
|
@@ -1,29 +1,48 @@
|
|
|
1
1
|
(function() {
|
|
2
2
|
"use strict";
|
|
3
|
+
var _a, _b;
|
|
3
4
|
const _LV = globalThis.__LIGHTVIEW_INTERNALS__ || (globalThis.__LIGHTVIEW_INTERNALS__ = {
|
|
4
5
|
currentEffect: null,
|
|
5
6
|
registry: /* @__PURE__ */ new Map(),
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
// Global name -> Signal/Proxy
|
|
8
|
+
localRegistries: /* @__PURE__ */ new WeakMap(),
|
|
9
|
+
// Object/Element -> Map(name -> Signal/Proxy)
|
|
10
|
+
futureSignals: /* @__PURE__ */ new Map(),
|
|
11
|
+
// name -> Set of (signal) => void
|
|
12
|
+
schemas: /* @__PURE__ */ new Map(),
|
|
13
|
+
// name -> Schema (Draft 7+ or Shorthand)
|
|
14
|
+
parents: /* @__PURE__ */ new WeakMap(),
|
|
15
|
+
// Proxy -> Parent (Proxy/Element)
|
|
16
|
+
helpers: /* @__PURE__ */ new Map(),
|
|
17
|
+
// name -> function (used for transforms and expressions)
|
|
18
|
+
hooks: {
|
|
19
|
+
validate: (value, schema) => true
|
|
20
|
+
// Hook for extensions (like JPRX) to provide full validation
|
|
21
|
+
}
|
|
8
22
|
});
|
|
23
|
+
const lookup = (name, scope) => {
|
|
24
|
+
let current = scope;
|
|
25
|
+
while (current && typeof current === "object") {
|
|
26
|
+
const registry = _LV.localRegistries.get(current);
|
|
27
|
+
if (registry && registry.has(name)) return registry.get(name);
|
|
28
|
+
current = current.parentElement || _LV.parents.get(current);
|
|
29
|
+
}
|
|
30
|
+
return _LV.registry.get(name);
|
|
31
|
+
};
|
|
9
32
|
const signal = (initialValue, optionsOrName) => {
|
|
10
|
-
|
|
33
|
+
const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
|
|
11
34
|
const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
|
|
35
|
+
const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
|
|
12
36
|
if (name && storage) {
|
|
13
37
|
try {
|
|
14
38
|
const stored = storage.getItem(name);
|
|
15
|
-
if (stored !== null)
|
|
16
|
-
initialValue = JSON.parse(stored);
|
|
17
|
-
}
|
|
39
|
+
if (stored !== null) initialValue = JSON.parse(stored);
|
|
18
40
|
} catch (e) {
|
|
19
41
|
}
|
|
20
42
|
}
|
|
21
43
|
let value = initialValue;
|
|
22
44
|
const subscribers = /* @__PURE__ */ new Set();
|
|
23
|
-
const f = (...args) =>
|
|
24
|
-
if (args.length === 0) return f.value;
|
|
25
|
-
f.value = args[0];
|
|
26
|
-
};
|
|
45
|
+
const f = (...args) => args.length === 0 ? f.value : f.value = args[0];
|
|
27
46
|
Object.defineProperty(f, "value", {
|
|
28
47
|
get() {
|
|
29
48
|
if (_LV.currentEffect) {
|
|
@@ -46,21 +65,39 @@
|
|
|
46
65
|
}
|
|
47
66
|
});
|
|
48
67
|
if (name) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
const registry = scope && typeof scope === "object" ? _LV.localRegistries.get(scope) || _LV.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : _LV.registry;
|
|
69
|
+
if (registry && registry.has(name) && registry.get(name) !== f) {
|
|
70
|
+
throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
|
|
71
|
+
}
|
|
72
|
+
if (registry) registry.set(name, f);
|
|
73
|
+
const futures = _LV.futureSignals.get(name);
|
|
74
|
+
if (futures) {
|
|
75
|
+
futures.forEach((resolve) => resolve(f));
|
|
55
76
|
}
|
|
56
77
|
}
|
|
57
78
|
return f;
|
|
58
79
|
};
|
|
59
|
-
const getSignal = (name,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
80
|
+
const getSignal = (name, defaultValueOrOptions) => {
|
|
81
|
+
const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
|
|
82
|
+
const { scope, defaultValue } = options;
|
|
83
|
+
const existing = lookup(name, scope);
|
|
84
|
+
if (existing) return existing;
|
|
85
|
+
if (defaultValue !== void 0) return signal(defaultValue, { name, scope });
|
|
86
|
+
const future = signal(void 0);
|
|
87
|
+
const handler = (realSignal) => {
|
|
88
|
+
const hasValue = realSignal && (typeof realSignal === "object" || typeof realSignal === "function") && "value" in realSignal;
|
|
89
|
+
if (hasValue) {
|
|
90
|
+
future.value = realSignal.value;
|
|
91
|
+
effect(() => {
|
|
92
|
+
future.value = realSignal.value;
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
future.value = realSignal;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
if (!_LV.futureSignals.has(name)) _LV.futureSignals.set(name, /* @__PURE__ */ new Set());
|
|
99
|
+
_LV.futureSignals.get(name).add(handler);
|
|
100
|
+
return future;
|
|
64
101
|
};
|
|
65
102
|
signal.get = getSignal;
|
|
66
103
|
const effect = (fn) => {
|
|
@@ -89,9 +126,38 @@
|
|
|
89
126
|
return execute;
|
|
90
127
|
};
|
|
91
128
|
const getRegistry = () => _LV.registry;
|
|
129
|
+
const internals = _LV;
|
|
92
130
|
const stateCache = /* @__PURE__ */ new WeakMap();
|
|
93
131
|
const stateSignals = /* @__PURE__ */ new WeakMap();
|
|
94
|
-
const
|
|
132
|
+
const stateSchemas = /* @__PURE__ */ new WeakMap();
|
|
133
|
+
const { parents, schemas, hooks } = internals;
|
|
134
|
+
const validate = (target, prop, value, schema) => {
|
|
135
|
+
var _a2, _b2;
|
|
136
|
+
const current = target[prop];
|
|
137
|
+
const type = typeof current;
|
|
138
|
+
const isNew = !(prop in target);
|
|
139
|
+
let behavior = schema;
|
|
140
|
+
if (typeof schema === "object" && schema !== null) behavior = schema.type;
|
|
141
|
+
if (behavior === "auto" && isNew) throw new Error(`Lightview: Cannot add new property "${prop}" to fixed 'auto' state.`);
|
|
142
|
+
if (behavior === "polymorphic" || typeof behavior === "object" && (behavior == null ? void 0 : behavior.coerce)) {
|
|
143
|
+
if (type === "number") return Number(value);
|
|
144
|
+
if (type === "boolean") return Boolean(value);
|
|
145
|
+
if (type === "string") return String(value);
|
|
146
|
+
} else if (behavior === "auto" || behavior === "dynamic") {
|
|
147
|
+
if (!isNew && typeof value !== type) {
|
|
148
|
+
throw new Error(`Lightview: Type mismatch for "${prop}". Expected ${type}, got ${typeof value}.`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (typeof schema === "object" && schema !== null && schema.transform) {
|
|
152
|
+
const trans = schema.transform;
|
|
153
|
+
const transformFn = typeof trans === "function" ? trans : internals.helpers.get(trans) || ((_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.helpers) == null ? void 0 : _b2[trans]);
|
|
154
|
+
if (transformFn) value = transformFn(value);
|
|
155
|
+
}
|
|
156
|
+
if (hooks.validate(value, schema) === false) {
|
|
157
|
+
throw new Error(`Lightview: Validation failed for "${prop}".`);
|
|
158
|
+
}
|
|
159
|
+
return value;
|
|
160
|
+
};
|
|
95
161
|
const protoMethods = (proto, test) => Object.getOwnPropertyNames(proto).filter((k) => typeof proto[k] === "function" && test(k));
|
|
96
162
|
const DATE_TRACKING = protoMethods(Date.prototype, (k) => /^(to|get|valueOf)/.test(k));
|
|
97
163
|
const DATE_MUTATING = protoMethods(Date.prototype, (k) => /^set/.test(k));
|
|
@@ -143,12 +209,14 @@
|
|
|
143
209
|
return val;
|
|
144
210
|
};
|
|
145
211
|
const proxySet = (target, prop, value, receiver, signals) => {
|
|
212
|
+
const schema = stateSchemas.get(receiver);
|
|
213
|
+
const validatedValue = schema ? validate(target, prop, value, schema) : value;
|
|
146
214
|
if (!signals.has(prop)) {
|
|
147
215
|
signals.set(prop, signal(Reflect.get(target, prop, receiver)));
|
|
148
216
|
}
|
|
149
|
-
const success = Reflect.set(target, prop,
|
|
217
|
+
const success = Reflect.set(target, prop, validatedValue, receiver);
|
|
150
218
|
const signal$1 = signals.get(prop);
|
|
151
|
-
if (success && signal$1) signal$1.value =
|
|
219
|
+
if (success && signal$1) signal$1.value = validatedValue;
|
|
152
220
|
return success;
|
|
153
221
|
};
|
|
154
222
|
const createSpecialProxy = (obj, monitor, trackingProps = []) => {
|
|
@@ -222,6 +290,8 @@
|
|
|
222
290
|
if (typeof obj !== "object" || obj === null) return obj;
|
|
223
291
|
const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
|
|
224
292
|
const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
|
|
293
|
+
const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
|
|
294
|
+
const schema = optionsOrName == null ? void 0 : optionsOrName.schema;
|
|
225
295
|
if (name && storage) {
|
|
226
296
|
try {
|
|
227
297
|
const item = storage.getItem(name);
|
|
@@ -250,6 +320,7 @@
|
|
|
250
320
|
stateCache.set(obj, proxy);
|
|
251
321
|
} else return obj;
|
|
252
322
|
}
|
|
323
|
+
if (schema) stateSchemas.set(proxy, schema);
|
|
253
324
|
if (name && storage) {
|
|
254
325
|
effect(() => {
|
|
255
326
|
try {
|
|
@@ -259,23 +330,31 @@
|
|
|
259
330
|
});
|
|
260
331
|
}
|
|
261
332
|
if (name) {
|
|
262
|
-
const registry = getRegistry();
|
|
263
|
-
if (registry.has(name)) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
333
|
+
const registry = scope && typeof scope === "object" ? internals.localRegistries.get(scope) || internals.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : getRegistry();
|
|
334
|
+
if (registry && registry.has(name) && registry.get(name) !== proxy) {
|
|
335
|
+
throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
|
|
336
|
+
}
|
|
337
|
+
if (registry) registry.set(name, proxy);
|
|
338
|
+
const futures = internals.futureSignals.get(name);
|
|
339
|
+
if (futures) {
|
|
340
|
+
futures.forEach((resolve) => resolve(proxy));
|
|
269
341
|
}
|
|
270
342
|
}
|
|
271
343
|
return proxy;
|
|
272
344
|
};
|
|
273
|
-
const getState = (name,
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return
|
|
345
|
+
const getState = (name, defaultValueOrOptions) => {
|
|
346
|
+
const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
|
|
347
|
+
const { scope, defaultValue } = options;
|
|
348
|
+
const existing = lookup(name, scope);
|
|
349
|
+
if (existing) return existing;
|
|
350
|
+
if (defaultValue !== void 0) return state(defaultValue, { name, scope });
|
|
351
|
+
const future = signal(void 0);
|
|
352
|
+
const handler = (realState) => {
|
|
353
|
+
future.value = realState;
|
|
354
|
+
};
|
|
355
|
+
if (!internals.futureSignals.has(name)) internals.futureSignals.set(name, /* @__PURE__ */ new Set());
|
|
356
|
+
internals.futureSignals.get(name).add(handler);
|
|
357
|
+
return future;
|
|
279
358
|
};
|
|
280
359
|
state.get = getState;
|
|
281
360
|
const STANDARD_SRC_TAGS = ["img", "script", "iframe", "video", "audio", "source", "track", "embed", "input"];
|
|
@@ -309,7 +388,7 @@
|
|
|
309
388
|
return keys.length === 1 && isValidTagName(keys[0]) && typeof obj[keys[0]] === "object";
|
|
310
389
|
};
|
|
311
390
|
const convertObjectDOM = (obj) => {
|
|
312
|
-
var
|
|
391
|
+
var _a2, _b2;
|
|
313
392
|
if (typeof obj !== "object" || obj === null) return obj;
|
|
314
393
|
if (Array.isArray(obj)) return obj.map(convertObjectDOM);
|
|
315
394
|
if (obj.tag) return { ...obj, children: obj.children ? convertObjectDOM(obj.children) : [] };
|
|
@@ -317,7 +396,7 @@
|
|
|
317
396
|
const tagKey = Object.keys(obj)[0];
|
|
318
397
|
const content = obj[tagKey];
|
|
319
398
|
const LV = typeof window !== "undefined" ? globalThis.Lightview : typeof globalThis !== "undefined" ? globalThis.Lightview : null;
|
|
320
|
-
const tag = ((
|
|
399
|
+
const tag = ((_b2 = (_a2 = LV == null ? void 0 : LV.tags) == null ? void 0 : _a2._customTags) == null ? void 0 : _b2[tagKey]) || tagKey;
|
|
321
400
|
const { children, ...attributes } = content;
|
|
322
401
|
return { tag, attributes, children: children ? convertObjectDOM(children) : [] };
|
|
323
402
|
};
|
|
@@ -605,12 +684,12 @@
|
|
|
605
684
|
const frag = document.createDocumentFragment();
|
|
606
685
|
frag.appendChild(createMarker(markerId, false));
|
|
607
686
|
elements.forEach((c) => {
|
|
608
|
-
var
|
|
687
|
+
var _a2, _b2, _c;
|
|
609
688
|
if (typeof c === "string") frag.appendChild(document.createTextNode(c));
|
|
610
689
|
else if (c.domEl) frag.appendChild(c.domEl);
|
|
611
690
|
else if (c instanceof Node) frag.appendChild(c);
|
|
612
691
|
else {
|
|
613
|
-
const v = ((_c = (
|
|
692
|
+
const v = ((_c = (_a2 = globalThis.Lightview) == null ? void 0 : (_b2 = _a2.hooks).processChild) == null ? void 0 : _c.call(_b2, c)) || c;
|
|
614
693
|
if (v.tag) {
|
|
615
694
|
const n = element(v.tag, v.attributes || {}, v.children || []);
|
|
616
695
|
if (n == null ? void 0 : n.domEl) frag.appendChild(n.domEl);
|
|
@@ -627,10 +706,10 @@
|
|
|
627
706
|
};
|
|
628
707
|
const isPath = (s) => typeof s === "string" && !isDangerousProtocol(s) && /^(https?:|\.|\/|[\w])|(\.(html|json|[vo]dom|cdomc?))$/i.test(s);
|
|
629
708
|
const fetchContent = async (src) => {
|
|
630
|
-
var
|
|
709
|
+
var _a2;
|
|
631
710
|
try {
|
|
632
711
|
const LV = globalThis.Lightview;
|
|
633
|
-
if (((
|
|
712
|
+
if (((_a2 = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a2.validateUrl) && !LV.hooks.validateUrl(src)) {
|
|
634
713
|
console.warn(`[LightviewX] Fetch blocked by validateUrl hook: ${src}`);
|
|
635
714
|
return null;
|
|
636
715
|
}
|
|
@@ -655,10 +734,10 @@
|
|
|
655
734
|
}
|
|
656
735
|
};
|
|
657
736
|
const parseElements = (content, isJson, isHtml, el, element, isCdom = false, ext = "") => {
|
|
658
|
-
var
|
|
737
|
+
var _a2;
|
|
659
738
|
if (isJson) return Array.isArray(content) ? content : [content];
|
|
660
739
|
if (isCdom && ext === "cdomc") {
|
|
661
|
-
const parser = (
|
|
740
|
+
const parser = (_a2 = globalThis.LightviewCDOM) == null ? void 0 : _a2.parseCDOMC;
|
|
662
741
|
if (parser) {
|
|
663
742
|
try {
|
|
664
743
|
const obj = parser(content);
|
|
@@ -691,11 +770,14 @@
|
|
|
691
770
|
return null;
|
|
692
771
|
}
|
|
693
772
|
};
|
|
694
|
-
const updateTargetContent = (el, elements, raw, loc, contentHash,
|
|
773
|
+
const updateTargetContent = (el, elements, raw, loc, contentHash, options, targetHash = null) => {
|
|
774
|
+
var _a2;
|
|
775
|
+
const { element, setupChildren, saveScrolls, restoreScrolls } = { ...options, ...(_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals };
|
|
695
776
|
const markerId = `${loc}-${contentHash.slice(0, 8)}`;
|
|
696
777
|
let track = getOrSet(insertedContentMap, el.domEl, () => ({}));
|
|
697
778
|
if (track[loc]) removeInsertedContent(el.domEl, `${loc}-${track[loc].slice(0, 8)}`);
|
|
698
779
|
track[loc] = contentHash;
|
|
780
|
+
const scrollMap = saveScrolls ? saveScrolls() : null;
|
|
699
781
|
const performScroll = (root) => {
|
|
700
782
|
if (!targetHash) return;
|
|
701
783
|
requestAnimationFrame(() => {
|
|
@@ -709,18 +791,24 @@
|
|
|
709
791
|
});
|
|
710
792
|
});
|
|
711
793
|
};
|
|
794
|
+
const runRestore = (root) => {
|
|
795
|
+
if (restoreScrolls && scrollMap) restoreScrolls(scrollMap, root);
|
|
796
|
+
};
|
|
712
797
|
if (loc === "shadow") {
|
|
713
798
|
if (!el.domEl.shadowRoot) el.domEl.attachShadow({ mode: "open" });
|
|
714
799
|
setupChildren(elements, el.domEl.shadowRoot);
|
|
715
800
|
executeScripts(el.domEl.shadowRoot);
|
|
716
801
|
performScroll(el.domEl.shadowRoot);
|
|
802
|
+
runRestore(el.domEl.shadowRoot);
|
|
717
803
|
} else if (loc === "innerhtml") {
|
|
718
804
|
el.children = elements;
|
|
719
805
|
executeScripts(el.domEl);
|
|
720
806
|
performScroll(document);
|
|
807
|
+
runRestore(el.domEl);
|
|
721
808
|
} else {
|
|
722
809
|
insert(elements, el.domEl, loc, markerId, { element, setupChildren });
|
|
723
810
|
performScroll(document);
|
|
811
|
+
runRestore(el.domEl);
|
|
724
812
|
}
|
|
725
813
|
};
|
|
726
814
|
const handleSrcAttribute = async (el, src, tagName, { element, setupChildren }) => {
|
|
@@ -753,9 +841,9 @@
|
|
|
753
841
|
if (root) {
|
|
754
842
|
requestAnimationFrame(() => {
|
|
755
843
|
requestAnimationFrame(() => {
|
|
756
|
-
var
|
|
844
|
+
var _a2;
|
|
757
845
|
const id = targetHash.startsWith("#") ? targetHash.slice(1) : targetHash;
|
|
758
|
-
const target = root.getElementById ? root.getElementById(id) : (
|
|
846
|
+
const target = root.getElementById ? root.getElementById(id) : (_a2 = root.querySelector) == null ? void 0 : _a2.call(root, `#${id}`);
|
|
759
847
|
if (target) {
|
|
760
848
|
target.style.scrollMarginTop = "calc(var(--site-nav-height, 0px) + 2rem)";
|
|
761
849
|
target.scrollIntoView({ behavior: "smooth", block: "start", inline: "start" });
|
|
@@ -782,7 +870,7 @@
|
|
|
782
870
|
return { selector: targetStr, location: null };
|
|
783
871
|
};
|
|
784
872
|
const handleNonStandardHref = (e, { domToElement, wrapDomElement }) => {
|
|
785
|
-
var
|
|
873
|
+
var _a2;
|
|
786
874
|
const clickedEl = e.target.closest("[href]");
|
|
787
875
|
if (!clickedEl) return;
|
|
788
876
|
const tagName = clickedEl.tagName.toLowerCase();
|
|
@@ -790,7 +878,7 @@
|
|
|
790
878
|
e.preventDefault();
|
|
791
879
|
const href = clickedEl.getAttribute("href");
|
|
792
880
|
const LV = globalThis.Lightview;
|
|
793
|
-
if (href && (isDangerousProtocol(href) || ((
|
|
881
|
+
if (href && (isDangerousProtocol(href) || ((_a2 = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a2.validateUrl) && !LV.hooks.validateUrl(href))) {
|
|
794
882
|
console.warn(`[LightviewX] Navigation or fetch blocked by security policy: ${href}`);
|
|
795
883
|
return;
|
|
796
884
|
}
|
|
@@ -950,9 +1038,9 @@
|
|
|
950
1038
|
return { events, exclusions, calls };
|
|
951
1039
|
};
|
|
952
1040
|
const globalBeforeInterceptor = async (e) => {
|
|
953
|
-
var
|
|
1041
|
+
var _a2, _b2;
|
|
954
1042
|
if (e[BYPASS_FLAG]) return;
|
|
955
|
-
const target = (
|
|
1043
|
+
const target = (_b2 = (_a2 = e.target).closest) == null ? void 0 : _b2.call(_a2, "[lv-before]");
|
|
956
1044
|
if (!target) return;
|
|
957
1045
|
const { events, exclusions, calls } = parseBeforeAttribute(target.getAttribute("lv-before"));
|
|
958
1046
|
const isExcluded = exclusions.includes(e.type);
|
|
@@ -1348,7 +1436,7 @@
|
|
|
1348
1436
|
}).filter(Boolean);
|
|
1349
1437
|
}
|
|
1350
1438
|
render() {
|
|
1351
|
-
var
|
|
1439
|
+
var _a2, _b2;
|
|
1352
1440
|
const props = { useShadow: false };
|
|
1353
1441
|
for (const attr of this.attributes) {
|
|
1354
1442
|
const name = attr.name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
@@ -1372,7 +1460,7 @@
|
|
|
1372
1460
|
const vdomChildren = this.parseChildrenToVDOM();
|
|
1373
1461
|
const children = Object.keys(childElements).length > 0 ? vdomChildren : [{ tag: globalThis.Lightview.tags.slot }];
|
|
1374
1462
|
const result = Component(props, ...children);
|
|
1375
|
-
if (((
|
|
1463
|
+
if (((_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals) == null ? void 0 : _b2.setupChildren) && this.themeWrapper) {
|
|
1376
1464
|
this.themeWrapper.innerHTML = "";
|
|
1377
1465
|
globalThis.Lightview.internals.setupChildren([result], this.themeWrapper);
|
|
1378
1466
|
}
|
|
@@ -1385,6 +1473,89 @@
|
|
|
1385
1473
|
}
|
|
1386
1474
|
};
|
|
1387
1475
|
};
|
|
1476
|
+
const validateJSONSchema = (value, schema) => {
|
|
1477
|
+
var _a2;
|
|
1478
|
+
if (!schema) return true;
|
|
1479
|
+
const errors = [];
|
|
1480
|
+
const internals2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals;
|
|
1481
|
+
const check = (val, sch, path = "") => {
|
|
1482
|
+
var _a3;
|
|
1483
|
+
if (!sch) return true;
|
|
1484
|
+
if (typeof sch === "string") {
|
|
1485
|
+
const registered = (_a3 = internals2 == null ? void 0 : internals2.schemas) == null ? void 0 : _a3.get(sch);
|
|
1486
|
+
if (registered) return check(val, registered, path);
|
|
1487
|
+
return true;
|
|
1488
|
+
}
|
|
1489
|
+
const type = sch.type;
|
|
1490
|
+
const getType = (v) => {
|
|
1491
|
+
if (v === null) return "null";
|
|
1492
|
+
if (Array.isArray(v)) return "array";
|
|
1493
|
+
return typeof v;
|
|
1494
|
+
};
|
|
1495
|
+
const currentType = getType(val);
|
|
1496
|
+
if (type && type !== currentType) {
|
|
1497
|
+
if (type === "integer" && Number.isInteger(val)) ;
|
|
1498
|
+
else if (!(type === "number" && typeof val === "number")) {
|
|
1499
|
+
errors.push({ path, message: `Expected type ${type}, got ${currentType}`, keyword: "type" });
|
|
1500
|
+
return false;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
if (currentType === "string") {
|
|
1504
|
+
if (sch.minLength !== void 0 && val.length < sch.minLength) errors.push({ path, keyword: "minLength" });
|
|
1505
|
+
if (sch.maxLength !== void 0 && val.length > sch.maxLength) errors.push({ path, keyword: "maxLength" });
|
|
1506
|
+
if (sch.pattern !== void 0 && !new RegExp(sch.pattern).test(val)) errors.push({ path, keyword: "pattern" });
|
|
1507
|
+
if (sch.format === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) errors.push({ path, keyword: "format" });
|
|
1508
|
+
}
|
|
1509
|
+
if (currentType === "number") {
|
|
1510
|
+
if (sch.minimum !== void 0 && val < sch.minimum) errors.push({ path, keyword: "minimum" });
|
|
1511
|
+
if (sch.maximum !== void 0 && val > sch.maximum) errors.push({ path, keyword: "maximum" });
|
|
1512
|
+
if (sch.multipleOf !== void 0 && val % sch.multipleOf !== 0) errors.push({ path, keyword: "multipleOf" });
|
|
1513
|
+
}
|
|
1514
|
+
if (currentType === "object") {
|
|
1515
|
+
if (sch.required && Array.isArray(sch.required)) {
|
|
1516
|
+
for (const key of sch.required) {
|
|
1517
|
+
if (!(key in val)) errors.push({ path: path ? `${path}.${key}` : key, keyword: "required" });
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
if (sch.properties) {
|
|
1521
|
+
for (const key in sch.properties) {
|
|
1522
|
+
if (key in val) check(val[key], sch.properties[key], path ? `${path}.${key}` : key);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
if (sch.additionalProperties === false) {
|
|
1526
|
+
for (const key in val) {
|
|
1527
|
+
if (!sch.properties || !(key in sch.properties)) errors.push({ path: path ? `${path}.${key}` : key, keyword: "additionalProperties" });
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
if (currentType === "array") {
|
|
1532
|
+
if (sch.minItems !== void 0 && val.length < sch.minItems) errors.push({ path, keyword: "minItems" });
|
|
1533
|
+
if (sch.maxItems !== void 0 && val.length > sch.maxItems) errors.push({ path, keyword: "maxItems" });
|
|
1534
|
+
if (sch.uniqueItems && new Set(val).size !== val.length) errors.push({ path, keyword: "uniqueItems" });
|
|
1535
|
+
if (sch.items) {
|
|
1536
|
+
val.forEach((item, i) => check(item, sch.items, `${path}[${i}]`));
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
if (sch.const !== void 0 && val !== sch.const) errors.push({ path, keyword: "const" });
|
|
1540
|
+
if (sch.enum && !sch.enum.includes(val)) errors.push({ path, keyword: "enum" });
|
|
1541
|
+
return errors.length === 0;
|
|
1542
|
+
};
|
|
1543
|
+
const valid = check(value, schema);
|
|
1544
|
+
return valid || errors;
|
|
1545
|
+
};
|
|
1546
|
+
const lvInternals = globalThis.__LIGHTVIEW_INTERNALS__ || ((_a = globalThis.Lightview) == null ? void 0 : _a.internals);
|
|
1547
|
+
if (lvInternals) {
|
|
1548
|
+
const hooks2 = lvInternals.hooks || ((_b = globalThis.Lightview) == null ? void 0 : _b.hooks);
|
|
1549
|
+
if (hooks2) {
|
|
1550
|
+
hooks2.validate = (value, schema) => {
|
|
1551
|
+
const result = validateJSONSchema(value, schema);
|
|
1552
|
+
if (result === true) return true;
|
|
1553
|
+
const msg = result.map((e) => `${e.path || "root"}: failed ${e.keyword}${e.message ? " (" + e.message + ")" : ""}`).join(", ");
|
|
1554
|
+
throw new Error(`Lightview Validation Error: ${msg}`);
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
if (globalThis.Lightview) globalThis.Lightview.validate = validateJSONSchema;
|
|
1558
|
+
}
|
|
1388
1559
|
const LightviewX = {
|
|
1389
1560
|
state,
|
|
1390
1561
|
themeSignal,
|
|
@@ -1402,6 +1573,7 @@
|
|
|
1402
1573
|
preloadComponentCSS,
|
|
1403
1574
|
createCustomElement,
|
|
1404
1575
|
customElementWrapper,
|
|
1576
|
+
validate: validateJSONSchema,
|
|
1405
1577
|
internals: {
|
|
1406
1578
|
handleSrcAttribute,
|
|
1407
1579
|
parseElements
|