lightview 1.6.6-b → 1.7.1-b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/components/chart/chart.html +15 -0
- package/components/chart/example.html +34 -0
- package/components/chart.html +22 -18
- package/components/components.js +93 -0
- package/components/gantt/example.html +27 -0
- package/components/gantt/gantt.html +35 -0
- package/components/gauge/example.html +28 -0
- package/components/gauge/guage.html +19 -0
- package/components/timeline.html +81 -0
- package/examples/counter.html +1 -1
- package/examples/duration.html +279 -0
- package/examples/forgeinform.html +1 -1
- package/examples/invalid-template-literals.html +1 -4
- package/examples/medium/remote.html +3 -2
- package/examples/message.html +0 -1
- package/examples/object-bound-form.html +32 -0
- package/examples/timeline.html +21 -0
- package/examples/todo.html +38 -0
- package/examples/types.html +2 -2
- package/lightview.js +271 -213
- package/package.json +1 -1
- package/test/basic.html +8 -4
- package/types.js +73 -2
package/lightview.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
MIT License
|
|
3
3
|
|
|
4
|
-
Copyright (c)
|
|
4
|
+
Copyright (c) 2022 AnyWhichWay, LLC - Lightview Small, simple, powerful UI creation ...
|
|
5
5
|
|
|
6
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -23,6 +23,10 @@ SOFTWARE.
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
// <script src="https://000686818.codepen.website/lightview.js?as=x-body"></script>
|
|
26
|
+
/*
|
|
27
|
+
self.variables({name:"string"})
|
|
28
|
+
imported(x) => exported(x) => reactive(x) => remote(x,{path:".
|
|
29
|
+
*/
|
|
26
30
|
|
|
27
31
|
const Lightview = {};
|
|
28
32
|
|
|
@@ -46,13 +50,27 @@ const {observe} = (() => {
|
|
|
46
50
|
}
|
|
47
51
|
Lightview.escapeHTML = escapeHTML;
|
|
48
52
|
|
|
49
|
-
const isArrowFunction = (f) =>
|
|
50
|
-
|
|
53
|
+
const isArrowFunction = (f) => typeof(f)==="function" && (f+"").match(/\(*.*\)*\s*=>/g);
|
|
54
|
+
|
|
55
|
+
const getTemplateVariableName = (template) => {
|
|
56
|
+
if(template && /^\$\{[a-zA-z_.]*\}$/g.test(template)) return template.substring(2, template.length - 1);
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
const
|
|
54
|
-
|
|
59
|
+
const walk = (target,path,depth=path.length-1,create) => {
|
|
60
|
+
for(let i=0;i<=depth;i++) {
|
|
61
|
+
target = (target[path[i]]==null && create ? target[path[i]] = (typeof(create)==="function" ? Object.create(create.prototype) : {}) : target[path[i]]);
|
|
62
|
+
if(target===undefined) return;
|
|
63
|
+
}
|
|
64
|
+
return target;
|
|
55
65
|
}
|
|
66
|
+
|
|
67
|
+
const addListener = (node, eventName, callback, self) => {
|
|
68
|
+
node.addEventListener(eventName, (event) => {
|
|
69
|
+
if(self) event.self = self;
|
|
70
|
+
callback(event);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
56
74
|
const anchorHandler = async (event) => {
|
|
57
75
|
event.preventDefault();
|
|
58
76
|
const target = event.target;
|
|
@@ -105,9 +123,7 @@ const {observe} = (() => {
|
|
|
105
123
|
if (toType === "string") return value + "";
|
|
106
124
|
const isfunction = typeof (toType) === "function";
|
|
107
125
|
if ((toType === "object" || isfunction)) {
|
|
108
|
-
if (type === "object" && isfunction)
|
|
109
|
-
if (value instanceof toType) return value;
|
|
110
|
-
}
|
|
126
|
+
if (type === "object" && isfunction && value instanceof toType) return value;
|
|
111
127
|
if (type === "string") {
|
|
112
128
|
value = value.trim();
|
|
113
129
|
try {
|
|
@@ -127,17 +143,19 @@ const {observe} = (() => {
|
|
|
127
143
|
}
|
|
128
144
|
instance.push(...parsed);
|
|
129
145
|
} else if (instance instanceof Date) {
|
|
130
|
-
|
|
146
|
+
const time = Date.parse(value);
|
|
147
|
+
if(!isNaN(time)) instance.setTime(time);
|
|
148
|
+
else return;
|
|
131
149
|
} else {
|
|
132
150
|
Object.assign(instance, JSON.parse(value));
|
|
133
151
|
}
|
|
134
|
-
if (toType !== Date) {
|
|
152
|
+
/*if (toType !== Date) {
|
|
135
153
|
Object.defineProperty(instance, "constructor", {
|
|
136
154
|
configurable: true,
|
|
137
155
|
writable: true,
|
|
138
156
|
value: toType.prototype.constructor || toType
|
|
139
157
|
});
|
|
140
|
-
}
|
|
158
|
+
}*/
|
|
141
159
|
return instance;
|
|
142
160
|
}
|
|
143
161
|
return JSON.parse(value);
|
|
@@ -159,17 +177,13 @@ const {observe} = (() => {
|
|
|
159
177
|
if(property=== "__dependents__") return dependents;
|
|
160
178
|
if(property=== "__reactorProxyTarget__") return data;
|
|
161
179
|
if (target instanceof Array) {
|
|
162
|
-
if (property === "toJSON") return function toJSON() {
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
if (property === "toString") return function toString() {
|
|
166
|
-
return JSON.stringify([...target]);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if(target instanceof Date) {
|
|
170
|
-
return Reflect.get(target,property);
|
|
180
|
+
if (property === "toJSON") return function toJSON() { return [...target]; }
|
|
181
|
+
if (property === "toString") return function toString() { return JSON.stringify([...target]); }
|
|
171
182
|
}
|
|
172
183
|
let value = target[property];
|
|
184
|
+
if(target instanceof Date && typeof(value)==="function") {
|
|
185
|
+
value = value.bind(target);
|
|
186
|
+
}
|
|
173
187
|
const type = typeof (value);
|
|
174
188
|
if (CURRENTOBSERVER && typeof (property) !== "symbol" && type !== "function") {
|
|
175
189
|
const observers = dependents[property] ||= new Set();
|
|
@@ -193,9 +207,7 @@ const {observe} = (() => {
|
|
|
193
207
|
console.warn(`Setting ${property} = ${value} on a Promise in Reactor`);
|
|
194
208
|
}
|
|
195
209
|
const type = typeof (value);
|
|
196
|
-
if(value && type==="object" && value instanceof Promise)
|
|
197
|
-
value = await value;
|
|
198
|
-
}
|
|
210
|
+
if(value && type==="object" && value instanceof Promise) value = await value;
|
|
199
211
|
if (target[property] !== value) {
|
|
200
212
|
if (value && type === "object") {
|
|
201
213
|
value = Reactor(value);
|
|
@@ -225,9 +237,7 @@ const {observe} = (() => {
|
|
|
225
237
|
const createVarsProxy = (vars, component, constructor) => {
|
|
226
238
|
return new Proxy(vars, {
|
|
227
239
|
get(target, property) {
|
|
228
|
-
if(target instanceof Date)
|
|
229
|
-
return Reflect.get(target,property);
|
|
230
|
-
}
|
|
240
|
+
if(target instanceof Date) return Reflect.get(target,property);
|
|
231
241
|
let {value,get} = target[property] || {};
|
|
232
242
|
if(get) return target[property].value = get.call(target[property]);
|
|
233
243
|
if (typeof (value) === "function") return value.bind(target);
|
|
@@ -256,13 +266,9 @@ const {observe} = (() => {
|
|
|
256
266
|
event.oldValue = value;
|
|
257
267
|
target[property].value = reactive ? Reactor(newValue) : newValue; // do first to prevent loops
|
|
258
268
|
target.postEvent.value("change", event);
|
|
259
|
-
if (event.defaultPrevented)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
remote.handleRemote({variable,config:remote.config},true);
|
|
263
|
-
} else if(variable.set) {
|
|
264
|
-
variable.set(newValue);
|
|
265
|
-
}
|
|
269
|
+
if (event.defaultPrevented) target[property].value = value;
|
|
270
|
+
else if(remote && (variable.reactive || remote.put)) remote.handleRemote({variable,config:remote.config},true);
|
|
271
|
+
else if(variable.set) variable.set(newValue);
|
|
266
272
|
}
|
|
267
273
|
return true;
|
|
268
274
|
}
|
|
@@ -270,9 +276,6 @@ const {observe} = (() => {
|
|
|
270
276
|
throw new TypeError(`Can't assign instance of '${newValue.constructor.name}' to variable '${property}:${type.name.replace("bound ", "")}'`)
|
|
271
277
|
}
|
|
272
278
|
throw new TypeError(`Can't assign '${typeof (newValue)} ${newtype === "string" ? '"' + newValue + '"' : newValue}' to variable '${property}:${typetype === "function" ? type.name.replace("bound ", "") : type} ${type.required ? "required" : ""}'`)
|
|
273
|
-
},
|
|
274
|
-
keys() {
|
|
275
|
-
return [...Object.keys(vars)];
|
|
276
279
|
}
|
|
277
280
|
});
|
|
278
281
|
}
|
|
@@ -289,10 +292,8 @@ const {observe} = (() => {
|
|
|
289
292
|
target.removeAttribute(name);
|
|
290
293
|
target.dispatchEvent(new CustomEvent("message", {detail: JSON.parse(value)}))
|
|
291
294
|
}
|
|
292
|
-
if (target.observedAttributes && target.observedAttributes.includes(name)) {
|
|
293
|
-
|
|
294
|
-
target.setVariableValue(name, value);
|
|
295
|
-
}
|
|
295
|
+
if (target.observedAttributes && target.observedAttributes.includes(name) && value !== mutation.oldValue) {
|
|
296
|
+
target.setVariableValue(name, value);
|
|
296
297
|
}
|
|
297
298
|
} else if (mutation.type === "childList") {
|
|
298
299
|
for (const target of mutation.removedNodes) {
|
|
@@ -319,45 +320,54 @@ const {observe} = (() => {
|
|
|
319
320
|
return nodes;
|
|
320
321
|
}
|
|
321
322
|
const getNodes = (root) => {
|
|
322
|
-
const nodes =
|
|
323
|
+
const nodes = new Set();
|
|
323
324
|
if (root.shadowRoot) {
|
|
324
|
-
nodes.
|
|
325
|
+
nodes.add(root);
|
|
326
|
+
getNodes(root.shadowRoot).forEach((node) => nodes.add(node))
|
|
325
327
|
} else {
|
|
326
328
|
for (const node of root.childNodes) {
|
|
327
329
|
if (node.tagName === "SCRIPT") continue;
|
|
328
330
|
if (node.nodeType === Node.TEXT_NODE && node.nodeValue?.includes("${")) {
|
|
329
331
|
node.template ||= node.nodeValue;
|
|
330
|
-
nodes.
|
|
332
|
+
nodes.add(node);
|
|
331
333
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
332
|
-
let skip
|
|
334
|
+
let skip;
|
|
333
335
|
[...node.attributes].forEach((attr) => {
|
|
334
336
|
if (attr.value.includes("${")) {
|
|
335
337
|
attr.template ||= attr.value;
|
|
336
|
-
|
|
337
|
-
nodes.push(node);
|
|
338
|
+
nodes.add(node);
|
|
338
339
|
} else if (attr.name.includes(":") || attr.name.startsWith("l-")) {
|
|
339
340
|
skip = attr.name.includes("l-for:");
|
|
340
|
-
|
|
341
|
-
nodes.push(node)
|
|
341
|
+
nodes.add(node)
|
|
342
342
|
}
|
|
343
343
|
})
|
|
344
|
-
if (
|
|
345
|
-
if (!skip && !node.shadowRoot)
|
|
344
|
+
if (node.getAttribute("type") === "radio") nodes.add(node);
|
|
345
|
+
if (!skip && !node.shadowRoot) getNodes(node).forEach((node) => nodes.add(node));
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
349
|
return nodes;
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
-
const resolveNodeOrText = (node, component, safe) => {
|
|
352
|
+
const resolveNodeOrText = (node, component, safe,extras=node.extras||{}) => {
|
|
353
353
|
const type = typeof (node),
|
|
354
354
|
template = type === "string" ? node.trim() : node.template;
|
|
355
355
|
if (template) {
|
|
356
|
+
const name = getTemplateVariableName(template);
|
|
356
357
|
try {
|
|
357
|
-
let value =
|
|
358
|
-
|
|
358
|
+
let value = (name
|
|
359
|
+
? walk(extras,name.split(".")) || walk(component.varsProxy,name.split(".")) || component[name]
|
|
360
|
+
: Function("context", "extras", "with(context) { with(extras) { return `" + (safe ? template : Lightview.sanitizeTemplate(template)) + "` } }")(component.varsProxy,extras));
|
|
361
|
+
//let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(template) + "` }")(component.varsProxy);
|
|
362
|
+
if(typeof(value)==="function") return value;
|
|
363
|
+
value = (name || node.nodeType === Node.TEXT_NODE || safe ? value : Lightview.escapeHTML(value));
|
|
359
364
|
if (type === "string") return value==="undefined" ? undefined : value;
|
|
360
|
-
|
|
365
|
+
if(name) {
|
|
366
|
+
node.nodeValue = value==null ? "" : typeof(value)==="string" ? value : JSON.stringify(value);
|
|
367
|
+
} else {
|
|
368
|
+
node.nodeValue = value == "null" || value == "undefined" ? "" : value;
|
|
369
|
+
}
|
|
370
|
+
return value;
|
|
361
371
|
} catch (e) {
|
|
362
372
|
//console.warn(e);
|
|
363
373
|
if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
|
|
@@ -381,25 +391,36 @@ const {observe} = (() => {
|
|
|
381
391
|
})
|
|
382
392
|
}
|
|
383
393
|
const bound = new WeakSet();
|
|
384
|
-
const bindInput = (input, variableName, component, value) => {
|
|
394
|
+
const bindInput = (input, variableName, component, value, object) => {
|
|
385
395
|
if (bound.has(input)) return;
|
|
386
396
|
bound.add(input);
|
|
387
397
|
const inputtype = input.tagName === "SELECT" || input.tagName === "TEXTAREA" ? "text" : input.getAttribute("type"),
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
398
|
+
nameparts = variableName.split(".");
|
|
399
|
+
let type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype);
|
|
400
|
+
const variable = walk(component.vars,nameparts) || {type};
|
|
401
|
+
if(type==="any") type = variable.type;
|
|
402
|
+
if(value==null) value = input.getAttribute("value");
|
|
403
|
+
if(object && nameparts.length>1) {
|
|
404
|
+
const [root,...path] = nameparts;
|
|
405
|
+
object = walk(object,path,path.length-2,true);
|
|
406
|
+
const key = path[path.length-1];
|
|
407
|
+
object[key] = coerce(value,type);
|
|
408
|
+
} else {
|
|
409
|
+
if (type !== variable.type) {
|
|
410
|
+
if (variable.type === "any" || variable.type === "unknown") variable.type = type;
|
|
411
|
+
else throw new TypeError(`Attempt to bind <input name="${variableName}" type="${type}"> to variable ${variableName}:${variable.type}`)
|
|
412
|
+
}
|
|
413
|
+
const existing = component.vars[variableName];
|
|
414
|
+
if(!existing || existing.type!==type || !existing.reactive) component.variables({[variableName]: type},{reactive});
|
|
415
|
+
if(inputtype!=="radio") {
|
|
416
|
+
if(typeof(value)==="string" && value.includes("${")) input.attributes.value.value = "";
|
|
417
|
+
else if(value!=="") component.setVariableValue(variableName, coerce(value,type));
|
|
418
|
+
}
|
|
400
419
|
}
|
|
401
420
|
let eventname = "change";
|
|
402
|
-
if
|
|
421
|
+
if(input.tagName==="FORM") {
|
|
422
|
+
eventname = "submit"
|
|
423
|
+
} else if (input.tagName !== "SELECT" && (!inputtype || input.tagName === "TEXTAREA" || ["text", "number", "tel", "email", "url", "search", "password"].includes(inputtype))) {
|
|
403
424
|
eventname = "input";
|
|
404
425
|
}
|
|
405
426
|
const listener = (event) => {
|
|
@@ -407,17 +428,21 @@ const {observe} = (() => {
|
|
|
407
428
|
let value = input.value;
|
|
408
429
|
if (inputtype === "checkbox") {
|
|
409
430
|
value = input.checked
|
|
410
|
-
} else if (input.tagName === "SELECT") {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
.filter((option) => option.selected || resolveNodeOrText(option.attributes.value || option.innerText, component) === value) //todo make sync comopat
|
|
415
|
-
.map((option) => option.getAttribute("value") || option.innerText);
|
|
416
|
-
}
|
|
431
|
+
} else if (input.tagName === "SELECT" && input.hasAttribute("multiple")) {
|
|
432
|
+
value = [...input.querySelectorAll("option")]
|
|
433
|
+
.filter((option) => option.selected || resolveNodeOrText(option.attributes.value || option.innerText, component) === value)
|
|
434
|
+
.map((option) => option.getAttribute("value") || option.innerText);
|
|
417
435
|
}
|
|
418
|
-
|
|
436
|
+
if(object) {
|
|
437
|
+
const [root,...path] = nameparts;
|
|
438
|
+
object = walk(object,nameparts,path.length-2,true);
|
|
439
|
+
} else {
|
|
440
|
+
object = walk(component.varsProxy,nameparts,nameparts.length-2,true);
|
|
441
|
+
}
|
|
442
|
+
const key = nameparts[nameparts.length-1];
|
|
443
|
+
object[key] = coerce(value,type);
|
|
419
444
|
};
|
|
420
|
-
addListener(input, eventname, listener);
|
|
445
|
+
addListener(input, eventname, listener,component);
|
|
421
446
|
}
|
|
422
447
|
const tryParse = (value) => {
|
|
423
448
|
try {
|
|
@@ -473,7 +498,6 @@ const {observe} = (() => {
|
|
|
473
498
|
}
|
|
474
499
|
}
|
|
475
500
|
variable.set(variable.value);
|
|
476
|
-
//component.changeListener.targets.add(name);
|
|
477
501
|
}
|
|
478
502
|
}
|
|
479
503
|
}
|
|
@@ -521,24 +545,19 @@ const {observe} = (() => {
|
|
|
521
545
|
static get instances() {
|
|
522
546
|
return instances;
|
|
523
547
|
}
|
|
524
|
-
|
|
525
548
|
constructor() {
|
|
526
549
|
super();
|
|
527
550
|
instances.add(this);
|
|
528
551
|
const currentComponent = this,
|
|
529
552
|
shadow = this.attachShadow({mode: "open"}),
|
|
530
553
|
eventlisteners = {};
|
|
531
|
-
// needs to be local to the instance
|
|
532
|
-
/*Object.defineProperty(this,"changeListener",{value:
|
|
533
|
-
function({variableName, value}) {
|
|
534
|
-
//if (currentComponent.changeListener.targets.has(variableName)) {
|
|
535
|
-
value = typeof (value) === "string" || !value ? value : JSON.stringify(value);
|
|
536
|
-
if (value == null) removeComponentAttribute(currentComponent, variableName);
|
|
537
|
-
else setComponentAttribute(currentComponent, variableName, value);
|
|
538
|
-
// }
|
|
539
|
-
}});*/
|
|
540
554
|
this.vars = {
|
|
541
555
|
...reserved,
|
|
556
|
+
observe: {
|
|
557
|
+
value: (...args) => observe(...args),
|
|
558
|
+
type: "function",
|
|
559
|
+
constant: true
|
|
560
|
+
},
|
|
542
561
|
addEventListener: {
|
|
543
562
|
value: (eventName, listener) => {
|
|
544
563
|
const listeners = eventlisteners[eventName] ||= new Set();
|
|
@@ -564,8 +583,6 @@ const {observe} = (() => {
|
|
|
564
583
|
};
|
|
565
584
|
this.defaultAttributes = domElementNode.tagName === "TEMPLATE" ? domElementNode.attributes : dom.attributes;
|
|
566
585
|
this.varsProxy = createVarsProxy(this.vars, this, CustomElement);
|
|
567
|
-
//this.changeListener.targets = new Set();
|
|
568
|
-
//this.varsProxy.addEventListener("change", this.changeListener);
|
|
569
586
|
if (framed || CustomElement.lightviewFramed) this.variables({message: Object}, {exported});
|
|
570
587
|
["getElementById", "querySelector", "querySelectorAll"]
|
|
571
588
|
.forEach((fname) => {
|
|
@@ -600,7 +617,10 @@ const {observe} = (() => {
|
|
|
600
617
|
for (const script of [...scripts]) {
|
|
601
618
|
if (script.attributes.src?.value?.includes("/lightview.js")) continue;
|
|
602
619
|
// remove comments;
|
|
603
|
-
const text = script.innerHTML
|
|
620
|
+
const text = script.innerHTML
|
|
621
|
+
.replaceAll(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, "$1")
|
|
622
|
+
.replaceAll(/\r?\n/g, "")
|
|
623
|
+
.replaceAll(/'(([^'\\]|\\.)*)'/g,"\\'$1\\'");
|
|
604
624
|
const currentScript = document.createElement("script");
|
|
605
625
|
if (script.className !== "lightview" && !((script.attributes.type?.value || "").includes("lightview/"))) {
|
|
606
626
|
for (const attr of script.attributes) currentScript.setAttribute(attr.name,attr.value);
|
|
@@ -626,6 +646,7 @@ const {observe} = (() => {
|
|
|
626
646
|
window[scriptid] = () => {
|
|
627
647
|
delete window[scriptid];
|
|
628
648
|
currentScript.remove();
|
|
649
|
+
script.remove();
|
|
629
650
|
resolve();
|
|
630
651
|
}
|
|
631
652
|
window[scriptid].ctx = ctx.varsProxy;
|
|
@@ -633,141 +654,180 @@ const {observe} = (() => {
|
|
|
633
654
|
})
|
|
634
655
|
}
|
|
635
656
|
// Promise.all(promises).then(() => {
|
|
636
|
-
const nodes = getNodes(ctx)
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
657
|
+
const nodes = getNodes(ctx),
|
|
658
|
+
processNodes = (nodes,object) => {
|
|
659
|
+
nodes.forEach((node) => {
|
|
660
|
+
if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
|
|
661
|
+
observe(() => resolveNodeOrText(node, this,true,node.extras));
|
|
662
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
663
|
+
// resolve the value before all else;
|
|
664
|
+
const attr = node.attributes.value,
|
|
665
|
+
template = attr?.template;
|
|
666
|
+
if (attr && template) {
|
|
667
|
+
//let value = resolveNodeOrText(attr, this),
|
|
668
|
+
// ;
|
|
669
|
+
const eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx, false,node.extras) : null,
|
|
670
|
+
template = attr.template;
|
|
671
|
+
if (template) {
|
|
672
|
+
const name = getTemplateVariableName(template);
|
|
673
|
+
if (name) {
|
|
674
|
+
const nameparts = name.split(".");
|
|
675
|
+
if(node.extras && node.extras[nameparts[0]]) {
|
|
676
|
+
object = node.extras[nameparts[0]];
|
|
677
|
+
}
|
|
678
|
+
if(!this.vars[nameparts[0]] || this.vars[nameparts[0]].reactive || object) {
|
|
679
|
+
bindInput(node, name, this, resolveNodeOrText(attr, this,false,node.extras), object);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
observe(() => {
|
|
683
|
+
const value = resolveNodeOrText(template, ctx,false,node.extras);
|
|
684
|
+
if(value!==undefined) {
|
|
685
|
+
if (eltype === "checkbox") {
|
|
686
|
+
if (coerce(value, "boolean") === true) {
|
|
687
|
+
node.setAttribute("checked", "");
|
|
688
|
+
node.checked = true;
|
|
689
|
+
} else {
|
|
690
|
+
node.removeAttribute("checked");
|
|
691
|
+
node.checked = false;
|
|
692
|
+
}
|
|
693
|
+
} else if (node.tagName === "SELECT") {
|
|
694
|
+
let values = [value];
|
|
695
|
+
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
696
|
+
[...node.querySelectorAll("option")].forEach(async (option) => {
|
|
697
|
+
if (option.hasAttribute("value")) {
|
|
698
|
+
if (values.includes(resolveNodeOrText(option.attributes.value, ctx,false,node.extras))) {
|
|
699
|
+
option.setAttribute("selected", "");
|
|
700
|
+
option.selected = true;
|
|
701
|
+
}
|
|
702
|
+
} else if (values.includes(resolveNodeOrText(option.innerText, ctx,false,node.extras))) {
|
|
703
|
+
option.setAttribute("selected", "");
|
|
704
|
+
option.selected = true;
|
|
705
|
+
}
|
|
706
|
+
})
|
|
707
|
+
} else if (eltype!=="radio") {
|
|
708
|
+
attr.value = value;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
});
|
|
653
712
|
}
|
|
654
713
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
714
|
+
[...node.attributes].forEach(async (attr) => {
|
|
715
|
+
if (attr.name === "value" && attr.template) return;
|
|
716
|
+
attr.template ||= attr.nodeValue?.includes("${") ? attr.nodeValue : undefined;
|
|
717
|
+
const {name, value} = attr,
|
|
718
|
+
vname = node.attributes.name?.value;
|
|
719
|
+
if (name === "type" && value=="radio" && vname) {
|
|
720
|
+
bindInput(node, vname, this, undefined, object);
|
|
721
|
+
observe(() => {
|
|
722
|
+
const varvalue = Function("context", "with(context) { return `${" + vname + "}` }")(ctx.varsProxy);
|
|
723
|
+
if (node.attributes.value.value == varvalue) {
|
|
660
724
|
node.setAttribute("checked", "");
|
|
661
725
|
node.checked = true;
|
|
662
726
|
} else {
|
|
663
727
|
node.removeAttribute("checked");
|
|
664
728
|
node.checked = false;
|
|
665
729
|
}
|
|
666
|
-
}
|
|
667
|
-
let values = [value];
|
|
668
|
-
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
669
|
-
[...node.querySelectorAll("option")].forEach(async (option) => {
|
|
670
|
-
if (option.hasAttribute("value")) {
|
|
671
|
-
if (values.includes(resolveNodeOrText(option.attributes.value, ctx))) {
|
|
672
|
-
option.setAttribute("selected", "");
|
|
673
|
-
option.selected = true;
|
|
674
|
-
}
|
|
675
|
-
} else if (values.includes(resolveNodeOrText(option.innerText, ctx))) {
|
|
676
|
-
option.setAttribute("selected", "");
|
|
677
|
-
option.selected = true;
|
|
678
|
-
}
|
|
679
|
-
})
|
|
680
|
-
} else if (eltype!=="radio") {
|
|
681
|
-
attr.value = value;
|
|
682
|
-
}
|
|
730
|
+
});
|
|
683
731
|
}
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
[...node.attributes].forEach(async (attr) => {
|
|
688
|
-
if (attr.name === "value" && attr.template) return;
|
|
689
|
-
const {name, value} = attr,
|
|
690
|
-
vname = node.attributes.name?.value;
|
|
691
|
-
if (name === "type" && value=="radio" && vname) {
|
|
692
|
-
bindInput(node, vname, this);
|
|
693
|
-
observe(() => {
|
|
694
|
-
const varvalue = Function("context", "with(context) { return `${" + vname + "}` }")(ctx.varsProxy);
|
|
695
|
-
if (node.attributes.value.value == varvalue) {
|
|
696
|
-
node.setAttribute("checked", "");
|
|
697
|
-
node.checked = true;
|
|
698
|
-
} else {
|
|
699
|
-
node.removeAttribute("checked");
|
|
700
|
-
node.checked = false;
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
732
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
733
|
+
const [type, ...params] = name.split(":");
|
|
734
|
+
if (type === "") { // name is :something
|
|
735
|
+
observe(() => {
|
|
736
|
+
const value = attr.value;
|
|
737
|
+
if (params[0]) {
|
|
738
|
+
if (value === "true") node.setAttribute(params[0], "")
|
|
739
|
+
else node.removeAttribute(params[0]);
|
|
740
|
+
} else {
|
|
741
|
+
const elvalue = node.attributes.value ? resolveNodeOrText(node.attributes.value, ctx,false,node.extras) : null,
|
|
742
|
+
eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx,false,node.extras) : null;
|
|
743
|
+
if (eltype === "checkbox" || node.tagName === "OPTION") {
|
|
744
|
+
if (elvalue === true) node.setAttribute("checked", "")
|
|
745
|
+
else node.removeAttribute("checked");
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
})
|
|
749
|
+
} else if (type === "l-on") {
|
|
750
|
+
let listener;
|
|
751
|
+
observe(() => {
|
|
752
|
+
const value = resolveNodeOrText(attr, this,true,node.extras);
|
|
753
|
+
if (listener) node.removeEventListener(params[0], listener);
|
|
754
|
+
listener = null;
|
|
755
|
+
if(typeof(value)==="function") {
|
|
756
|
+
listener = value;
|
|
757
|
+
} else {
|
|
758
|
+
try {
|
|
759
|
+
listener = Function("return " + value)();
|
|
760
|
+
} catch(e) {
|
|
761
|
+
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if(listener) addListener(node, params[0], listener,ctx);
|
|
765
|
+
})
|
|
766
|
+
} else if (type === "l-if") {
|
|
767
|
+
observe(() => {
|
|
768
|
+
const value = resolveNodeOrText(attr, this,true,node.extras);
|
|
769
|
+
node.style.setProperty("display", value == true ? "revert" : "none");
|
|
770
|
+
})
|
|
771
|
+
} else if (type === "l-for") {
|
|
772
|
+
node.template ||= node.innerHTML;
|
|
773
|
+
node.clone ||= node.cloneNode(true);
|
|
774
|
+
observe(() => {
|
|
775
|
+
const [what = "each", vname = "item", index = "index", array = "array", after = false] = params,
|
|
776
|
+
value = resolveNodeOrText(attr, this,false,node.extras),
|
|
777
|
+
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
778
|
+
target = what === "each" ? coerced : Object[what](coerced),
|
|
779
|
+
children = target.reduce((children,item,i,target) => {
|
|
780
|
+
const clone = node.clone.cloneNode(true),
|
|
781
|
+
extras = node.extras = {
|
|
782
|
+
[vname]: item,
|
|
783
|
+
[index]: i,
|
|
784
|
+
[array]: target
|
|
785
|
+
},
|
|
786
|
+
nodes = [...getNodes(clone)].map((node) => {
|
|
787
|
+
node.extras = extras;
|
|
788
|
+
return node;
|
|
789
|
+
});
|
|
790
|
+
processNodes(nodes);
|
|
791
|
+
children.push(...clone.childNodes);
|
|
792
|
+
return children;
|
|
793
|
+
},[]);
|
|
794
|
+
if (!window.lightviewDebug) {
|
|
795
|
+
if (after) {
|
|
796
|
+
node.style.setProperty("display", "none")
|
|
797
|
+
} else {
|
|
798
|
+
while (node.lastElementChild) node.lastElementChild.remove();
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
children.forEach((child) => {
|
|
802
|
+
if (after) node.parentElement.insertBefore(child, node);
|
|
803
|
+
else node.appendChild(child);
|
|
804
|
+
})
|
|
805
|
+
})
|
|
806
|
+
} else if(attr.template) {
|
|
807
|
+
observe(() => {
|
|
808
|
+
resolveNodeOrText(attr, this,false,node.extras);
|
|
809
|
+
})
|
|
761
810
|
}
|
|
762
811
|
})
|
|
763
|
-
} else if(attr.template) {
|
|
764
|
-
observe(() => {
|
|
765
|
-
resolveNodeOrText(attr, this);
|
|
766
|
-
})
|
|
767
812
|
}
|
|
768
813
|
})
|
|
814
|
+
};
|
|
815
|
+
nodes.forEach((node) => {
|
|
816
|
+
if(node.tagName==="FORM") {
|
|
817
|
+
const value = node.getAttribute("value"),
|
|
818
|
+
name = getTemplateVariableName(value);
|
|
819
|
+
if(name) {
|
|
820
|
+
const childnodes = [...nodes].filter((childnode) => node!==childnode && node.contains(childnode));
|
|
821
|
+
childnodes.forEach((node) => nodes.delete(node));
|
|
822
|
+
const variable = ctx.vars[name] ||= {type: "object", reactive:true, value: Reactor({})};
|
|
823
|
+
if(variable.type !== "object" || !variable.reactive || !variable.value || typeof(variable.value)!=="object") {
|
|
824
|
+
throw new TypeError(`Can't bind form ${node.getAttribute("id")} to non-object variable ${name}`);
|
|
825
|
+
}
|
|
826
|
+
processNodes(childnodes,variable.value);
|
|
827
|
+
}
|
|
769
828
|
}
|
|
770
829
|
})
|
|
830
|
+
processNodes(nodes);
|
|
771
831
|
shadow.normalize();
|
|
772
832
|
observer ||= createObserver(ctx, framed);
|
|
773
833
|
observer.observe(ctx, {attributeOldValue: true, subtree:true, characterData:true, characterDataOldValue:true});
|
|
@@ -778,11 +838,9 @@ const {observe} = (() => {
|
|
|
778
838
|
}
|
|
779
839
|
adoptedCallback(callback) {
|
|
780
840
|
this.dispatchEvent(new Event("adopted"));
|
|
781
|
-
//this.vars.postEvent.value("adopted");
|
|
782
841
|
}
|
|
783
842
|
disconnectedCallback() {
|
|
784
843
|
this.dispatchEvent(new Event("disconnected"));
|
|
785
|
-
//this.vars.postEvent.value("disconnected");
|
|
786
844
|
}
|
|
787
845
|
get observedAttributes() {
|
|
788
846
|
return CustomElement.observedAttributes;
|
|
@@ -793,7 +851,7 @@ const {observe} = (() => {
|
|
|
793
851
|
|
|
794
852
|
getVariableNames() {
|
|
795
853
|
return Object.keys(this.vars)
|
|
796
|
-
.filter(name => !(name in reserved) && !["self", "addEventListener", "postEvent"].includes(name))
|
|
854
|
+
.filter(name => !(name in reserved) && !["self", "addEventListener", "postEvent","observe"].includes(name))
|
|
797
855
|
}
|
|
798
856
|
|
|
799
857
|
getVariable(name) {
|