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 CHANGED
@@ -1,4 +1,4 @@
1
- # lightview v1.4.7b (BETA)
1
+ # lightview v1.4.8b (BETA)
2
2
 
3
3
  Small, simple, powerful web UI and micro front end creation ...
4
4
 
File without changes
@@ -2,7 +2,7 @@
2
2
 
3
3
  <head>
4
4
  <title>Directives</title>
5
- <script src="./lightview.js?as=x-body"></script>
5
+ <script src="../lightview.js?as=x-body"></script>
6
6
  </head>
7
7
 
8
8
  <body>
@@ -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
  <head>
3
3
  <title>Nested</title>
4
4
  <link href="./message.html" rel="module">
5
- <script src="./lightview.js?as=x-body"></script>
5
+ <script src="../lightview.js?as=x-body"></script>
6
6
  </head>
7
7
  <body>
8
8
  <l-message value="Hello One"></l-message>
@@ -4,7 +4,7 @@
4
4
  <title>Remote</title>
5
5
  <link type="module" src="">
6
6
  <meta name="l-enableFrames">
7
- <script src="./lightview.js"></script>
7
+ <script src="../lightview.js"></script>
8
8
  </head>
9
9
 
10
10
  <body>
@@ -2,7 +2,7 @@
2
2
 
3
3
  <head>
4
4
  <title>Form</title>
5
- <script src="./lightview.js?as=x-body"></script>
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="./lightview.js?as=x-body"></script>
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}">
@@ -2,7 +2,7 @@
2
2
  <head>
3
3
  <title>Top</title>
4
4
  <link href="./nested.html" rel="module">
5
- <script src="./lightview.js?as=x-body"></script>
5
+ <script src="../lightview.js?as=x-body"></script>
6
6
  </head>
7
7
  <body>
8
8
  <l-nested></l-nested>
@@ -29,7 +29,7 @@
29
29
  </script>
30
30
  </template>
31
31
  <title>Form</title>
32
- <script src="./lightview.js"></script>
32
+ <script src="../lightview.js"></script>
33
33
  <script>
34
34
  Lightview.createComponent("x-audiostream", document.getElementById("audiostream"))
35
35
  </script>
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 resolveNode = (node, component,safe) => {
316
- if (node?.template) {
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(node?.template) + "` }")(component.varsProxy);
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 bindInput = (input, name, component,value) => {
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[name] || {type};
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="${name}" type="${type}"> to variable ${name}:${variable.type}`)
366
+ else throw new TypeError(`Attempt to bind <input name="${variableName}" type="${type}"> to variable ${variableName}:${variable.type}`)
360
367
  }
361
- component.variables({[name]: type});
362
- component.setValue(name,value);
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
- addListener(input, eventname, (event) => {
368
- event.stopImmediatePropagation();
369
- const target = event.target;
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 (target.tagName === "SELECT") {
374
- if (target.hasAttribute("multiple")) {
375
- value = [...target.querySelectorAll("option")]
376
- .filter((option) => option.selected || resolveNode(option.attributes.value, component) == value || option.innerText == value)
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[name] = coerce(value, type);
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, () => resolveNode(node, this))
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 = resolveNode(attr, this),
509
- eltype = resolveNode(node.attributes.type, ctx);
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 = resolveNode(node.attributes.name, ctx);
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(resolveNode(option.attributes.value, ctx))) {
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.trim() === value) {
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 = resolveNode(node.attributes.name, ctx);
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 = resolveNode(node.attributes.name, ctx),
562
+ const name = resolveNodeOrText(node.attributes.name, ctx),
555
563
  varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
556
- if (varvalue == resolveNode(node.attributes.value, ctx)) {
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 = resolveNode(node.attributes.value, ctx),
576
- eltype = resolveNode(node.attributes.type, ctx),
577
- elname = resolveNode(node.attributes.name, ctx);
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 = resolveNode(attr, this);
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 = resolveNode(attr, this);
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 = resolveNode(attr, this),
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, () => resolveNode(attr, this));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightview",
3
- "version": "1.4.7b",
3
+ "version": "1.4.8b",
4
4
  "description": "Small, simple, powerful web UI and micro front end creation ... Great ideas from Svelte, React, Vue and Riot combined.",
5
5
  "main": "lightview.js",
6
6
  "scripts": {