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 +2 -2
- package/codepen-no-tabs-embed.css +2 -0
- package/counter.html +1 -0
- package/lightview.js +76 -48
- package/message.html +13 -0
- package/nested.html +11 -0
- package/package.json +2 -2
- package/top.html +10 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# lightview v1.4.
|
|
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: <
|
|
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
|
|
package/counter.html
CHANGED
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
|
-
|
|
303
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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(
|
|
624
|
-
this.connectedCallback =
|
|
645
|
+
connected(callback) {
|
|
646
|
+
this.connectedCallback = callback;
|
|
625
647
|
//Object.defineProperty(this, "connectedCallback", {configurable: true, writable: true, value});
|
|
626
648
|
}
|
|
627
649
|
|
|
628
|
-
attributeChanged(
|
|
629
|
-
this.attributeChangedCallback =
|
|
650
|
+
attributeChanged(callback) {
|
|
651
|
+
this.attributeChangedCallback = callback;
|
|
630
652
|
//Object.defineProperty(this, "attributeChangedCallback", {configurable: true, writable: true, value});
|
|
631
653
|
}
|
|
632
654
|
|
|
633
|
-
disconnected(
|
|
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(
|
|
661
|
+
super.disconnectedCallback(callback);
|
|
640
662
|
}
|
|
641
663
|
});
|
|
642
664
|
}
|
|
@@ -647,19 +669,19 @@ const {observe} = (() => {
|
|
|
647
669
|
})
|
|
648
670
|
}
|
|
649
671
|
|
|
650
|
-
setValue(
|
|
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[
|
|
677
|
+
let {type} = this.vars[variableName] || {};
|
|
656
678
|
if (type) {
|
|
657
679
|
value = coerce(value, type);
|
|
658
|
-
if (this.varsProxy[
|
|
659
|
-
const variable = this.vars[
|
|
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:
|
|
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[
|
|
692
|
+
this.varsProxy[variableName] = value;
|
|
671
693
|
}
|
|
672
694
|
}
|
|
673
695
|
return true;
|
|
674
696
|
}
|
|
675
|
-
this.vars[
|
|
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
|
|
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,
|
|
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
|
-
|
|
770
|
-
|
|
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
|
|
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(
|
|
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,
|
|
814
|
+
const bodyAsComponent = ({as = "x-body", unhide, framed} = {}) => {
|
|
784
815
|
const parent = document.body.parentElement;
|
|
785
|
-
createComponent(as, document.body, {
|
|
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
|
-
|
|
831
|
-
const
|
|
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,
|
|
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
|
|
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 = (
|
|
975
|
+
const whenFramed = (callback, {isolated} = {}) => {
|
|
946
976
|
// loads for framed content
|
|
947
|
-
addListener(document, "DOMContentLoaded", (event) => loader(
|
|
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
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.
|
|
4
|
-
"description": "Small, simple, powerful web UI and micro front end creation ...
|
|
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"
|