lightview 1.4.4-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,8 +1,8 @@
1
- # lightview v1.4.4b (BETA)
1
+ # lightview v1.4.8b (BETA)
2
2
 
3
3
  Small, simple, powerful web UI and micro front end creation ...
4
4
 
5
- Great ideas from Svelte, React, Vue and Riot combined into one small tool: < 6K (minified/gzipped).
5
+ Great ideas from Svelte, React, Vue and Riot combined into one small tool: < 7K (minified/gzipped).
6
6
 
7
7
  See the docs and examples at [https://lightview.dev](https://lightview.dev).
8
8
 
@@ -0,0 +1,2 @@
1
+ body.split-output #output > .active { width: 75%; }
2
+ body.split-output #output > #result-box.active { width: 25% }
@@ -9,6 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <script type="lightview/module">
12
+ debugger;
12
13
  self.variables({
13
14
  count: number
14
15
  }, {
@@ -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>
@@ -0,0 +1,13 @@
1
+ <html>
2
+ <head>
3
+ <meta charset="UTF-8">
4
+ <title>Message</title>
5
+ </head>
6
+ <body>
7
+ ${value}
8
+ <script type="lightview/module">
9
+ debugger;
10
+ self.variables({value:string},{imported});
11
+ </script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <head>
3
+ <title>Nested</title>
4
+ <link href="./message.html" rel="module">
5
+ <script src="../lightview.js?as=x-body"></script>
6
+ </head>
7
+ <body>
8
+ <l-message value="Hello One"></l-message>
9
+ <l-message value="Hello Two"></l-message>
10
+ </body>
11
+ </html>
@@ -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}">
@@ -0,0 +1,10 @@
1
+ <!DOCTYPE html>
2
+ <head>
3
+ <title>Top</title>
4
+ <link href="./nested.html" rel="module">
5
+ <script src="../lightview.js?as=x-body"></script>
6
+ </head>
7
+ <body>
8
+ <l-nested></l-nested>
9
+ </body>
10
+ </html>
@@ -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
@@ -30,6 +30,22 @@ const {observe} = (() => {
30
30
  let CURRENTOBSERVER;
31
31
  const parser = new DOMParser();
32
32
 
33
+ const templateSanitizer = (string) => {
34
+ return string.replace(/function\s+/g,"")
35
+ .replace(/function\(/g,"")
36
+ .replace(/=\s*>/g,"")
37
+ .replace(/(while|do|for|alert)\s*\(/g,"")
38
+ .replace(/console\.[a-zA-Z$]+\s*\(/g,"");
39
+ }
40
+ Lightview.sanitizeTemplate = templateSanitizer;
41
+
42
+ const escaper = document.createElement('textarea');
43
+ function escapeHTML(html) {
44
+ escaper.textContent = html;
45
+ return escaper.innerHTML;
46
+ }
47
+ Lightview.escapeHTML = escapeHTML;
48
+
33
49
  const addListener = (node, eventName, callback) => {
34
50
  node.addEventListener(eventName, callback); // just used to make code footprint smaller
35
51
  }
@@ -73,6 +89,7 @@ const {observe} = (() => {
73
89
  if (toType === "number") return parseFloat(value + "");
74
90
  if (toType === "boolean") {
75
91
  if (["on", "checked", "selected"].includes(value)) return true;
92
+ if(value==null || value==="") return false;
76
93
  try {
77
94
  const parsed = JSON.parse(value + "");
78
95
  if (typeof (parsed) === "boolean") return parsed;
@@ -127,7 +144,7 @@ const {observe} = (() => {
127
144
  }
128
145
  throw new TypeError(`Unable to coerce ${value} to ${toType}`)
129
146
  }
130
- const Reactor = (value,component) => {
147
+ const Reactor = (value) => {
131
148
  if (value && typeof (value) === "object") {
132
149
  if (value.__isReactor__) return value;
133
150
  const childReactors = [],
@@ -155,7 +172,7 @@ const {observe} = (() => {
155
172
  return value;
156
173
  }
157
174
  if (value && type === "object") {
158
- value = Reactor(value,component);
175
+ value = Reactor(value);
159
176
  childReactors.push(value);
160
177
  }
161
178
  target[property] = value;
@@ -165,7 +182,7 @@ const {observe} = (() => {
165
182
  const type = typeof (value);
166
183
  if (target[property] !== value) {
167
184
  if (value && type === "object") {
168
- value = Reactor(value,component);
185
+ value = Reactor(value);
169
186
  childReactors.push(value);
170
187
  }
171
188
  target[property] = value;
@@ -211,7 +228,7 @@ const {observe} = (() => {
211
228
  if (newValue == null || type === "any" || newtype === type || (typetype === "function" && newValue && newtype === "object" && newValue instanceof type)) {
212
229
  if (value !== newValue) {
213
230
  event.oldValue = value;
214
- target[property].value = reactive ? Reactor(newValue,component) : newValue; // do first to prevent loops
231
+ target[property].value = reactive ? Reactor(newValue) : newValue; // do first to prevent loops
215
232
  target.postEvent.value("change", event);
216
233
  if (event.defaultPrevented) target[property].value = value;
217
234
  }
@@ -231,7 +248,7 @@ const {observe} = (() => {
231
248
  const observer = new MutationObserver((mutations) => {
232
249
  mutations.forEach((mutation) => {
233
250
  if (mutation.type === "attributes") {
234
- if (framed) debugger;
251
+ //if (framed) debugger;
235
252
  const name = mutation.attributeName,
236
253
  target = mutation.target,
237
254
  value = target.getAttribute(name);
@@ -274,6 +291,7 @@ const {observe} = (() => {
274
291
  nodes.push(root, ...getNodes(root.shadowRoot))
275
292
  } else {
276
293
  for (const node of root.childNodes) {
294
+ if(node.tagName==="SCRIPT") continue;
277
295
  if (node.nodeType === Node.TEXT_NODE && node.nodeValue?.includes("${")) {
278
296
  node.template ||= node.nodeValue;
279
297
  nodes.push(node);
@@ -295,12 +313,17 @@ const {observe} = (() => {
295
313
  }
296
314
  return nodes;
297
315
  }
298
- const resolveNode = (node, component) => {
299
- 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) {
300
320
  try {
301
- const value = Function("context", "with(context) { return `" + node.template + "` }")(component.varsProxy);
302
- node.nodeValue = value === "null" || value === "undefined" ? "" : value;
321
+ let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(template) + "` }")(component.varsProxy);
322
+ value = node.nodeType===Node.TEXT_NODE || !safe ? value : Lightview.escapeHTML(value);
323
+ if(type==="string") return value;
324
+ node.nodeValue = value=="null" || value=="undefined" ? "" : value;
303
325
  } catch (e) {
326
+ console.warn(e);
304
327
  if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
305
328
  }
306
329
  }
@@ -323,43 +346,47 @@ const {observe} = (() => {
323
346
  if (["checkbox"].includes(inputType)) return "boolean";
324
347
  return "any";
325
348
  }
326
- const _importAnchors = (node, component) => {
327
- [...node.querySelectorAll('a[href][target^="#"]')].forEach((node) => {
349
+ const importAnchors = (node, component) => {
350
+ [...node.querySelectorAll('a[href$=".html"][target^="#"]')].forEach((node) => {
328
351
  node.removeEventListener("click", anchorHandler);
329
352
  addListener(node, "click", anchorHandler);
330
353
  })
331
354
  }
332
- 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);
333
359
  const inputtype = input.tagName === "SELECT" || input.tagName === "TEXTAREA" ? "text" : input.getAttribute("type"),
334
360
  type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype),
335
361
  deflt = input.getAttribute("default");
336
362
  value ||= input.getAttribute("value");
337
- let variable = component.vars[name] || {type};
363
+ let variable = component.vars[variableName] || {type};
338
364
  if (type !== variable.type) {
339
365
  if (variable.type === "any" || variable.type === "unknown") variable.type = type;
340
- 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}`)
341
367
  }
342
- component.variables({[name]: type});
343
- component.setValue(name,value);
368
+ component.variables({[variableName]: type});
369
+ if(inputtype!=="radio") component.setValue(variableName,value);
344
370
  let eventname = "change";
345
371
  if (input.tagName !== "SELECT" && (!inputtype || input.tagName === "TEXTAREA" || ["text", "number", "tel", "email", "url", "search", "password"].includes(inputtype))) {
346
372
  eventname = "input";
347
373
  }
348
- addListener(input, eventname, (event) => {
349
- event.stopImmediatePropagation();
350
- const target = event.target;
351
- let value = target.value;
374
+ const listener = (event) => {
375
+ if(event) event.stopImmediatePropagation();
376
+ let value = input.value;
352
377
  if (inputtype === "checkbox") {
353
378
  value = input.checked
354
- } else if (target.tagName === "SELECT") {
355
- if (target.hasAttribute("multiple")) {
356
- value = [...target.querySelectorAll("option")]
357
- .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)
358
384
  .map((option) => option.getAttribute("value") || option.innerText);
359
385
  }
360
386
  }
361
- component.varsProxy[name] = coerce(value, type);
362
- })
387
+ component.varsProxy[variableName] = coerce(value, type);
388
+ };
389
+ addListener(input, eventname, listener);
363
390
  }
364
391
  const tryParse = (value) => {
365
392
  try {
@@ -369,16 +396,18 @@ const {observe} = (() => {
369
396
  }
370
397
  }
371
398
  let reserved = {
399
+ any: {value: "any",constant: true},
372
400
  boolean: {value: "boolean", constant: true},
373
401
  string: {value: "string", constant: true},
374
402
  number: {value: "number", constant: true},
403
+ object: {value: "object", constant: true},
375
404
  observed: {value: true, constant: true},
376
405
  reactive: {value: true, constant: true},
377
406
  shared: {value: true, constant: true},
378
407
  exported: {value: true, constant: true},
379
408
  imported: {value: true, constant: true}
380
409
  };
381
- const createClass = (domElementNode, {observer, importAnchors, framed}) => {
410
+ const createClass = (domElementNode, {observer, framed}) => {
382
411
  const instances = new Set(),
383
412
  dom = domElementNode.tagName === "TEMPLATE"
384
413
  ? domElementNode.content.cloneNode(true)
@@ -432,7 +461,7 @@ const {observe} = (() => {
432
461
  })
433
462
  });
434
463
  [...dom.childNodes].forEach((child) => shadow.appendChild(child.cloneNode(true)));
435
- if (importAnchors) _importAnchors(shadow, this);
464
+ importAnchors(shadow, this);
436
465
  }
437
466
 
438
467
  get siblings() {
@@ -478,15 +507,21 @@ const {observe} = (() => {
478
507
  const nodes = getNodes(ctx);
479
508
  nodes.forEach((node) => {
480
509
  if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
481
- render(!!node.template, () => resolveNode(node, this))
510
+ render(!!node.template, () => resolveNodeOrText(node, this))
482
511
  } else if (node.nodeType === Node.ELEMENT_NODE) {
483
512
  // resolve the value before all else;
484
513
  const attr = node.attributes.value;
485
- let name;
486
514
  if (attr && attr.template) {
487
515
  render(!!attr.template, () => {
488
- const value = resolveNode(attr, this),
489
- eltype = resolveNode(node.attributes.type, ctx);
516
+ const value = resolveNodeOrText(attr, this),
517
+ eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx) : null;
518
+ if(node.attributes.value) {
519
+ const template = attr.template;
520
+ if(/\$\{[a-zA-z_]+\}/g.test(template)) {
521
+ const name = template.substring(2,template.length-1);
522
+ bindInput(node,name,this,value);
523
+ }
524
+ }
490
525
  if (eltype === "checkbox") {
491
526
  if (coerce(value, "boolean") === true) {
492
527
  node.setAttribute("checked", "");
@@ -495,7 +530,7 @@ const {observe} = (() => {
495
530
  node.removeAttribute("checked");
496
531
  node.checked = false;
497
532
  }
498
- const vname = resolveNode(node.attributes.name, ctx);
533
+ const vname = resolveNodeOrText(node.attributes.name, ctx);
499
534
  if (vname) ctx.setValue(vname, node.checked, {coerceTo: "boolean"});
500
535
  }
501
536
  if (node.tagName === "SELECT") {
@@ -503,38 +538,30 @@ const {observe} = (() => {
503
538
  if (node.hasAttribute("multiple")) values = coerce(value, Array);
504
539
  [...node.querySelectorAll("option")].forEach((option) => {
505
540
  if (option.hasAttribute("value")) {
506
- if (values.includes(resolveNode(option.attributes.value, ctx))) {
541
+ if (values.includes(resolveNodeOrText(option.attributes.value, ctx))) {
507
542
  option.setAttribute("selected", "");
508
543
  option.selected = true;
509
544
  }
510
- } else if (option.innerText.trim() === value) {
545
+ } else if (values.includes(resolveNodeOrText(option.innerText,ctx))) {
511
546
  option.setAttribute("selected", "");
512
547
  option.selected = true;
513
548
  }
514
549
  })
515
550
  }
516
- if(node.attributes.value) {
517
- const valueattr = node.attributes.value,
518
- template = valueattr.template || valueattr.template.value;
519
- if(template.startsWith("${") && template.endsWith("}")) {
520
- const name = template.substring(2,template.length-1);
521
- if(!name.includes(" ")) bindInput(node,name,this,value);
522
- }
523
- }
524
551
  });
525
552
  }
526
553
  [...node.attributes].forEach((attr) => {
527
- if (attr.name === "value") return;
554
+ if (attr.name === "value" && attr.template) return;
528
555
  const {name, value} = attr;
529
556
  if (name === "type") {
530
557
  if (value === "radio") {
531
- const name = resolveNode(node.attributes.name, ctx);
558
+ const name = resolveNodeOrText(node.attributes.name, ctx);
532
559
  for (const vname of this.getVariableNames()) {
533
560
  if (vname === name) {
534
561
  render(true, () => {
535
- const name = resolveNode(node.attributes.name, ctx),
562
+ const name = resolveNodeOrText(node.attributes.name, ctx),
536
563
  varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
537
- if (varvalue == resolveNode(node.attributes.value, ctx)) {
564
+ if (varvalue == resolveNodeOrText(node.attributes.value, ctx)) {
538
565
  node.setAttribute("checked", "");
539
566
  node.checked = true;
540
567
  } else {
@@ -553,9 +580,9 @@ const {observe} = (() => {
553
580
  if (type === "") { // name is :something
554
581
  render(!!attr.template, () => {
555
582
  const value = attr.value,
556
- elvalue = resolveNode(node.attributes.value, ctx),
557
- eltype = resolveNode(node.attributes.type, ctx),
558
- 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);
559
586
  if (params[0]) {
560
587
  if (value === "true") node.setAttribute(params[0], "")
561
588
  else node.removeAttribute(params[0]);
@@ -567,29 +594,31 @@ const {observe} = (() => {
567
594
  } else if (type === "l-on") {
568
595
  let listener;
569
596
  render(!!attr.template, () => {
570
- const value = resolveNode(attr, this);
597
+ const value = resolveNodeOrText(attr, this);
571
598
  if (listener) node.removeEventListener(params[0], listener);
572
599
  listener = this[value] || window[value] || Function(value);
573
600
  addListener(node, params[0], listener);
574
601
  })
575
602
  } else if (type === "l-if") {
576
603
  render(!!attr.template, () => {
577
- const value = resolveNode(attr, this);
604
+ const value = resolveNodeOrText(attr, this);
578
605
  node.style.setProperty("display", value === "true" ? "revert" : "none");
579
606
  })
580
607
  } else if (type === "l-for") {
581
608
  node.template ||= node.innerHTML;
582
609
  render(!!attr.template, () => {
583
610
  const [what = "each", vname = "item", index = "index", array = "array", after = false] = params,
584
- value = resolveNode(attr, this),
611
+ value = resolveNodeOrText(attr, this),
585
612
  coerced = coerce(value, what === "each" ? Array : "object"),
586
613
  target = what === "each" ? coerced : Object[what](coerced),
587
614
  html = target.reduce((html, item, i, target) => {
588
- return html += Function("context", "with(context) { return `" + node.template + "` }")({
589
- [vname]: item,
590
- [index]: i,
591
- [array]: target
592
- })
615
+ return html += Function("vars","context", "with(vars) { with(context) { return `" + node.template + "` }}")(
616
+ ctx.varsProxy,
617
+ {
618
+ [vname]: item,
619
+ [index]: i,
620
+ [array]: target
621
+ })
593
622
  }, ""),
594
623
  parsed = parser.parseFromString(html, "text/html");
595
624
  if (!window.lightviewDebug) {
@@ -605,7 +634,7 @@ const {observe} = (() => {
605
634
  }
606
635
  })
607
636
  } else if (attr.template) {
608
- render(!!attr.template, () => resolveNode(attr, this));
637
+ render(!!attr.template, () => resolveNodeOrText(attr, this));
609
638
  }
610
639
  })
611
640
  }
@@ -621,23 +650,23 @@ const {observe} = (() => {
621
650
  //Object.defineProperty(this, "adoptedCallback", {configurable: true, writable: true, value});
622
651
  }
623
652
 
624
- connected(value) {
625
- this.connectedCallback = value;
653
+ connected(callback) {
654
+ this.connectedCallback = callback;
626
655
  //Object.defineProperty(this, "connectedCallback", {configurable: true, writable: true, value});
627
656
  }
628
657
 
629
- attributeChanged(value) {
630
- this.attributeChangedCallback = value;
658
+ attributeChanged(callback) {
659
+ this.attributeChangedCallback = callback;
631
660
  //Object.defineProperty(this, "attributeChangedCallback", {configurable: true, writable: true, value});
632
661
  }
633
662
 
634
- disconnected(value) {
663
+ disconnected(callback) {
635
664
  Object.defineProperty(this, "disconnectedCallback", {
636
665
  configurable: true,
637
666
  writable: true,
638
667
  value: () => {
639
668
  value();
640
- super.disconnectedCallback(value);
669
+ super.disconnectedCallback(callback);
641
670
  }
642
671
  });
643
672
  }
@@ -648,19 +677,19 @@ const {observe} = (() => {
648
677
  })
649
678
  }
650
679
 
651
- setValue(name, value, {shared, coerceTo = typeof (value)} = {}) {
680
+ setValue(variableName, value, {coerceTo = typeof (value)} = {}) {
652
681
  if (!this.isConnected) {
653
682
  instances.delete(this);
654
683
  return false;
655
684
  }
656
- let {type} = this.vars[name] || {};
685
+ let {type} = this.vars[variableName] || {};
657
686
  if (type) {
658
687
  value = coerce(value, type);
659
- if (this.varsProxy[name] !== value) {
660
- const variable = this.vars[name];
688
+ if (this.varsProxy[variableName] !== value) {
689
+ const variable = this.vars[variableName];
661
690
  if (variable.shared) {
662
691
  const event = new VariableEvent({
663
- variableName: name,
692
+ variableName: variableName,
664
693
  value: value,
665
694
  oldValue: variable.value
666
695
  });
@@ -668,12 +697,12 @@ const {observe} = (() => {
668
697
  this.vars.postEvent.value("change", event);
669
698
  if (event.defaultPrevented) variable.value = value;
670
699
  } else {
671
- this.varsProxy[name] = value;
700
+ this.varsProxy[variableName] = value;
672
701
  }
673
702
  }
674
703
  return true;
675
704
  }
676
- this.vars[name] = {name, type: coerceTo, value: coerce(value, coerceTo)};
705
+ this.vars[variableName] = {name, type: coerceTo, value: coerce(value, coerceTo)};
677
706
  return false;
678
707
  }
679
708
 
@@ -694,7 +723,7 @@ const {observe} = (() => {
694
723
  }
695
724
  if (reactive) {
696
725
  variable.reactive = true;
697
- this.vars[key] = Reactor(variable,this);
726
+ this.vars[key] = Reactor(variable);
698
727
  }
699
728
  if (shared) {
700
729
  variable.shared = true;
@@ -742,7 +771,7 @@ const {observe} = (() => {
742
771
  }
743
772
  }
744
773
  }
745
- const createComponent = (name, node, {observer, importAnchors, framed} = {}) => {
774
+ const createComponent = (name, node, {framed,observer} = {}) => {
746
775
  let ctor = customElements.get(name);
747
776
  if (ctor) {
748
777
  if (framed && !ctor.lightviewFramed) {
@@ -752,38 +781,47 @@ const {observe} = (() => {
752
781
  }
753
782
  return ctor;
754
783
  }
755
- ctor = createClass(node, {observer, importAnchors, framed});
784
+ ctor = createClass(node, {observer, framed});
756
785
  customElements.define(name, ctor);
786
+ Lightview.customElements.set(name,ctor);
757
787
  return ctor;
758
788
  }
789
+ Lightview.customElements = new Map();
759
790
  Lightview.createComponent = createComponent;
760
791
  //Object.defineProperty(Lightview, "createComponent", {writable: true, configurable: true, value: createComponent})
761
792
  const importLink = async (link, observer) => {
762
793
  const url = (new URL(link.getAttribute("href"), window.location.href)),
763
794
  as = link.getAttribute("as") || getNameFromPath(url.pathname);
764
- if (url.hostname !== window.location.hostname) {
765
- throw new URIError(`importLink:HTML imports must be from same domain: ${url.hostname}!=${location.hostname}`)
795
+ if (url.hostname !== window.location.hostname && !link.getAttribute("crossorigin")) {
796
+ throw new URIError(`importLink:HTML imports must be from same domain: ${url.hostname}!=${location.hostname} unless 'crossorigin' attribute is set.`)
766
797
  }
767
798
  if (!customElements.get(as)) {
768
799
  const html = await (await fetch(url.href)).text(),
769
800
  dom = parser.parseFromString(html, "text/html"),
770
- importAnchors = !!dom.head.querySelector('meta[name="l-importAnchors"]'),
771
- unhide = !!dom.head.querySelector('meta[name="l-unhide"]');
801
+ unhide = !!dom.head.querySelector('meta[name="l-unhide"]'),
802
+ links = dom.head.querySelectorAll('link[href$=".html"][rel=module]');
803
+ for(const childlink of links) {
804
+ const href = childlink.getAttribute("href"),
805
+ childurl = new URL(href,url.href);
806
+ childlink.setAttribute("href",childurl.href);
807
+ if(link.hasAttribute("crossorigin")) childlink.setAttribute("crossorigin",link.getAttribute("crossorigin"))
808
+ await importLink(childlink,observer);
809
+ }
772
810
  if (unhide) dom.body.removeAttribute("hidden");
773
- createComponent(as, dom.body, {observer, importAnchors});
811
+ createComponent(as, dom.body, {observer});
774
812
  }
775
813
  return {as};
776
814
  }
777
815
  const importLinks = async () => {
778
816
  const observer = createObserver(document.body);
779
- for (const link of [...document.querySelectorAll("link[href][rel=module]")]) {
780
- await importLink(link);
817
+ for (const link of [...document.querySelectorAll(`link[href$=".html"][rel=module]`)]) {
818
+ await importLink(link,observer);
781
819
  }
782
820
  }
783
821
 
784
- const bodyAsComponent = ({as = "x-body", unhide, importAnchors, framed} = {}) => {
822
+ const bodyAsComponent = ({as = "x-body", unhide, framed} = {}) => {
785
823
  const parent = document.body.parentElement;
786
- createComponent(as, document.body, {importAnchors, framed});
824
+ createComponent(as, document.body, {framed});
787
825
  const component = document.createElement(as);
788
826
  parent.replaceChild(component, document.body);
789
827
  Object.defineProperty(document, "body", {
@@ -828,13 +866,12 @@ const {observe} = (() => {
828
866
  if (!domContentLoadedEvent) addListener(window, "DOMContentLoaded", (event) => domContentLoadedEvent = event);
829
867
  let OBSERVER;
830
868
  const loader = async (whenFramed) => {
831
- if (!!document.querySelector('meta[name="l-importLinks"]')) await importLinks();
832
- const importAnchors = !!document.querySelector('meta[name="l-importAnchors"]'),
833
- unhide = !!document.querySelector('meta[name="l-unhide"]'),
869
+ await importLinks();
870
+ const unhide = !!document.querySelector('meta[name="l-unhide"]'),
834
871
  isolated = !!document.querySelector('meta[name="l-isolate"]'),
835
872
  enableFrames = !!document.querySelector('meta[name="l-enableFrames"]');
836
873
  if (whenFramed) {
837
- whenFramed({unhide, importAnchors, isolated, enableFrames, framed: true});
874
+ whenFramed({unhide, isolated, enableFrames, framed: true});
838
875
  if (!isolated) {
839
876
  postMessage.enabled = true;
840
877
  addListener(window, "message", ({data}) => {
@@ -869,7 +906,7 @@ const {observe} = (() => {
869
906
  postMessage({type: "DOMContentLoaded"})
870
907
  }
871
908
  } else if (url.searchParams.has("as")) {
872
- bodyAsComponent({as: url.searchParams.get("as"), unhide, importAnchors});
909
+ bodyAsComponent({as: url.searchParams.get("as"), unhide});
873
910
  }
874
911
  if (enableFrames) {
875
912
  postMessage.enabled = true;
@@ -943,9 +980,9 @@ const {observe} = (() => {
943
980
  }
944
981
  }
945
982
  }
946
- const whenFramed = (f, {isolated} = {}) => {
983
+ const whenFramed = (callback, {isolated} = {}) => {
947
984
  // loads for framed content
948
- addListener(document, "DOMContentLoaded", (event) => loader(f));
985
+ addListener(document, "DOMContentLoaded", (event) => loader(callback));
949
986
  }
950
987
  Lightview.whenFramed = whenFramed;
951
988
  //Object.defineProperty(Lightview, "whenFramed", {configurable: true, writable: true, value: whenFramed});
@@ -956,6 +993,4 @@ const {observe} = (() => {
956
993
  }
957
994
 
958
995
  return {observe}
959
- })();
960
-
961
-
996
+ })();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lightview",
3
- "version": "1.4.4b",
4
- "description": "Small, simple, powerful web UI and micro front end creation ... imagine a blend of Svelte, React, Vue, Riot and more.",
3
+ "version": "1.4.8b",
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": {
7
7
  "test": "set NODE_OPTIONS=--experimental-vm-modules && jest ./test"