lightview 2.2.2 → 2.3.5

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 (43) hide show
  1. package/docs/about.html +15 -12
  2. package/docs/api/computed.html +1 -1
  3. package/docs/api/effects.html +1 -1
  4. package/docs/api/elements.html +56 -25
  5. package/docs/api/enhance.html +1 -1
  6. package/docs/api/hypermedia.html +1 -1
  7. package/docs/api/index.html +1 -1
  8. package/docs/api/nav.html +28 -3
  9. package/docs/api/signals.html +1 -1
  10. package/docs/api/state.html +283 -85
  11. package/docs/assets/js/examplify.js +2 -1
  12. package/docs/calculator.html +427 -0
  13. package/docs/cdom-nav.html +2 -1
  14. package/docs/cdom.html +382 -113
  15. package/jprx/README.md +113 -72
  16. package/jprx/helpers/calc.js +82 -0
  17. package/jprx/helpers/lookup.js +39 -0
  18. package/jprx/helpers/math.js +4 -0
  19. package/jprx/helpers/state.js +21 -0
  20. package/jprx/index.js +3 -0
  21. package/jprx/package.json +2 -2
  22. package/jprx/parser.js +171 -89
  23. package/jprx/specs/expressions.json +71 -0
  24. package/jprx/specs/helpers.json +150 -0
  25. package/lightview-all.js +2375 -487
  26. package/lightview-cdom.js +1997 -590
  27. package/lightview-router.js +6 -0
  28. package/lightview-x.js +226 -54
  29. package/lightview.js +351 -42
  30. package/package.json +3 -1
  31. package/src/lightview-cdom.js +213 -315
  32. package/src/lightview-router.js +10 -0
  33. package/src/lightview-x.js +121 -1
  34. package/src/lightview.js +88 -16
  35. package/src/reactivity/signal.js +73 -29
  36. package/src/reactivity/state.js +84 -21
  37. package/start-dev.js +1 -1
  38. package/tests/cdom/fixtures/helpers.cdomc +24 -24
  39. package/tests/cdom/helpers.test.js +28 -28
  40. package/tests/cdom/parser.test.js +39 -114
  41. package/tests/cdom/reactivity.test.js +32 -29
  42. package/tests/jprx/spec.test.js +99 -0
  43. package/tests/cdom/loader.test.js +0 -125
package/lightview-all.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
- dependencyMap: /* @__PURE__ */ new WeakMap()
7
- // Tracking signals -> subscribers
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
  });
9
- const signal = (initialValue, optionsOrName) => {
10
- let name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
23
+ const lookup$1 = (name, scope) => {
24
+ let current = scope;
25
+ while (current && typeof current === "object") {
26
+ const registry2 = _LV.localRegistries.get(current);
27
+ if (registry2 && registry2.has(name)) return registry2.get(name);
28
+ current = current.parentElement || _LV.parents.get(current);
29
+ }
30
+ return _LV.registry.get(name);
31
+ };
32
+ const signal$1 = (initialValue, optionsOrName) => {
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,23 +65,41 @@
46
65
  }
47
66
  });
