lightview 1.4.7-b → 1.4.8-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/{counter.html → examples/counter.html} +0 -0
- package/{directives.html → examples/directives.html} +1 -1
- package/examples/form.html +65 -0
- package/{message.html → examples/message.html} +0 -0
- package/{nested.html → examples/nested.html} +1 -1
- package/{remote.html → examples/remote.html} +1 -1
- package/{remoteform.html → examples/remoteform.html} +1 -1
- package/{scratch.html → examples/scratch.html} +1 -1
- package/{top.html → examples/top.html} +1 -1
- package/{xor.html → examples/xor.html} +1 -1
- package/lightview.js +43 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<title>Form</title>
|
|
6
|
+
<script src="../lightview.js?as=x-body"></script>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body>
|
|
10
|
+
<div style="margin:20px;padding:5px;border:1px;border-style:solid;border-color:${color}">
|
|
11
|
+
<p>
|
|
12
|
+
<input type="text" value="${color}">
|
|
13
|
+
<input type="radio" name="color" value="red">
|
|
14
|
+
<input type="radio" name="color" value="yellow">
|
|
15
|
+
<input type="radio" name="color" value="green">
|
|
16
|
+
<select value="${color}">
|
|
17
|
+
<option value="red">red</option>
|
|
18
|
+
<option>yellow</option>
|
|
19
|
+
<option> green</option>
|
|
20
|
+
</select>
|
|
21
|
+
<div>Hamburger options:</div>
|
|
22
|
+
<select value="${hamburger}" multiple>
|
|
23
|
+
<option value="lettuce">lettuce</option>
|
|
24
|
+
<option value="tomato">tomato</option>
|
|
25
|
+
<option>cheese</option>
|
|
26
|
+
</select>
|
|
27
|
+
</p>
|
|
28
|
+
Expose: <input type="checkbox" value="${checked}">
|
|
29
|
+
<p l-if="${checked}">
|
|
30
|
+
Now you've done it. You've exposed me.
|
|
31
|
+
</p>
|
|
32
|
+
<p id="variables">
|
|
33
|
+
|
|
34
|
+
</p>
|
|
35
|
+
</div>
|
|
36
|
+
<script type="lightview/module">
|
|
37
|
+
self.variables({
|
|
38
|
+
color: string,
|
|
39
|
+
checked: boolean,
|
|
40
|
+
hamburger: Array
|
|
41
|
+
}, {
|
|
42
|
+
reactive
|
|
43
|
+
});
|
|
44
|
+
color = "red";
|
|
45
|
+
checked = true;
|
|
46
|
+
hamburger = ["cheese"];
|
|
47
|
+
|
|
48
|
+
// demo instrumentation
|
|
49
|
+
const variableValues = () => {
|
|
50
|
+
const el = self.getElementById("variables");
|
|
51
|
+
while (el.lastElementChild) el.lastElementChild.remove();
|
|
52
|
+
self.getVariableNames().forEach((name) => {
|
|
53
|
+
const line = document.createElement("div");
|
|
54
|
+
line.innerText = `${name} = ${JSON.stringify(self.getValue(name))}`;
|
|
55
|
+
el.appendChild(line);
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
variableValues();
|
|
59
|
+
addEventListener("change", () => {
|
|
60
|
+
variableValues()
|
|
61
|
+
});
|
|
62
|
+
</script>
|
|
63
|
+
</body>
|
|
64
|
+
|
|
65
|
+
</html>
|
|
File without changes
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<head>
|
|
4
4
|
<title>Form</title>
|
|
5
|
-
<script src="
|
|
5
|
+
<script src="../lightview.js?as=x-body"></script>
|
|
6
6
|
<script>Lightview.whenFramed(({as,unhide,importAnchors,isolated,enableFrames}) => {
|
|
7
7
|
Lightview.bodyAsComponent({as,unhide,importAnchors,isolated,enableFrames});
|
|
8
8
|
})</script>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<title>Scratch</title>
|
|
6
|
-
<script src="
|
|
6
|
+
<script src="../lightview.js?as=x-body"></script>
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
9
|
<div style="margin:20px;padding:5px;border:1px;border-style:solid;border-color:${color}">
|
package/lightview.js
CHANGED
|
@@ -291,6 +291,7 @@ const {observe} = (() => {
|
|
|
291
291
|
nodes.push(root, ...getNodes(root.shadowRoot))
|
|
292
292
|
} else {
|
|
293
293
|
for (const node of root.childNodes) {
|
|
294
|
+
if(node.tagName==="SCRIPT") continue;
|
|
294
295
|
if (node.nodeType === Node.TEXT_NODE && node.nodeValue?.includes("${")) {
|
|
295
296
|
node.template ||= node.nodeValue;
|
|
296
297
|
nodes.push(node);
|
|
@@ -312,11 +313,14 @@ const {observe} = (() => {
|
|
|
312
313
|
}
|
|
313
314
|
return nodes;
|
|
314
315
|
}
|
|
315
|
-
const
|
|
316
|
-
|
|
316
|
+
const resolveNodeOrText = (node, component,safe) => {
|
|
317
|
+
const type = typeof(node),
|
|
318
|
+
template = type === "string" ? node.trim() : node.template;
|
|
319
|
+
if (template) {
|
|
317
320
|
try {
|
|
318
|
-
let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(
|
|
321
|
+
let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(template) + "` }")(component.varsProxy);
|
|
319
322
|
value = node.nodeType===Node.TEXT_NODE || !safe ? value : Lightview.escapeHTML(value);
|
|
323
|
+
if(type==="string") return value;
|
|
320
324
|
node.nodeValue = value=="null" || value=="undefined" ? "" : value;
|
|
321
325
|
} catch (e) {
|
|
322
326
|
console.warn(e);
|
|
@@ -348,37 +352,41 @@ const {observe} = (() => {
|
|
|
348
352
|
addListener(node, "click", anchorHandler);
|
|
349
353
|
})
|
|
350
354
|
}
|
|
351
|
-
const
|
|
355
|
+
const bound = new WeakSet();
|
|
356
|
+
const bindInput = (input, variableName, component,value) => {
|
|
357
|
+
if(bound.has(input)) return;
|
|
358
|
+
bound.add(input);
|
|
352
359
|
const inputtype = input.tagName === "SELECT" || input.tagName === "TEXTAREA" ? "text" : input.getAttribute("type"),
|
|
353
360
|
type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype),
|
|
354
361
|
deflt = input.getAttribute("default");
|
|
355
362
|
value ||= input.getAttribute("value");
|
|
356
|
-
let variable = component.vars[
|
|
363
|
+
let variable = component.vars[variableName] || {type};
|
|
357
364
|
if (type !== variable.type) {
|
|
358
365
|
if (variable.type === "any" || variable.type === "unknown") variable.type = type;
|
|
359
|
-
else throw new TypeError(`Attempt to bind <input name="${
|
|
366
|
+
else throw new TypeError(`Attempt to bind <input name="${variableName}" type="${type}"> to variable ${variableName}:${variable.type}`)
|
|
360
367
|
}
|
|
361
|
-
component.variables({[
|
|
362
|
-
component.setValue(
|
|
368
|
+
component.variables({[variableName]: type});
|
|
369
|
+
if(inputtype!=="radio") component.setValue(variableName,value);
|
|
363
370
|
let eventname = "change";
|
|
364
371
|
if (input.tagName !== "SELECT" && (!inputtype || input.tagName === "TEXTAREA" || ["text", "number", "tel", "email", "url", "search", "password"].includes(inputtype))) {
|
|
365
372
|
eventname = "input";
|
|
366
373
|
}
|
|
367
|
-
|
|
368
|
-
event.stopImmediatePropagation();
|
|
369
|
-
|
|
370
|
-
let value = target.value;
|
|
374
|
+
const listener = (event) => {
|
|
375
|
+
if(event) event.stopImmediatePropagation();
|
|
376
|
+
let value = input.value;
|
|
371
377
|
if (inputtype === "checkbox") {
|
|
372
378
|
value = input.checked
|
|
373
|
-
} else if (
|
|
374
|
-
if (
|
|
375
|
-
|
|
376
|
-
|
|
379
|
+
} else if (input.tagName === "SELECT") {
|
|
380
|
+
if (input.hasAttribute("multiple")) {
|
|
381
|
+
const varvalue = component.varsProxy[variableName];
|
|
382
|
+
value = [...input.querySelectorAll("option")]
|
|
383
|
+
.filter((option) => option.selected || resolveNodeOrText(option.attributes.value||option.innerText,component)===value)
|
|
377
384
|
.map((option) => option.getAttribute("value") || option.innerText);
|
|
378
385
|
}
|
|
379
386
|
}
|
|
380
|
-
component.varsProxy[
|
|
381
|
-
}
|
|
387
|
+
component.varsProxy[variableName] = coerce(value, type);
|
|
388
|
+
};
|
|
389
|
+
addListener(input, eventname, listener);
|
|
382
390
|
}
|
|
383
391
|
const tryParse = (value) => {
|
|
384
392
|
try {
|
|
@@ -499,14 +507,14 @@ const {observe} = (() => {
|
|
|
499
507
|
const nodes = getNodes(ctx);
|
|
500
508
|
nodes.forEach((node) => {
|
|
501
509
|
if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
|
|
502
|
-
render(!!node.template, () =>
|
|
510
|
+
render(!!node.template, () => resolveNodeOrText(node, this))
|
|
503
511
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
504
512
|
// resolve the value before all else;
|
|
505
513
|
const attr = node.attributes.value;
|
|
506
514
|
if (attr && attr.template) {
|
|
507
515
|
render(!!attr.template, () => {
|
|
508
|
-
const value =
|
|
509
|
-
eltype =
|
|
516
|
+
const value = resolveNodeOrText(attr, this),
|
|
517
|
+
eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx) : null;
|
|
510
518
|
if(node.attributes.value) {
|
|
511
519
|
const template = attr.template;
|
|
512
520
|
if(/\$\{[a-zA-z_]+\}/g.test(template)) {
|
|
@@ -522,7 +530,7 @@ const {observe} = (() => {
|
|
|
522
530
|
node.removeAttribute("checked");
|
|
523
531
|
node.checked = false;
|
|
524
532
|
}
|
|
525
|
-
const vname =
|
|
533
|
+
const vname = resolveNodeOrText(node.attributes.name, ctx);
|
|
526
534
|
if (vname) ctx.setValue(vname, node.checked, {coerceTo: "boolean"});
|
|
527
535
|
}
|
|
528
536
|
if (node.tagName === "SELECT") {
|
|
@@ -530,11 +538,11 @@ const {observe} = (() => {
|
|
|
530
538
|
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
531
539
|
[...node.querySelectorAll("option")].forEach((option) => {
|
|
532
540
|
if (option.hasAttribute("value")) {
|
|
533
|
-
if (values.includes(
|
|
541
|
+
if (values.includes(resolveNodeOrText(option.attributes.value, ctx))) {
|
|
534
542
|
option.setAttribute("selected", "");
|
|
535
543
|
option.selected = true;
|
|
536
544
|
}
|
|
537
|
-
} else if (option.innerText
|
|
545
|
+
} else if (values.includes(resolveNodeOrText(option.innerText,ctx))) {
|
|
538
546
|
option.setAttribute("selected", "");
|
|
539
547
|
option.selected = true;
|
|
540
548
|
}
|
|
@@ -547,13 +555,13 @@ const {observe} = (() => {
|
|
|
547
555
|
const {name, value} = attr;
|
|
548
556
|
if (name === "type") {
|
|
549
557
|
if (value === "radio") {
|
|
550
|
-
const name =
|
|
558
|
+
const name = resolveNodeOrText(node.attributes.name, ctx);
|
|
551
559
|
for (const vname of this.getVariableNames()) {
|
|
552
560
|
if (vname === name) {
|
|
553
561
|
render(true, () => {
|
|
554
|
-
const name =
|
|
562
|
+
const name = resolveNodeOrText(node.attributes.name, ctx),
|
|
555
563
|
varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
|
|
556
|
-
if (varvalue ==
|
|
564
|
+
if (varvalue == resolveNodeOrText(node.attributes.value, ctx)) {
|
|
557
565
|
node.setAttribute("checked", "");
|
|
558
566
|
node.checked = true;
|
|
559
567
|
} else {
|
|
@@ -572,9 +580,9 @@ const {observe} = (() => {
|
|
|
572
580
|
if (type === "") { // name is :something
|
|
573
581
|
render(!!attr.template, () => {
|
|
574
582
|
const value = attr.value,
|
|
575
|
-
elvalue =
|
|
576
|
-
eltype =
|
|
577
|
-
elname =
|
|
583
|
+
elvalue = resolveNodeOrText(node.attributes.value, ctx),
|
|
584
|
+
eltype = resolveNodeOrText(node.attributes.type, ctx),
|
|
585
|
+
elname = resolveNodeOrText(node.attributes.name, ctx);
|
|
578
586
|
if (params[0]) {
|
|
579
587
|
if (value === "true") node.setAttribute(params[0], "")
|
|
580
588
|
else node.removeAttribute(params[0]);
|
|
@@ -586,21 +594,21 @@ const {observe} = (() => {
|
|
|
586
594
|
} else if (type === "l-on") {
|
|
587
595
|
let listener;
|
|
588
596
|
render(!!attr.template, () => {
|
|
589
|
-
const value =
|
|
597
|
+
const value = resolveNodeOrText(attr, this);
|
|
590
598
|
if (listener) node.removeEventListener(params[0], listener);
|
|
591
599
|
listener = this[value] || window[value] || Function(value);
|
|
592
600
|
addListener(node, params[0], listener);
|
|
593
601
|
})
|
|
594
602
|
} else if (type === "l-if") {
|
|
595
603
|
render(!!attr.template, () => {
|
|
596
|
-
const value =
|
|
604
|
+
const value = resolveNodeOrText(attr, this);
|
|
597
605
|
node.style.setProperty("display", value === "true" ? "revert" : "none");
|
|
598
606
|
})
|
|
599
607
|
} else if (type === "l-for") {
|
|
600
608
|
node.template ||= node.innerHTML;
|
|
601
609
|
render(!!attr.template, () => {
|
|
602
610
|
const [what = "each", vname = "item", index = "index", array = "array", after = false] = params,
|
|
603
|
-
value =
|
|
611
|
+
value = resolveNodeOrText(attr, this),
|
|
604
612
|
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
605
613
|
target = what === "each" ? coerced : Object[what](coerced),
|
|
606
614
|
html = target.reduce((html, item, i, target) => {
|
|
@@ -626,7 +634,7 @@ const {observe} = (() => {
|
|
|
626
634
|
}
|
|
627
635
|
})
|
|
628
636
|
} else if (attr.template) {
|
|
629
|
-
render(!!attr.template, () =>
|
|
637
|
+
render(!!attr.template, () => resolveNodeOrText(attr, this));
|
|
630
638
|
}
|
|
631
639
|
})
|
|
632
640
|
}
|
|
@@ -985,4 +993,4 @@ const {observe} = (() => {
|
|
|
985
993
|
}
|
|
986
994
|
|
|
987
995
|
return {observe}
|
|
988
|
-
})();
|
|
996
|
+
})();
|
package/package.json
CHANGED