lightview 1.4.5-b → 1.4.7-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.5b (BETA)
1
+ # lightview v1.4.7b (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% }
package/counter.html CHANGED
@@ -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
  }, {
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
  }
@@ -232,7 +248,7 @@ const {observe} = (() => {
232
248
  const observer = new MutationObserver((mutations) => {
233
249
  mutations.forEach((mutation) => {
234
250
  if (mutation.type === "attributes") {
235
- if (framed) debugger;
251
+ //if (framed) debugger;
236
252
  const name = mutation.attributeName,
237
253
  target = mutation.target,
238
254
  value = target.getAttribute(name);
@@ -296,12 +312,14 @@ const {observe} = (() => {
296
312
  }
297
313
  return nodes;
298
314
  }
299
- const resolveNode = (node, component) => {
315
+ const resolveNode = (node, component,safe) => {
300
316
  if (node?.template) {
301
317
  try {
302
- const value = Function("context", "with(context) { return `" + node.template + "` }")(component.varsProxy);
303
- node.nodeValue = value === "null" || value === "undefined" ? "" : value;
318
+ let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(node?.template) + "` }")(component.varsProxy);
319
+ value = node.nodeType===Node.TEXT_NODE || !safe ? value : Lightview.escapeHTML(value);
320
+ node.nodeValue = value=="null" || value=="undefined" ? "" : value;
304
321
  } catch (e) {
322
+ console.warn(e);
305
323
  if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
306
324
  }
307
325
  }
@@ -324,8 +342,8 @@ const {observe} = (() => {
324
342
  if (["checkbox"].includes(inputType)) return "boolean";
325
343
  return "any";
326
344
  }
327
- const _importAnchors = (node, component) => {
328
- [...node.querySelectorAll('a[href][target^="#"]')].forEach((node) => {
345
+ const importAnchors = (node, component) => {
346
+ [...node.querySelectorAll('a[href$=".html"][target^="#"]')].forEach((node) => {
329
347
  node.removeEventListener("click", anchorHandler);
330
348
  addListener(node, "click", anchorHandler);
331
349
  })
@@ -370,16 +388,18 @@ const {observe} = (() => {
370
388
  }
371
389
  }
372
390
  let reserved = {
391
+ any: {value: "any",constant: true},
373
392
  boolean: {value: "boolean", constant: true},
374
393
  string: {value: "string", constant: true},
375
394
  number: {value: "number", constant: true},
395
+ object: {value: "object", constant: true},
376
396
  observed: {value: true, constant: true},
377
397
  reactive: {value: true, constant: true},
378
398
  shared: {value: true, constant: true},
379
399
  exported: {value: true, constant: true},
380
400
  imported: {value: true, constant: true}
381
401
  };
382
- const createClass = (domElementNode, {observer, importAnchors, framed}) => {
402
+ const createClass = (domElementNode, {observer, framed}) => {
383
403
  const instances = new Set(),
384
404
  dom = domElementNode.tagName === "TEMPLATE"
385
405
  ? domElementNode.content.cloneNode(true)
@@ -433,7 +453,7 @@ const {observe} = (() => {
433
453
  })
434
454
  });
435
455
  [...dom.childNodes].forEach((child) => shadow.appendChild(child.cloneNode(true)));
436
- if (importAnchors) _importAnchors(shadow, this);
456
+ importAnchors(shadow, this);
437
457
  }
438
458
 
439
459
  get siblings() {
@@ -491,7 +511,7 @@ const {observe} = (() => {
491
511
  const template = attr.template;
492
512
  if(/\$\{[a-zA-z_]+\}/g.test(template)) {
493
513
  const name = template.substring(2,template.length-1);
494
- if(!name.includes(" ")) bindInput(node,name,this,value);
514
+ bindInput(node,name,this,value);
495
515
  }
496
516
  }
497
517
  if (eltype === "checkbox") {
@@ -523,7 +543,7 @@ const {observe} = (() => {
523
543
  });
524
544
  }
525
545
  [...node.attributes].forEach((attr) => {
526
- if (attr.name === "value") return;
546
+ if (attr.name === "value" && attr.template) return;
527
547
  const {name, value} = attr;
528
548
  if (name === "type") {
529
549
  if (value === "radio") {
@@ -584,11 +604,13 @@ const {observe} = (() => {
584
604
  coerced = coerce(value, what === "each" ? Array : "object"),
585
605
  target = what === "each" ? coerced : Object[what](coerced),
586
606
  html = target.reduce((html, item, i, target) => {
587
- return html += Function("context", "with(context) { return `" + node.template + "` }")({
588
- [vname]: item,
589
- [index]: i,
590
- [array]: target
591
- })
607
+ return html += Function("vars","context", "with(vars) { with(context) { return `" + node.template + "` }}")(
608
+ ctx.varsProxy,
609
+ {
610
+ [vname]: item,
611
+ [index]: i,
612
+ [array]: target
613
+ })
592
614
  }, ""),
593
615
  parsed = parser.parseFromString(html, "text/html");
594
616
  if (!window.lightviewDebug) {
@@ -620,23 +642,23 @@ const {observe} = (() => {
620
642
  //Object.defineProperty(this, "adoptedCallback", {configurable: true, writable: true, value});
621
643
  }
622
644
 
623
- connected(value) {
624
- this.connectedCallback = value;
645
+ connected(callback) {
646
+ this.connectedCallback = callback;
625
647
  //Object.defineProperty(this, "connectedCallback", {configurable: true, writable: true, value});
626
648
  }
627
649
 
628
- attributeChanged(value) {
629
- this.attributeChangedCallback = value;
650
+ attributeChanged(callback) {
651
+ this.attributeChangedCallback = callback;
630
652
  //Object.defineProperty(this, "attributeChangedCallback", {configurable: true, writable: true, value});
631
653
  }
632
654
 
633
- disconnected(value) {
655
+ disconnected(callback) {
634
656
  Object.defineProperty(this, "disconnectedCallback", {
635
657
  configurable: true,
636
658
  writable: true,
637
659
  value: () => {
638
660
  value();
639
- super.disconnectedCallback(value);
661
+ super.disconnectedCallback(callback);
640
662
  }
641
663
  });
642
664
  }
@@ -647,19 +669,19 @@ const {observe} = (() => {
647
669
  })
648
670
  }
649
671
 
650
- setValue(name, value, {shared, coerceTo = typeof (value)} = {}) {
672
+ setValue(variableName, value, {coerceTo = typeof (value)} = {}) {
651
673
  if (!this.isConnected) {
652
674
  instances.delete(this);
653
675
  return false;
654
676
  }
655
- let {type} = this.vars[name] || {};
677
+ let {type} = this.vars[variableName] || {};
656
678
  if (type) {
657
679
  value = coerce(value, type);
658
- if (this.varsProxy[name] !== value) {
659
- const variable = this.vars[name];
680
+ if (this.varsProxy[variableName] !== value) {
681
+ const variable = this.vars[variableName];
660
682
  if (variable.shared) {
661
683
  const event = new VariableEvent({
662
- variableName: name,
684
+ variableName: variableName,
663
685
  value: value,
664
686
  oldValue: variable.value
665
687
  });
@@ -667,12 +689,12 @@ const {observe} = (() => {
667
689
  this.vars.postEvent.value("change", event);
668
690
  if (event.defaultPrevented) variable.value = value;
669
691
  } else {
670
- this.varsProxy[name] = value;
692
+ this.varsProxy[variableName] = value;
671
693
  }
672
694
  }
673
695
  return true;
674
696
  }
675
- this.vars[name] = {name, type: coerceTo, value: coerce(value, coerceTo)};
697
+ this.vars[variableName] = {name, type: coerceTo, value: coerce(value, coerceTo)};
676
698
  return false;
677
699
  }
678
700
 
@@ -741,7 +763,7 @@ const {observe} = (() => {
741
763
  }
742
764
  }
743
765
  }
744
- const createComponent = (name, node, {observer, importAnchors, framed} = {}) => {
766
+ const createComponent = (name, node, {framed,observer} = {}) => {
745
767
  let ctor = customElements.get(name);
746
768
  if (ctor) {
747
769
  if (framed && !ctor.lightviewFramed) {
@@ -751,38 +773,47 @@ const {observe} = (() => {
751
773
  }
752
774
  return ctor;
753
775
  }
754
- ctor = createClass(node, {observer, importAnchors, framed});
776
+ ctor = createClass(node, {observer, framed});
755
777
  customElements.define(name, ctor);
778
+ Lightview.customElements.set(name,ctor);
756
779
  return ctor;
757
780
  }
781
+ Lightview.customElements = new Map();
758
782
  Lightview.createComponent = createComponent;
759
783
  //Object.defineProperty(Lightview, "createComponent", {writable: true, configurable: true, value: createComponent})
760
784
  const importLink = async (link, observer) => {
761
785
  const url = (new URL(link.getAttribute("href"), window.location.href)),
762
786
  as = link.getAttribute("as") || getNameFromPath(url.pathname);
763
- if (url.hostname !== window.location.hostname) {
764
- throw new URIError(`importLink:HTML imports must be from same domain: ${url.hostname}!=${location.hostname}`)
787
+ if (url.hostname !== window.location.hostname && !link.getAttribute("crossorigin")) {
788
+ throw new URIError(`importLink:HTML imports must be from same domain: ${url.hostname}!=${location.hostname} unless 'crossorigin' attribute is set.`)
765
789
  }
766
790
  if (!customElements.get(as)) {
767
791
  const html = await (await fetch(url.href)).text(),
768
792
  dom = parser.parseFromString(html, "text/html"),
769
- importAnchors = !!dom.head.querySelector('meta[name="l-importAnchors"]'),
770
- unhide = !!dom.head.querySelector('meta[name="l-unhide"]');
793
+ unhide = !!dom.head.querySelector('meta[name="l-unhide"]'),
794
+ links = dom.head.querySelectorAll('link[href$=".html"][rel=module]');
795
+ for(const childlink of links) {
796
+ const href = childlink.getAttribute("href"),
797
+ childurl = new URL(href,url.href);
798
+ childlink.setAttribute("href",childurl.href);
799
+ if(link.hasAttribute("crossorigin")) childlink.setAttribute("crossorigin",link.getAttribute("crossorigin"))
800
+ await importLink(childlink,observer);
801
+ }
771
802
  if (unhide) dom.body.removeAttribute("hidden");
772
- createComponent(as, dom.body, {observer, importAnchors});
803
+ createComponent(as, dom.body, {observer});
773
804
  }
774
805
  return {as};
775
806
  }
776
807
  const importLinks = async () => {
777
808
  const observer = createObserver(document.body);
778
- for (const link of [...document.querySelectorAll("link[href][rel=module]")]) {
779
- await importLink(link);
809
+ for (const link of [...document.querySelectorAll(`link[href$=".html"][rel=module]`)]) {
810
+ await importLink(link,observer);
780
811
  }
781
812
  }
782
813
 
783
- const bodyAsComponent = ({as = "x-body", unhide, importAnchors, framed} = {}) => {
814
+ const bodyAsComponent = ({as = "x-body", unhide, framed} = {}) => {
784
815
  const parent = document.body.parentElement;
785
- createComponent(as, document.body, {importAnchors, framed});
816
+ createComponent(as, document.body, {framed});
786
817
  const component = document.createElement(as);
787
818
  parent.replaceChild(component, document.body);
788
819
  Object.defineProperty(document, "body", {
@@ -827,13 +858,12 @@ const {observe} = (() => {
827
858
  if (!domContentLoadedEvent) addListener(window, "DOMContentLoaded", (event) => domContentLoadedEvent = event);
828
859
  let OBSERVER;
829
860
  const loader = async (whenFramed) => {
830
- if (!!document.querySelector('meta[name="l-importLinks"]')) await importLinks();
831
- const importAnchors = !!document.querySelector('meta[name="l-importAnchors"]'),
832
- unhide = !!document.querySelector('meta[name="l-unhide"]'),
861
+ await importLinks();
862
+ const unhide = !!document.querySelector('meta[name="l-unhide"]'),
833
863
  isolated = !!document.querySelector('meta[name="l-isolate"]'),
834
864
  enableFrames = !!document.querySelector('meta[name="l-enableFrames"]');
835
865
  if (whenFramed) {
836
- whenFramed({unhide, importAnchors, isolated, enableFrames, framed: true});
866
+ whenFramed({unhide, isolated, enableFrames, framed: true});
837
867
  if (!isolated) {
838
868
  postMessage.enabled = true;
839
869
  addListener(window, "message", ({data}) => {
@@ -868,7 +898,7 @@ const {observe} = (() => {
868
898
  postMessage({type: "DOMContentLoaded"})
869
899
  }
870
900
  } else if (url.searchParams.has("as")) {
871
- bodyAsComponent({as: url.searchParams.get("as"), unhide, importAnchors});
901
+ bodyAsComponent({as: url.searchParams.get("as"), unhide});
872
902
  }
873
903
  if (enableFrames) {
874
904
  postMessage.enabled = true;
@@ -942,9 +972,9 @@ const {observe} = (() => {
942
972
  }
943
973
  }
944
974
  }
945
- const whenFramed = (f, {isolated} = {}) => {
975
+ const whenFramed = (callback, {isolated} = {}) => {
946
976
  // loads for framed content
947
- addListener(document, "DOMContentLoaded", (event) => loader(f));
977
+ addListener(document, "DOMContentLoaded", (event) => loader(callback));
948
978
  }
949
979
  Lightview.whenFramed = whenFramed;
950
980
  //Object.defineProperty(Lightview, "whenFramed", {configurable: true, writable: true, value: whenFramed});
@@ -956,5 +986,3 @@ const {observe} = (() => {
956
986
 
957
987
  return {observe}
958
988
  })();
959
-
960
-
package/message.html ADDED
@@ -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>
package/nested.html ADDED
@@ -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>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lightview",
3
- "version": "1.4.5b",
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.7b",
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"
package/top.html ADDED
@@ -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>