48
67
  if (name) {
49
- if (_LV.registry.has(name)) {
50
- if (_LV.registry.get(name) !== f) {
51
- throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
52
- }
53
- } else {
54
- _LV.registry.set(name, f);
68
+ const registry2 = scope && typeof scope === "object" ? _LV.localRegistries.get(scope) || _LV.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : _LV.registry;
69
+ if (registry2 && registry2.has(name) && registry2.get(name) !== f) {
70
+ throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
71
+ }
72
+ if (registry2) registry2.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, defaultValue) => {
60
- if (!_LV.registry.has(name) && defaultValue !== void 0) {
61
- return signal(defaultValue, name);
62
- }
63
- return _LV.registry.get(name);
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$1(name, scope);
84
+ if (existing) return existing;
85
+ if (defaultValue !== void 0) return signal$1(defaultValue, { name, scope });
86
+ const future = signal$1(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
- signal.get = getSignal;
102
+ signal$1.get = getSignal;
66
103
  const effect = (fn) => {
67
104
  const execute = () => {
68
105
  if (!execute.active || execute.running) return;
@@ -89,16 +126,45 @@
89
126
  return execute;
90
127
  };
91
128
  const computed = (fn) => {
92
- const sig = signal(void 0);
129
+ const sig = signal$1(void 0);
93
130
  effect(() => {
94
131
  sig.value = fn();
95
132
  });
96
133
  return sig;
97
134
  };
98
135
  const getRegistry$1 = () => _LV.registry;
136
+ const internals = _LV;
99
137
  const stateCache = /* @__PURE__ */ new WeakMap();
100
138
  const stateSignals = /* @__PURE__ */ new WeakMap();
101
- const parents = /* @__PURE__ */ new WeakMap();
139
+ const stateSchemas = /* @__PURE__ */ new WeakMap();
140
+ const { parents, schemas, hooks } = internals;
141
+ const validate = (target, prop, value, schema) => {
142
+ var _a2, _b2;
143
+ const current = target[prop];
144
+ const type = typeof current;
145
+ const isNew = !(prop in target);
146
+ let behavior = schema;
147
+ if (typeof schema === "object" && schema !== null) behavior = schema.type;
148
+ if (behavior === "auto" && isNew) throw new Error(`Lightview: Cannot add new property "${prop}" to fixed 'auto' state.`);
149
+ if (behavior === "polymorphic" || typeof behavior === "object" && (behavior == null ? void 0 : behavior.coerce)) {
150
+ if (type === "number") return Number(value);
151
+ if (type === "boolean") return Boolean(value);
152
+ if (type === "string") return String(value);
153
+ } else if (behavior === "auto" || behavior === "dynamic") {
154
+ if (!isNew && typeof value !== type) {
155
+ throw new Error(`Lightview: Type mismatch for "${prop}". Expected ${type}, got ${typeof value}.`);
156
+ }
157
+ }
158
+ if (typeof schema === "object" && schema !== null && schema.transform) {
159
+ const trans = schema.transform;
160
+ const transformFn = typeof trans === "function" ? trans : internals.helpers.get(trans) || ((_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.helpers) == null ? void 0 : _b2[trans]);
161
+ if (transformFn) value = transformFn(value);
162
+ }
163
+ if (hooks.validate(value, schema) === false) {
164
+ throw new Error(`Lightview: Validation failed for "${prop}".`);
165
+ }
166
+ return value;
167
+ };
102
168
  const protoMethods = (proto, test) => Object.getOwnPropertyNames(proto).filter((k) => typeof proto[k] === "function" && test(k));
103
169
  const DATE_TRACKING = protoMethods(Date.prototype, (k) => /^(to|get|valueOf)/.test(k));
104
170
  const DATE_MUTATING = protoMethods(Date.prototype, (k) => /^set/.test(k));
@@ -138,31 +204,33 @@
138
204
  const proxyGet = (target, prop, receiver, signals) => {
139
205
  if (prop === "__parent__") return parents.get(receiver);
140
206
  if (!signals.has(prop)) {
141
- signals.set(prop, signal(Reflect.get(target, prop, receiver)));
207
+ signals.set(prop, signal$1(Reflect.get(target, prop, receiver)));
142
208
  }
143
- const signal$1 = signals.get(prop);
144
- const val = signal$1.value;
209
+ const signal2 = signals.get(prop);
210
+ const val = signal2.value;
145
211
  if (typeof val === "object" && val !== null) {
146
- const childProxy = state(val);
212
+ const childProxy = state$1(val);
147
213
  parents.set(childProxy, receiver);
148
214
  return childProxy;
149
215
  }
150
216
  return val;
151
217
  };
152
218
  const proxySet = (target, prop, value, receiver, signals) => {
219
+ const schema = stateSchemas.get(receiver);
220
+ const validatedValue = schema ? validate(target, prop, value, schema) : value;
153
221
  if (!signals.has(prop)) {
154
- signals.set(prop, signal(Reflect.get(target, prop, receiver)));
222
+ signals.set(prop, signal$1(Reflect.get(target, prop, receiver)));
155
223
  }
156
- const success = Reflect.set(target, prop, value, receiver);
157
- const signal$1 = signals.get(prop);
158
- if (success && signal$1) signal$1.value = value;
224
+ const success = Reflect.set(target, prop, validatedValue, receiver);
225
+ const signal2 = signals.get(prop);
226
+ if (success && signal2) signal2.value = validatedValue;
159
227
  return success;
160
228
  };
161
229
  const createSpecialProxy = (obj, monitor, trackingProps = []) => {
162
230
  const signals = getOrSet(stateSignals, obj, () => /* @__PURE__ */ new Map());
163
231
  if (!signals.has(monitor)) {
164
232
  const initialValue = typeof obj[monitor] === "function" ? obj[monitor].call(obj) : obj[monitor];
165
- signals.set(monitor, signal(initialValue));
233
+ signals.set(monitor, signal$1(initialValue));
166
234
  }
167
235
  const isDate = obj instanceof Date;
168
236
  const isArray = Array.isArray(obj);
@@ -184,7 +252,7 @@
184
252
  if (isArray && ARRAY_ITERATION.includes(prop) && typeof args[0] === "function") {
185
253
  const originalCallback = args[0];
186
254
  args[0] = function(element2, index2, array) {
187
- const wrappedElement = typeof element2 === "object" && element2 !== null ? state(element2) : element2;
255
+ const wrappedElement = typeof element2 === "object" && element2 !== null ? state$1(element2) : element2;
188
256
  if (wrappedElement && typeof wrappedElement === "object") {
189
257
  parents.set(wrappedElement, receiver);
190
258
  }
@@ -225,10 +293,12 @@
225
293
  }
226
294
  });
227
295
  };
228
- const state = (obj, optionsOrName) => {
296
+ const state$1 = (obj, optionsOrName) => {
229
297
  if (typeof obj !== "object" || obj === null) return obj;
230
298
  const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
231
299
  const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
300
+ const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
301
+ const schema = optionsOrName == null ? void 0 : optionsOrName.schema;
232
302
  if (name && storage) {
233
303
  try {
234
304
  const item = storage.getItem(name);
@@ -257,6 +327,7 @@
257
327
  stateCache.set(obj, proxy);
258
328
  } else return obj;
259
329
  }
330
+ if (schema) stateSchemas.set(proxy, schema);
260
331
  if (name && storage) {
261
332
  effect(() => {
262
333
  try {
@@ -266,25 +337,33 @@
266
337
  });
267
338
  }
268
339
  if (name) {
269
- const registry2 = getRegistry$1();
270
- if (registry2.has(name)) {
271
- if (registry2.get(name) !== proxy) {
272
- throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
273
- }
274
- } else {
275
- registry2.set(name, proxy);
340
+ const registry2 = scope && typeof scope === "object" ? internals.localRegistries.get(scope) || internals.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : getRegistry$1();
341
+ if (registry2 && registry2.has(name) && registry2.get(name) !== proxy) {
342
+ throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
343
+ }
344
+ if (registry2) registry2.set(name, proxy);
345
+ const futures = internals.futureSignals.get(name);
346
+ if (futures) {
347
+ futures.forEach((resolve) => resolve(proxy));
276
348
  }
277
349
  }
278
350
  return proxy;
279
351
  };
280
- const getState = (name, defaultValue) => {
281
- const registry2 = getRegistry$1();
282
- if (!registry2.has(name) && defaultValue !== void 0) {
283
- return state(defaultValue, name);
284
- }
285
- return registry2.get(name);
352
+ const getState = (name, defaultValueOrOptions) => {
353
+ const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
354
+ const { scope, defaultValue } = options;
355
+ const existing = lookup$1(name, scope);
356
+ if (existing) return existing;
357
+ if (defaultValue !== void 0) return state$1(defaultValue, { name, scope });
358
+ const future = signal$1(void 0);
359
+ const handler = (realState) => {
360
+ future.value = realState;
361
+ };
362
+ if (!internals.futureSignals.has(name)) internals.futureSignals.set(name, /* @__PURE__ */ new Set());
363
+ internals.futureSignals.get(name).add(handler);
364
+ return future;
286
365
  };
287
- state.get = getState;
366
+ state$1.get = getState;
288
367
  const core = {
289
368
  get currentEffect() {
290
369
  return (globalThis.__LIGHTVIEW_INTERNALS__ || (globalThis.__LIGHTVIEW_INTERNALS__ = {})).currentEffect;
@@ -293,6 +372,38 @@
293
372
  const nodeState = /* @__PURE__ */ new WeakMap();
294
373
  const nodeStateFactory = () => ({ effects: [], onmount: null, onunmount: null });
295
374
  const registry = getRegistry$1();
375
+ const scrollMemory = /* @__PURE__ */ new Map();
376
+ const initScrollMemory = () => {
377
+ if (typeof document === "undefined") return;
378
+ document.addEventListener("scroll", (e) => {
379
+ const el = e.target;
380
+ if (el === document || el === document.documentElement) return;
381
+ const key = el.id || el.getAttribute && el.getAttribute("data-preserve-scroll");
382
+ if (key) {
383
+ scrollMemory.set(key, { top: el.scrollTop, left: el.scrollLeft });
384
+ }
385
+ }, true);
386
+ };
387
+ if (typeof document !== "undefined") {
388
+ if (document.readyState === "loading") {
389
+ document.addEventListener("DOMContentLoaded", initScrollMemory);
390
+ } else {
391
+ initScrollMemory();
392
+ }
393
+ }
394
+ const saveScrolls = () => new Map(scrollMemory);
395
+ const restoreScrolls = (map2, root = document) => {
396
+ if (!map2 || map2.size === 0) return;
397
+ requestAnimationFrame(() => {
398
+ map2.forEach((pos, key) => {
399
+ const node = document.getElementById(key) || document.querySelector(`[data-preserve-scroll="${key}"]`);
400
+ if (node) {
401
+ node.scrollTop = pos.top;
402
+ node.scrollLeft = pos.left;
403
+ }
404
+ });
405
+ });
406
+ };
296
407
  const trackEffect = (node, effectFn) => {
297
408
  const state2 = getOrSet(nodeState, node, nodeStateFactory);
298
409
  if (!state2.effects) state2.effects = [];
@@ -490,6 +601,26 @@
490
601
  domNode.setAttribute(key, value);
491
602
  }
492
603
  reactiveAttrs[key] = value;
604
+ } else if (typeof value === "object" && value !== null && Lightview.hooks.processAttribute) {
605
+ const processed = Lightview.hooks.processAttribute(domNode, key, value);
606
+ if (processed !== void 0) {
607
+ reactiveAttrs[key] = processed;
608
+ } else if (key === "style") {
609
+ Object.entries(value).forEach(([styleKey, styleValue]) => {
610
+ if (typeof styleValue === "function") {
611
+ const runner = effect(() => {
612
+ domNode.style[styleKey] = styleValue();
613
+ });
614
+ trackEffect(domNode, runner);
615
+ } else {
616
+ domNode.style[styleKey] = styleValue;
617
+ }
618
+ });
619
+ reactiveAttrs[key] = value;
620
+ } else {
621
+ setAttributeValue(domNode, key, value);
622
+ reactiveAttrs[key] = value;
623
+ }
493
624
  } else if (typeof value === "function") {
494
625
  const runner = effect(() => {
495
626
  const result = value();
@@ -501,18 +632,6 @@
501
632
  });
502
633
  trackEffect(domNode, runner);
503
634
  reactiveAttrs[key] = value;
504
- } else if (key === "style" && typeof value === "object") {
505
- Object.entries(value).forEach(([styleKey, styleValue]) => {
506
- if (typeof styleValue === "function") {
507
- const runner = effect(() => {
508
- domNode.style[styleKey] = styleValue();
509
- });
510
- trackEffect(domNode, runner);
511
- } else {
512
- domNode.style[styleKey] = styleValue;
513
- }
514
- });
515
- reactiveAttrs[key] = value;
516
635
  } else {
517
636
  setAttributeValue(domNode, key, value);
518
637
  reactiveAttrs[key] = value;
@@ -691,8 +810,11 @@
691
810
  }
692
811
  });
693
812
  const Lightview = {
694
- signal,
695
- get: signal.get,
813
+ state: state$1,
814
+ getState,
815
+ registerSchema: (name, definition) => internals.schemas.set(name, definition),
816
+ signal: signal$1,
817
+ get: signal$1.get,
696
818
  computed,
697
819
  effect,
698
820
  registry,
@@ -705,14 +827,24 @@
705
827
  hooks: {
706
828
  onNonStandardHref: null,
707
829
  processChild: null,
708
- validateUrl: null
830
+ processAttribute: null,
831
+ validateUrl: null,
832
+ validate: (value, schema) => internals.hooks.validate(value, schema)
709
833
  },
710
834
  // Internals exposed for extensions
711
835
  internals: {
712
836
  core,
713
837
  domToElement,
714
838
  wrapDomElement,
715
- setupChildren
839
+ setupChildren,
840
+ trackEffect,
841
+ saveScrolls,
842
+ restoreScrolls,
843
+ localRegistries: internals.localRegistries,
844
+ futureSignals: internals.futureSignals,
845
+ schemas: internals.schemas,
846
+ parents: internals.parents,
847
+ hooks: internals.hooks
716
848
  }
717
849
  };
718
850
  if (typeof module !== "undefined" && module.exports) {
@@ -723,8 +855,8 @@
723
855
  globalThis.addEventListener("click", (e) => {
724
856
  const path = e.composedPath();
725
857
  const link = path.find((el) => {
726
- var _a, _b;
727
- return el.tagName === "A" && ((_b = (_a = el.getAttribute) == null ? void 0 : _a.call(el, "href")) == null ? void 0 : _b.startsWith("#"));
858
+ var _a2, _b2;
859
+ return el.tagName === "A" && ((_b2 = (_a2 = el.getAttribute) == null ? void 0 : _a2.call(el, "href")) == null ? void 0 : _b2.startsWith("#"));
728
860
  });
729
861
  if (link && !e.defaultPrevented) {
730
862
  const href = link.getAttribute("href");
@@ -749,23 +881,23 @@
749
881
  });
750
882
  if (typeof MutationObserver !== "undefined") {
751
883
  const walkNodes = (node, fn) => {
752
- var _a;
884
+ var _a2;
753
885
  fn(node);
754
- (_a = node.childNodes) == null ? void 0 : _a.forEach((n) => walkNodes(n, fn));
886
+ (_a2 = node.childNodes) == null ? void 0 : _a2.forEach((n) => walkNodes(n, fn));
755
887
  if (node.shadowRoot) walkNodes(node.shadowRoot, fn);
756
888
  };
757
889
  const cleanupNode = (node) => walkNodes(node, (n) => {
758
- var _a, _b;
890
+ var _a2, _b2;
759
891
  const s = nodeState.get(n);
760
892
  if (s) {
761
- (_a = s.effects) == null ? void 0 : _a.forEach((e) => e.stop());
762
- (_b = s.onunmount) == null ? void 0 : _b.call(s, n);
893
+ (_a2 = s.effects) == null ? void 0 : _a2.forEach((e) => e.stop());
894
+ (_b2 = s.onunmount) == null ? void 0 : _b2.call(s, n);
763
895
  nodeState.delete(n);
764
896
  }
765
897
  });
766
898
  const mountNode = (node) => walkNodes(node, (n) => {
767
- var _a, _b;
768
- (_b = (_a = nodeState.get(n)) == null ? void 0 : _a.onmount) == null ? void 0 : _b.call(_a, n);
899
+ var _a2, _b2;
900
+ (_b2 = (_a2 = nodeState.get(n)) == null ? void 0 : _a2.onmount) == null ? void 0 : _b2.call(_a2, n);
769
901
  });
770
902
  const observer = new MutationObserver((mutations) => {
771
903
  mutations.forEach((mutation) => {
@@ -819,7 +951,7 @@
819
951
  return keys.length === 1 && isValidTagName(keys[0]) && typeof obj[keys[0]] === "object";
820
952
  };
821
953
  const convertObjectDOM = (obj) => {
822
- var _a, _b;
954
+ var _a2, _b2;
823
955
  if (typeof obj !== "object" || obj === null) return obj;
824
956
  if (Array.isArray(obj)) return obj.map(convertObjectDOM);
825
957
  if (obj.tag) return { ...obj, children: obj.children ? convertObjectDOM(obj.children) : [] };
@@ -827,7 +959,7 @@
827
959
  const tagKey = Object.keys(obj)[0];
828
960
  const content = obj[tagKey];
829
961
  const LV = typeof window !== "undefined" ? globalThis.Lightview : typeof globalThis !== "undefined" ? globalThis.Lightview : null;
830
- const tag = ((_b = (_a = LV == null ? void 0 : LV.tags) == null ? void 0 : _a._customTags) == null ? void 0 : _b[tagKey]) || tagKey;
962
+ const tag = ((_b2 = (_a2 = LV == null ? void 0 : LV.tags) == null ? void 0 : _a2._customTags) == null ? void 0 : _b2[tagKey]) || tagKey;
831
963
  const { children, ...attributes } = content;
832
964
  return { tag, attributes, children: children ? convertObjectDOM(children) : [] };
833
965
  };
@@ -889,7 +1021,7 @@
889
1021
  return null;
890
1022
  }
891
1023
  };
892
- const themeSignal = signal(
1024
+ const themeSignal = signal$1(
893
1025
  typeof document !== "undefined" && document.documentElement.getAttribute("data-theme") || getSavedTheme() || "light"
894
1026
  );
895
1027
  const setTheme = (themeName) => {
@@ -1115,12 +1247,12 @@
1115
1247
  const frag = document.createDocumentFragment();
1116
1248
  frag.appendChild(createMarker(markerId, false));
1117
1249
  elements.forEach((c) => {
1118
- var _a, _b, _c;
1250
+ var _a2, _b2, _c;
1119
1251
  if (typeof c === "string") frag.appendChild(document.createTextNode(c));
1120
1252
  else if (c.domEl) frag.appendChild(c.domEl);
1121
1253
  else if (c instanceof Node) frag.appendChild(c);
1122
1254
  else {
1123
- const v = ((_c = (_a = globalThis.Lightview) == null ? void 0 : (_b = _a.hooks).processChild) == null ? void 0 : _c.call(_b, c)) || c;
1255
+ const v = ((_c = (_a2 = globalThis.Lightview) == null ? void 0 : (_b2 = _a2.hooks).processChild) == null ? void 0 : _c.call(_b2, c)) || c;
1124
1256
  if (v.tag) {
1125
1257
  const n = element2(v.tag, v.attributes || {}, v.children || []);
1126
1258
  if (n == null ? void 0 : n.domEl) frag.appendChild(n.domEl);
@@ -1137,10 +1269,10 @@
1137
1269
  };
1138
1270
  const isPath = (s) => typeof s === "string" && !isDangerousProtocol(s) && /^(https?:|\.|\/|[\w])|(\.(html|json|[vo]dom|cdomc?))$/i.test(s);
1139
1271
  const fetchContent = async (src) => {
1140
- var _a;
1272
+ var _a2;
1141
1273
  try {
1142
1274
  const LV = globalThis.Lightview;
1143
- if (((_a = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a.validateUrl) && !LV.hooks.validateUrl(src)) {
1275
+ if (((_a2 = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a2.validateUrl) && !LV.hooks.validateUrl(src)) {
1144
1276
  console.warn(`[LightviewX] Fetch blocked by validateUrl hook: ${src}`);
1145
1277
  return null;
1146
1278
  }
@@ -1165,10 +1297,10 @@
1165
1297
  }
1166
1298
  };
1167
1299
  const parseElements = (content, isJson, isHtml, el, element2, isCdom = false, ext = "") => {
1168
- var _a;
1300
+ var _a2;
1169
1301
  if (isJson) return Array.isArray(content) ? content : [content];
1170
1302
  if (isCdom && ext === "cdomc") {
1171
- const parser = (_a = globalThis.LightviewCDOM) == null ? void 0 : _a.parseCDOMC;
1303
+ const parser = (_a2 = globalThis.LightviewCDOM) == null ? void 0 : _a2.parseCDOMC;
1172
1304
  if (parser) {
1173
1305
  try {
1174
1306
  const obj = parser(content);
@@ -1201,11 +1333,14 @@
1201
1333
  return null;
1202
1334
  }
1203
1335
  };
1204
- const updateTargetContent = (el, elements, raw, loc, contentHash, { element: element2, setupChildren: setupChildren2 }, targetHash = null) => {
1336
+ const updateTargetContent = (el, elements, raw, loc, contentHash, options, targetHash = null) => {
1337
+ var _a2;
1338
+ const { element: element2, setupChildren: setupChildren2, saveScrolls: saveScrolls2, restoreScrolls: restoreScrolls2 } = { ...options, ...(_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals };
1205
1339
  const markerId = `${loc}-${contentHash.slice(0, 8)}`;
1206
1340
  let track = getOrSet(insertedContentMap, el.domEl, () => ({}));
1207
1341
  if (track[loc]) removeInsertedContent(el.domEl, `${loc}-${track[loc].slice(0, 8)}`);
1208
1342
  track[loc] = contentHash;
1343
+ const scrollMap = saveScrolls2 ? saveScrolls2() : null;
1209
1344
  const performScroll = (root) => {
1210
1345
  if (!targetHash) return;
1211
1346
  requestAnimationFrame(() => {
@@ -1219,18 +1354,24 @@
1219
1354
  });
1220
1355
  });
1221
1356
  };
1357
+ const runRestore = (root) => {
1358
+ if (restoreScrolls2 && scrollMap) restoreScrolls2(scrollMap, root);
1359
+ };
1222
1360
  if (loc === "shadow") {
1223
1361
  if (!el.domEl.shadowRoot) el.domEl.attachShadow({ mode: "open" });
1224
1362
  setupChildren2(elements, el.domEl.shadowRoot);
1225
1363
  executeScripts(el.domEl.shadowRoot);
1226
1364
  performScroll(el.domEl.shadowRoot);
1365
+ runRestore(el.domEl.shadowRoot);
1227
1366
  } else if (loc === "innerhtml") {
1228
1367
  el.children = elements;
1229
1368
  executeScripts(el.domEl);
1230
1369
  performScroll(document);
1370
+ runRestore(el.domEl);
1231
1371
  } else {
1232
1372
  insert(elements, el.domEl, loc, markerId, { element: element2, setupChildren: setupChildren2 });
1233
1373
  performScroll(document);
1374
+ runRestore(el.domEl);
1234
1375
  }
1235
1376
  };
1236
1377
  const handleSrcAttribute = async (el, src, tagName, { element: element2, setupChildren: setupChildren2 }) => {
@@ -1263,9 +1404,9 @@
1263
1404
  if (root) {
1264
1405
  requestAnimationFrame(() => {
1265
1406
  requestAnimationFrame(() => {
1266
- var _a;
1407
+ var _a2;
1267
1408
  const id = targetHash.startsWith("#") ? targetHash.slice(1) : targetHash;
1268
- const target = root.getElementById ? root.getElementById(id) : (_a = root.querySelector) == null ? void 0 : _a.call(root, `#${id}`);
1409
+ const target = root.getElementById ? root.getElementById(id) : (_a2 = root.querySelector) == null ? void 0 : _a2.call(root, `#${id}`);
1269
1410
  if (target) {
1270
1411
  target.style.scrollMarginTop = "calc(var(--site-nav-height, 0px) + 2rem)";
1271
1412
  target.scrollIntoView({ behavior: "smooth", block: "start", inline: "start" });
@@ -1292,7 +1433,7 @@
1292
1433
  return { selector: targetStr, location: null };
1293
1434
  };
1294
1435
  const handleNonStandardHref = (e, { domToElement: domToElement2, wrapDomElement: wrapDomElement2 }) => {
1295
- var _a;
1436
+ var _a2;
1296
1437
  const clickedEl = e.target.closest("[href]");
1297
1438
  if (!clickedEl) return;
1298
1439
  const tagName = clickedEl.tagName.toLowerCase();
@@ -1300,7 +1441,7 @@
1300
1441
  e.preventDefault();
1301
1442
  const href = clickedEl.getAttribute("href");
1302
1443
  const LV = globalThis.Lightview;
1303
- if (href && (isDangerousProtocol(href) || ((_a = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a.validateUrl) && !LV.hooks.validateUrl(href))) {
1444
+ if (href && (isDangerousProtocol(href) || ((_a2 = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a2.validateUrl) && !LV.hooks.validateUrl(href))) {
1304
1445
  console.warn(`[LightviewX] Navigation or fetch blocked by security policy: ${href}`);
1305
1446
  return;
1306
1447
  }
@@ -1460,9 +1601,9 @@
1460
1601
  return { events, exclusions, calls };
1461
1602
  };
1462
1603
  const globalBeforeInterceptor = async (e) => {
1463
- var _a, _b;
1604
+ var _a2, _b2;
1464
1605
  if (e[BYPASS_FLAG]) return;
1465
- const target = (_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, "[lv-before]");
1606
+ const target = (_b2 = (_a2 = e.target).closest) == null ? void 0 : _b2.call(_a2, "[lv-before]");
1466
1607
  if (!target) return;
1467
1608
  const { events, exclusions, calls } = parseBeforeAttribute(target.getAttribute("lv-before"));
1468
1609
  const isExcluded = exclusions.includes(e.type);
@@ -1677,7 +1818,7 @@
1677
1818
  }
1678
1819
  }
1679
1820
  return processTemplateChild(child, {
1680
- state,
1821
+ state: state$1,
1681
1822
  signal: LV.signal
1682
1823
  });
1683
1824
  };
@@ -1858,7 +1999,7 @@
1858
1999
  }).filter(Boolean);
1859
2000
  }
1860
2001
  render() {
1861
- var _a, _b;
2002
+ var _a2, _b2;
1862
2003
  const props = { useShadow: false };
1863
2004
  for (const attr of this.attributes) {
1864
2005
  const name = attr.name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
@@ -1882,7 +2023,7 @@
1882
2023
  const vdomChildren = this.parseChildrenToVDOM();
1883
2024
  const children = Object.keys(childElements).length > 0 ? vdomChildren : [{ tag: globalThis.Lightview.tags.slot }];
1884
2025
  const result = Component(props, ...children);
1885
- if (((_b = (_a = globalThis.Lightview) == null ? void 0 : _a.internals) == null ? void 0 : _b.setupChildren) && this.themeWrapper) {
2026
+ if (((_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals) == null ? void 0 : _b2.setupChildren) && this.themeWrapper) {
1886
2027
  this.themeWrapper.innerHTML = "";
1887
2028
  globalThis.Lightview.internals.setupChildren([result], this.themeWrapper);
1888
2029
  }
@@ -1895,8 +2036,91 @@
1895
2036
  }
1896
2037
  };
1897
2038
  };
2039
+ const validateJSONSchema = (value, schema) => {
2040
+ var _a2;
2041
+ if (!schema) return true;
2042
+ const errors = [];
2043
+ const internals2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals;
2044
+ const check = (val, sch, path = "") => {
2045
+ var _a3;
2046
+ if (!sch) return true;
2047
+ if (typeof sch === "string") {
2048
+ const registered = (_a3 = internals2 == null ? void 0 : internals2.schemas) == null ? void 0 : _a3.get(sch);
2049
+ if (registered) return check(val, registered, path);
2050
+ return true;
2051
+ }
2052
+ const type = sch.type;
2053
+ const getType = (v) => {
2054
+ if (v === null) return "null";
2055
+ if (Array.isArray(v)) return "array";
2056
+ return typeof v;
2057
+ };
2058
+ const currentType = getType(val);
2059
+ if (type && type !== currentType) {
2060
+ if (type === "integer" && Number.isInteger(val)) ;
2061
+ else if (!(type === "number" && typeof val === "number")) {
2062
+ errors.push({ path, message: `Expected type ${type}, got ${currentType}`, keyword: "type" });
2063
+ return false;
2064
+ }
2065
+ }
2066
+ if (currentType === "string") {
2067
+ if (sch.minLength !== void 0 && val.length < sch.minLength) errors.push({ path, keyword: "minLength" });
2068
+ if (sch.maxLength !== void 0 && val.length > sch.maxLength) errors.push({ path, keyword: "maxLength" });
2069
+ if (sch.pattern !== void 0 && !new RegExp(sch.pattern).test(val)) errors.push({ path, keyword: "pattern" });
2070
+ if (sch.format === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) errors.push({ path, keyword: "format" });
2071
+ }
2072
+ if (currentType === "number") {
2073
+ if (sch.minimum !== void 0 && val < sch.minimum) errors.push({ path, keyword: "minimum" });
2074
+ if (sch.maximum !== void 0 && val > sch.maximum) errors.push({ path, keyword: "maximum" });
2075
+ if (sch.multipleOf !== void 0 && val % sch.multipleOf !== 0) errors.push({ path, keyword: "multipleOf" });
2076
+ }
2077
+ if (currentType === "object") {
2078
+ if (sch.required && Array.isArray(sch.required)) {
2079
+ for (const key of sch.required) {
2080
+ if (!(key in val)) errors.push({ path: path ? `${path}.${key}` : key, keyword: "required" });
2081
+ }
2082
+ }
2083
+ if (sch.properties) {
2084
+ for (const key in sch.properties) {
2085
+ if (key in val) check(val[key], sch.properties[key], path ? `${path}.${key}` : key);
2086
+ }
2087
+ }
2088
+ if (sch.additionalProperties === false) {
2089
+ for (const key in val) {
2090
+ if (!sch.properties || !(key in sch.properties)) errors.push({ path: path ? `${path}.${key}` : key, keyword: "additionalProperties" });
2091
+ }
2092
+ }
2093
+ }
2094
+ if (currentType === "array") {
2095
+ if (sch.minItems !== void 0 && val.length < sch.minItems) errors.push({ path, keyword: "minItems" });
2096
+ if (sch.maxItems !== void 0 && val.length > sch.maxItems) errors.push({ path, keyword: "maxItems" });
2097
+ if (sch.uniqueItems && new Set(val).size !== val.length) errors.push({ path, keyword: "uniqueItems" });
2098
+ if (sch.items) {
2099
+ val.forEach((item, i) => check(item, sch.items, `${path}[${i}]`));
2100
+ }
2101
+ }
2102
+ if (sch.const !== void 0 && val !== sch.const) errors.push({ path, keyword: "const" });
2103
+ if (sch.enum && !sch.enum.includes(val)) errors.push({ path, keyword: "enum" });
2104
+ return errors.length === 0;
2105
+ };
2106
+ const valid = check(value, schema);
2107
+ return valid || errors;
2108
+ };
2109
+ const lvInternals = globalThis.__LIGHTVIEW_INTERNALS__ || ((_a = globalThis.Lightview) == null ? void 0 : _a.internals);
2110
+ if (lvInternals) {
2111
+ const hooks2 = lvInternals.hooks || ((_b = globalThis.Lightview) == null ? void 0 : _b.hooks);
2112
+ if (hooks2) {
2113
+ hooks2.validate = (value, schema) => {
2114
+ const result = validateJSONSchema(value, schema);
2115
+ if (result === true) return true;
2116
+ const msg = result.map((e) => `${e.path || "root"}: failed ${e.keyword}${e.message ? " (" + e.message + ")" : ""}`).join(", ");
2117
+ throw new Error(`Lightview Validation Error: ${msg}`);
2118
+ };
2119
+ }
2120
+ if (globalThis.Lightview) globalThis.Lightview.validate = validateJSONSchema;
2121
+ }
1898
2122
  const LightviewX = {
1899
- state,
2123
+ state: state$1,
1900
2124
  themeSignal,
1901
2125
  setTheme,
1902
2126
  registerStyleSheet,
@@ -1912,6 +2136,7 @@
1912
2136
  preloadComponentCSS,
1913
2137
  createCustomElement,
1914
2138
  customElementWrapper,
2139
+ validate: validateJSONSchema,
1915
2140
  internals: {
1916
2141
  handleSrcAttribute,
1917
2142
  parseElements
@@ -1959,23 +2184,26 @@
1959
2184
  };
1960
2185
  const registerHelper = (name, fn, options = {}) => {
1961
2186
  helpers.set(name, fn);
2187
+ if (globalThis.__LIGHTVIEW_INTERNALS__) {
2188
+ globalThis.__LIGHTVIEW_INTERNALS__.helpers.set(name, fn);
2189
+ }
1962
2190
  if (options) helperOptions.set(name, options);
1963
2191
  };
1964
2192
  const registerOperator = (helperName, symbol, position, precedence) => {
1965
- var _a;
2193
+ var _a2;
1966
2194
  if (!["prefix", "postfix", "infix"].includes(position)) {
1967
2195
  throw new Error(`Invalid operator position: ${position}. Must be 'prefix', 'postfix', or 'infix'.`);
1968
2196
  }
1969
2197
  if (!helpers.has(helperName)) {
1970
- (_a = globalThis.console) == null ? void 0 : _a.warn(`LightviewCDOM: Operator "${symbol}" registered for helper "${helperName}" which is not yet registered.`);
2198
+ (_a2 = globalThis.console) == null ? void 0 : _a2.warn(`LightviewCDOM: Operator "${symbol}" registered for helper "${helperName}" which is not yet registered.`);
1971
2199
  }
1972
2200
  const prec = precedence ?? DEFAULT_PRECEDENCE[position];
1973
2201
  operators[position].set(symbol, { helper: helperName, precedence: prec });
1974
2202
  };
1975
2203
  const getLV = () => globalThis.Lightview || null;
1976
2204
  const getRegistry = () => {
1977
- var _a;
1978
- return ((_a = getLV()) == null ? void 0 : _a.registry) || null;
2205
+ var _a2;
2206
+ return ((_a2 = getLV()) == null ? void 0 : _a2.registry) || null;
1979
2207
  };
1980
2208
  class BindingTarget {
1981
2209
  constructor(parent, key) {
@@ -2032,19 +2260,12 @@
2032
2260
  if (typeof path !== "string") return path;
2033
2261
  const registry2 = getRegistry();
2034
2262
  if (path === ".") return unwrapSignal(context);
2035
- if (path.startsWith("$/")) {
2263
+ if (path.startsWith("=/")) {
2036
2264
  const [rootName, ...rest] = path.slice(2).split("/");
2037
- let cur = context;
2038
- while (cur) {
2039
- const localState = cur.__state__;
2040
- if (localState && rootName in localState) {
2041
- return traverse(localState[rootName], rest);
2042
- }
2043
- cur = cur.__parent__;
2044
- }
2045
- const rootSignal = registry2 == null ? void 0 : registry2.get(rootName);
2046
- if (!rootSignal) return void 0;
2047
- return traverse(rootSignal, rest);
2265
+ const LV = getLV();
2266
+ const root = LV ? LV.get(rootName, { scope: (context == null ? void 0 : context.__node__) || context }) : registry2 == null ? void 0 : registry2.get(rootName);
2267
+ if (!root) return void 0;
2268
+ return traverse(root, rest);
2048
2269
  }
2049
2270
  if (path.startsWith("./")) {
2050
2271
  return traverse(context, path.slice(2).split("/"));
@@ -2067,20 +2288,13 @@
2067
2288
  if (typeof path !== "string") return path;
2068
2289
  const registry2 = getRegistry();
2069
2290
  if (path === ".") return context;
2070
- if (path.startsWith("$/")) {
2291
+ if (path.startsWith("=/")) {
2071
2292
  const segments = path.slice(2).split(/[/.]/);
2072
2293
  const rootName = segments.shift();
2073
- let cur = context;
2074
- while (cur) {
2075
- const localState = cur.__state__;
2076
- if (localState && rootName in localState) {
2077
- return traverseAsContext(localState[rootName], segments);
2078
- }
2079
- cur = cur.__parent__;
2080
- }
2081
- const rootSignal = registry2 == null ? void 0 : registry2.get(rootName);
2082
- if (!rootSignal) return void 0;
2083
- return traverseAsContext(rootSignal, segments);
2294
+ const LV = getLV();
2295
+ const root = LV ? LV.get(rootName, { scope: (context == null ? void 0 : context.__node__) || context }) : registry2 == null ? void 0 : registry2.get(rootName);
2296
+ if (!root) return void 0;
2297
+ return traverseAsContext(root, segments);
2084
2298
  }
2085
2299
  if (path.startsWith("./")) {
2086
2300
  return traverseAsContext(context, path.slice(2).split(/[\/.]/));
@@ -2108,6 +2322,7 @@
2108
2322
  return this.fn(context);
2109
2323
  }
2110
2324
  }
2325
+ const isNode = (val) => val && typeof val === "object" && globalThis.Node && val instanceof globalThis.Node;
2111
2326
  const resolveArgument = (arg, context, globalMode = false) => {
2112
2327
  if (arg.startsWith("'") && arg.endsWith("'") || arg.startsWith('"') && arg.endsWith('"')) {
2113
2328
  return { value: arg.slice(1, -1), isLiteral: true };
@@ -2128,9 +2343,21 @@
2128
2343
  isLazy: true
2129
2344
  };
2130
2345
  }
2346
+ if (arg === "$this" || arg.startsWith("$this/") || arg.startsWith("$this.")) {
2347
+ return {
2348
+ value: new LazyValue((context2) => {
2349
+ const node = (context2 == null ? void 0 : context2.__node__) || context2;
2350
+ if (arg === "$this") return node;
2351
+ const path = arg.startsWith("$this.") ? arg.slice(6) : arg.slice(6);
2352
+ return resolvePath(path, node);
2353
+ }),
2354
+ isLazy: true
2355
+ };
2356
+ }
2131
2357
  if (arg === "$event" || arg.startsWith("$event/") || arg.startsWith("$event.")) {
2132
2358
  return {
2133
- value: new LazyValue((event) => {
2359
+ value: new LazyValue((context2) => {
2360
+ const event = (context2 == null ? void 0 : context2.$event) || (context2 == null ? void 0 : context2.event) || context2;
2134
2361
  if (arg === "$event") return event;
2135
2362
  const path = arg.startsWith("$event.") ? arg.slice(7) : arg.slice(7);
2136
2363
  return resolvePath(path, event);
@@ -2143,11 +2370,23 @@
2143
2370
  const data = parseJPRX(arg);
2144
2371
  const resolveTemplate = (node, context2) => {
2145
2372
  if (typeof node === "string") {
2146
- if (node.startsWith("$")) {
2147
- const res = resolveExpression(node, context2);
2373
+ if (node.startsWith("=")) {
2374
+ const res = resolveExpression$1(node, context2);
2148
2375
  const final = res instanceof LazyValue ? res.resolve(context2) : res;
2149
2376
  return unwrapSignal(final);
2150
2377
  }
2378
+ if (node === "$this" || node.startsWith("$this/") || node.startsWith("$this.")) {
2379
+ const path = node.startsWith("$this.") || node.startsWith("$this/") ? node.slice(6) : node.slice(6);
2380
+ const ctxNode = (context2 == null ? void 0 : context2.__node__) || context2;
2381
+ const res = node === "$this" ? ctxNode : resolvePath(path, ctxNode);
2382
+ return unwrapSignal(res);
2383
+ }
2384
+ if (node === "$event" || node.startsWith("$event/") || node.startsWith("$event.")) {
2385
+ const path = node.startsWith("$event.") || node.startsWith("$event/") ? node.slice(7) : node.slice(7);
2386
+ const event = (context2 == null ? void 0 : context2.$event) || (context2 == null ? void 0 : context2.event) || (context2 && !isNode(context2) ? context2 : null);
2387
+ const res = node === "$event" ? event : resolvePath(path, event);
2388
+ return unwrapSignal(res);
2389
+ }
2151
2390
  if (node === "_" || node.startsWith("_/") || node.startsWith("_.")) {
2152
2391
  const path = node.startsWith("_.") || node.startsWith("_/") ? node.slice(2) : node.slice(2);
2153
2392
  const res = node === "_" ? context2 : resolvePath(path, context2);
@@ -2165,7 +2404,7 @@
2165
2404
  };
2166
2405
  const hasReactive = (obj) => {
2167
2406
  if (typeof obj === "string") {
2168
- return obj.startsWith("$") || obj.startsWith("_") || obj.startsWith("../");
2407
+ return obj.startsWith("=") || obj.startsWith("_") || obj.startsWith("../");
2169
2408
  }
2170
2409
  if (Array.isArray(obj)) return obj.some(hasReactive);
2171
2410
  if (obj && typeof obj === "object") return Object.values(obj).some(hasReactive);
@@ -2184,11 +2423,11 @@
2184
2423
  if (arg.includes("(")) {
2185
2424
  let nestedExpr = arg;
2186
2425
  if (arg.startsWith("/")) {
2187
- nestedExpr = "$" + arg;
2188
- } else if (globalMode && !arg.startsWith("$") && !arg.startsWith("./")) {
2189
- nestedExpr = `$/${arg}`;
2426
+ nestedExpr = "=" + arg;
2427
+ } else if (globalMode && !arg.startsWith("=") && !arg.startsWith("./")) {
2428
+ nestedExpr = `=/${arg}`;
2190
2429
  }
2191
- const val = resolveExpression(nestedExpr, context);
2430
+ const val = resolveExpression$1(nestedExpr, context);
2192
2431
  if (val instanceof LazyValue) {
2193
2432
  return { value: val, isLazy: true };
2194
2433
  }
@@ -2196,11 +2435,11 @@
2196
2435
  }
2197
2436
  let normalizedPath;
2198
2437
  if (arg.startsWith("/")) {
2199
- normalizedPath = "$" + arg;
2200
- } else if (arg.startsWith("$") || arg.startsWith("./") || arg.startsWith("../")) {
2438
+ normalizedPath = "=" + arg;
2439
+ } else if (arg.startsWith("=") || arg.startsWith("./") || arg.startsWith("../")) {
2201
2440
  normalizedPath = arg;
2202
2441
  } else if (globalMode) {
2203
- normalizedPath = `$/${arg}`;
2442
+ normalizedPath = `=/${arg}`;
2204
2443
  } else {
2205
2444
  normalizedPath = `./${arg}`;
2206
2445
  }
@@ -2245,6 +2484,8 @@
2245
2484
  // ... suffix
2246
2485
  PLACEHOLDER: "PLACEHOLDER",
2247
2486
  // _, _/path
2487
+ THIS: "THIS",
2488
+ // $this
2248
2489
  EVENT: "EVENT",
2249
2490
  // $event, $event.target
2250
2491
  EOF: "EOF"
@@ -2267,15 +2508,16 @@
2267
2508
  i++;
2268
2509
  continue;
2269
2510
  }
2270
- if (expr[i] === "$" && i + 1 < len2) {
2271
- let isOpAfter = false;
2272
- for (const op of opSymbols) {
2511
+ if (expr[i] === "=" && i + 1 < len2) {
2512
+ const prefixOps = [...operators.prefix.keys()].sort((a, b) => b.length - a.length);
2513
+ let isPrefixOp = false;
2514
+ for (const op of prefixOps) {
2273
2515
  if (expr.slice(i + 1, i + 1 + op.length) === op) {
2274
- isOpAfter = true;
2516
+ isPrefixOp = true;
2275
2517
  break;
2276
2518
  }
2277
2519
  }
2278
- if (isOpAfter) {
2520
+ if (isPrefixOp) {
2279
2521
  i++;
2280
2522
  continue;
2281
2523
  }
@@ -2311,7 +2553,7 @@
2311
2553
  continue;
2312
2554
  }
2313
2555
  const validBefore = /[\s)]/.test(before) || i === 0 || tokens.length === 0 || tokens[tokens.length - 1].type === TokenType.LPAREN || tokens[tokens.length - 1].type === TokenType.COMMA || tokens[tokens.length - 1].type === TokenType.OPERATOR;
2314
- const validAfter = /[\s($./'"0-9_]/.test(after) || i + op.length >= len2 || opSymbols.some((o) => expr.slice(i + op.length).startsWith(o));
2556
+ const validAfter = /[\s(=./'"0-9_]/.test(after) || i + op.length >= len2 || opSymbols.some((o) => expr.slice(i + op.length).startsWith(o));
2315
2557
  if (validBefore || validAfter) {
2316
2558
  matchedOp = op;
2317
2559
  break;
@@ -2367,6 +2609,16 @@
2367
2609
  tokens.push({ type: TokenType.PLACEHOLDER, value: placeholder });
2368
2610
  continue;
2369
2611
  }
2612
+ if (expr.slice(i, i + 5) === "$this") {
2613
+ let thisPath = "$this";
2614
+ i += 5;
2615
+ while (i < len2 && /[a-zA-Z0-9_./]/.test(expr[i])) {
2616
+ thisPath += expr[i];
2617
+ i++;
2618
+ }
2619
+ tokens.push({ type: TokenType.THIS, value: thisPath });
2620
+ continue;
2621
+ }
2370
2622
  if (expr.slice(i, i + 6) === "$event") {
2371
2623
  let eventPath = "$event";
2372
2624
  i += 6;
@@ -2377,7 +2629,7 @@
2377
2629
  tokens.push({ type: TokenType.EVENT, value: eventPath });
2378
2630
  continue;
2379
2631
  }
2380
- if (expr[i] === "$" || expr[i] === "." || expr[i] === "/") {
2632
+ if (expr[i] === "=" || expr[i] === "." || expr[i] === "/") {
2381
2633
  let path = "";
2382
2634
  while (i < len2) {
2383
2635
  let isOp = false;
@@ -2437,7 +2689,7 @@
2437
2689
  const hasOperatorSyntax = (expr) => {
2438
2690
  if (!expr || typeof expr !== "string") return false;
2439
2691
  if (expr.includes("(")) return false;
2440
- if (/^\$(\+\+|--|!!)\/?/.test(expr)) {
2692
+ if (/^=(\+\+|--|!!)\/?/.test(expr)) {
2441
2693
  return true;
2442
2694
  }
2443
2695
  if (/(\+\+|--)$/.test(expr)) {
@@ -2500,6 +2752,9 @@
2500
2752
  tok = this.peek();
2501
2753
  continue;
2502
2754
  }
2755
+ if (!operators.postfix.has(tok.value) && !operators.infix.has(tok.value)) {
2756
+ break;
2757
+ }
2503
2758
  this.consume();
2504
2759
  const nextTok = this.peek();
2505
2760
  if (nextTok.type === TokenType.PATH || nextTok.type === TokenType.LITERAL || nextTok.type === TokenType.LPAREN || nextTok.type === TokenType.PLACEHOLDER || nextTok.type === TokenType.EVENT || nextTok.type === TokenType.OPERATOR && operators.prefix.has(nextTok.value)) {
@@ -2537,6 +2792,10 @@
2537
2792
  this.consume();
2538
2793
  return { type: "Placeholder", value: tok.value };
2539
2794
  }
2795
+ if (tok.type === TokenType.THIS) {
2796
+ this.consume();
2797
+ return { type: "This", value: tok.value };
2798
+ }
2540
2799
  if (tok.type === TokenType.EVENT) {
2541
2800
  this.consume();
2542
2801
  return { type: "Event", value: tok.value };
@@ -2572,8 +2831,17 @@
2572
2831
  return resolvePath(path, item);
2573
2832
  });
2574
2833
  }
2834
+ case "This": {
2835
+ return new LazyValue((context2) => {
2836
+ const node = (context2 == null ? void 0 : context2.__node__) || context2;
2837
+ if (ast.value === "$this") return node;
2838
+ const path = ast.value.startsWith("$this.") ? ast.value.slice(6) : ast.value.slice(6);
2839
+ return resolvePath(path, node);
2840
+ });
2841
+ }
2575
2842
  case "Event": {
2576
- return new LazyValue((event) => {
2843
+ return new LazyValue((context2) => {
2844
+ const event = (context2 == null ? void 0 : context2.$event) || (context2 == null ? void 0 : context2.event) || context2;
2577
2845
  if (ast.value === "$event") return event;
2578
2846
  const path = ast.value.startsWith("$event.") ? ast.value.slice(7) : ast.value.slice(7);
2579
2847
  return resolvePath(path, event);
@@ -2621,7 +2889,12 @@
2621
2889
  const opts = helperOptions.get(opInfo.helper) || {};
2622
2890
  const left = evaluateAST(ast.left, context, opts.pathAware);
2623
2891
  const right = evaluateAST(ast.right, context, false);
2624
- return helper(unwrapSignal(left), unwrapSignal(right));
2892
+ const finalArgs = [];
2893
+ if (Array.isArray(left) && ast.left.type === "Explosion") finalArgs.push(...left);
2894
+ else finalArgs.push(unwrapSignal(left));
2895
+ if (Array.isArray(right) && ast.right.type === "Explosion") finalArgs.push(...right);
2896
+ else finalArgs.push(unwrapSignal(right));
2897
+ return helper(...finalArgs);
2625
2898
  }
2626
2899
  default:
2627
2900
  throw new Error(`JPRX: Unknown AST node type: ${ast.type}`);
@@ -2633,14 +2906,14 @@
2633
2906
  const ast = parser.parseExpression(0);
2634
2907
  return evaluateAST(ast, context);
2635
2908
  };
2636
- const resolveExpression = (expr, context) => {
2637
- var _a, _b;
2909
+ const resolveExpression$1 = (expr, context) => {
2910
+ var _a2, _b2;
2638
2911
  if (typeof expr !== "string") return expr;
2639
2912
  if (hasOperatorSyntax(expr)) {
2640
2913
  try {
2641
2914
  return parseWithPratt(expr, context);
2642
2915
  } catch (e) {
2643
- (_a = globalThis.console) == null ? void 0 : _a.warn("JPRX: Pratt parser failed, falling back to legacy:", e.message);
2916
+ (_a2 = globalThis.console) == null ? void 0 : _a2.warn("JPRX: Pratt parser failed, falling back to legacy:", e.message);
2644
2917
  }
2645
2918
  }
2646
2919
  const funcStart = expr.indexOf("(");
@@ -2648,19 +2921,19 @@
2648
2921
  const fullPath = expr.slice(0, funcStart).trim();
2649
2922
  const argsStr = expr.slice(funcStart + 1, -1);
2650
2923
  const segments = fullPath.split("/");
2651
- let funcName = segments.pop().replace(/^\$/, "");
2924
+ let funcName = segments.pop().replace(/^=/, "");
2652
2925
  if (funcName === "" && (segments.length > 0 || fullPath === "/")) {
2653
2926
  funcName = "/";
2654
2927
  }
2655
2928
  const navPath = segments.join("/");
2656
- const isGlobalExpr = expr.startsWith("$/") || expr.startsWith("$");
2929
+ const isGlobalExpr = expr.startsWith("=/") || expr.startsWith("=");
2657
2930
  let baseContext = context;
2658
- if (navPath && navPath !== "$") {
2931
+ if (navPath && navPath !== "=") {
2659
2932
  baseContext = resolvePathAsContext(navPath, context);
2660
2933
  }
2661
2934
  const helper = helpers.get(funcName);
2662
2935
  if (!helper) {
2663
- (_b = globalThis.console) == null ? void 0 : _b.warn(`LightviewCDOM: Helper "${funcName}" not found.`);
2936
+ (_b2 = globalThis.console) == null ? void 0 : _b2.warn(`LightviewCDOM: Helper "${funcName}" not found.`);
2664
2937
  return expr;
2665
2938
  }
2666
2939
  const options = helperOptions.get(funcName) || {};
@@ -2688,7 +2961,7 @@
2688
2961
  let hasLazy = false;
2689
2962
  for (let i = 0; i < argsList.length; i++) {
2690
2963
  const arg = argsList[i];
2691
- const useGlobalMode = isGlobalExpr && (navPath === "$" || !navPath);
2964
+ const useGlobalMode = isGlobalExpr && (navPath === "=" || !navPath);
2692
2965
  const res = resolveArgument(arg, baseContext, useGlobalMode);
2693
2966
  if (res.isLazy) hasLazy = true;
2694
2967
  const shouldUnwrap = !(options.pathAware && i === 0);
@@ -2712,7 +2985,7 @@
2712
2985
  return helper(...finalArgs);
2713
2986
  });
2714
2987
  }
2715
- const result = helper(...resolvedArgs);
2988
+ const result = helper.apply((context == null ? void 0 : context.__node__) || null, resolvedArgs);
2716
2989
  return unwrapSignal(result);
2717
2990
  }
2718
2991
  return unwrapSignal(resolvePath(expr, context));
@@ -2720,7 +2993,7 @@
2720
2993
  const parseExpression = (expr, context) => {
2721
2994
  const LV = getLV();
2722
2995
  if (!LV || typeof expr !== "string") return expr;
2723
- return LV.computed(() => resolveExpression(expr, context));
2996
+ return LV.computed(() => resolveExpression$1(expr, context));
2724
2997
  };
2725
2998
  const parseCDOMC = (input) => {
2726
2999
  let i = 0;
@@ -2834,7 +3107,7 @@
2834
3107
  i++;
2835
3108
  }
2836
3109
  const word = input.slice(start, i);
2837
- if (word.startsWith("$")) {
3110
+ if (word.startsWith("=")) {
2838
3111
  return word;
2839
3112
  }
2840
3113
  if (word === "true") return true;
@@ -2920,7 +3193,7 @@
2920
3193
  return res;
2921
3194
  };
2922
3195
  const parseJPRX = (input) => {
2923
- var _a, _b;
3196
+ var _a2, _b2;
2924
3197
  let result = "";
2925
3198
  let i = 0;
2926
3199
  const len2 = input.length;
@@ -2972,7 +3245,7 @@
2972
3245
  i++;
2973
3246
  continue;
2974
3247
  }
2975
- if (char === "$") {
3248
+ if (char === "=") {
2976
3249
  let expr = "";
2977
3250
  let parenDepth = 0;
2978
3251
  let braceDepth = 0;
@@ -3001,7 +3274,7 @@
3001
3274
  result += JSON.stringify(expr);
3002
3275
  continue;
3003
3276
  }
3004
- if (/[a-zA-Z_./]/.test(char)) {
3277
+ if (/[a-zA-Z_$/./]/.test(char)) {
3005
3278
  let word = "";
3006
3279
  while (i < len2 && /[a-zA-Z0-9_$/.-]/.test(input[i])) {
3007
3280
  word += input[i];
@@ -3011,6 +3284,32 @@
3011
3284
  while (j < len2 && /\s/.test(input[j])) j++;
3012
3285
  if (input[j] === ":") {
3013
3286
  result += `"${word}"`;
3287
+ } else if (input[j] === "(") {
3288
+ let expr = word;
3289
+ i = j;
3290
+ let parenDepth = 0;
3291
+ let inQuote = null;
3292
+ while (i < len2) {
3293
+ const c = input[i];
3294
+ if (inQuote) {
3295
+ if (c === inQuote && input[i - 1] !== "\\") inQuote = null;
3296
+ } else if (c === '"' || c === "'") {
3297
+ inQuote = c;
3298
+ } else {
3299
+ if (c === "(") parenDepth++;
3300
+ else if (c === ")") {
3301
+ parenDepth--;
3302
+ if (parenDepth === 0) {
3303
+ expr += c;
3304
+ i++;
3305
+ break;
3306
+ }
3307
+ }
3308
+ }
3309
+ expr += c;
3310
+ i++;
3311
+ }
3312
+ result += JSON.stringify("=" + expr);
3014
3313
  } else {
3015
3314
  if (word === "true" || word === "false" || word === "null") {
3016
3315
  result += word;
@@ -3037,12 +3336,12 @@
3037
3336
  try {
3038
3337
  return JSON.parse(result);
3039
3338
  } catch (e) {
3040
- (_a = globalThis.console) == null ? void 0 : _a.error("parseJPRX: JSON parse failed", e);
3041
- (_b = globalThis.console) == null ? void 0 : _b.error("Transformed input:", result);
3339
+ (_a2 = globalThis.console) == null ? void 0 : _a2.error("parseJPRX: JSON parse failed", e);
3340
+ (_b2 = globalThis.console) == null ? void 0 : _b2.error("Transformed input:", result);
3042
3341
  throw e;
3043
3342
  }
3044
3343
  };
3045
- const add = (...args) => args.reduce((a, b) => Number(a) + Number(b), 0);
3344
+ const add$1 = (...args) => args.reduce((a, b) => Number(a) + Number(b), 0);
3046
3345
  const subtract = (a, b) => Number(a) - Number(b);
3047
3346
  const multiply = (...args) => args.reduce((a, b) => Number(a) * Number(b), 1);
3048
3347
  const divide = (a, b) => Number(a) / Number(b);
@@ -3050,12 +3349,14 @@
3050
3349
  const ceil = (val) => Math.ceil(val);
3051
3350
  const floor = (val) => Math.floor(val);
3052
3351
  const abs = (val) => Math.abs(val);
3053
- const mod = (a, b) => a % b;
3352
+ const mod$1 = (a, b) => a % b;
3054
3353
  const pow = (a, b) => Math.pow(a, b);
3055
3354
  const sqrt = (val) => Math.sqrt(val);
3355
+ const negate = (val) => -Number(val);
3356
+ const toPercent = (val) => Number(val) / 100;
3056
3357
  const registerMathHelpers = (register) => {
3057
- register("+", add);
3058
- register("add", add);
3358
+ register("+", add$1);
3359
+ register("add", add$1);
3059
3360
  register("-", subtract);
3060
3361
  register("sub", subtract);
3061
3362
  register("*", multiply);
@@ -3066,11 +3367,13 @@
3066
3367
  register("ceil", ceil);
3067
3368
  register("floor", floor);
3068
3369
  register("abs", abs);
3069
- register("mod", mod);
3370
+ register("mod", mod$1);
3070
3371
  register("pow", pow);
3071
3372
  register("sqrt", sqrt);
3373
+ register("negate", negate);
3374
+ register("toPercent", toPercent);
3072
3375
  };
3073
- const ifHelper = (condition, thenVal, elseVal) => condition ? thenVal : elseVal;
3376
+ const ifHelper = (condition2, thenVal, elseVal) => condition2 ? thenVal : elseVal;
3074
3377
  const andHelper = (...args) => args.every(Boolean);
3075
3378
  const orHelper = (...args) => args.some(Boolean);
3076
3379
  const notHelper = (val) => !val;
@@ -3094,7 +3397,7 @@
3094
3397
  const items = args.slice(0, -1);
3095
3398
  return items.join(separator);
3096
3399
  };
3097
- const concat = (...args) => args.join("");
3400
+ const concat$1 = (...args) => args.join("");
3098
3401
  const upper = (s) => String(s).toUpperCase();
3099
3402
  const lower = (s) => String(s).toLowerCase();
3100
3403
  const trim = (s) => String(s).trim();
@@ -3108,13 +3411,13 @@
3108
3411
  const titleCase = (s) => {
3109
3412
  return String(s).toLowerCase().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
3110
3413
  };
3111
- const contains$1 = (s, search) => String(s).includes(search);
3414
+ const contains$2 = (s, search) => String(s).includes(search);
3112
3415
  const startsWith = (s, prefix) => String(s).startsWith(prefix);
3113
3416
  const endsWith = (s, suffix) => String(s).endsWith(suffix);
3114
3417
  const defaultHelper = (val, fallback) => val !== void 0 && val !== null ? val : fallback;
3115
3418
  const registerStringHelpers = (register) => {
3116
3419
  register("join", join$1);
3117
- register("concat", concat);
3420
+ register("concat", concat$1);
3118
3421
  register("upper", upper);
3119
3422
  register("lower", lower);
3120
3423
  register("trim", trim);
@@ -3123,7 +3426,7 @@
3123
3426
  register("split", split);
3124
3427
  register("capitalize", capitalize);
3125
3428
  register("titleCase", titleCase);
3126
- register("contains", contains$1);
3429
+ register("contains", contains$2);
3127
3430
  register("startsWith", startsWith);
3128
3431
  register("endsWith", endsWith);
3129
3432
  register("default", defaultHelper);
@@ -3196,7 +3499,7 @@
3196
3499
  const lte = (a, b) => a <= b;
3197
3500
  const neq = (a, b) => a !== b;
3198
3501
  const between = (val, min2, max2) => val >= min2 && val <= max2;
3199
- const contains = (arr, val) => Array.isArray(arr) && arr.includes(val);
3502
+ const contains$1 = (arr, val) => Array.isArray(arr) && arr.includes(val);
3200
3503
  const registerCompareHelpers = (register) => {
3201
3504
  register("gt", gt);
3202
3505
  register(">", gt);
@@ -3209,7 +3512,7 @@
3209
3512
  register("neq", neq);
3210
3513
  register("!=", neq);
3211
3514
  register("between", between);
3212
- register("in", contains);
3515
+ register("in", contains$1);
3213
3516
  };
3214
3517
  const sumIf = (arr, predicate) => {
3215
3518
  if (!Array.isArray(arr)) return 0;
@@ -3300,16 +3603,35 @@
3300
3603
  };
3301
3604
  const index = (arr, idx) => Array.isArray(arr) ? arr[idx] : void 0;
3302
3605
  const match = (val, arr) => Array.isArray(arr) ? arr.indexOf(val) : -1;
3606
+ const pathRef = (path, context) => {
3607
+ if (path && typeof path === "object" && "value" in path) {
3608
+ return unwrapSignal(path.value);
3609
+ }
3610
+ if (typeof path === "string") {
3611
+ const normalized = path.startsWith("=") ? path : "=" + path;
3612
+ const resolved = resolvePath(normalized, context);
3613
+ const value = unwrapSignal(resolved);
3614
+ if (typeof value === "number") return value;
3615
+ if (typeof value === "string" && value !== "" && !isNaN(parseFloat(value)) && isFinite(Number(value))) {
3616
+ return parseFloat(value);
3617
+ }
3618
+ return value;
3619
+ }
3620
+ return unwrapSignal(path);
3621
+ };
3303
3622
  const registerLookupHelpers = (register) => {
3304
3623
  register("lookup", lookup);
3305
3624
  register("vlookup", vlookup);
3306
3625
  register("index", index);
3307
3626
  register("match", match);
3627
+ register("$", pathRef, { pathAware: true });
3628
+ register("val", pathRef, { pathAware: true });
3629
+ register("indirect", pathRef, { pathAware: true });
3308
3630
  };
3309
3631
  const sum = (...args) => args.reduce((a, b) => a + (Number(b) || 0), 0);
3310
3632
  const avg = (...args) => args.length === 0 ? 0 : sum(...args) / args.length;
3311
- const min = (...args) => Math.min(...args);
3312
- const max = (...args) => Math.max(...args);
3633
+ const min$1 = (...args) => Math.min(...args);
3634
+ const max$1 = (...args) => Math.max(...args);
3313
3635
  const median = (...args) => {
3314
3636
  if (args.length === 0) return 0;
3315
3637
  const sorted = [...args].sort((a, b) => a - b);
@@ -3331,8 +3653,8 @@
3331
3653
  const registerStatsHelpers = (register) => {
3332
3654
  register("sum", sum);
3333
3655
  register("avg", avg);
3334
- register("min", min);
3335
- register("max", max);
3656
+ register("min", min$1);
3657
+ register("max", max$1);
3336
3658
  register("median", median);
3337
3659
  register("stdev", stdev);
3338
3660
  register("var", variance);
@@ -3391,6 +3713,21 @@
3391
3713
  if (typeof current === "object" && current !== null) return set(target, {});
3392
3714
  return set(target, null);
3393
3715
  };
3716
+ function state(val, options) {
3717
+ if (globalThis.Lightview) {
3718
+ const finalOptions = typeof options === "string" ? { name: options } : options;
3719
+ return globalThis.Lightview.state(val, finalOptions);
3720
+ }
3721
+ throw new Error("JPRX: $state requires a UI library implementation.");
3722
+ }
3723
+ function signal(val, options) {
3724
+ if (globalThis.Lightview) {
3725
+ const finalOptions = typeof options === "string" ? { name: options } : options;
3726
+ return globalThis.Lightview.signal(val, finalOptions);
3727
+ }
3728
+ throw new Error("JPRX: $signal requires a UI library implementation.");
3729
+ }
3730
+ const bind = (path, options) => ({ __JPRX_BIND__: true, path, options });
3394
3731
  const registerStateHelpers = (register) => {
3395
3732
  const opts = { pathAware: true };
3396
3733
  register("set", set, opts);
@@ -3404,6 +3741,9 @@
3404
3741
  register("pop", pop, opts);
3405
3742
  register("assign", assign, opts);
3406
3743
  register("clear", clear, opts);
3744
+ register("state", state);
3745
+ register("signal", signal);
3746
+ register("bind", bind);
3407
3747
  };
3408
3748
  const fetchHelper = (url, options = {}) => {
3409
3749
  const fetchOptions = { ...options };
@@ -3429,351 +3769,1899 @@
3429
3769
  const registerNetworkHelpers = (register) => {
3430
3770
  register("fetch", fetchHelper);
3431
3771
  };
3432
- registerMathHelpers(registerHelper);
3433
- registerLogicHelpers(registerHelper);
3434
- registerStringHelpers(registerHelper);
3435
- registerArrayHelpers(registerHelper);
3436
- registerCompareHelpers(registerHelper);
3437
- registerConditionalHelpers(registerHelper);
3438
- registerDateTimeHelpers(registerHelper);
3439
- registerFormatHelpers(registerHelper);
3440
- registerLookupHelpers(registerHelper);
3441
- registerStatsHelpers(registerHelper);
3442
- registerStateHelpers((name, fn) => registerHelper(name, fn, { pathAware: true }));
3443
- registerNetworkHelpers(registerHelper);
3444
- registerOperator("increment", "++", "prefix", 80);
3445
- registerOperator("increment", "++", "postfix", 80);
3446
- registerOperator("decrement", "--", "prefix", 80);
3447
- registerOperator("decrement", "--", "postfix", 80);
3448
- registerOperator("toggle", "!!", "prefix", 80);
3449
- registerOperator("+", "+", "infix", 50);
3450
- registerOperator("-", "-", "infix", 50);
3451
- registerOperator("*", "*", "infix", 60);
3452
- registerOperator("/", "/", "infix", 60);
3453
- registerOperator("gt", ">", "infix", 40);
3454
- registerOperator("lt", "<", "infix", 40);
3455
- registerOperator("gte", ">=", "infix", 40);
3456
- registerOperator("lte", "<=", "infix", 40);
3457
- registerOperator("neq", "!=", "infix", 40);
3458
- const localStates = /* @__PURE__ */ new WeakMap();
3459
- const getContext = (node, event = null) => {
3460
- const chain = [];
3461
- let cur = node;
3462
- const ShadowRoot2 = globalThis.ShadowRoot;
3463
- while (cur) {
3464
- const local = localStates.get(cur) || (cur && typeof cur === "object" ? cur.__state__ : null);
3465
- if (local) chain.unshift(local);
3466
- cur = cur.parentElement || (cur && typeof cur === "object" ? cur.__parent__ : null) || (ShadowRoot2 && cur.parentNode instanceof ShadowRoot2 ? cur.parentNode.host : null);
3467
- }
3468
- const globalRegistry = getRegistry$1();
3469
- const handler = {
3470
- get(target, prop, receiver) {
3471
- var _a;
3472
- if (prop === "$event" || prop === "event") return event;
3473
- if (prop === "__parent__") return void 0;
3474
- for (let i = chain.length - 1; i >= 0; i--) {
3475
- const s = chain[i];
3476
- if (prop in s) return s[prop];
3477
- }
3478
- if (globalRegistry && globalRegistry.has(prop)) return unwrapSignal(globalRegistry.get(prop));
3479
- const globalState = (_a = globalThis.Lightview) == null ? void 0 : _a.state;
3480
- if (globalState && prop in globalState) return unwrapSignal(globalState[prop]);
3481
- return void 0;
3482
- },
3483
- set(target, prop, value, receiver) {
3484
- var _a;
3485
- for (let i = chain.length - 1; i >= 0; i--) {
3486
- const s = chain[i];
3487
- if (prop in s) {
3488
- s[prop] = value;
3489
- return true;
3772
+ var INUMBER = "INUMBER";
3773
+ var IOP1 = "IOP1";
3774
+ var IOP2 = "IOP2";
3775
+ var IOP3 = "IOP3";
3776
+ var IVAR = "IVAR";
3777
+ var IVARNAME = "IVARNAME";
3778
+ var IFUNCALL = "IFUNCALL";
3779
+ var IFUNDEF = "IFUNDEF";
3780
+ var IEXPR = "IEXPR";
3781
+ var IEXPREVAL = "IEXPREVAL";
3782
+ var IMEMBER = "IMEMBER";
3783
+ var IENDSTATEMENT = "IENDSTATEMENT";
3784
+ var IARRAY = "IARRAY";
3785
+ function Instruction(type, value) {
3786
+ this.type = type;
3787
+ this.value = value !== void 0 && value !== null ? value : 0;
3788
+ }
3789
+ Instruction.prototype.toString = function() {
3790
+ switch (this.type) {
3791
+ case INUMBER:
3792
+ case IOP1:
3793
+ case IOP2:
3794
+ case IOP3:
3795
+ case IVAR:
3796
+ case IVARNAME:
3797
+ case IENDSTATEMENT:
3798
+ return this.value;
3799
+ case IFUNCALL:
3800
+ return "CALL " + this.value;
3801
+ case IFUNDEF:
3802
+ return "DEF " + this.value;
3803
+ case IARRAY:
3804
+ return "ARRAY " + this.value;
3805
+ case IMEMBER:
3806
+ return "." + this.value;
3807
+ default:
3808
+ return "Invalid Instruction";
3809
+ }
3810
+ };
3811
+ function unaryInstruction(value) {
3812
+ return new Instruction(IOP1, value);
3813
+ }
3814
+ function binaryInstruction(value) {
3815
+ return new Instruction(IOP2, value);
3816
+ }
3817
+ function ternaryInstruction(value) {
3818
+ return new Instruction(IOP3, value);
3819
+ }
3820
+ function simplify(tokens, unaryOps, binaryOps, ternaryOps, values) {
3821
+ var nstack = [];
3822
+ var newexpression = [];
3823
+ var n1, n2, n3;
3824
+ var f;
3825
+ for (var i = 0; i < tokens.length; i++) {
3826
+ var item = tokens[i];
3827
+ var type = item.type;
3828
+ if (type === INUMBER || type === IVARNAME) {
3829
+ if (Array.isArray(item.value)) {
3830
+ nstack.push.apply(nstack, simplify(item.value.map(function(x) {
3831
+ return new Instruction(INUMBER, x);
3832
+ }).concat(new Instruction(IARRAY, item.value.length)), unaryOps, binaryOps, ternaryOps, values));
3833
+ } else {
3834
+ nstack.push(item);
3835
+ }
3836
+ } else if (type === IVAR && values.hasOwnProperty(item.value)) {
3837
+ item = new Instruction(INUMBER, values[item.value]);
3838
+ nstack.push(item);
3839
+ } else if (type === IOP2 && nstack.length > 1) {
3840
+ n2 = nstack.pop();
3841
+ n1 = nstack.pop();
3842
+ f = binaryOps[item.value];
3843
+ item = new Instruction(INUMBER, f(n1.value, n2.value));
3844
+ nstack.push(item);
3845
+ } else if (type === IOP3 && nstack.length > 2) {
3846
+ n3 = nstack.pop();
3847
+ n2 = nstack.pop();
3848
+ n1 = nstack.pop();
3849
+ if (item.value === "?") {
3850
+ nstack.push(n1.value ? n2.value : n3.value);
3851
+ } else {
3852
+ f = ternaryOps[item.value];
3853
+ item = new Instruction(INUMBER, f(n1.value, n2.value, n3.value));
3854
+ nstack.push(item);
3855
+ }
3856
+ } else if (type === IOP1 && nstack.length > 0) {
3857
+ n1 = nstack.pop();
3858
+ f = unaryOps[item.value];
3859
+ item = new Instruction(INUMBER, f(n1.value));
3860
+ nstack.push(item);
3861
+ } else if (type === IEXPR) {
3862
+ while (nstack.length > 0) {
3863
+ newexpression.push(nstack.shift());
3864
+ }
3865
+ newexpression.push(new Instruction(IEXPR, simplify(item.value, unaryOps, binaryOps, ternaryOps, values)));
3866
+ } else if (type === IMEMBER && nstack.length > 0) {
3867
+ n1 = nstack.pop();
3868
+ nstack.push(new Instruction(INUMBER, n1.value[item.value]));
3869
+ } else {
3870
+ while (nstack.length > 0) {
3871
+ newexpression.push(nstack.shift());
3872
+ }
3873
+ newexpression.push(item);
3874
+ }
3875
+ }
3876
+ while (nstack.length > 0) {
3877
+ newexpression.push(nstack.shift());
3878
+ }
3879
+ return newexpression;
3880
+ }
3881
+ function substitute(tokens, variable, expr) {
3882
+ var newexpression = [];
3883
+ for (var i = 0; i < tokens.length; i++) {
3884
+ var item = tokens[i];
3885
+ var type = item.type;
3886
+ if (type === IVAR && item.value === variable) {
3887
+ for (var j = 0; j < expr.tokens.length; j++) {
3888
+ var expritem = expr.tokens[j];
3889
+ var replitem;
3890
+ if (expritem.type === IOP1) {
3891
+ replitem = unaryInstruction(expritem.value);
3892
+ } else if (expritem.type === IOP2) {
3893
+ replitem = binaryInstruction(expritem.value);
3894
+ } else if (expritem.type === IOP3) {
3895
+ replitem = ternaryInstruction(expritem.value);
3896
+ } else {
3897
+ replitem = new Instruction(expritem.type, expritem.value);
3490
3898
  }
3899
+ newexpression.push(replitem);
3491
3900
  }
3492
- if (chain.length > 0) {
3493
- chain[chain.length - 1][prop] = value;
3494
- return true;
3901
+ } else if (type === IEXPR) {
3902
+ newexpression.push(new Instruction(IEXPR, substitute(item.value, variable, expr)));
3903
+ } else {
3904
+ newexpression.push(item);
3905
+ }
3906
+ }
3907
+ return newexpression;
3908
+ }
3909
+ function evaluate(tokens, expr, values) {
3910
+ var nstack = [];
3911
+ var n1, n2, n3;
3912
+ var f, args, argCount;
3913
+ if (isExpressionEvaluator(tokens)) {
3914
+ return resolveExpression(tokens, values);
3915
+ }
3916
+ var numTokens = tokens.length;
3917
+ for (var i = 0; i < numTokens; i++) {
3918
+ var item = tokens[i];
3919
+ var type = item.type;
3920
+ if (type === INUMBER || type === IVARNAME) {
3921
+ nstack.push(item.value);
3922
+ } else if (type === IOP2) {
3923
+ n2 = nstack.pop();
3924
+ n1 = nstack.pop();
3925
+ if (item.value === "and") {
3926
+ nstack.push(n1 ? !!evaluate(n2, expr, values) : false);
3927
+ } else if (item.value === "or") {
3928
+ nstack.push(n1 ? true : !!evaluate(n2, expr, values));
3929
+ } else if (item.value === "=") {
3930
+ f = expr.binaryOps[item.value];
3931
+ nstack.push(f(n1, evaluate(n2, expr, values), values));
3932
+ } else {
3933
+ f = expr.binaryOps[item.value];
3934
+ nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values)));
3495
3935
  }
3496
- const globalState = (_a = globalThis.Lightview) == null ? void 0 : _a.state;
3497
- if (globalState && prop in globalState) {
3498
- globalState[prop] = value;
3499
- return true;
3936
+ } else if (type === IOP3) {
3937
+ n3 = nstack.pop();
3938
+ n2 = nstack.pop();
3939
+ n1 = nstack.pop();
3940
+ if (item.value === "?") {
3941
+ nstack.push(evaluate(n1 ? n2 : n3, expr, values));
3942
+ } else {
3943
+ f = expr.ternaryOps[item.value];
3944
+ nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values), resolveExpression(n3, values)));
3500
3945
  }
3501
- if (globalRegistry && globalRegistry.has(prop)) {
3502
- const s = globalRegistry.get(prop);
3503
- if (s && (typeof s === "object" || typeof s === "function") && "value" in s) {
3504
- s.value = value;
3505
- return true;
3946
+ } else if (type === IVAR) {
3947
+ if (item.value in expr.functions) {
3948
+ nstack.push(expr.functions[item.value]);
3949
+ } else if (item.value in expr.unaryOps && expr.parser.isOperatorEnabled(item.value)) {
3950
+ nstack.push(expr.unaryOps[item.value]);
3951
+ } else {
3952
+ var v = values[item.value];
3953
+ if (v !== void 0) {
3954
+ nstack.push(v);
3955
+ } else {
3956
+ throw new Error("undefined variable: " + item.value);
3506
3957
  }
3507
3958
  }
3508
- return false;
3509
- },
3510
- has(target, prop) {
3511
- var _a;
3512
- const exists = prop === "$event" || prop === "event" || !!chain.find((s) => prop in s);
3513
- const inGlobal = ((_a = globalThis.Lightview) == null ? void 0 : _a.state) && prop in globalThis.Lightview.state || globalRegistry && globalRegistry.has(prop);
3514
- return exists || inGlobal;
3515
- },
3516
- ownKeys(target) {
3517
- var _a;
3518
- const keys = /* @__PURE__ */ new Set();
3519
- if (event) {
3520
- keys.add("$event");
3521
- keys.add("event");
3959
+ } else if (type === IOP1) {
3960
+ n1 = nstack.pop();
3961
+ f = expr.unaryOps[item.value];
3962
+ nstack.push(f(resolveExpression(n1, values)));
3963
+ } else if (type === IFUNCALL) {
3964
+ argCount = item.value;
3965
+ args = [];
3966
+ while (argCount-- > 0) {
3967
+ args.unshift(resolveExpression(nstack.pop(), values));
3522
3968
  }
3523
- for (const s of chain) {
3524
- for (const key in s) keys.add(key);
3969
+ f = nstack.pop();
3970
+ if (f.apply && f.call) {
3971
+ nstack.push(f.apply(void 0, args));
3972
+ } else {
3973
+ throw new Error(f + " is not a function");
3525
3974
  }
3526
- const globalState = (_a = globalThis.Lightview) == null ? void 0 : _a.state;
3527
- if (globalState) {
3528
- for (const key in globalState) keys.add(key);
3975
+ } else if (type === IFUNDEF) {
3976
+ nstack.push(function() {
3977
+ var n22 = nstack.pop();
3978
+ var args2 = [];
3979
+ var argCount2 = item.value;
3980
+ while (argCount2-- > 0) {
3981
+ args2.unshift(nstack.pop());
3982
+ }
3983
+ var n12 = nstack.pop();
3984
+ var f2 = function() {
3985
+ var scope = Object.assign({}, values);
3986
+ for (var i2 = 0, len2 = args2.length; i2 < len2; i2++) {
3987
+ scope[args2[i2]] = arguments[i2];
3988
+ }
3989
+ return evaluate(n22, expr, scope);
3990
+ };
3991
+ Object.defineProperty(f2, "name", {
3992
+ value: n12,
3993
+ writable: false
3994
+ });
3995
+ values[n12] = f2;
3996
+ return f2;
3997
+ }());
3998
+ } else if (type === IEXPR) {
3999
+ nstack.push(createExpressionEvaluator(item, expr));
4000
+ } else if (type === IEXPREVAL) {
4001
+ nstack.push(item);
4002
+ } else if (type === IMEMBER) {
4003
+ n1 = nstack.pop();
4004
+ nstack.push(n1[item.value]);
4005
+ } else if (type === IENDSTATEMENT) {
4006
+ nstack.pop();
4007
+ } else if (type === IARRAY) {
4008
+ argCount = item.value;
4009
+ args = [];
4010
+ while (argCount-- > 0) {
4011
+ args.unshift(nstack.pop());
3529
4012
  }
3530
- return Array.from(keys);
3531
- },
3532
- getOwnPropertyDescriptor(target, prop) {
3533
- return { enumerable: true, configurable: true };
3534
- }
3535
- };
3536
- return new Proxy({}, handler);
3537
- };
3538
- const handleCDOMState = (node) => {
3539
- var _a;
3540
- const attr = node["cdom-state"] || node.getAttribute && node.getAttribute("cdom-state");
3541
- if (!attr || localStates.has(node)) return;
3542
- try {
3543
- const data = typeof attr === "object" ? attr : JSON.parse(attr);
3544
- const s = state(data);
3545
- localStates.set(node, s);
3546
- if (node && typeof node === "object") {
3547
- node.__state__ = s;
3548
- }
3549
- } catch (e) {
3550
- (_a = globalThis.console) == null ? void 0 : _a.error("LightviewCDOM: Failed to parse cdom-state", e);
3551
- }
3552
- };
3553
- const handleCDOMBind = (node) => {
3554
- const path = node["cdom-bind"] || node.getAttribute("cdom-bind");
3555
- if (!path) return;
3556
- const type = node.type || "";
3557
- const tagName = node.tagName.toLowerCase();
3558
- let prop = "value";
3559
- let event = "input";
3560
- if (type === "checkbox" || type === "radio") {
3561
- prop = "checked";
3562
- event = "change";
3563
- } else if (tagName === "select") {
3564
- event = "change";
3565
- }
3566
- const context = getContext(node);
3567
- let target = resolvePathAsContext(path, context);
3568
- if (target && target.isBindingTarget && target.value === void 0) {
3569
- const val = node[prop];
3570
- if (val !== void 0 && val !== "") {
3571
- set(context, { [target.key]: val });
3572
- target = resolvePathAsContext(path, context);
3573
- }
3574
- }
3575
- effect(() => {
3576
- const val = unwrapSignal(target);
3577
- if (node[prop] !== val) {
3578
- node[prop] = val === void 0 ? "" : val;
3579
- }
3580
- });
3581
- node.addEventListener(event, () => {
3582
- const val = node[prop];
3583
- if (target && target.isBindingTarget) {
3584
- target.value = val;
4013
+ nstack.push(args);
3585
4014
  } else {
3586
- set(context, { [path]: val });
3587
- }
3588
- });
3589
- };
3590
- const activate = (root = document.body) => {
3591
- const walk = (node) => {
3592
- if (node.nodeType === 1) {
3593
- if (node.hasAttribute("cdom-state")) handleCDOMState(node);
3594
- if (node.hasAttribute("cdom-bind")) handleCDOMBind(node);
4015
+ throw new Error("invalid Expression");
3595
4016
  }
3596
- let child = node.firstChild;
3597
- while (child) {
3598
- walk(child);
3599
- child = child.nextSibling;
3600
- }
3601
- };
3602
- walk(root);
3603
- };
3604
- const hydrate = (node, parent = null) => {
3605
- if (!node) return node;
3606
- if (typeof node === "string" && node.startsWith("$")) {
3607
- return parseExpression(node, parent);
3608
4017
  }
3609
- if (Array.isArray(node)) {
3610
- return node.map((item) => hydrate(item, parent));
4018
+ if (nstack.length > 1) {
4019
+ throw new Error("invalid Expression (parity)");
3611
4020
  }
3612
- if (node instanceof String) {
3613
- return node.toString();
3614
- }
3615
- if (typeof node === "object" && node !== null) {
3616
- if (parent && !("__parent__" in node)) {
3617
- Object.defineProperty(node, "__parent__", {
3618
- value: parent,
3619
- enumerable: false,
3620
- writable: true,
3621
- configurable: true
3622
- });
4021
+ return nstack[0] === 0 ? 0 : resolveExpression(nstack[0], values);
4022
+ }
4023
+ function createExpressionEvaluator(token, expr, values) {
4024
+ if (isExpressionEvaluator(token)) return token;
4025
+ return {
4026
+ type: IEXPREVAL,
4027
+ value: function(scope) {
4028
+ return evaluate(token.value, expr, scope);
3623
4029
  }
3624
- if (!node.tag) {
3625
- let potentialTag = null;
3626
- for (const key in node) {
3627
- if (key === "children" || key === "attributes" || key === "tag" || key.startsWith("cdom-") || key.startsWith("on") || key === "__parent__") {
3628
- continue;
4030
+ };
4031
+ }
4032
+ function isExpressionEvaluator(n) {
4033
+ return n && n.type === IEXPREVAL;
4034
+ }
4035
+ function resolveExpression(n, values) {
4036
+ return isExpressionEvaluator(n) ? n.value(values) : n;
4037
+ }
4038
+ function expressionToString(tokens, toJS) {
4039
+ var nstack = [];
4040
+ var n1, n2, n3;
4041
+ var f, args, argCount;
4042
+ for (var i = 0; i < tokens.length; i++) {
4043
+ var item = tokens[i];
4044
+ var type = item.type;
4045
+ if (type === INUMBER) {
4046
+ if (typeof item.value === "number" && item.value < 0) {
4047
+ nstack.push("(" + item.value + ")");
4048
+ } else if (Array.isArray(item.value)) {
4049
+ nstack.push("[" + item.value.map(escapeValue).join(", ") + "]");
4050
+ } else {
4051
+ nstack.push(escapeValue(item.value));
4052
+ }
4053
+ } else if (type === IOP2) {
4054
+ n2 = nstack.pop();
4055
+ n1 = nstack.pop();
4056
+ f = item.value;
4057
+ if (toJS) {
4058
+ if (f === "^") {
4059
+ nstack.push("Math.pow(" + n1 + ", " + n2 + ")");
4060
+ } else if (f === "and") {
4061
+ nstack.push("(!!" + n1 + " && !!" + n2 + ")");
4062
+ } else if (f === "or") {
4063
+ nstack.push("(!!" + n1 + " || !!" + n2 + ")");
4064
+ } else if (f === "||") {
4065
+ nstack.push("(function(a,b){ return Array.isArray(a) && Array.isArray(b) ? a.concat(b) : String(a) + String(b); }((" + n1 + "),(" + n2 + ")))");
4066
+ } else if (f === "==") {
4067
+ nstack.push("(" + n1 + " === " + n2 + ")");
4068
+ } else if (f === "!=") {
4069
+ nstack.push("(" + n1 + " !== " + n2 + ")");
4070
+ } else if (f === "[") {
4071
+ nstack.push(n1 + "[(" + n2 + ") | 0]");
4072
+ } else {
4073
+ nstack.push("(" + n1 + " " + f + " " + n2 + ")");
3629
4074
  }
3630
- const attrNames = [
3631
- // Form/input attributes
3632
- "type",
3633
- "name",
3634
- "value",
3635
- "placeholder",
3636
- "step",
3637
- "min",
3638
- "max",
3639
- "pattern",
3640
- "disabled",
3641
- "checked",
3642
- "selected",
3643
- "readonly",
3644
- "required",
3645
- "multiple",
3646
- "rows",
3647
- "cols",
3648
- "size",
3649
- "maxlength",
3650
- "minlength",
3651
- "autocomplete",
3652
- // Common element attributes
3653
- "id",
3654
- "class",
3655
- "className",
3656
- "style",
3657
- "title",
3658
- "tabindex",
3659
- "role",
3660
- "href",
3661
- "src",
3662
- "alt",
3663
- "width",
3664
- "height",
3665
- "target",
3666
- "rel",
3667
- // Data attributes
3668
- "data",
3669
- "label",
3670
- "text",
3671
- "description",
3672
- "content",
3673
- // Common data property names
3674
- "price",
3675
- "qty",
3676
- "items",
3677
- "count",
3678
- "total",
3679
- "amount",
3680
- "url"
3681
- ];
3682
- if (attrNames.includes(key)) {
3683
- continue;
4075
+ } else {
4076
+ if (f === "[") {
4077
+ nstack.push(n1 + "[" + n2 + "]");
4078
+ } else {
4079
+ nstack.push("(" + n1 + " " + f + " " + n2 + ")");
3684
4080
  }
3685
- potentialTag = key;
3686
- break;
3687
4081
  }
3688
- if (potentialTag) {
3689
- const content = node[potentialTag];
3690
- if (content !== void 0 && content !== null) {
3691
- node.tag = potentialTag;
3692
- if (Array.isArray(content)) {
3693
- node.children = content;
3694
- } else if (typeof content === "object") {
3695
- node.attributes = node.attributes || {};
3696
- for (const k in content) {
3697
- if (k === "children") {
3698
- node.children = content[k];
3699
- } else if (k.startsWith("cdom-")) {
3700
- node[k] = content[k];
3701
- } else {
3702
- node.attributes[k] = content[k];
3703
- }
3704
- }
3705
- } else {
3706
- node.children = [content];
3707
- }
3708
- delete node[potentialTag];
4082
+ } else if (type === IOP3) {
4083
+ n3 = nstack.pop();
4084
+ n2 = nstack.pop();
4085
+ n1 = nstack.pop();
4086
+ f = item.value;
4087
+ if (f === "?") {
4088
+ nstack.push("(" + n1 + " ? " + n2 + " : " + n3 + ")");
4089
+ } else {
4090
+ throw new Error("invalid Expression");
4091
+ }
4092
+ } else if (type === IVAR || type === IVARNAME) {
4093
+ nstack.push(item.value);
4094
+ } else if (type === IOP1) {
4095
+ n1 = nstack.pop();
4096
+ f = item.value;
4097
+ if (f === "-" || f === "+") {
4098
+ nstack.push("(" + f + n1 + ")");
4099
+ } else if (toJS) {
4100
+ if (f === "not") {
4101
+ nstack.push("(!" + n1 + ")");
4102
+ } else if (f === "!") {
4103
+ nstack.push("fac(" + n1 + ")");
4104
+ } else {
4105
+ nstack.push(f + "(" + n1 + ")");
3709
4106
  }
4107
+ } else if (f === "!") {
4108
+ nstack.push("(" + n1 + "!)");
4109
+ } else {
4110
+ nstack.push("(" + f + " " + n1 + ")");
4111
+ }
4112
+ } else if (type === IFUNCALL) {
4113
+ argCount = item.value;
4114
+ args = [];
4115
+ while (argCount-- > 0) {
4116
+ args.unshift(nstack.pop());
4117
+ }
4118
+ f = nstack.pop();
4119
+ nstack.push(f + "(" + args.join(", ") + ")");
4120
+ } else if (type === IFUNDEF) {
4121
+ n2 = nstack.pop();
4122
+ argCount = item.value;
4123
+ args = [];
4124
+ while (argCount-- > 0) {
4125
+ args.unshift(nstack.pop());
4126
+ }
4127
+ n1 = nstack.pop();
4128
+ if (toJS) {
4129
+ nstack.push("(" + n1 + " = function(" + args.join(", ") + ") { return " + n2 + " })");
4130
+ } else {
4131
+ nstack.push("(" + n1 + "(" + args.join(", ") + ") = " + n2 + ")");
4132
+ }
4133
+ } else if (type === IMEMBER) {
4134
+ n1 = nstack.pop();
4135
+ nstack.push(n1 + "." + item.value);
4136
+ } else if (type === IARRAY) {
4137
+ argCount = item.value;
4138
+ args = [];
4139
+ while (argCount-- > 0) {
4140
+ args.unshift(nstack.pop());
3710
4141
  }
4142
+ nstack.push("[" + args.join(", ") + "]");
4143
+ } else if (type === IEXPR) {
4144
+ nstack.push("(" + expressionToString(item.value, toJS) + ")");
4145
+ } else if (type === IENDSTATEMENT) ;
4146
+ else {
4147
+ throw new Error("invalid Expression");
3711
4148
  }
3712
- if (node["cdom-state"]) {
3713
- handleCDOMState(node);
4149
+ }
4150
+ if (nstack.length > 1) {
4151
+ if (toJS) {
4152
+ nstack = [nstack.join(",")];
4153
+ } else {
4154
+ nstack = [nstack.join(";")];
3714
4155
  }
3715
- for (const key in node) {
3716
- const value = node[key];
3717
- if (key === "cdom-state") {
3718
- continue;
3719
- }
3720
- if (typeof value === "string" && value.startsWith("$")) {
3721
- if (key.startsWith("on")) {
3722
- node[key] = (event) => {
3723
- const element2 = event.currentTarget;
3724
- const context = getContext(element2, event);
3725
- const result = resolveExpression(value, context);
3726
- if (result && typeof result === "object" && result.isLazy && typeof result.resolve === "function") {
3727
- return result.resolve(event);
3728
- }
3729
- return result;
3730
- };
3731
- } else if (key === "children") {
3732
- node[key] = [parseExpression(value, node)];
3733
- } else {
3734
- node[key] = parseExpression(value, node);
3735
- }
3736
- } else if (key === "attributes" && typeof value === "object" && value !== null) {
3737
- for (const attrKey in value) {
3738
- const attrValue = value[attrKey];
3739
- if (typeof attrValue === "string" && attrValue.startsWith("$")) {
3740
- if (attrKey.startsWith("on")) {
3741
- value[attrKey] = (event) => {
3742
- const element2 = event.currentTarget;
3743
- const context = getContext(element2, event);
3744
- const result = resolveExpression(attrValue, context);
3745
- if (result && typeof result === "object" && result.isLazy && typeof result.resolve === "function") {
3746
- return result.resolve(event);
3747
- }
3748
- return result;
3749
- };
3750
- } else {
3751
- value[attrKey] = parseExpression(attrValue, node);
3752
- }
3753
- }
4156
+ }
4157
+ return String(nstack[0]);
4158
+ }
4159
+ function escapeValue(v) {
4160
+ if (typeof v === "string") {
4161
+ return JSON.stringify(v).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
4162
+ }
4163
+ return v;
4164
+ }
4165
+ function contains(array, obj) {
4166
+ for (var i = 0; i < array.length; i++) {
4167
+ if (array[i] === obj) {
4168
+ return true;
4169
+ }
4170
+ }
4171
+ return false;
4172
+ }
4173
+ function getSymbols(tokens, symbols, options) {
4174
+ options = options || {};
4175
+ var withMembers = !!options.withMembers;
4176
+ var prevVar = null;
4177
+ for (var i = 0; i < tokens.length; i++) {
4178
+ var item = tokens[i];
4179
+ if (item.type === IVAR || item.type === IVARNAME) {
4180
+ if (!withMembers && !contains(symbols, item.value)) {
4181
+ symbols.push(item.value);
4182
+ } else if (prevVar !== null) {
4183
+ if (!contains(symbols, prevVar)) {
4184
+ symbols.push(prevVar);
3754
4185
  }
3755
- node[key] = value;
4186
+ prevVar = item.value;
3756
4187
  } else {
3757
- node[key] = hydrate(value, node);
4188
+ prevVar = item.value;
4189
+ }
4190
+ } else if (item.type === IMEMBER && withMembers && prevVar !== null) {
4191
+ prevVar += "." + item.value;
4192
+ } else if (item.type === IEXPR) {
4193
+ getSymbols(item.value, symbols, options);
4194
+ } else if (prevVar !== null) {
4195
+ if (!contains(symbols, prevVar)) {
4196
+ symbols.push(prevVar);
3758
4197
  }
4198
+ prevVar = null;
3759
4199
  }
3760
- return node;
3761
4200
  }
3762
- return node;
4201
+ if (prevVar !== null && !contains(symbols, prevVar)) {
4202
+ symbols.push(prevVar);
4203
+ }
4204
+ }
4205
+ function Expression(tokens, parser) {
4206
+ this.tokens = tokens;
4207
+ this.parser = parser;
4208
+ this.unaryOps = parser.unaryOps;
4209
+ this.binaryOps = parser.binaryOps;
4210
+ this.ternaryOps = parser.ternaryOps;
4211
+ this.functions = parser.functions;
4212
+ }
4213
+ Expression.prototype.simplify = function(values) {
4214
+ values = values || {};
4215
+ return new Expression(simplify(this.tokens, this.unaryOps, this.binaryOps, this.ternaryOps, values), this.parser);
4216
+ };
4217
+ Expression.prototype.substitute = function(variable, expr) {
4218
+ if (!(expr instanceof Expression)) {
4219
+ expr = this.parser.parse(String(expr));
4220
+ }
4221
+ return new Expression(substitute(this.tokens, variable, expr), this.parser);
4222
+ };
4223
+ Expression.prototype.evaluate = function(values) {
4224
+ values = values || {};
4225
+ return evaluate(this.tokens, this, values);
4226
+ };
4227
+ Expression.prototype.toString = function() {
4228
+ return expressionToString(this.tokens, false);
4229
+ };
4230
+ Expression.prototype.symbols = function(options) {
4231
+ options = options || {};
4232
+ var vars = [];
4233
+ getSymbols(this.tokens, vars, options);
4234
+ return vars;
4235
+ };
4236
+ Expression.prototype.variables = function(options) {
4237
+ options = options || {};
4238
+ var vars = [];
4239
+ getSymbols(this.tokens, vars, options);
4240
+ var functions = this.functions;
4241
+ return vars.filter(function(name) {
4242
+ return !(name in functions);
4243
+ });
3763
4244
  };
3764
- const LightviewCDOM = {
3765
- registerHelper,
3766
- registerOperator,
4245
+ Expression.prototype.toJSFunction = function(param, variables) {
4246
+ var expr = this;
4247
+ var f = new Function(param, "with(this.functions) with (this.ternaryOps) with (this.binaryOps) with (this.unaryOps) { return " + expressionToString(this.simplify(variables).tokens, true) + "; }");
4248
+ return function() {
4249
+ return f.apply(expr, arguments);
4250
+ };
4251
+ };
4252
+ var TEOF = "TEOF";
4253
+ var TOP = "TOP";
4254
+ var TNUMBER = "TNUMBER";
4255
+ var TSTRING = "TSTRING";
4256
+ var TPAREN = "TPAREN";
4257
+ var TBRACKET = "TBRACKET";
4258
+ var TCOMMA = "TCOMMA";
4259
+ var TNAME = "TNAME";
4260
+ var TSEMICOLON = "TSEMICOLON";
4261
+ function Token(type, value, index2) {
4262
+ this.type = type;
4263
+ this.value = value;
4264
+ this.index = index2;
4265
+ }
4266
+ Token.prototype.toString = function() {
4267
+ return this.type + ": " + this.value;
4268
+ };
4269
+ function TokenStream(parser, expression) {
4270
+ this.pos = 0;
4271
+ this.current = null;
4272
+ this.unaryOps = parser.unaryOps;
4273
+ this.binaryOps = parser.binaryOps;
4274
+ this.ternaryOps = parser.ternaryOps;
4275
+ this.consts = parser.consts;
4276
+ this.expression = expression;
4277
+ this.savedPosition = 0;
4278
+ this.savedCurrent = null;
4279
+ this.options = parser.options;
4280
+ this.parser = parser;
4281
+ }
4282
+ TokenStream.prototype.newToken = function(type, value, pos) {
4283
+ return new Token(type, value, pos != null ? pos : this.pos);
4284
+ };
4285
+ TokenStream.prototype.save = function() {
4286
+ this.savedPosition = this.pos;
4287
+ this.savedCurrent = this.current;
4288
+ };
4289
+ TokenStream.prototype.restore = function() {
4290
+ this.pos = this.savedPosition;
4291
+ this.current = this.savedCurrent;
4292
+ };
4293
+ TokenStream.prototype.next = function() {
4294
+ if (this.pos >= this.expression.length) {
4295
+ return this.newToken(TEOF, "EOF");
4296
+ }
4297
+ if (this.isWhitespace() || this.isComment()) {
4298
+ return this.next();
4299
+ } else if (this.isRadixInteger() || this.isNumber() || this.isOperator() || this.isString() || this.isParen() || this.isBracket() || this.isComma() || this.isSemicolon() || this.isNamedOp() || this.isConst() || this.isName()) {
4300
+ return this.current;
4301
+ } else {
4302
+ this.parseError('Unknown character "' + this.expression.charAt(this.pos) + '"');
4303
+ }
4304
+ };
4305
+ TokenStream.prototype.isString = function() {
4306
+ var r = false;
4307
+ var startPos = this.pos;
4308
+ var quote = this.expression.charAt(startPos);
4309
+ if (quote === "'" || quote === '"') {
4310
+ var index2 = this.expression.indexOf(quote, startPos + 1);
4311
+ while (index2 >= 0 && this.pos < this.expression.length) {
4312
+ this.pos = index2 + 1;
4313
+ if (this.expression.charAt(index2 - 1) !== "\\") {
4314
+ var rawString = this.expression.substring(startPos + 1, index2);
4315
+ this.current = this.newToken(TSTRING, this.unescape(rawString), startPos);
4316
+ r = true;
4317
+ break;
4318
+ }
4319
+ index2 = this.expression.indexOf(quote, index2 + 1);
4320
+ }
4321
+ }
4322
+ return r;
4323
+ };
4324
+ TokenStream.prototype.isParen = function() {
4325
+ var c = this.expression.charAt(this.pos);
4326
+ if (c === "(" || c === ")") {
4327
+ this.current = this.newToken(TPAREN, c);
4328
+ this.pos++;
4329
+ return true;
4330
+ }
4331
+ return false;
4332
+ };
4333
+ TokenStream.prototype.isBracket = function() {
4334
+ var c = this.expression.charAt(this.pos);
4335
+ if ((c === "[" || c === "]") && this.isOperatorEnabled("[")) {
4336
+ this.current = this.newToken(TBRACKET, c);
4337
+ this.pos++;
4338
+ return true;
4339
+ }
4340
+ return false;
4341
+ };
4342
+ TokenStream.prototype.isComma = function() {
4343
+ var c = this.expression.charAt(this.pos);
4344
+ if (c === ",") {
4345
+ this.current = this.newToken(TCOMMA, ",");
4346
+ this.pos++;
4347
+ return true;
4348
+ }
4349
+ return false;
4350
+ };
4351
+ TokenStream.prototype.isSemicolon = function() {
4352
+ var c = this.expression.charAt(this.pos);
4353
+ if (c === ";") {
4354
+ this.current = this.newToken(TSEMICOLON, ";");
4355
+ this.pos++;
4356
+ return true;
4357
+ }
4358
+ return false;
4359
+ };
4360
+ TokenStream.prototype.isConst = function() {
4361
+ var startPos = this.pos;
4362
+ var i = startPos;
4363
+ for (; i < this.expression.length; i++) {
4364
+ var c = this.expression.charAt(i);
4365
+ if (c.toUpperCase() === c.toLowerCase()) {
4366
+ if (i === this.pos || c !== "_" && c !== "." && (c < "0" || c > "9")) {
4367
+ break;
4368
+ }
4369
+ }
4370
+ }
4371
+ if (i > startPos) {
4372
+ var str = this.expression.substring(startPos, i);
4373
+ if (str in this.consts) {
4374
+ this.current = this.newToken(TNUMBER, this.consts[str]);
4375
+ this.pos += str.length;
4376
+ return true;
4377
+ }
4378
+ }
4379
+ return false;
4380
+ };
4381
+ TokenStream.prototype.isNamedOp = function() {
4382
+ var startPos = this.pos;
4383
+ var i = startPos;
4384
+ for (; i < this.expression.length; i++) {
4385
+ var c = this.expression.charAt(i);
4386
+ if (c.toUpperCase() === c.toLowerCase()) {
4387
+ if (i === this.pos || c !== "_" && (c < "0" || c > "9")) {
4388
+ break;
4389
+ }
4390
+ }
4391
+ }
4392
+ if (i > startPos) {
4393
+ var str = this.expression.substring(startPos, i);
4394
+ if (this.isOperatorEnabled(str) && (str in this.binaryOps || str in this.unaryOps || str in this.ternaryOps)) {
4395
+ this.current = this.newToken(TOP, str);
4396
+ this.pos += str.length;
4397
+ return true;
4398
+ }
4399
+ }
4400
+ return false;
4401
+ };
4402
+ TokenStream.prototype.isName = function() {
4403
+ var startPos = this.pos;
4404
+ var i = startPos;
4405
+ var hasLetter = false;
4406
+ for (; i < this.expression.length; i++) {
4407
+ var c = this.expression.charAt(i);
4408
+ if (c.toUpperCase() === c.toLowerCase()) {
4409
+ if (i === this.pos && (c === "$" || c === "_")) {
4410
+ if (c === "_") {
4411
+ hasLetter = true;
4412
+ }
4413
+ continue;
4414
+ } else if (i === this.pos || !hasLetter || c !== "_" && (c < "0" || c > "9")) {
4415
+ break;
4416
+ }
4417
+ } else {
4418
+ hasLetter = true;
4419
+ }
4420
+ }
4421
+ if (hasLetter) {
4422
+ var str = this.expression.substring(startPos, i);
4423
+ this.current = this.newToken(TNAME, str);
4424
+ this.pos += str.length;
4425
+ return true;
4426
+ }
4427
+ return false;
4428
+ };
4429
+ TokenStream.prototype.isWhitespace = function() {
4430
+ var r = false;
4431
+ var c = this.expression.charAt(this.pos);
4432
+ while (c === " " || c === " " || c === "\n" || c === "\r") {
4433
+ r = true;
4434
+ this.pos++;
4435
+ if (this.pos >= this.expression.length) {
4436
+ break;
4437
+ }
4438
+ c = this.expression.charAt(this.pos);
4439
+ }
4440
+ return r;
4441
+ };
4442
+ var codePointPattern = /^[0-9a-f]{4}$/i;
4443
+ TokenStream.prototype.unescape = function(v) {
4444
+ var index2 = v.indexOf("\\");
4445
+ if (index2 < 0) {
4446
+ return v;
4447
+ }
4448
+ var buffer = v.substring(0, index2);
4449
+ while (index2 >= 0) {
4450
+ var c = v.charAt(++index2);
4451
+ switch (c) {
4452
+ case "'":
4453
+ buffer += "'";
4454
+ break;
4455
+ case '"':
4456
+ buffer += '"';
4457
+ break;
4458
+ case "\\":
4459
+ buffer += "\\";
4460
+ break;
4461
+ case "/":
4462
+ buffer += "/";
4463
+ break;
4464
+ case "b":
4465
+ buffer += "\b";
4466
+ break;
4467
+ case "f":
4468
+ buffer += "\f";
4469
+ break;
4470
+ case "n":
4471
+ buffer += "\n";
4472
+ break;
4473
+ case "r":
4474
+ buffer += "\r";
4475
+ break;
4476
+ case "t":
4477
+ buffer += " ";
4478
+ break;
4479
+ case "u":
4480
+ var codePoint = v.substring(index2 + 1, index2 + 5);
4481
+ if (!codePointPattern.test(codePoint)) {
4482
+ this.parseError("Illegal escape sequence: \\u" + codePoint);
4483
+ }
4484
+ buffer += String.fromCharCode(parseInt(codePoint, 16));
4485
+ index2 += 4;
4486
+ break;
4487
+ default:
4488
+ throw this.parseError('Illegal escape sequence: "\\' + c + '"');
4489
+ }
4490
+ ++index2;
4491
+ var backslash = v.indexOf("\\", index2);
4492
+ buffer += v.substring(index2, backslash < 0 ? v.length : backslash);
4493
+ index2 = backslash;
4494
+ }
4495
+ return buffer;
4496
+ };
4497
+ TokenStream.prototype.isComment = function() {
4498
+ var c = this.expression.charAt(this.pos);
4499
+ if (c === "/" && this.expression.charAt(this.pos + 1) === "*") {
4500
+ this.pos = this.expression.indexOf("*/", this.pos) + 2;
4501
+ if (this.pos === 1) {
4502
+ this.pos = this.expression.length;
4503
+ }
4504
+ return true;
4505
+ }
4506
+ return false;
4507
+ };
4508
+ TokenStream.prototype.isRadixInteger = function() {
4509
+ var pos = this.pos;
4510
+ if (pos >= this.expression.length - 2 || this.expression.charAt(pos) !== "0") {
4511
+ return false;
4512
+ }
4513
+ ++pos;
4514
+ var radix;
4515
+ var validDigit;
4516
+ if (this.expression.charAt(pos) === "x") {
4517
+ radix = 16;
4518
+ validDigit = /^[0-9a-f]$/i;
4519
+ ++pos;
4520
+ } else if (this.expression.charAt(pos) === "b") {
4521
+ radix = 2;
4522
+ validDigit = /^[01]$/i;
4523
+ ++pos;
4524
+ } else {
4525
+ return false;
4526
+ }
4527
+ var valid = false;
4528
+ var startPos = pos;
4529
+ while (pos < this.expression.length) {
4530
+ var c = this.expression.charAt(pos);
4531
+ if (validDigit.test(c)) {
4532
+ pos++;
4533
+ valid = true;
4534
+ } else {
4535
+ break;
4536
+ }
4537
+ }
4538
+ if (valid) {
4539
+ this.current = this.newToken(TNUMBER, parseInt(this.expression.substring(startPos, pos), radix));
4540
+ this.pos = pos;
4541
+ }
4542
+ return valid;
4543
+ };
4544
+ TokenStream.prototype.isNumber = function() {
4545
+ var valid = false;
4546
+ var pos = this.pos;
4547
+ var startPos = pos;
4548
+ var resetPos = pos;
4549
+ var foundDot = false;
4550
+ var foundDigits = false;
4551
+ var c;
4552
+ while (pos < this.expression.length) {
4553
+ c = this.expression.charAt(pos);
4554
+ if (c >= "0" && c <= "9" || !foundDot && c === ".") {
4555
+ if (c === ".") {
4556
+ foundDot = true;
4557
+ } else {
4558
+ foundDigits = true;
4559
+ }
4560
+ pos++;
4561
+ valid = foundDigits;
4562
+ } else {
4563
+ break;
4564
+ }
4565
+ }
4566
+ if (valid) {
4567
+ resetPos = pos;
4568
+ }
4569
+ if (c === "e" || c === "E") {
4570
+ pos++;
4571
+ var acceptSign = true;
4572
+ var validExponent = false;
4573
+ while (pos < this.expression.length) {
4574
+ c = this.expression.charAt(pos);
4575
+ if (acceptSign && (c === "+" || c === "-")) {
4576
+ acceptSign = false;
4577
+ } else if (c >= "0" && c <= "9") {
4578
+ validExponent = true;
4579
+ acceptSign = false;
4580
+ } else {
4581
+ break;
4582
+ }
4583
+ pos++;
4584
+ }
4585
+ if (!validExponent) {
4586
+ pos = resetPos;
4587
+ }
4588
+ }
4589
+ if (valid) {
4590
+ this.current = this.newToken(TNUMBER, parseFloat(this.expression.substring(startPos, pos)));
4591
+ this.pos = pos;
4592
+ } else {
4593
+ this.pos = resetPos;
4594
+ }
4595
+ return valid;
4596
+ };
4597
+ TokenStream.prototype.isOperator = function() {
4598
+ var startPos = this.pos;
4599
+ var c = this.expression.charAt(this.pos);
4600
+ if (c === "+" || c === "-" || c === "*" || c === "/" || c === "%" || c === "^" || c === "?" || c === ":" || c === ".") {
4601
+ this.current = this.newToken(TOP, c);
4602
+ } else if (c === "∙" || c === "•") {
4603
+ this.current = this.newToken(TOP, "*");
4604
+ } else if (c === ">") {
4605
+ if (this.expression.charAt(this.pos + 1) === "=") {
4606
+ this.current = this.newToken(TOP, ">=");
4607
+ this.pos++;
4608
+ } else {
4609
+ this.current = this.newToken(TOP, ">");
4610
+ }
4611
+ } else if (c === "<") {
4612
+ if (this.expression.charAt(this.pos + 1) === "=") {
4613
+ this.current = this.newToken(TOP, "<=");
4614
+ this.pos++;
4615
+ } else {
4616
+ this.current = this.newToken(TOP, "<");
4617
+ }
4618
+ } else if (c === "|") {
4619
+ if (this.expression.charAt(this.pos + 1) === "|") {
4620
+ this.current = this.newToken(TOP, "||");
4621
+ this.pos++;
4622
+ } else {
4623
+ return false;
4624
+ }
4625
+ } else if (c === "=") {
4626
+ if (this.expression.charAt(this.pos + 1) === "=") {
4627
+ this.current = this.newToken(TOP, "==");
4628
+ this.pos++;
4629
+ } else {
4630
+ this.current = this.newToken(TOP, c);
4631
+ }
4632
+ } else if (c === "!") {
4633
+ if (this.expression.charAt(this.pos + 1) === "=") {
4634
+ this.current = this.newToken(TOP, "!=");
4635
+ this.pos++;
4636
+ } else {
4637
+ this.current = this.newToken(TOP, c);
4638
+ }
4639
+ } else {
4640
+ return false;
4641
+ }
4642
+ this.pos++;
4643
+ if (this.isOperatorEnabled(this.current.value)) {
4644
+ return true;
4645
+ } else {
4646
+ this.pos = startPos;
4647
+ return false;
4648
+ }
4649
+ };
4650
+ TokenStream.prototype.isOperatorEnabled = function(op) {
4651
+ return this.parser.isOperatorEnabled(op);
4652
+ };
4653
+ TokenStream.prototype.getCoordinates = function() {
4654
+ var line = 0;
4655
+ var column;
4656
+ var newline = -1;
4657
+ do {
4658
+ line++;
4659
+ column = this.pos - newline;
4660
+ newline = this.expression.indexOf("\n", newline + 1);
4661
+ } while (newline >= 0 && newline < this.pos);
4662
+ return {
4663
+ line,
4664
+ column
4665
+ };
4666
+ };
4667
+ TokenStream.prototype.parseError = function(msg) {
4668
+ var coords = this.getCoordinates();
4669
+ throw new Error("parse error [" + coords.line + ":" + coords.column + "]: " + msg);
4670
+ };
4671
+ function ParserState(parser, tokenStream, options) {
4672
+ this.parser = parser;
4673
+ this.tokens = tokenStream;
4674
+ this.current = null;
4675
+ this.nextToken = null;
4676
+ this.next();
4677
+ this.savedCurrent = null;
4678
+ this.savedNextToken = null;
4679
+ this.allowMemberAccess = options.allowMemberAccess !== false;
4680
+ }
4681
+ ParserState.prototype.next = function() {
4682
+ this.current = this.nextToken;
4683
+ return this.nextToken = this.tokens.next();
4684
+ };
4685
+ ParserState.prototype.tokenMatches = function(token, value) {
4686
+ if (typeof value === "undefined") {
4687
+ return true;
4688
+ } else if (Array.isArray(value)) {
4689
+ return contains(value, token.value);
4690
+ } else if (typeof value === "function") {
4691
+ return value(token);
4692
+ } else {
4693
+ return token.value === value;
4694
+ }
4695
+ };
4696
+ ParserState.prototype.save = function() {
4697
+ this.savedCurrent = this.current;
4698
+ this.savedNextToken = this.nextToken;
4699
+ this.tokens.save();
4700
+ };
4701
+ ParserState.prototype.restore = function() {
4702
+ this.tokens.restore();
4703
+ this.current = this.savedCurrent;
4704
+ this.nextToken = this.savedNextToken;
4705
+ };
4706
+ ParserState.prototype.accept = function(type, value) {
4707
+ if (this.nextToken.type === type && this.tokenMatches(this.nextToken, value)) {
4708
+ this.next();
4709
+ return true;
4710
+ }
4711
+ return false;
4712
+ };
4713
+ ParserState.prototype.expect = function(type, value) {
4714
+ if (!this.accept(type, value)) {
4715
+ var coords = this.tokens.getCoordinates();
4716
+ throw new Error("parse error [" + coords.line + ":" + coords.column + "]: Expected " + (value || type));
4717
+ }
4718
+ };
4719
+ ParserState.prototype.parseAtom = function(instr) {
4720
+ var unaryOps = this.tokens.unaryOps;
4721
+ function isPrefixOperator(token) {
4722
+ return token.value in unaryOps;
4723
+ }
4724
+ if (this.accept(TNAME) || this.accept(TOP, isPrefixOperator)) {
4725
+ instr.push(new Instruction(IVAR, this.current.value));
4726
+ } else if (this.accept(TNUMBER)) {
4727
+ instr.push(new Instruction(INUMBER, this.current.value));
4728
+ } else if (this.accept(TSTRING)) {
4729
+ instr.push(new Instruction(INUMBER, this.current.value));
4730
+ } else if (this.accept(TPAREN, "(")) {
4731
+ this.parseExpression(instr);
4732
+ this.expect(TPAREN, ")");
4733
+ } else if (this.accept(TBRACKET, "[")) {
4734
+ if (this.accept(TBRACKET, "]")) {
4735
+ instr.push(new Instruction(IARRAY, 0));
4736
+ } else {
4737
+ var argCount = this.parseArrayList(instr);
4738
+ instr.push(new Instruction(IARRAY, argCount));
4739
+ }
4740
+ } else {
4741
+ throw new Error("unexpected " + this.nextToken);
4742
+ }
4743
+ };
4744
+ ParserState.prototype.parseExpression = function(instr) {
4745
+ var exprInstr = [];
4746
+ if (this.parseUntilEndStatement(instr, exprInstr)) {
4747
+ return;
4748
+ }
4749
+ this.parseVariableAssignmentExpression(exprInstr);
4750
+ if (this.parseUntilEndStatement(instr, exprInstr)) {
4751
+ return;
4752
+ }
4753
+ this.pushExpression(instr, exprInstr);
4754
+ };
4755
+ ParserState.prototype.pushExpression = function(instr, exprInstr) {
4756
+ for (var i = 0, len2 = exprInstr.length; i < len2; i++) {
4757
+ instr.push(exprInstr[i]);
4758
+ }
4759
+ };
4760
+ ParserState.prototype.parseUntilEndStatement = function(instr, exprInstr) {
4761
+ if (!this.accept(TSEMICOLON)) return false;
4762
+ if (this.nextToken && this.nextToken.type !== TEOF && !(this.nextToken.type === TPAREN && this.nextToken.value === ")")) {
4763
+ exprInstr.push(new Instruction(IENDSTATEMENT));
4764
+ }
4765
+ if (this.nextToken.type !== TEOF) {
4766
+ this.parseExpression(exprInstr);
4767
+ }
4768
+ instr.push(new Instruction(IEXPR, exprInstr));
4769
+ return true;
4770
+ };
4771
+ ParserState.prototype.parseArrayList = function(instr) {
4772
+ var argCount = 0;
4773
+ while (!this.accept(TBRACKET, "]")) {
4774
+ this.parseExpression(instr);
4775
+ ++argCount;
4776
+ while (this.accept(TCOMMA)) {
4777
+ this.parseExpression(instr);
4778
+ ++argCount;
4779
+ }
4780
+ }
4781
+ return argCount;
4782
+ };
4783
+ ParserState.prototype.parseVariableAssignmentExpression = function(instr) {
4784
+ this.parseConditionalExpression(instr);
4785
+ while (this.accept(TOP, "=")) {
4786
+ var varName = instr.pop();
4787
+ var varValue = [];
4788
+ var lastInstrIndex = instr.length - 1;
4789
+ if (varName.type === IFUNCALL) {
4790
+ if (!this.tokens.isOperatorEnabled("()=")) {
4791
+ throw new Error("function definition is not permitted");
4792
+ }
4793
+ for (var i = 0, len2 = varName.value + 1; i < len2; i++) {
4794
+ var index2 = lastInstrIndex - i;
4795
+ if (instr[index2].type === IVAR) {
4796
+ instr[index2] = new Instruction(IVARNAME, instr[index2].value);
4797
+ }
4798
+ }
4799
+ this.parseVariableAssignmentExpression(varValue);
4800
+ instr.push(new Instruction(IEXPR, varValue));
4801
+ instr.push(new Instruction(IFUNDEF, varName.value));
4802
+ continue;
4803
+ }
4804
+ if (varName.type !== IVAR && varName.type !== IMEMBER) {
4805
+ throw new Error("expected variable for assignment");
4806
+ }
4807
+ this.parseVariableAssignmentExpression(varValue);
4808
+ instr.push(new Instruction(IVARNAME, varName.value));
4809
+ instr.push(new Instruction(IEXPR, varValue));
4810
+ instr.push(binaryInstruction("="));
4811
+ }
4812
+ };
4813
+ ParserState.prototype.parseConditionalExpression = function(instr) {
4814
+ this.parseOrExpression(instr);
4815
+ while (this.accept(TOP, "?")) {
4816
+ var trueBranch = [];
4817
+ var falseBranch = [];
4818
+ this.parseConditionalExpression(trueBranch);
4819
+ this.expect(TOP, ":");
4820
+ this.parseConditionalExpression(falseBranch);
4821
+ instr.push(new Instruction(IEXPR, trueBranch));
4822
+ instr.push(new Instruction(IEXPR, falseBranch));
4823
+ instr.push(ternaryInstruction("?"));
4824
+ }
4825
+ };
4826
+ ParserState.prototype.parseOrExpression = function(instr) {
4827
+ this.parseAndExpression(instr);
4828
+ while (this.accept(TOP, "or")) {
4829
+ var falseBranch = [];
4830
+ this.parseAndExpression(falseBranch);
4831
+ instr.push(new Instruction(IEXPR, falseBranch));
4832
+ instr.push(binaryInstruction("or"));
4833
+ }
4834
+ };
4835
+ ParserState.prototype.parseAndExpression = function(instr) {
4836
+ this.parseComparison(instr);
4837
+ while (this.accept(TOP, "and")) {
4838
+ var trueBranch = [];
4839
+ this.parseComparison(trueBranch);
4840
+ instr.push(new Instruction(IEXPR, trueBranch));
4841
+ instr.push(binaryInstruction("and"));
4842
+ }
4843
+ };
4844
+ var COMPARISON_OPERATORS = ["==", "!=", "<", "<=", ">=", ">", "in"];
4845
+ ParserState.prototype.parseComparison = function(instr) {
4846
+ this.parseAddSub(instr);
4847
+ while (this.accept(TOP, COMPARISON_OPERATORS)) {
4848
+ var op = this.current;
4849
+ this.parseAddSub(instr);
4850
+ instr.push(binaryInstruction(op.value));
4851
+ }
4852
+ };
4853
+ var ADD_SUB_OPERATORS = ["+", "-", "||"];
4854
+ ParserState.prototype.parseAddSub = function(instr) {
4855
+ this.parseTerm(instr);
4856
+ while (this.accept(TOP, ADD_SUB_OPERATORS)) {
4857
+ var op = this.current;
4858
+ this.parseTerm(instr);
4859
+ instr.push(binaryInstruction(op.value));
4860
+ }
4861
+ };
4862
+ var TERM_OPERATORS = ["*", "/", "%"];
4863
+ ParserState.prototype.parseTerm = function(instr) {
4864
+ this.parseFactor(instr);
4865
+ while (this.accept(TOP, TERM_OPERATORS)) {
4866
+ var op = this.current;
4867
+ this.parseFactor(instr);
4868
+ instr.push(binaryInstruction(op.value));
4869
+ }
4870
+ };
4871
+ ParserState.prototype.parseFactor = function(instr) {
4872
+ var unaryOps = this.tokens.unaryOps;
4873
+ function isPrefixOperator(token) {
4874
+ return token.value in unaryOps;
4875
+ }
4876
+ this.save();
4877
+ if (this.accept(TOP, isPrefixOperator)) {
4878
+ if (this.current.value !== "-" && this.current.value !== "+") {
4879
+ if (this.nextToken.type === TPAREN && this.nextToken.value === "(") {
4880
+ this.restore();
4881
+ this.parseExponential(instr);
4882
+ return;
4883
+ } else if (this.nextToken.type === TSEMICOLON || this.nextToken.type === TCOMMA || this.nextToken.type === TEOF || this.nextToken.type === TPAREN && this.nextToken.value === ")") {
4884
+ this.restore();
4885
+ this.parseAtom(instr);
4886
+ return;
4887
+ }
4888
+ }
4889
+ var op = this.current;
4890
+ this.parseFactor(instr);
4891
+ instr.push(unaryInstruction(op.value));
4892
+ } else {
4893
+ this.parseExponential(instr);
4894
+ }
4895
+ };
4896
+ ParserState.prototype.parseExponential = function(instr) {
4897
+ this.parsePostfixExpression(instr);
4898
+ while (this.accept(TOP, "^")) {
4899
+ this.parseFactor(instr);
4900
+ instr.push(binaryInstruction("^"));
4901
+ }
4902
+ };
4903
+ ParserState.prototype.parsePostfixExpression = function(instr) {
4904
+ this.parseFunctionCall(instr);
4905
+ while (this.accept(TOP, "!")) {
4906
+ instr.push(unaryInstruction("!"));
4907
+ }
4908
+ };
4909
+ ParserState.prototype.parseFunctionCall = function(instr) {
4910
+ var unaryOps = this.tokens.unaryOps;
4911
+ function isPrefixOperator(token) {
4912
+ return token.value in unaryOps;
4913
+ }
4914
+ if (this.accept(TOP, isPrefixOperator)) {
4915
+ var op = this.current;
4916
+ this.parseAtom(instr);
4917
+ instr.push(unaryInstruction(op.value));
4918
+ } else {
4919
+ this.parseMemberExpression(instr);
4920
+ while (this.accept(TPAREN, "(")) {
4921
+ if (this.accept(TPAREN, ")")) {
4922
+ instr.push(new Instruction(IFUNCALL, 0));
4923
+ } else {
4924
+ var argCount = this.parseArgumentList(instr);
4925
+ instr.push(new Instruction(IFUNCALL, argCount));
4926
+ }
4927
+ }
4928
+ }
4929
+ };
4930
+ ParserState.prototype.parseArgumentList = function(instr) {
4931
+ var argCount = 0;
4932
+ while (!this.accept(TPAREN, ")")) {
4933
+ this.parseExpression(instr);
4934
+ ++argCount;
4935
+ while (this.accept(TCOMMA)) {
4936
+ this.parseExpression(instr);
4937
+ ++argCount;
4938
+ }
4939
+ }
4940
+ return argCount;
4941
+ };
4942
+ ParserState.prototype.parseMemberExpression = function(instr) {
4943
+ this.parseAtom(instr);
4944
+ while (this.accept(TOP, ".") || this.accept(TBRACKET, "[")) {
4945
+ var op = this.current;
4946
+ if (op.value === ".") {
4947
+ if (!this.allowMemberAccess) {
4948
+ throw new Error('unexpected ".", member access is not permitted');
4949
+ }
4950
+ this.expect(TNAME);
4951
+ instr.push(new Instruction(IMEMBER, this.current.value));
4952
+ } else if (op.value === "[") {
4953
+ if (!this.tokens.isOperatorEnabled("[")) {
4954
+ throw new Error('unexpected "[]", arrays are disabled');
4955
+ }
4956
+ this.parseExpression(instr);
4957
+ this.expect(TBRACKET, "]");
4958
+ instr.push(binaryInstruction("["));
4959
+ } else {
4960
+ throw new Error("unexpected symbol: " + op.value);
4961
+ }
4962
+ }
4963
+ };
4964
+ function add(a, b) {
4965
+ return Number(a) + Number(b);
4966
+ }
4967
+ function sub(a, b) {
4968
+ return a - b;
4969
+ }
4970
+ function mul(a, b) {
4971
+ return a * b;
4972
+ }
4973
+ function div(a, b) {
4974
+ return a / b;
4975
+ }
4976
+ function mod(a, b) {
4977
+ return a % b;
4978
+ }
4979
+ function concat(a, b) {
4980
+ if (Array.isArray(a) && Array.isArray(b)) {
4981
+ return a.concat(b);
4982
+ }
4983
+ return "" + a + b;
4984
+ }
4985
+ function equal(a, b) {
4986
+ return a === b;
4987
+ }
4988
+ function notEqual(a, b) {
4989
+ return a !== b;
4990
+ }
4991
+ function greaterThan(a, b) {
4992
+ return a > b;
4993
+ }
4994
+ function lessThan(a, b) {
4995
+ return a < b;
4996
+ }
4997
+ function greaterThanEqual(a, b) {
4998
+ return a >= b;
4999
+ }
5000
+ function lessThanEqual(a, b) {
5001
+ return a <= b;
5002
+ }
5003
+ function andOperator(a, b) {
5004
+ return Boolean(a && b);
5005
+ }
5006
+ function orOperator(a, b) {
5007
+ return Boolean(a || b);
5008
+ }
5009
+ function inOperator(a, b) {
5010
+ return contains(b, a);
5011
+ }
5012
+ function sinh(a) {
5013
+ return (Math.exp(a) - Math.exp(-a)) / 2;
5014
+ }
5015
+ function cosh(a) {
5016
+ return (Math.exp(a) + Math.exp(-a)) / 2;
5017
+ }
5018
+ function tanh(a) {
5019
+ if (a === Infinity) return 1;
5020
+ if (a === -Infinity) return -1;
5021
+ return (Math.exp(a) - Math.exp(-a)) / (Math.exp(a) + Math.exp(-a));
5022
+ }
5023
+ function asinh(a) {
5024
+ if (a === -Infinity) return a;
5025
+ return Math.log(a + Math.sqrt(a * a + 1));
5026
+ }
5027
+ function acosh(a) {
5028
+ return Math.log(a + Math.sqrt(a * a - 1));
5029
+ }
5030
+ function atanh(a) {
5031
+ return Math.log((1 + a) / (1 - a)) / 2;
5032
+ }
5033
+ function log10(a) {
5034
+ return Math.log(a) * Math.LOG10E;
5035
+ }
5036
+ function neg(a) {
5037
+ return -a;
5038
+ }
5039
+ function not(a) {
5040
+ return !a;
5041
+ }
5042
+ function trunc(a) {
5043
+ return a < 0 ? Math.ceil(a) : Math.floor(a);
5044
+ }
5045
+ function random(a) {
5046
+ return Math.random() * (a || 1);
5047
+ }
5048
+ function factorial(a) {
5049
+ return gamma(a + 1);
5050
+ }
5051
+ function isInteger(value) {
5052
+ return isFinite(value) && value === Math.round(value);
5053
+ }
5054
+ var GAMMA_G = 4.7421875;
5055
+ var GAMMA_P = [
5056
+ 0.9999999999999971,
5057
+ 57.15623566586292,
5058
+ -59.59796035547549,
5059
+ 14.136097974741746,
5060
+ -0.4919138160976202,
5061
+ 3399464998481189e-20,
5062
+ 4652362892704858e-20,
5063
+ -9837447530487956e-20,
5064
+ 1580887032249125e-19,
5065
+ -21026444172410488e-20,
5066
+ 21743961811521265e-20,
5067
+ -1643181065367639e-19,
5068
+ 8441822398385275e-20,
5069
+ -26190838401581408e-21,
5070
+ 36899182659531625e-22
5071
+ ];
5072
+ function gamma(n) {
5073
+ var t, x;
5074
+ if (isInteger(n)) {
5075
+ if (n <= 0) {
5076
+ return isFinite(n) ? Infinity : NaN;
5077
+ }
5078
+ if (n > 171) {
5079
+ return Infinity;
5080
+ }
5081
+ var value = n - 2;
5082
+ var res = n - 1;
5083
+ while (value > 1) {
5084
+ res *= value;
5085
+ value--;
5086
+ }
5087
+ if (res === 0) {
5088
+ res = 1;
5089
+ }
5090
+ return res;
5091
+ }
5092
+ if (n < 0.5) {
5093
+ return Math.PI / (Math.sin(Math.PI * n) * gamma(1 - n));
5094
+ }
5095
+ if (n >= 171.35) {
5096
+ return Infinity;
5097
+ }
5098
+ if (n > 85) {
5099
+ var twoN = n * n;
5100
+ var threeN = twoN * n;
5101
+ var fourN = threeN * n;
5102
+ var fiveN = fourN * n;
5103
+ return Math.sqrt(2 * Math.PI / n) * Math.pow(n / Math.E, n) * (1 + 1 / (12 * n) + 1 / (288 * twoN) - 139 / (51840 * threeN) - 571 / (2488320 * fourN) + 163879 / (209018880 * fiveN) + 5246819 / (75246796800 * fiveN * n));
5104
+ }
5105
+ --n;
5106
+ x = GAMMA_P[0];
5107
+ for (var i = 1; i < GAMMA_P.length; ++i) {
5108
+ x += GAMMA_P[i] / (n + i);
5109
+ }
5110
+ t = n + GAMMA_G + 0.5;
5111
+ return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x;
5112
+ }
5113
+ function stringOrArrayLength(s) {
5114
+ if (Array.isArray(s)) {
5115
+ return s.length;
5116
+ }
5117
+ return String(s).length;
5118
+ }
5119
+ function hypot() {
5120
+ var sum2 = 0;
5121
+ var larg = 0;
5122
+ for (var i = 0; i < arguments.length; i++) {
5123
+ var arg = Math.abs(arguments[i]);
5124
+ var div2;
5125
+ if (larg < arg) {
5126
+ div2 = larg / arg;
5127
+ sum2 = sum2 * div2 * div2 + 1;
5128
+ larg = arg;
5129
+ } else if (arg > 0) {
5130
+ div2 = arg / larg;
5131
+ sum2 += div2 * div2;
5132
+ } else {
5133
+ sum2 += arg;
5134
+ }
5135
+ }
5136
+ return larg === Infinity ? Infinity : larg * Math.sqrt(sum2);
5137
+ }
5138
+ function condition(cond, yep, nope) {
5139
+ return cond ? yep : nope;
5140
+ }
5141
+ function roundTo(value, exp) {
5142
+ if (typeof exp === "undefined" || +exp === 0) {
5143
+ return Math.round(value);
5144
+ }
5145
+ value = +value;
5146
+ exp = -+exp;
5147
+ if (isNaN(value) || !(typeof exp === "number" && exp % 1 === 0)) {
5148
+ return NaN;
5149
+ }
5150
+ value = value.toString().split("e");
5151
+ value = Math.round(+(value[0] + "e" + (value[1] ? +value[1] - exp : -exp)));
5152
+ value = value.toString().split("e");
5153
+ return +(value[0] + "e" + (value[1] ? +value[1] + exp : exp));
5154
+ }
5155
+ function setVar(name, value, variables) {
5156
+ if (variables) variables[name] = value;
5157
+ return value;
5158
+ }
5159
+ function arrayIndex(array, index2) {
5160
+ return array[index2 | 0];
5161
+ }
5162
+ function max(array) {
5163
+ if (arguments.length === 1 && Array.isArray(array)) {
5164
+ return Math.max.apply(Math, array);
5165
+ } else {
5166
+ return Math.max.apply(Math, arguments);
5167
+ }
5168
+ }
5169
+ function min(array) {
5170
+ if (arguments.length === 1 && Array.isArray(array)) {
5171
+ return Math.min.apply(Math, array);
5172
+ } else {
5173
+ return Math.min.apply(Math, arguments);
5174
+ }
5175
+ }
5176
+ function arrayMap(f, a) {
5177
+ if (typeof f !== "function") {
5178
+ throw new Error("First argument to map is not a function");
5179
+ }
5180
+ if (!Array.isArray(a)) {
5181
+ throw new Error("Second argument to map is not an array");
5182
+ }
5183
+ return a.map(function(x, i) {
5184
+ return f(x, i);
5185
+ });
5186
+ }
5187
+ function arrayFold(f, init, a) {
5188
+ if (typeof f !== "function") {
5189
+ throw new Error("First argument to fold is not a function");
5190
+ }
5191
+ if (!Array.isArray(a)) {
5192
+ throw new Error("Second argument to fold is not an array");
5193
+ }
5194
+ return a.reduce(function(acc, x, i) {
5195
+ return f(acc, x, i);
5196
+ }, init);
5197
+ }
5198
+ function arrayFilter(f, a) {
5199
+ if (typeof f !== "function") {
5200
+ throw new Error("First argument to filter is not a function");
5201
+ }
5202
+ if (!Array.isArray(a)) {
5203
+ throw new Error("Second argument to filter is not an array");
5204
+ }
5205
+ return a.filter(function(x, i) {
5206
+ return f(x, i);
5207
+ });
5208
+ }
5209
+ function stringOrArrayIndexOf(target, s) {
5210
+ if (!(Array.isArray(s) || typeof s === "string")) {
5211
+ throw new Error("Second argument to indexOf is not a string or array");
5212
+ }
5213
+ return s.indexOf(target);
5214
+ }
5215
+ function arrayJoin(sep, a) {
5216
+ if (!Array.isArray(a)) {
5217
+ throw new Error("Second argument to join is not an array");
5218
+ }
5219
+ return a.join(sep);
5220
+ }
5221
+ function sign(x) {
5222
+ return (x > 0) - (x < 0) || +x;
5223
+ }
5224
+ var ONE_THIRD = 1 / 3;
5225
+ function cbrt(x) {
5226
+ return x < 0 ? -Math.pow(-x, ONE_THIRD) : Math.pow(x, ONE_THIRD);
5227
+ }
5228
+ function expm1(x) {
5229
+ return Math.exp(x) - 1;
5230
+ }
5231
+ function log1p(x) {
5232
+ return Math.log(1 + x);
5233
+ }
5234
+ function log2(x) {
5235
+ return Math.log(x) / Math.LN2;
5236
+ }
5237
+ function Parser(options) {
5238
+ this.options = options || {};
5239
+ this.unaryOps = {
5240
+ sin: Math.sin,
5241
+ cos: Math.cos,
5242
+ tan: Math.tan,
5243
+ asin: Math.asin,
5244
+ acos: Math.acos,
5245
+ atan: Math.atan,
5246
+ sinh: Math.sinh || sinh,
5247
+ cosh: Math.cosh || cosh,
5248
+ tanh: Math.tanh || tanh,
5249
+ asinh: Math.asinh || asinh,
5250
+ acosh: Math.acosh || acosh,
5251
+ atanh: Math.atanh || atanh,
5252
+ sqrt: Math.sqrt,
5253
+ cbrt: Math.cbrt || cbrt,
5254
+ log: Math.log,
5255
+ log2: Math.log2 || log2,
5256
+ ln: Math.log,
5257
+ lg: Math.log10 || log10,
5258
+ log10: Math.log10 || log10,
5259
+ expm1: Math.expm1 || expm1,
5260
+ log1p: Math.log1p || log1p,
5261
+ abs: Math.abs,
5262
+ ceil: Math.ceil,
5263
+ floor: Math.floor,
5264
+ round: Math.round,
5265
+ trunc: Math.trunc || trunc,
5266
+ "-": neg,
5267
+ "+": Number,
5268
+ exp: Math.exp,
5269
+ not,
5270
+ length: stringOrArrayLength,
5271
+ "!": factorial,
5272
+ sign: Math.sign || sign
5273
+ };
5274
+ this.binaryOps = {
5275
+ "+": add,
5276
+ "-": sub,
5277
+ "*": mul,
5278
+ "/": div,
5279
+ "%": mod,
5280
+ "^": Math.pow,
5281
+ "||": concat,
5282
+ "==": equal,
5283
+ "!=": notEqual,
5284
+ ">": greaterThan,
5285
+ "<": lessThan,
5286
+ ">=": greaterThanEqual,
5287
+ "<=": lessThanEqual,
5288
+ and: andOperator,
5289
+ or: orOperator,
5290
+ "in": inOperator,
5291
+ "=": setVar,
5292
+ "[": arrayIndex
5293
+ };
5294
+ this.ternaryOps = {
5295
+ "?": condition
5296
+ };
5297
+ this.functions = {
5298
+ random,
5299
+ fac: factorial,
5300
+ min,
5301
+ max,
5302
+ hypot: Math.hypot || hypot,
5303
+ pyt: Math.hypot || hypot,
5304
+ // backward compat
5305
+ pow: Math.pow,
5306
+ atan2: Math.atan2,
5307
+ "if": condition,
5308
+ gamma,
5309
+ roundTo,
5310
+ map: arrayMap,
5311
+ fold: arrayFold,
5312
+ filter: arrayFilter,
5313
+ indexOf: stringOrArrayIndexOf,
5314
+ join: arrayJoin
5315
+ };
5316
+ this.consts = {
5317
+ E: Math.E,
5318
+ PI: Math.PI,
5319
+ "true": true,
5320
+ "false": false
5321
+ };
5322
+ }
5323
+ Parser.prototype.parse = function(expr) {
5324
+ var instr = [];
5325
+ var parserState = new ParserState(
5326
+ this,
5327
+ new TokenStream(this, expr),
5328
+ { allowMemberAccess: this.options.allowMemberAccess }
5329
+ );
5330
+ parserState.parseExpression(instr);
5331
+ parserState.expect(TEOF, "EOF");
5332
+ return new Expression(instr, this);
5333
+ };
5334
+ Parser.prototype.evaluate = function(expr, variables) {
5335
+ return this.parse(expr).evaluate(variables);
5336
+ };
5337
+ var sharedParser = new Parser();
5338
+ Parser.parse = function(expr) {
5339
+ return sharedParser.parse(expr);
5340
+ };
5341
+ Parser.evaluate = function(expr, variables) {
5342
+ return sharedParser.parse(expr).evaluate(variables);
5343
+ };
5344
+ var optionNameMap = {
5345
+ "+": "add",
5346
+ "-": "subtract",
5347
+ "*": "multiply",
5348
+ "/": "divide",
5349
+ "%": "remainder",
5350
+ "^": "power",
5351
+ "!": "factorial",
5352
+ "<": "comparison",
5353
+ ">": "comparison",
5354
+ "<=": "comparison",
5355
+ ">=": "comparison",
5356
+ "==": "comparison",
5357
+ "!=": "comparison",
5358
+ "||": "concatenate",
5359
+ "and": "logical",
5360
+ "or": "logical",
5361
+ "not": "logical",
5362
+ "?": "conditional",
5363
+ ":": "conditional",
5364
+ "=": "assignment",
5365
+ "[": "array",
5366
+ "()=": "fndef"
5367
+ };
5368
+ function getOptionName(op) {
5369
+ return optionNameMap.hasOwnProperty(op) ? optionNameMap[op] : op;
5370
+ }
5371
+ Parser.prototype.isOperatorEnabled = function(op) {
5372
+ var optionName = getOptionName(op);
5373
+ var operators2 = this.options.operators || {};
5374
+ return !(optionName in operators2) || !!operators2[optionName];
5375
+ };
5376
+ const calc = (expression, context) => {
5377
+ if (typeof expression !== "string") {
5378
+ return expression;
5379
+ }
5380
+ let processedExpression = expression;
5381
+ try {
5382
+ const pathResolver = (path) => {
5383
+ let currentPath = path;
5384
+ let value;
5385
+ let depth = 0;
5386
+ while (typeof currentPath === "string" && (currentPath.startsWith("/") || currentPath.startsWith("=/")) && depth < 5) {
5387
+ const normalizedPath = currentPath.startsWith("/") ? "=" + currentPath : currentPath;
5388
+ const resolved = resolvePath(normalizedPath, context);
5389
+ value = unwrapSignal(resolved);
5390
+ if (typeof value === "string" && (value.startsWith("/") || value.startsWith("=/")) && value !== currentPath) {
5391
+ currentPath = value;
5392
+ depth++;
5393
+ } else {
5394
+ break;
5395
+ }
5396
+ }
5397
+ if (typeof value === "number") return value;
5398
+ if (typeof value === "string") {
5399
+ const num = parseFloat(value);
5400
+ if (!isNaN(num) && isFinite(Number(value))) return num;
5401
+ return value === "" ? 0 : `"${value.replace(/"/g, '\\"')}"`;
5402
+ }
5403
+ return value === void 0 || value === null ? 0 : value;
5404
+ };
5405
+ const pathRegex = /\$\(\s*['"](.*?)['"]\s*\)/g;
5406
+ processedExpression = expression.replace(pathRegex, (match2, path) => {
5407
+ const val = pathResolver(path);
5408
+ return val;
5409
+ });
5410
+ const parser = new Parser();
5411
+ const parsed = parser.parse(processedExpression);
5412
+ return parsed.evaluate();
5413
+ } catch (error) {
5414
+ console.error("JPRX calc error:", error.message);
5415
+ console.error("Original expression:", expression);
5416
+ console.error("Processed expression:", processedExpression);
5417
+ return NaN;
5418
+ }
5419
+ };
5420
+ const registerCalcHelpers = (register) => {
5421
+ register("calc", calc, { pathAware: true });
5422
+ };
5423
+ registerMathHelpers(registerHelper);
5424
+ registerLogicHelpers(registerHelper);
5425
+ registerStringHelpers(registerHelper);
5426
+ registerArrayHelpers(registerHelper);
5427
+ registerCompareHelpers(registerHelper);
5428
+ registerConditionalHelpers(registerHelper);
5429
+ registerDateTimeHelpers(registerHelper);
5430
+ registerFormatHelpers(registerHelper);
5431
+ registerLookupHelpers(registerHelper);
5432
+ registerStatsHelpers(registerHelper);
5433
+ registerStateHelpers((name, fn) => registerHelper(name, fn, { pathAware: true }));
5434
+ registerNetworkHelpers(registerHelper);
5435
+ registerCalcHelpers(registerHelper);
5436
+ registerHelper("move", (selector, location = "beforeend") => {
5437
+ return {
5438
+ isLazy: true,
5439
+ resolve: (eventOrNode) => {
5440
+ const isEvent = eventOrNode && typeof eventOrNode === "object" && "target" in eventOrNode;
5441
+ const node = isEvent ? eventOrNode.currentTarget || eventOrNode.target : eventOrNode;
5442
+ if (!(node instanceof Node) || !selector) return;
5443
+ const target = document.querySelector(selector);
5444
+ if (!target) {
5445
+ console.warn(`[Lightview-CDOM] move target not found: ${selector}`);
5446
+ return;
5447
+ }
5448
+ if (node.id) {
5449
+ const escapedId = CSS.escape(node.id);
5450
+ if (target.id === node.id && target !== node) {
5451
+ target.replaceWith(node);
5452
+ return;
5453
+ }
5454
+ const existing = target.querySelector(`#${escapedId}`);
5455
+ if (existing && existing !== node) {
5456
+ existing.replaceWith(node);
5457
+ return;
5458
+ }
5459
+ }
5460
+ globalThis.Lightview.$(target).content(node, location);
5461
+ }
5462
+ };
5463
+ }, { pathAware: true });
5464
+ registerHelper("mount", async (url, options = {}) => {
5465
+ const { target = "body", location = "beforeend" } = options;
5466
+ try {
5467
+ const fetchOptions = { ...options };
5468
+ delete fetchOptions.target;
5469
+ delete fetchOptions.location;
5470
+ const headers = { ...fetchOptions.headers };
5471
+ let body = fetchOptions.body;
5472
+ if (body !== void 0) {
5473
+ if (body !== null && typeof body === "object") {
5474
+ body = JSON.stringify(body);
5475
+ if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
5476
+ } else {
5477
+ body = String(body);
5478
+ if (!headers["Content-Type"]) headers["Content-Type"] = "text/plain";
5479
+ }
5480
+ fetchOptions.body = body;
5481
+ fetchOptions.headers = headers;
5482
+ }
5483
+ const response = await globalThis.fetch(url, fetchOptions);
5484
+ const contentType = response.headers.get("Content-Type") || "";
5485
+ const text = await response.text();
5486
+ let content = text;
5487
+ const isCDOM = contentType.includes("application/cdom") || contentType.includes("application/jprx") || contentType.includes("application/vdom") || contentType.includes("application/odom") || url.endsWith(".cdom") || url.endsWith(".jprx") || url.endsWith(".vdom") || url.endsWith(".odom");
5488
+ if (isCDOM || contentType.includes("application/json") && text.trim().startsWith("{")) {
5489
+ try {
5490
+ content = hydrate(parseJPRX(text));
5491
+ } catch (e) {
5492
+ }
5493
+ }
5494
+ const targetEl = document.querySelector(target);
5495
+ if (targetEl) {
5496
+ globalThis.Lightview.$(targetEl).content(content, location);
5497
+ } else {
5498
+ console.warn(`[Lightview-CDOM] $mount target not found: ${target}`);
5499
+ }
5500
+ } catch (err) {
5501
+ console.error(`[Lightview-CDOM] $mount failed for ${url}:`, err);
5502
+ }
5503
+ });
5504
+ registerOperator("increment", "++", "prefix", 80);
5505
+ registerOperator("increment", "++", "postfix", 80);
5506
+ registerOperator("decrement", "--", "prefix", 80);
5507
+ registerOperator("decrement", "--", "postfix", 80);
5508
+ registerOperator("toggle", "!!", "prefix", 80);
5509
+ registerOperator("+", "+", "infix", 50);
5510
+ registerOperator("-", "-", "infix", 50);
5511
+ registerOperator("*", "*", "infix", 60);
5512
+ registerOperator("/", "/", "infix", 60);
5513
+ registerOperator("gt", ">", "infix", 40);
5514
+ registerOperator("lt", "<", "infix", 40);
5515
+ registerOperator("gte", ">=", "infix", 40);
5516
+ registerOperator("lte", "<=", "infix", 40);
5517
+ registerOperator("neq", "!=", "infix", 40);
5518
+ const getContext = (node, event = null) => {
5519
+ return new Proxy({}, {
5520
+ get(_, prop) {
5521
+ if (prop === "$event" || prop === "event") return event;
5522
+ if (prop === "$this" || prop === "this" || prop === "__node__") return node;
5523
+ return unwrapSignal(globalThis.Lightview.getState(prop, { scope: node }));
5524
+ },
5525
+ set(_, prop, value) {
5526
+ const res = globalThis.Lightview.getState(prop, { scope: node });
5527
+ if (res && (typeof res === "object" || typeof res === "function") && "value" in res) {
5528
+ res.value = value;
5529
+ return true;
5530
+ }
5531
+ return false;
5532
+ }
5533
+ });
5534
+ };
5535
+ globalThis.Lightview.hooks.processAttribute = (domNode, key, value) => {
5536
+ if (value == null ? void 0 : value.__JPRX_BIND__) {
5537
+ const { path, options } = value;
5538
+ const type = domNode.type || "";
5539
+ const tagName = domNode.tagName.toLowerCase();
5540
+ let prop = "value";
5541
+ let event = "input";
5542
+ if (type === "checkbox" || type === "radio") {
5543
+ prop = "checked";
5544
+ event = "change";
5545
+ } else if (tagName === "select") {
5546
+ event = "change";
5547
+ }
5548
+ const res = globalThis.Lightview.get(path.replace(/^=/, ""), { scope: domNode });
5549
+ const runner = globalThis.Lightview.effect(() => {
5550
+ const val = unwrapSignal(res);
5551
+ if (domNode[prop] !== val) {
5552
+ domNode[prop] = val === void 0 ? "" : val;
5553
+ }
5554
+ });
5555
+ globalThis.Lightview.internals.trackEffect(domNode, runner);
5556
+ domNode.addEventListener(event, () => {
5557
+ if (res && "value" in res) res.value = domNode[prop];
5558
+ });
5559
+ return unwrapSignal(res) ?? domNode[prop];
5560
+ }
5561
+ return void 0;
5562
+ };
5563
+ const activate = (root = document.body) => {
5564
+ };
5565
+ const makeEventHandler = (expr) => (eventOrNode) => {
5566
+ const isEvent = eventOrNode && typeof eventOrNode === "object" && "target" in eventOrNode;
5567
+ const target = isEvent ? eventOrNode.currentTarget || eventOrNode.target : eventOrNode;
5568
+ const context = getContext(target, isEvent ? eventOrNode : null);
5569
+ const result = resolveExpression$1(expr, context);
5570
+ if (result && typeof result === "object" && result.isLazy) return result.resolve(context);
5571
+ return result;
5572
+ };
5573
+ const hydrate = (node, parent = null) => {
5574
+ var _a2, _b2, _c;
5575
+ if (!node) return node;
5576
+ if (typeof node === "string" && node.startsWith("'=")) {
5577
+ return node.slice(1);
5578
+ }
5579
+ if (typeof node === "string" && node.startsWith("=")) {
5580
+ return parseExpression(node, parent);
5581
+ }
5582
+ if (typeof node !== "object") return node;
5583
+ if (Array.isArray(node)) {
5584
+ return node.map((item) => hydrate(item, parent));
5585
+ }
5586
+ if (node instanceof String) return node.toString();
5587
+ if (parent && !("__parent__" in node)) {
5588
+ Object.defineProperty(node, "__parent__", { value: parent, enumerable: false, writable: true });
5589
+ (_c = (_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals) == null ? void 0 : _b2.parents) == null ? void 0 : _c.set(node, parent);
5590
+ }
5591
+ if (!node.tag) {
5592
+ let potentialTag = null;
5593
+ const reserved = ["children", "attributes", "tag", "__parent__"];
5594
+ for (const key in node) {
5595
+ if (reserved.includes(key) || key.startsWith("on")) continue;
5596
+ potentialTag = key;
5597
+ break;
5598
+ }
5599
+ if (potentialTag) {
5600
+ const content = node[potentialTag];
5601
+ node.tag = potentialTag;
5602
+ if (Array.isArray(content)) {
5603
+ node.children = content;
5604
+ } else if (typeof content === "object") {
5605
+ node.attributes = node.attributes || {};
5606
+ for (const k in content) {
5607
+ if (k === "children") node.children = content[k];
5608
+ else node.attributes[k] = content[k];
5609
+ }
5610
+ } else node.children = [content];
5611
+ delete node[potentialTag];
5612
+ }
5613
+ }
5614
+ for (const key in node) {
5615
+ if (key === "tag" || key === "__parent__") continue;
5616
+ const value = node[key];
5617
+ if (key === "attributes" && typeof value === "object" && value !== null) {
5618
+ for (const attrKey in value) {
5619
+ const attrVal = value[attrKey];
5620
+ if (typeof attrVal === "string" && attrVal.startsWith("'=")) {
5621
+ value[attrKey] = attrVal.slice(1);
5622
+ } else if (typeof attrVal === "string" && attrVal.startsWith("=")) {
5623
+ if (attrKey.startsWith("on")) {
5624
+ value[attrKey] = makeEventHandler(attrVal);
5625
+ } else {
5626
+ value[attrKey] = parseExpression(attrVal, node);
5627
+ }
5628
+ } else if (typeof attrVal === "object" && attrVal !== null) {
5629
+ value[attrKey] = hydrate(attrVal, node);
5630
+ }
5631
+ }
5632
+ continue;
5633
+ }
5634
+ if (typeof value === "string" && value.startsWith("'=")) {
5635
+ node[key] = value.slice(1);
5636
+ } else if (typeof value === "string" && value.startsWith("=")) {
5637
+ if (key === "onmount" || key === "onunmount" || key.startsWith("on")) {
5638
+ node[key] = makeEventHandler(value);
5639
+ } else if (key === "children") {
5640
+ node[key] = [parseExpression(value, node)];
5641
+ } else {
5642
+ node[key] = parseExpression(value, node);
5643
+ }
5644
+ } else {
5645
+ node[key] = hydrate(value, node);
5646
+ }
5647
+ }
5648
+ return node;
5649
+ };
5650
+ const LightviewCDOM = {
5651
+ registerHelper,
5652
+ registerOperator,
3767
5653
  parseExpression,
3768
5654
  resolvePath,
3769
5655
  resolvePathAsContext,
3770
- resolveExpression,
5656
+ resolveExpression: resolveExpression$1,
3771
5657
  parseCDOMC,
3772
5658
  parseJPRX,
3773
5659
  unwrapSignal,
3774
5660
  getContext,
3775
- handleCDOMState,
3776
- handleCDOMBind,
5661
+ handleCDOMState: () => {
5662
+ },
5663
+ handleCDOMBind: () => {
5664
+ },
3777
5665
  activate,
3778
5666
  hydrate,
3779
5667
  version: "1.0.0"