lightview 1.2.0-b → 1.3.1-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 +3 -1
- package/counter.html +30 -0
- package/directives.html +80 -0
- package/lightview.js +376 -281
- package/package.json +6 -3
- package/remoteform.html +1 -1
- package/xor.html +62 -0
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# lightview v1.
|
|
1
|
+
# lightview v1.3.1b (BETA)
|
|
2
2
|
|
|
3
3
|
Small, simple, powerful web UI creation ...
|
|
4
4
|
|
|
5
|
+
Great ideas from Svelte, React, Vue and Riot combined into one small tool: < 6K (minified/gzipped).
|
|
6
|
+
|
|
5
7
|
See the docs and examples at [https://lightview.dev](https://lightview.dev).
|
|
6
8
|
|
package/counter.html
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<head>
|
|
2
|
+
<title>Counter</title>
|
|
3
|
+
<script src="../lightview.js?as=x-body"></script>
|
|
4
|
+
</head>
|
|
5
|
+
|
|
6
|
+
<body>
|
|
7
|
+
<p>
|
|
8
|
+
<button l-on:click="bump">Click count:${count}</button>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<script type="lightview/module">
|
|
12
|
+
self.variables({
|
|
13
|
+
count: number
|
|
14
|
+
}, {
|
|
15
|
+
reactive
|
|
16
|
+
});
|
|
17
|
+
debugger;
|
|
18
|
+
count = 0;
|
|
19
|
+
self.bump = () => count++;
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<style>
|
|
23
|
+
button {
|
|
24
|
+
margin: 20px;
|
|
25
|
+
background: gray
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
28
|
+
</body>
|
|
29
|
+
|
|
30
|
+
</html>
|
package/directives.html
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
|
|
3
|
+
<head>
|
|
4
|
+
<title>Directives</title>
|
|
5
|
+
<script src="./lightview.js?as=x-body"></script>
|
|
6
|
+
</head>
|
|
7
|
+
|
|
8
|
+
<body>
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
<p>
|
|
12
|
+
Show: <input type="checkbox" :="${on}" l-bind="on">
|
|
13
|
+
<div l-if="${on}">
|
|
14
|
+
Show is true
|
|
15
|
+
</div>
|
|
16
|
+
</p>
|
|
17
|
+
<p>
|
|
18
|
+
|
|
19
|
+
<input id="red" type="radio" name="myradio" value="red" :="${color}" l-bind="color"> Red
|
|
20
|
+
<input id="yellow" type="radio" name="myradio" value="yellow" :="${color}" l-bind="color"> Yellow
|
|
21
|
+
<input id="green" type="radio" name="myradio" value="green" :="${color}" l-bind="color"> Green
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p>
|
|
25
|
+
<select l-bind="color" value="${color}">
|
|
26
|
+
<option value="red">Red</option>
|
|
27
|
+
<option value="yellow">Yellow</option>
|
|
28
|
+
<option value="green">Green</option>
|
|
29
|
+
</select>
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
<p>
|
|
34
|
+
How would you like that burger?<br>
|
|
35
|
+
<select l-bind="options" value="${options}" multiple>
|
|
36
|
+
<option>lettuce</option>
|
|
37
|
+
<option>tomato</option>
|
|
38
|
+
<option>cheese</option>
|
|
39
|
+
</select>
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
<ul l-for:each="${children}">
|
|
45
|
+
<li>${index}:${element}</li>
|
|
46
|
+
</ul>
|
|
47
|
+
<ul l-for:values:value:index='{"1":"v1","2":"v2","3":"v3"}'>
|
|
48
|
+
<li>${value}:${index}</li>
|
|
49
|
+
</ul>
|
|
50
|
+
<ul l-for:keys:key='{"name":"joe","age":27}'>
|
|
51
|
+
<li>${key}</li>
|
|
52
|
+
</ul>
|
|
53
|
+
<ul l-for:entries:entry="${children}">
|
|
54
|
+
<li>${entry[0]}:${entry[1]}</li>
|
|
55
|
+
</ul>
|
|
56
|
+
|
|
57
|
+
Variable Values
|
|
58
|
+
<p id="variables"></p>
|
|
59
|
+
|
|
60
|
+
<script type="lightview/module">
|
|
61
|
+
self.variables({on:boolean,off:boolean,color:string,children:Array,options:Array},{reactive});
|
|
62
|
+
|
|
63
|
+
on = true;
|
|
64
|
+
color = "yellow";
|
|
65
|
+
children = ["John","Mary","Jane"];
|
|
66
|
+
options = ["tomato"];
|
|
67
|
+
|
|
68
|
+
addEventListener("change",()=> {
|
|
69
|
+
const el = self.getElementById("variables");
|
|
70
|
+
while(el.lastElementChild) el.lastElementChild.remove();
|
|
71
|
+
self.getVariableNames().forEach((name) => {
|
|
72
|
+
const line = document.createElement("div");
|
|
73
|
+
line.innerText = `${name} = ${JSON.stringify(self.getValue(name))}`;
|
|
74
|
+
el.appendChild(line);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
</script>
|
|
78
|
+
</body>
|
|
79
|
+
|
|
80
|
+
</html>
|
package/lightview.js
CHANGED
|
@@ -29,6 +29,10 @@ const Lightview = {};
|
|
|
29
29
|
const {observe} = (() => {
|
|
30
30
|
let CURRENTOBSERVER;
|
|
31
31
|
const parser = new DOMParser();
|
|
32
|
+
|
|
33
|
+
const addListener = (node,eventName,callback) => {
|
|
34
|
+
node.addEventListener(eventName,callback); // just used to make code footprint smaller
|
|
35
|
+
}
|
|
32
36
|
const anchorHandler = async (event) => {
|
|
33
37
|
event.preventDefault();
|
|
34
38
|
const target = event.target;
|
|
@@ -63,15 +67,16 @@ const {observe} = (() => {
|
|
|
63
67
|
return observer;
|
|
64
68
|
}
|
|
65
69
|
const coerce = (value, toType) => {
|
|
70
|
+
if (value + "" === "null" || value + "" === "undefined") return value;
|
|
66
71
|
const type = typeof (value);
|
|
67
72
|
if (type === toType) return value;
|
|
68
73
|
if (toType === "number") return parseFloat(value + "");
|
|
69
74
|
if (toType === "boolean") {
|
|
70
|
-
if(["on","checked","selected"].includes(value)) return true;
|
|
75
|
+
if (["on", "checked", "selected"].includes(value)) return true;
|
|
71
76
|
try {
|
|
72
77
|
const parsed = JSON.parse(value + "");
|
|
73
78
|
if (typeof (parsed) === "boolean") return parsed;
|
|
74
|
-
return [1,"on","checked","selected"].includes(parsed);
|
|
79
|
+
return [1, "on", "checked", "selected"].includes(parsed);
|
|
75
80
|
} catch (e) {
|
|
76
81
|
throw new TypeError(`Unable to convert ${value} into 'boolean'`);
|
|
77
82
|
}
|
|
@@ -79,10 +84,10 @@ const {observe} = (() => {
|
|
|
79
84
|
if (toType === "string") return value + "";
|
|
80
85
|
const isfunction = typeof (toType) === "function";
|
|
81
86
|
if ((toType === "object" || isfunction)) {
|
|
82
|
-
if(type==="object")
|
|
83
|
-
if(value instanceof toType) return value;
|
|
87
|
+
if (type === "object") {
|
|
88
|
+
if (value instanceof toType) return value;
|
|
84
89
|
}
|
|
85
|
-
if(type === "string") {
|
|
90
|
+
if (type === "string") {
|
|
86
91
|
value = value.trim();
|
|
87
92
|
try {
|
|
88
93
|
if (isfunction) {
|
|
@@ -121,9 +126,13 @@ const {observe} = (() => {
|
|
|
121
126
|
proxy = new Proxy(value, {
|
|
122
127
|
get(target, property) {
|
|
123
128
|
if (property === "__isReactor__") return true;
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
if (target instanceof Array) {
|
|
130
|
+
if (property === "toJSON") return function toJSON() {
|
|
131
|
+
return [...target];
|
|
132
|
+
}
|
|
133
|
+
if (property === "toString") return function toString() {
|
|
134
|
+
return JSON.stringify(target);
|
|
135
|
+
}
|
|
127
136
|
}
|
|
128
137
|
let value = target[property];
|
|
129
138
|
const type = typeof (value);
|
|
@@ -183,19 +192,19 @@ const {observe} = (() => {
|
|
|
183
192
|
if (target[property] === undefined) {
|
|
184
193
|
target[property] = {type: "any", value: newValue}; // should we allow this, do first to prevent loops
|
|
185
194
|
target.postEvent.value("change", event);
|
|
186
|
-
if(event.defaultPrevented) delete target[property].value;
|
|
195
|
+
if (event.defaultPrevented) delete target[property].value;
|
|
187
196
|
return true;
|
|
188
197
|
}
|
|
189
|
-
const {type, value, shared, exported, constant,reactive} = target[property];
|
|
198
|
+
const {type, value, shared, exported, constant, reactive} = target[property];
|
|
190
199
|
if (constant) throw new TypeError(`${property}:${type} is a constant`);
|
|
191
200
|
const newtype = typeof (newValue),
|
|
192
201
|
typetype = typeof (type);
|
|
193
|
-
if (newValue==null || type === "any" || newtype === type || (typetype === "function" && newValue && newtype === "object" && newValue instanceof type)) {
|
|
202
|
+
if (newValue == null || type === "any" || newtype === type || (typetype === "function" && newValue && newtype === "object" && newValue instanceof type)) {
|
|
194
203
|
if (value !== newValue) {
|
|
195
204
|
event.oldValue = value;
|
|
196
205
|
target[property].value = reactive ? Reactor(newValue) : newValue; // do first to prevent loops
|
|
197
206
|
target.postEvent.value("change", event);
|
|
198
|
-
if(event.defaultPrevented) target[property].value = value;
|
|
207
|
+
if (event.defaultPrevented) target[property].value = value;
|
|
199
208
|
}
|
|
200
209
|
return true;
|
|
201
210
|
}
|
|
@@ -218,7 +227,7 @@ const {observe} = (() => {
|
|
|
218
227
|
if (target.observedAttributes && target.observedAttributes.includes(name)) {
|
|
219
228
|
const value = target.getAttribute(name);
|
|
220
229
|
if (value !== mutation.oldValue) {
|
|
221
|
-
target.
|
|
230
|
+
target.setValue(name, value);
|
|
222
231
|
if (target.attributeChangedCallback) target.attributeChangedCallback(name, value, mutation.oldValue);
|
|
223
232
|
}
|
|
224
233
|
}
|
|
@@ -265,9 +274,7 @@ const {observe} = (() => {
|
|
|
265
274
|
if (!nodes.includes(node)) nodes.push(node);
|
|
266
275
|
}
|
|
267
276
|
})
|
|
268
|
-
if (!skip)
|
|
269
|
-
if (!node.shadowRoot) nodes.push(...getNodes(node));
|
|
270
|
-
}
|
|
277
|
+
if (!skip && !node.shadowRoot) nodes.push(...getNodes(node));
|
|
271
278
|
}
|
|
272
279
|
}
|
|
273
280
|
}
|
|
@@ -277,7 +284,7 @@ const {observe} = (() => {
|
|
|
277
284
|
if (node.template) {
|
|
278
285
|
try {
|
|
279
286
|
const value = Function("context", "with(context) { return `" + node.template + "` }")(component.varsProxy);
|
|
280
|
-
node.nodeValue = value==="null" || value==="undefined" ? "" : value;
|
|
287
|
+
node.nodeValue = value === "null" || value === "undefined" ? "" : value;
|
|
281
288
|
} catch (e) {
|
|
282
289
|
if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
|
|
283
290
|
}
|
|
@@ -298,57 +305,79 @@ const {observe} = (() => {
|
|
|
298
305
|
if (["text", "tel", "email", "url", "search", "radio"].includes(inputType)) return "string";
|
|
299
306
|
if (["number", "range"].includes(inputType)) return "number";
|
|
300
307
|
if (["datetime"].includes(inputType)) return Date;
|
|
301
|
-
if(["checkbox"].includes(inputType)) return "boolean";
|
|
308
|
+
if (["checkbox"].includes(inputType)) return "boolean";
|
|
302
309
|
return "any";
|
|
303
310
|
}
|
|
304
|
-
const _importAnchors = (node,component) => {
|
|
311
|
+
const _importAnchors = (node, component) => {
|
|
305
312
|
[...node.querySelectorAll('a[href][target^="#"]')].forEach((node) => {
|
|
306
313
|
node.removeEventListener("click", anchorHandler);
|
|
307
|
-
node
|
|
314
|
+
addListener(node,"click", anchorHandler);
|
|
308
315
|
})
|
|
309
316
|
}
|
|
310
317
|
const _bindForms = (node, component) => {
|
|
311
|
-
[...node.querySelectorAll("input")].forEach((input) =>
|
|
312
|
-
bindInput(input,component);
|
|
313
|
-
})
|
|
318
|
+
[...node.querySelectorAll("input")].forEach((input) => bindInput(input, component))
|
|
314
319
|
}
|
|
315
|
-
const bindInput = (input,component) => {
|
|
316
|
-
|
|
317
|
-
vname = input.getAttribute("l-bind")||name;
|
|
320
|
+
const bindInput = (input, component) => {
|
|
321
|
+
let name = input.getAttribute("name"),
|
|
322
|
+
vname = input.getAttribute("l-bind") || name;
|
|
323
|
+
name ||= vname;
|
|
318
324
|
if (name) {
|
|
319
|
-
if(!input.hasAttribute("l-bind")) input.setAttribute("l-bind",vname)
|
|
320
|
-
const
|
|
325
|
+
if (!input.hasAttribute("l-bind")) input.setAttribute("l-bind", vname)
|
|
326
|
+
const inputtype = input.tagName === "SELECT" ? "text" : input.getAttribute("type"),
|
|
327
|
+
type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype),
|
|
321
328
|
deflt = input.getAttribute("default"),
|
|
322
329
|
value = input.getAttribute("value");
|
|
323
330
|
let variable = component.vars[vname] || {type};
|
|
324
|
-
if(type!==variable.type) {
|
|
325
|
-
if(variable.type==="any" || variable.type==="unknown") variable.type = type;
|
|
331
|
+
if (type !== variable.type) {
|
|
332
|
+
if (variable.type === "any" || variable.type === "unknown") variable.type = type;
|
|
326
333
|
else throw new TypeError(`Attempt to bind <input name="${name}" type="${type}"> to variable ${vname}:${variable.type}`)
|
|
327
334
|
}
|
|
328
|
-
component.variables({[vname]:type});
|
|
329
|
-
variable = component.vars[vname]
|
|
330
|
-
if (value || deflt) {
|
|
335
|
+
component.variables({[vname]: type});
|
|
336
|
+
variable = component.vars[vname];
|
|
337
|
+
//if (value || deflt) {
|
|
338
|
+
if (inputtype !== "radio") {
|
|
331
339
|
if (value && !value.includes("${")) {
|
|
332
340
|
variable.value = coerce(value, type);
|
|
333
|
-
input.setAttribute("value", `\${${name}}`);
|
|
334
|
-
}
|
|
335
|
-
if (deflt && !deflt.includes("${")) {
|
|
341
|
+
//input.setAttribute("value", `\${${name}}`);
|
|
342
|
+
} else if (deflt && !deflt.includes("${")) {
|
|
336
343
|
variable.value = coerce(deflt, type);
|
|
337
|
-
input.setAttribute("default", `\${${name}}`);
|
|
344
|
+
//input.setAttribute("default", `\${${name}}`);
|
|
338
345
|
}
|
|
339
346
|
}
|
|
340
|
-
|
|
347
|
+
//}
|
|
348
|
+
addListener(input,"change", (event) => {
|
|
341
349
|
event.stopImmediatePropagation();
|
|
342
|
-
|
|
350
|
+
const target = event.target;
|
|
351
|
+
let value = target.value;
|
|
352
|
+
if (inputtype === "checkbox") {
|
|
353
|
+
value = input.checked
|
|
354
|
+
} else if (target.tagName === "SELECT") {
|
|
355
|
+
if (target.hasAttribute("multiple")) {
|
|
356
|
+
value = [...target.querySelectorAll("option")]
|
|
357
|
+
.filter((option) => option.selected || option.getAttribute("value") == value || option.innerText == value)
|
|
358
|
+
.map((option) => option.getAttribute("value") || option.innerText);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
component.varsProxy[vname] = coerce(value, type);
|
|
343
362
|
})
|
|
344
363
|
}
|
|
345
364
|
}
|
|
346
|
-
|
|
365
|
+
let reserved = {
|
|
366
|
+
boolean: {value: "boolean", constant: true},
|
|
367
|
+
string: {value: "string", constant: true},
|
|
368
|
+
number: {value: "number", constant: true},
|
|
369
|
+
observed: {value: true, constant: true},
|
|
370
|
+
reactive: {value: true, constant: true},
|
|
371
|
+
shared: {value: true, constant: true},
|
|
372
|
+
exported: {value: true, constant: true},
|
|
373
|
+
imported: {value: true, constant: true}
|
|
374
|
+
};
|
|
375
|
+
const createClass = (domElementNode, {observer, bindForms, importAnchors}) => {
|
|
347
376
|
const instances = new Set(),
|
|
348
|
-
dom = domElementNode.tagName==="TEMPLATE"
|
|
377
|
+
dom = domElementNode.tagName === "TEMPLATE"
|
|
349
378
|
? domElementNode.content.cloneNode(true)
|
|
350
379
|
: domElementNode.cloneNode(true);
|
|
351
|
-
if(domElementNode.tagName==="TEMPLATE") domElementNode = domElementNode.cloneNode(true);
|
|
380
|
+
if (domElementNode.tagName === "TEMPLATE") domElementNode = domElementNode.cloneNode(true);
|
|
352
381
|
return class CustomElement extends HTMLElement {
|
|
353
382
|
static get instances() {
|
|
354
383
|
return instances;
|
|
@@ -362,6 +391,7 @@ const {observe} = (() => {
|
|
|
362
391
|
shadow = this.attachShadow({mode: "open"}),
|
|
363
392
|
eventlisteners = {};
|
|
364
393
|
this.vars = {
|
|
394
|
+
...reserved,
|
|
365
395
|
addEventListener: {
|
|
366
396
|
value: (eventName, listener) => {
|
|
367
397
|
const listeners = eventlisteners[eventName] ||= new Set();
|
|
@@ -382,19 +412,11 @@ const {observe} = (() => {
|
|
|
382
412
|
type: "function",
|
|
383
413
|
constant: true
|
|
384
414
|
},
|
|
385
|
-
self: {value: currentComponent, type: CustomElement, constant: true}
|
|
386
|
-
boolean: {value: "boolean", constant: true},
|
|
387
|
-
string: {value: "string", constant: true},
|
|
388
|
-
number: {value: "number", constant: true},
|
|
389
|
-
observed: {value: true, constant: true},
|
|
390
|
-
reactive: {value: true, constant: true},
|
|
391
|
-
shared: {value: true, constant: true},
|
|
392
|
-
exported: {value: true, constant: true},
|
|
393
|
-
imported: {value: true, constant: true}
|
|
415
|
+
self: {value: currentComponent, type: CustomElement, constant: true}
|
|
394
416
|
};
|
|
395
|
-
this.defaultAttributes = domElementNode.tagName==="TEMPLATE" ? domElementNode.attributes : dom.attributes;
|
|
417
|
+
this.defaultAttributes = domElementNode.tagName === "TEMPLATE" ? domElementNode.attributes : dom.attributes;
|
|
396
418
|
this.varsProxy = createVarsProxy(this.vars, this, CustomElement);
|
|
397
|
-
|
|
419
|
+
["getElementById", "querySelector", "querySelectorAll"]
|
|
398
420
|
.forEach((fname) => {
|
|
399
421
|
Object.defineProperty(this, fname, {
|
|
400
422
|
configurable: true,
|
|
@@ -402,15 +424,13 @@ const {observe} = (() => {
|
|
|
402
424
|
value: (...args) => this.shadowRoot[fname](...args)
|
|
403
425
|
})
|
|
404
426
|
});
|
|
405
|
-
[...dom.childNodes].forEach((child) =>
|
|
406
|
-
shadow.appendChild(child.cloneNode(true));
|
|
407
|
-
})
|
|
427
|
+
[...dom.childNodes].forEach((child) => shadow.appendChild(child.cloneNode(true)));
|
|
408
428
|
if (bindForms) _bindForms(shadow, this);
|
|
409
|
-
if(importAnchors) _importAnchors(shadow,this);
|
|
429
|
+
if (importAnchors) _importAnchors(shadow, this);
|
|
410
430
|
}
|
|
411
431
|
|
|
412
432
|
get siblings() {
|
|
413
|
-
return [...CustomElement.instances].filter((sibling) => sibling!=this);
|
|
433
|
+
return [...CustomElement.instances].filter((sibling) => sibling != this);
|
|
414
434
|
}
|
|
415
435
|
|
|
416
436
|
adoptedCallback() {
|
|
@@ -451,114 +471,137 @@ const {observe} = (() => {
|
|
|
451
471
|
ctx.appendChild(currentScript);
|
|
452
472
|
}
|
|
453
473
|
Promise.all(promises).then(() => {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
474
|
+
const inputs = [...ctx.shadowRoot.querySelectorAll("input[l-bind]"), ...ctx.shadowRoot.querySelectorAll("select[l-bind]")];
|
|
475
|
+
inputs.forEach((input) => {
|
|
476
|
+
bindInput(input, ctx);
|
|
477
|
+
})
|
|
478
|
+
const nodes = getNodes(ctx);
|
|
479
|
+
nodes.forEach((node) => {
|
|
480
|
+
if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
|
|
481
|
+
render(!!node.template, () => resolveNode(node, this))
|
|
482
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
483
|
+
[...node.attributes].forEach((attr) => {
|
|
484
|
+
const {name, value} = attr,
|
|
485
|
+
[type, ...params] = name.split(":");
|
|
486
|
+
if (type === "" || type=="checked" || node.tagName === "SELECT") { // name is :something
|
|
487
|
+
render(!!attr.template, () => {
|
|
488
|
+
const attrtype = node.getAttribute("type"),
|
|
489
|
+
value = resolveNode(attr, this),
|
|
490
|
+
elvalue = node.getAttribute("value"),
|
|
491
|
+
elname = node.getAttribute("name");
|
|
492
|
+
if (params[0]) {
|
|
493
|
+
if (value === "true") node.setAttribute(params[0], "")
|
|
494
|
+
else node.removeAttribute(params[0]);
|
|
495
|
+
} else if (attrtype === "checkbox" || node.tagName === "OPTION") {
|
|
496
|
+
if (value === "true") {
|
|
497
|
+
node.setAttribute("checked", "");
|
|
478
498
|
} else {
|
|
479
|
-
|
|
499
|
+
node.removeAttribute("checked");
|
|
480
500
|
}
|
|
481
|
-
})
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const value = resolveNode(attr, this);
|
|
485
|
-
if (value === "true") node.setAttribute(name, "");
|
|
486
|
-
else node.removeAttribute(name);
|
|
487
|
-
})
|
|
488
|
-
} else if (type === "l-on") {
|
|
489
|
-
let listener;
|
|
490
|
-
render(!!attr.template, () => {
|
|
491
|
-
const value = resolveNode(attr, this);
|
|
492
|
-
if (listener) node.removeEventListener(params[0], listener);
|
|
493
|
-
listener = this[value] || window[value] || Function(value);
|
|
494
|
-
node.addEventListener(params[0], listener);
|
|
495
|
-
})
|
|
496
|
-
} else if (type === "l-if") {
|
|
497
|
-
render(!!attr.template, () => {
|
|
498
|
-
const value = resolveNode(attr, this);
|
|
499
|
-
node.style.setProperty("display", value === "true" ? "revert" : "none");
|
|
500
|
-
})
|
|
501
|
-
} else if (type === "l-for") {
|
|
502
|
-
node.template ||= node.innerHTML;
|
|
503
|
-
render(!!attr.template, () => {
|
|
504
|
-
const [what = "each", vname = "element", index = "index", array = "array", after = false] = params,
|
|
505
|
-
value = resolveNode(attr, this),
|
|
506
|
-
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
507
|
-
target = what === "each" ? coerced : Object[what](coerced),
|
|
508
|
-
html = target.reduce((html, item, i, target) => {
|
|
509
|
-
return html += Function("context", "with(context) { return `" + node.template + "` }")({
|
|
510
|
-
[vname]: item,
|
|
511
|
-
[index]: i,
|
|
512
|
-
[array]: target
|
|
513
|
-
})
|
|
514
|
-
}, ""),
|
|
515
|
-
parsed = parser.parseFromString(html, "text/html");
|
|
516
|
-
if (!window.lightviewDebug) {
|
|
517
|
-
if (after) {
|
|
518
|
-
node.style.setProperty("display", "none")
|
|
519
|
-
} else {
|
|
520
|
-
while (node.lastElementChild) node.lastElementChild.remove();
|
|
521
|
-
}
|
|
501
|
+
} else if (attrtype === "radio") {
|
|
502
|
+
if (elvalue === value) {
|
|
503
|
+
node.setAttribute("checked", "");
|
|
522
504
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
505
|
+
} else if (name === "value" && node.tagName === "SELECT") {
|
|
506
|
+
node.setAttribute("value", value);
|
|
507
|
+
const values = value[0] === "[" ? JSON.parse(value) : value.split(","); // handle multiselect
|
|
508
|
+
[...node.querySelectorAll("option")].forEach((option) => {
|
|
509
|
+
if (option.hasAttribute("value")) {
|
|
510
|
+
if (values.includes(option.getAttribute("value"))) {
|
|
511
|
+
option.setAttribute("selected", true);
|
|
512
|
+
}
|
|
513
|
+
} else if (values.includes(option.innerText)) {
|
|
514
|
+
option.setAttribute("selected", true);
|
|
515
|
+
}
|
|
516
|
+
})
|
|
517
|
+
}
|
|
518
|
+
})
|
|
519
|
+
} else if (type === "l-on") {
|
|
520
|
+
let listener;
|
|
521
|
+
render(!!attr.template, () => {
|
|
522
|
+
const value = resolveNode(attr, this);
|
|
523
|
+
if (listener) node.removeEventListener(params[0], listener);
|
|
524
|
+
listener = this[value] || window[value] || Function(value);
|
|
525
|
+
addListener(node,params[0], listener);
|
|
526
|
+
})
|
|
527
|
+
} else if (type === "l-if") {
|
|
528
|
+
render(!!attr.template, () => {
|
|
529
|
+
const value = resolveNode(attr, this);
|
|
530
|
+
node.style.setProperty("display", value === "true" ? "revert" : "none");
|
|
531
|
+
})
|
|
532
|
+
} else if (type === "l-for") {
|
|
533
|
+
node.template ||= node.innerHTML;
|
|
534
|
+
render(!!attr.template, () => {
|
|
535
|
+
const [what = "each", vname = "element", index = "index", array = "array", after = false] = params,
|
|
536
|
+
value = resolveNode(attr, this),
|
|
537
|
+
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
538
|
+
target = what === "each" ? coerced : Object[what](coerced),
|
|
539
|
+
html = target.reduce((html, item, i, target) => {
|
|
540
|
+
return html += Function("context", "with(context) { return `" + node.template + "` }")({
|
|
541
|
+
[vname]: item,
|
|
542
|
+
[index]: i,
|
|
543
|
+
[array]: target
|
|
544
|
+
})
|
|
545
|
+
}, ""),
|
|
546
|
+
parsed = parser.parseFromString(html, "text/html");
|
|
547
|
+
if (!window.lightviewDebug) {
|
|
548
|
+
if (after) {
|
|
549
|
+
node.style.setProperty("display", "none")
|
|
550
|
+
} else {
|
|
551
|
+
while (node.lastElementChild) node.lastElementChild.remove();
|
|
526
552
|
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
553
|
+
}
|
|
554
|
+
while (parsed.body.firstChild) {
|
|
555
|
+
if (after) node.parentElement.insertBefore(parsed.body.firstChild, node);
|
|
556
|
+
else node.appendChild(parsed.body.firstChild);
|
|
557
|
+
}
|
|
558
|
+
})
|
|
559
|
+
} else if (attr.template) {
|
|
560
|
+
render(!!attr.template, () => resolveNode(attr, this));
|
|
561
|
+
}
|
|
562
|
+
})
|
|
563
|
+
}
|
|
537
564
|
})
|
|
538
|
-
|
|
565
|
+
shadow.normalize();
|
|
566
|
+
observer.observe(ctx, {attributeOldValue: true});
|
|
567
|
+
if (ctx.hasOwnProperty("connectedCallback")) ctx.connectedCallback();
|
|
568
|
+
})
|
|
569
|
+
}
|
|
539
570
|
|
|
540
571
|
adopted(value) {
|
|
541
|
-
|
|
572
|
+
this.adoptedCallback = value;
|
|
573
|
+
//Object.defineProperty(this, "adoptedCallback", {configurable: true, writable: true, value});
|
|
542
574
|
}
|
|
543
575
|
|
|
544
576
|
connected(value) {
|
|
545
|
-
|
|
577
|
+
this.connectedCallback = value;
|
|
578
|
+
//Object.defineProperty(this, "connectedCallback", {configurable: true, writable: true, value});
|
|
546
579
|
}
|
|
547
580
|
|
|
548
581
|
attributeChanged(value) {
|
|
549
|
-
|
|
582
|
+
this.attributeChangedCallback = value;
|
|
583
|
+
//Object.defineProperty(this, "attributeChangedCallback", {configurable: true, writable: true, value});
|
|
550
584
|
}
|
|
551
585
|
|
|
552
586
|
disconnected(value) {
|
|
553
587
|
Object.defineProperty(this, "disconnectedCallback", {
|
|
554
588
|
configurable: true,
|
|
555
589
|
writable: true,
|
|
556
|
-
value:() => {
|
|
590
|
+
value: () => {
|
|
591
|
+
value();
|
|
592
|
+
super.disconnectedCallback(value);
|
|
593
|
+
}
|
|
557
594
|
});
|
|
558
595
|
}
|
|
559
596
|
|
|
560
|
-
|
|
561
|
-
|
|
597
|
+
getVariableNames() {
|
|
598
|
+
return Object.keys(this.vars).filter((name) => {
|
|
599
|
+
return !(name in reserved) && !["self","addEventListener","postEvent"].includes(name)
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
setValue(name, value, {shared, coerceTo = typeof (value)} = {}) {
|
|
604
|
+
if (!this.isConnected) {
|
|
562
605
|
instances.delete(this);
|
|
563
606
|
return false;
|
|
564
607
|
}
|
|
@@ -567,29 +610,37 @@ const {observe} = (() => {
|
|
|
567
610
|
value = coerce(value, type);
|
|
568
611
|
if (this.varsProxy[name] !== value) {
|
|
569
612
|
const variable = this.vars[name];
|
|
570
|
-
if(variable.shared) {
|
|
571
|
-
const event = new VariableEvent({
|
|
613
|
+
if (variable.shared) {
|
|
614
|
+
const event = new VariableEvent({
|
|
615
|
+
variableName: name,
|
|
616
|
+
value: value,
|
|
617
|
+
oldValue: variable.value
|
|
618
|
+
});
|
|
572
619
|
variable.value = value;
|
|
573
|
-
this.vars.postEvent.value("change",event);
|
|
574
|
-
if(event.defaultPrevented)
|
|
620
|
+
this.vars.postEvent.value("change", event);
|
|
621
|
+
if (event.defaultPrevented) variable.value = value;
|
|
575
622
|
} else {
|
|
576
623
|
this.varsProxy[name] = value;
|
|
577
624
|
}
|
|
578
625
|
}
|
|
579
626
|
return true;
|
|
580
627
|
}
|
|
581
|
-
this.vars[name] = {type:coerceTo, value: coerce(value, coerceTo)};
|
|
628
|
+
this.vars[name] = {name, type: coerceTo, value: coerce(value, coerceTo)};
|
|
582
629
|
return false;
|
|
583
630
|
}
|
|
584
631
|
|
|
632
|
+
getValue(variableName) {
|
|
633
|
+
return this.vars[variableName]?.value;
|
|
634
|
+
}
|
|
635
|
+
|
|
585
636
|
variables(variables, {observed, reactive, shared, exported, imported} = {}) { // options = {observed,reactive,shared,exported,imported}
|
|
586
637
|
const addEventListener = this.varsProxy.addEventListener;
|
|
587
638
|
if (variables !== undefined) {
|
|
588
639
|
Object.entries(variables)
|
|
589
640
|
.forEach(([key, type]) => {
|
|
590
|
-
const variable = this.vars[key] ||= {type};
|
|
641
|
+
const variable = this.vars[key] ||= {name: key, type};
|
|
591
642
|
if (observed || imported) {
|
|
592
|
-
variable.value = coerce(this.getAttribute(key), variable.type);
|
|
643
|
+
variable.value = this.hasAttribute(key) ? coerce(this.getAttribute(key), variable.type) : variable.value;
|
|
593
644
|
variable.observed = observed;
|
|
594
645
|
variable.imported = imported;
|
|
595
646
|
}
|
|
@@ -599,55 +650,65 @@ const {observe} = (() => {
|
|
|
599
650
|
}
|
|
600
651
|
if (shared) {
|
|
601
652
|
variable.shared = true;
|
|
602
|
-
addEventListener("change",({variableName,value}) => {
|
|
603
|
-
if(this.vars[variableName]?.shared) {
|
|
653
|
+
addEventListener("change", ({variableName, value}) => {
|
|
654
|
+
if (this.vars[variableName]?.shared) {
|
|
604
655
|
this.siblings.forEach((instance) => {
|
|
605
|
-
instance.
|
|
656
|
+
instance.setValue(variableName, value);
|
|
606
657
|
})
|
|
607
658
|
}
|
|
608
659
|
})
|
|
609
660
|
}
|
|
610
661
|
if (exported) {
|
|
611
662
|
variable.exported = true;
|
|
612
|
-
// in case the export goes up to
|
|
613
|
-
setComponentAttribute(this,key,variable.value);
|
|
614
|
-
addEventListener("change",({variableName,value}) => {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if(value==null) {
|
|
619
|
-
removeComponentAttribute(this,variableName);
|
|
620
|
-
} else {
|
|
621
|
-
setComponentAttribute(this,variableName,value);
|
|
622
|
-
}
|
|
663
|
+
// in case the export goes up to an iframe
|
|
664
|
+
if (variable.value != null) setComponentAttribute(this, key, variable.value);
|
|
665
|
+
addEventListener("change", ({variableName, value}) => {
|
|
666
|
+
value = typeof (value) === "string" || !value ? value : JSON.stringify(value);
|
|
667
|
+
if (value == null) removeComponentAttribute(this, variableName);
|
|
668
|
+
else setComponentAttribute(this, variableName, value);
|
|
623
669
|
})
|
|
624
670
|
}
|
|
625
671
|
});
|
|
626
|
-
addEventListener("change",({variableName,value}) => {
|
|
627
|
-
[...this.shadowRoot.querySelectorAll(`input[l-bind=${variableName}]`)
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
value = typeof(value)==="string" || value==null ? value : JSON.stringify(value);
|
|
635
|
-
const oldvalue = input.getAttribute("value")||"";
|
|
636
|
-
if(oldvalue!==value) {
|
|
637
|
-
if(value==null) {
|
|
638
|
-
input.removeAttribute("value");
|
|
672
|
+
addEventListener("change", ({variableName, value}) => {
|
|
673
|
+
[...this.shadowRoot.querySelectorAll(`input[l-bind=${variableName}]`),
|
|
674
|
+
...this.shadowRoot.querySelectorAll(`select[l-bind=${variableName}]`)]
|
|
675
|
+
.forEach((input) => {
|
|
676
|
+
const eltype = input.getAttribute("type");
|
|
677
|
+
if (eltype === "checkbox") { // at el option selected
|
|
678
|
+
if(!!value) {
|
|
679
|
+
input.setAttribute("checked", "");
|
|
639
680
|
} else {
|
|
640
|
-
input.
|
|
681
|
+
input.removeAttribute("checked");
|
|
682
|
+
}
|
|
683
|
+
input.checked = !!value;
|
|
684
|
+
} else if (eltype === "radio") {
|
|
685
|
+
if (input.getAttribute("value") === value) {
|
|
686
|
+
input.setAttribute("checked", "");
|
|
687
|
+
input.checked = true;
|
|
641
688
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
689
|
+
} else if (input.tagName === "SELECT") {
|
|
690
|
+
const values = value && typeof (value) === "object" && value instanceof Array ? value : [value];
|
|
691
|
+
[...input.querySelectorAll("option")].forEach((option) => {
|
|
692
|
+
if (values.includes(option.getAttribute("value") || option.innerText)) {
|
|
693
|
+
option.setAttribute("selected", "");
|
|
694
|
+
option.selected = true;
|
|
695
|
+
}
|
|
696
|
+
})
|
|
697
|
+
} else if (!eltype || eltype === "text") {
|
|
698
|
+
value = typeof (value) === "string" || value == null ? value : JSON.stringify(value);
|
|
699
|
+
const oldvalue = input.getAttribute("value") || "";
|
|
700
|
+
if (oldvalue !== value) {
|
|
701
|
+
if (value == null) input.removeAttribute("value");
|
|
702
|
+
else input.setAttribute("value", value);
|
|
703
|
+
try {
|
|
704
|
+
input.setSelectionRange(0, Math.max(oldvalue.length, value ? value.length : 0)); // shadowDom sometimes fails to rerender unless this is done;
|
|
705
|
+
input.setRangeText(value || "", 0, Math.max(oldvalue.length, value ? value.length : 0));
|
|
706
|
+
} catch (e) {
|
|
646
707
|
|
|
708
|
+
}
|
|
647
709
|
}
|
|
648
710
|
}
|
|
649
|
-
}
|
|
650
|
-
})
|
|
711
|
+
})
|
|
651
712
|
})
|
|
652
713
|
}
|
|
653
714
|
return Object.entries(this.vars)
|
|
@@ -676,17 +737,18 @@ const {observe} = (() => {
|
|
|
676
737
|
}
|
|
677
738
|
}
|
|
678
739
|
}
|
|
679
|
-
const createComponent = (name, node, {observer,bindForms,importAnchors}={}) => {
|
|
740
|
+
const createComponent = (name, node, {observer, bindForms, importAnchors} = {}) => {
|
|
680
741
|
let ctor = customElements.get(name);
|
|
681
|
-
if(ctor) {
|
|
742
|
+
if (ctor) {
|
|
682
743
|
console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
|
|
683
744
|
return ctor;
|
|
684
745
|
}
|
|
685
|
-
ctor = createClass(node, {observer,bindForms,importAnchors});
|
|
746
|
+
ctor = createClass(node, {observer, bindForms, importAnchors});
|
|
686
747
|
customElements.define(name, ctor);
|
|
687
748
|
return ctor;
|
|
688
749
|
}
|
|
689
|
-
|
|
750
|
+
Lightview.createComponent = createComponent;
|
|
751
|
+
//Object.defineProperty(Lightview, "createComponent", {writable: true, configurable: true, value: createComponent})
|
|
690
752
|
const importLink = async (link, observer) => {
|
|
691
753
|
const url = (new URL(link.getAttribute("href"), window.location.href)),
|
|
692
754
|
as = link.getAttribute("as") || getNameFromPath(url.pathname);
|
|
@@ -699,8 +761,8 @@ const {observe} = (() => {
|
|
|
699
761
|
importAnchors = !!dom.head.querySelector('meta[name="l-importAnchors"]'),
|
|
700
762
|
bindForms = !!dom.head.querySelector('meta[name="l-bindForms"]'),
|
|
701
763
|
unhide = !!dom.head.querySelector('meta[name="l-unhide"]');
|
|
702
|
-
if(unhide) dom.body.removeAttribute("hidden");
|
|
703
|
-
createComponent(as, dom.body, {observer,importAnchors,bindForms});
|
|
764
|
+
if (unhide) dom.body.removeAttribute("hidden");
|
|
765
|
+
createComponent(as, dom.body, {observer, importAnchors, bindForms});
|
|
704
766
|
}
|
|
705
767
|
return {as};
|
|
706
768
|
}
|
|
@@ -711,126 +773,159 @@ const {observe} = (() => {
|
|
|
711
773
|
}
|
|
712
774
|
}
|
|
713
775
|
|
|
714
|
-
const bodyAsComponent = ({as="x-body",unhide,importAnchors,bindForms}={}) => {
|
|
776
|
+
const bodyAsComponent = ({as = "x-body", unhide, importAnchors, bindForms} = {}) => {
|
|
715
777
|
const parent = document.body.parentElement;
|
|
716
|
-
createComponent(as, document.body,{importAnchors,bindForms});
|
|
778
|
+
createComponent(as, document.body, {importAnchors, bindForms});
|
|
717
779
|
const component = document.createElement(as);
|
|
718
780
|
parent.replaceChild(component, document.body);
|
|
719
|
-
Object.defineProperty(document,"body",
|
|
781
|
+
Object.defineProperty(document, "body", {
|
|
782
|
+
enumerable: true, configurable: true, get() {
|
|
783
|
+
return component;
|
|
784
|
+
}
|
|
785
|
+
});
|
|
720
786
|
if (unhide) component.removeAttribute("hidden");
|
|
721
787
|
}
|
|
722
788
|
Lightview.bodyAsComponent = bodyAsComponent;
|
|
723
|
-
const postMessage = (data,target=window.parent) => {
|
|
724
|
-
if(postMessage.enabled) {
|
|
725
|
-
if(target instanceof HTMLIFrameElement) {
|
|
726
|
-
data = {...data,href:window.location.href};
|
|
727
|
-
target.contentWindow.postMessage(JSON.stringify(data),"*");
|
|
789
|
+
const postMessage = (data, target = window.parent) => {
|
|
790
|
+
if (postMessage.enabled) {
|
|
791
|
+
if (target instanceof HTMLIFrameElement) {
|
|
792
|
+
data = {...data, href: window.location.href};
|
|
793
|
+
target.contentWindow.postMessage(JSON.stringify(data), "*");
|
|
728
794
|
} else {
|
|
729
|
-
data = {...data,iframeId:document.lightviewId,href:window.location.href};
|
|
730
|
-
target.postMessage(JSON.stringify(data),"*");
|
|
795
|
+
data = {...data, iframeId: document.lightviewId, href: window.location.href};
|
|
796
|
+
target.postMessage(JSON.stringify(data), "*");
|
|
731
797
|
}
|
|
732
798
|
}
|
|
733
799
|
}
|
|
734
|
-
const setComponentAttribute = (node,name,value) => {
|
|
735
|
-
if(node.getAttribute(name)!==value) node.setAttribute(name,value);
|
|
736
|
-
postMessage({type:"setAttribute",argsList:[name,value]});
|
|
800
|
+
const setComponentAttribute = (node, name, value) => {
|
|
801
|
+
if (node.getAttribute(name) !== value) node.setAttribute(name, value);
|
|
802
|
+
postMessage({type: "setAttribute", argsList: [name, value]});
|
|
737
803
|
}
|
|
738
|
-
const removeComponentAttribute = (node,name,value) => {
|
|
804
|
+
const removeComponentAttribute = (node, name, value) => {
|
|
739
805
|
node.removeAttribute(name);
|
|
740
|
-
postMessage({type:"removeAttribute",argsList:[name]});
|
|
806
|
+
postMessage({type: "removeAttribute", argsList: [name]});
|
|
741
807
|
}
|
|
742
|
-
const getNodePath = (node,path=[]) => {
|
|
808
|
+
const getNodePath = (node, path = []) => {
|
|
743
809
|
path.unshift(node);
|
|
744
|
-
if(node.parentNode && node.parentNode!==node.parentNode) getNodePath(node.parentNode,path);
|
|
810
|
+
if (node.parentNode && node.parentNode !== node.parentNode) getNodePath(node.parentNode, path);
|
|
745
811
|
return path;
|
|
746
812
|
}
|
|
747
813
|
const onresize = (node, callback) => {
|
|
748
|
-
const resizeObserver = new ResizeObserver(() => callback()
|
|
814
|
+
const resizeObserver = new ResizeObserver(() => callback());
|
|
749
815
|
resizeObserver.observe(node);
|
|
750
816
|
};
|
|
751
817
|
|
|
752
818
|
const url = new URL(document.currentScript.getAttribute("src"), window.location.href);
|
|
753
819
|
let domContentLoadedEvent;
|
|
754
|
-
window
|
|
820
|
+
addListener(window,"DOMContentLoaded", (event) => domContentLoadedEvent = event);
|
|
755
821
|
const loader = async (whenFramed) => {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
postMessage.enabled = true;
|
|
766
|
-
window.addEventListener("message",({data}) => {
|
|
767
|
-
data = JSON.parse(data);
|
|
768
|
-
if(data.type==="framed") {
|
|
769
|
-
const resize = () => {
|
|
770
|
-
const {width,height} = document.body.getBoundingClientRect();
|
|
771
|
-
postMessage({type:"setAttribute",argsList:["width",width]})
|
|
772
|
-
postMessage({type:"setAttribute",argsList:["height",height+20]});
|
|
773
|
-
}
|
|
774
|
-
resize();
|
|
775
|
-
onresize(document.body,() => {
|
|
776
|
-
resize();
|
|
777
|
-
})
|
|
778
|
-
}
|
|
779
|
-
const event = new CustomEvent(data.type,{detail:data});
|
|
780
|
-
|
|
781
|
-
});
|
|
782
|
-
/* window.addEventListener("resize",() => {
|
|
783
|
-
const {width,height} = document.body.getBoundingClientRect();
|
|
784
|
-
postMessage({type:"setAttribute",argsList:["width",width]})
|
|
785
|
-
postMessage({type:"setAttribute",argsList:["height",height]})
|
|
786
|
-
})*/
|
|
787
|
-
const url = new URL(window.location.href);
|
|
788
|
-
document.lightviewId = url.searchParams.get("id");
|
|
789
|
-
postMessage({type:"DOMContentLoaded"})
|
|
790
|
-
}
|
|
791
|
-
} else if (url.searchParams.has("as")) {
|
|
792
|
-
bodyAsComponent({as:url.searchParams.get("as"),unhide,importAnchors,bindForms});
|
|
793
|
-
}
|
|
794
|
-
if(enableFrames) {
|
|
822
|
+
if (!!document.querySelector('meta[name="l-importLinks"]')) await importLinks();
|
|
823
|
+
const importAnchors = !!document.querySelector('meta[name="l-importAnchors"]'),
|
|
824
|
+
bindForms = !!document.querySelector('meta[name="l-bindForms"]'),
|
|
825
|
+
unhide = !!document.querySelector('meta[name="l-unhide"]'),
|
|
826
|
+
isolated = !!document.querySelector('meta[name="l-isolate"]'),
|
|
827
|
+
enableFrames = !!document.querySelector('meta[name="l-enableFrames"]');
|
|
828
|
+
if (whenFramed) {
|
|
829
|
+
whenFramed({unhide, importAnchors, bindForms, isolated, enableFrames});
|
|
830
|
+
if (!isolated) {
|
|
795
831
|
postMessage.enabled = true;
|
|
796
|
-
window
|
|
797
|
-
const {type,
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
postMessage({type:"
|
|
802
|
-
|
|
803
|
-
domContentLoadedEvent.href = href;
|
|
804
|
-
domContentLoadedEvent.srcElement = iframe;
|
|
805
|
-
domContentLoadedEvent.bubbles = false;
|
|
806
|
-
domContentLoadedEvent.path = getNodePath(iframe);
|
|
807
|
-
Object.defineProperty(domContentLoadedEvent,"timeStamp",{enumerable:false,configurable:true,value:performance.now()})
|
|
808
|
-
iframe.dispatchEvent(domContentLoadedEvent);
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
if(type==="setAttribute") {
|
|
812
|
-
const [name,value] = [...argsList];
|
|
813
|
-
if(iframe.getAttribute(name)!==value+"") iframe.setAttribute(name,value);
|
|
814
|
-
return;
|
|
815
|
-
}
|
|
816
|
-
if(type==="removeAttribute") {
|
|
817
|
-
iframe.removeAttribute(...argsList);
|
|
818
|
-
return;
|
|
832
|
+
addListener(window,"message", ({data}) => {
|
|
833
|
+
const {type, argsList} = JSON.parse(data);
|
|
834
|
+
if (type === "framed") {
|
|
835
|
+
const resize = () => {
|
|
836
|
+
const {width, height} = document.body.getBoundingClientRect();
|
|
837
|
+
postMessage({type: "setAttribute", argsList: ["width", width]})
|
|
838
|
+
postMessage({type: "setAttribute", argsList: ["height", height + 20]});
|
|
819
839
|
}
|
|
840
|
+
resize();
|
|
841
|
+
onresize(document.body, () => {
|
|
842
|
+
resize();
|
|
843
|
+
});
|
|
844
|
+
return
|
|
845
|
+
}
|
|
846
|
+
if (type === "setAttribute") {
|
|
847
|
+
const [name, value] = [...argsList],
|
|
848
|
+
variable = document.body.vars[name];
|
|
849
|
+
if (variable && variable.imported) document.body.setValue(name, value);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (type === "removeAttribute") {
|
|
853
|
+
const [name] = argsList[0],
|
|
854
|
+
variable = document.body.vars[name];
|
|
855
|
+
if (variable && variable.imported) document.body.setValue(name, undefined);
|
|
856
|
+
|
|
820
857
|
}
|
|
821
|
-
console.warn("iframe posted a message without providing an id",message);
|
|
822
858
|
});
|
|
859
|
+
const url = new URL(window.location.href);
|
|
860
|
+
document.lightviewId = url.searchParams.get("id");
|
|
861
|
+
postMessage({type: "DOMContentLoaded"})
|
|
823
862
|
}
|
|
863
|
+
} else if (url.searchParams.has("as")) {
|
|
864
|
+
bodyAsComponent({as: url.searchParams.get("as"), unhide, importAnchors, bindForms});
|
|
824
865
|
}
|
|
825
|
-
|
|
826
|
-
|
|
866
|
+
if (enableFrames) {
|
|
867
|
+
postMessage.enabled = true;
|
|
868
|
+
addListener(window,"message", (message) => {
|
|
869
|
+
const {type, iframeId, argsList, href} = JSON.parse(message.data),
|
|
870
|
+
iframe = document.getElementById(iframeId);
|
|
871
|
+
if (iframe) {
|
|
872
|
+
if (type === "DOMContentLoaded") {
|
|
873
|
+
postMessage({type: "framed", href: window.location.href}, iframe);
|
|
874
|
+
Object.defineProperty(domContentLoadedEvent, "currentTarget", {
|
|
875
|
+
enumerable: false,
|
|
876
|
+
configurable: true,
|
|
877
|
+
value: iframe
|
|
878
|
+
});
|
|
879
|
+
domContentLoadedEvent.href = href;
|
|
880
|
+
domContentLoadedEvent.srcElement = iframe;
|
|
881
|
+
domContentLoadedEvent.bubbles = false;
|
|
882
|
+
domContentLoadedEvent.path = getNodePath(iframe);
|
|
883
|
+
Object.defineProperty(domContentLoadedEvent, "timeStamp", {
|
|
884
|
+
enumerable: false,
|
|
885
|
+
configurable: true,
|
|
886
|
+
value: performance.now()
|
|
887
|
+
})
|
|
888
|
+
iframe.dispatchEvent(domContentLoadedEvent);
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
if (type === "setAttribute") {
|
|
892
|
+
const [name, value] = [...argsList];
|
|
893
|
+
if (iframe.getAttribute(name) !== value + "") iframe.setAttribute(name, value);
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (type === "removeAttribute") {
|
|
897
|
+
iframe.removeAttribute(...argsList);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
console.warn("iframe posted a message without providing an id", message);
|
|
902
|
+
});
|
|
903
|
+
const mutationCallback = (mutationsList) => {
|
|
904
|
+
const console = document.getElementById("console");
|
|
905
|
+
for (const {target, attributeName, oldValue} of mutationsList) {
|
|
906
|
+
if (!["height", "width"].includes(attributeName)) {
|
|
907
|
+
const value = target.getAttribute(attributeName);
|
|
908
|
+
if (!value) postMessage({type: "removeAttribute", argsList: [attributeName]}, iframe)
|
|
909
|
+
else if (value !== oldValue) postMessage({
|
|
910
|
+
type: "setAttribute",
|
|
911
|
+
argsList: [attributeName, value]
|
|
912
|
+
}, iframe)
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
const observer = new MutationObserver(mutationCallback),
|
|
917
|
+
iframe = document.getElementById("myframe");
|
|
918
|
+
observer.observe(iframe, {attributes: true, attributeOldValue: true});
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const whenFramed = (f, {isolated} = {}) => {
|
|
922
|
+
addListener(document,"DOMContentLoaded", (event) => loader(f));
|
|
827
923
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
924
|
+
Lightview.whenFramed = whenFramed;
|
|
925
|
+
//Object.defineProperty(Lightview, "whenFramed", {configurable: true, writable: true, value: whenFramed});
|
|
926
|
+
if (window.location === window.parent.location || !(window.parent instanceof Window) || window.parent !== window) { // CodePen mucks with window.parent
|
|
927
|
+
addListener(document,"DOMContentLoaded", () => loader())
|
|
831
928
|
}
|
|
832
929
|
|
|
833
930
|
return {observe}
|
|
834
|
-
})();
|
|
835
|
-
|
|
836
|
-
|
|
931
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightview",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Small, simple, powerful web UI creation ...",
|
|
3
|
+
"version": "1.3.1b",
|
|
4
|
+
"description": "Small, simple, powerful web UI creation ... imagine a blend of Svelte, React, Vue, Riot and more.",
|
|
5
5
|
"main": "lightview.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
"react",
|
|
16
16
|
"angular",
|
|
17
17
|
"riot",
|
|
18
|
-
"vue"
|
|
18
|
+
"vue",
|
|
19
|
+
"moon",
|
|
20
|
+
"hyperapp",
|
|
21
|
+
"hyperhtml"
|
|
19
22
|
],
|
|
20
23
|
"author": "Simon Y. Blackwell",
|
|
21
24
|
"license": "MIT",
|
package/remoteform.html
CHANGED
package/xor.html
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<template id="audiostream">
|
|
6
|
+
<p>${name}</p>
|
|
7
|
+
<p>
|
|
8
|
+
Play: <input name="play" type="checkbox" l-bind="run" checked="${run}">
|
|
9
|
+
</p>
|
|
10
|
+
<script type="lightview/module">
|
|
11
|
+
self.variables({
|
|
12
|
+
run: boolean
|
|
13
|
+
});
|
|
14
|
+
self.variables({
|
|
15
|
+
name: string
|
|
16
|
+
}, {
|
|
17
|
+
imported
|
|
18
|
+
});
|
|
19
|
+
addEventListener("change", ({
|
|
20
|
+
variableName,
|
|
21
|
+
value
|
|
22
|
+
}) => {
|
|
23
|
+
if (variableName === "run" && value === true) {
|
|
24
|
+
self.siblings.forEach((sibling) => {
|
|
25
|
+
sibling.setValue(variableName, false);
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
</script>
|
|
30
|
+
</template>
|
|
31
|
+
<title>Form</title>
|
|
32
|
+
<script src="./lightview.js"></script>
|
|
33
|
+
<script>
|
|
34
|
+
Lightview.createComponent("x-audiostream", document.getElementById("audiostream"), {
|
|
35
|
+
bindForms: true
|
|
36
|
+
})
|
|
37
|
+
</script>
|
|
38
|
+
</head>
|
|
39
|
+
|
|
40
|
+
<body>
|
|
41
|
+
<div style="margin:20px">
|
|
42
|
+
<table>
|
|
43
|
+
<th>
|
|
44
|
+
<td colspan="3">Audio Streams</td>
|
|
45
|
+
</th>
|
|
46
|
+
<tr>
|
|
47
|
+
<td style="width:33%;text-align:center">
|
|
48
|
+
<x-audiostream name="Classical"></x-audiostream>
|
|
49
|
+
</td>
|
|
50
|
+
<td style="width:33%;text-align:center">
|
|
51
|
+
<x-audiostream name="Country"></x-audiostream>
|
|
52
|
+
</td>
|
|
53
|
+
<td style="width:33%;text-align:center">
|
|
54
|
+
<x-audiostream name="Classic Rock"></x-audiostream>
|
|
55
|
+
</td>
|
|
56
|
+
</tr>
|
|
57
|
+
</table>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
</body>
|
|
61
|
+
|
|
62
|
+
</html>
|