lightview 1.4.7-b → 1.5.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 +1 -1
- package/components/chart.html +76 -0
- package/components/gauge.html +57 -0
- package/examples/chart.html +64 -0
- package/{counter.html → examples/counter.html} +1 -2
- package/{directives.html → examples/directives.html} +3 -3
- package/{remote.html → examples/foreign.html} +2 -2
- package/{remoteform.html → examples/forgeinform.html} +4 -4
- package/examples/form.html +59 -0
- package/examples/gauge.html +16 -0
- package/{message.html → examples/message.html} +1 -1
- package/{nested.html → examples/nested.html} +1 -1
- package/examples/remote-server.js +51 -0
- package/examples/remote.html +30 -0
- package/examples/remote.json +1 -0
- package/{scratch.html → examples/scratch.html} +1 -1
- package/examples/template.html +33 -0
- package/{top.html → examples/top.html} +1 -1
- package/examples/types.html +93 -0
- package/{xor.html → examples/xor.html} +4 -4
- package/lightview.js +371 -207
- package/package.json +36 -36
- package/test/basic.html +30 -21
- package/test/basic.test.mjs +98 -21
- package/types.js +312 -0
package/lightview.js
CHANGED
|
@@ -31,19 +31,21 @@ const {observe} = (() => {
|
|
|
31
31
|
const parser = new DOMParser();
|
|
32
32
|
|
|
33
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,"");
|
|
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
39
|
}
|
|
40
40
|
Lightview.sanitizeTemplate = templateSanitizer;
|
|
41
41
|
|
|
42
42
|
const escaper = document.createElement('textarea');
|
|
43
|
+
|
|
43
44
|
function escapeHTML(html) {
|
|
44
45
|
escaper.textContent = html;
|
|
45
46
|
return escaper.innerHTML;
|
|
46
47
|
}
|
|
48
|
+
|
|
47
49
|
Lightview.escapeHTML = escapeHTML;
|
|
48
50
|
|
|
49
51
|
const addListener = (node, eventName, callback) => {
|
|
@@ -68,7 +70,8 @@ const {observe} = (() => {
|
|
|
68
70
|
return "l-" + name;
|
|
69
71
|
}
|
|
70
72
|
const observe = (f, thisArg, argsList = []) => {
|
|
71
|
-
|
|
73
|
+
const observer = (...args) => {
|
|
74
|
+
if(observer.cancelled) return;
|
|
72
75
|
CURRENTOBSERVER = observer;
|
|
73
76
|
try {
|
|
74
77
|
f.call(thisArg || this, ...argsList, ...args);
|
|
@@ -77,19 +80,18 @@ const {observe} = (() => {
|
|
|
77
80
|
}
|
|
78
81
|
CURRENTOBSERVER = null;
|
|
79
82
|
}
|
|
80
|
-
|
|
81
83
|
observer.cancel = () => observer.cancelled = true;
|
|
82
84
|
observer();
|
|
83
85
|
return observer;
|
|
84
86
|
}
|
|
85
87
|
const coerce = (value, toType) => {
|
|
86
|
-
if (value + "" === "null" || value + "" === "undefined") return value;
|
|
88
|
+
if (value + "" === "null" || value + "" === "undefined" || toType==="any") return value;
|
|
87
89
|
const type = typeof (value);
|
|
88
90
|
if (type === toType) return value;
|
|
89
91
|
if (toType === "number") return parseFloat(value + "");
|
|
90
92
|
if (toType === "boolean") {
|
|
91
93
|
if (["on", "checked", "selected"].includes(value)) return true;
|
|
92
|
-
if(value==null || value==="") return false;
|
|
94
|
+
if (value == null || value === "") return false;
|
|
93
95
|
try {
|
|
94
96
|
const parsed = JSON.parse(value + "");
|
|
95
97
|
if (typeof (parsed) === "boolean") return parsed;
|
|
@@ -101,7 +103,7 @@ const {observe} = (() => {
|
|
|
101
103
|
if (toType === "string") return value + "";
|
|
102
104
|
const isfunction = typeof (toType) === "function";
|
|
103
105
|
if ((toType === "object" || isfunction)) {
|
|
104
|
-
if (type === "object") {
|
|
106
|
+
if (type === "object" && isfunction) {
|
|
105
107
|
if (value instanceof toType) return value;
|
|
106
108
|
}
|
|
107
109
|
if (type === "string") {
|
|
@@ -112,10 +114,10 @@ const {observe} = (() => {
|
|
|
112
114
|
if (instance instanceof Array) {
|
|
113
115
|
let parsed = tryParse(value.startsWith("[") ? value : `[${value}]`);
|
|
114
116
|
if (!Array.isArray(parsed)) {
|
|
115
|
-
if(value.includes(",")) parsed = value.split(",");
|
|
117
|
+
if (value.includes(",")) parsed = value.split(",");
|
|
116
118
|
else {
|
|
117
119
|
parsed = tryParse(`["${value}"]`);
|
|
118
|
-
if(!Array.isArray(parsed) || parsed[0]!==value && parsed.length!==1) parsed = null;
|
|
120
|
+
if (!Array.isArray(parsed) || parsed[0] !== value && parsed.length !== 1) parsed = null;
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
if (!Array.isArray(parsed)) {
|
|
@@ -144,14 +146,16 @@ const {observe} = (() => {
|
|
|
144
146
|
}
|
|
145
147
|
throw new TypeError(`Unable to coerce ${value} to ${toType}`)
|
|
146
148
|
}
|
|
147
|
-
const Reactor = (
|
|
148
|
-
if (
|
|
149
|
-
if (
|
|
149
|
+
const Reactor = (data) => {
|
|
150
|
+
if (data && typeof (data) === "object") {
|
|
151
|
+
if (data.__isReactor__) return data;
|
|
150
152
|
const childReactors = [],
|
|
151
153
|
dependents = {},
|
|
152
|
-
proxy = new Proxy(
|
|
154
|
+
proxy = new Proxy(data, {
|
|
153
155
|
get(target, property) {
|
|
154
156
|
if (property === "__isReactor__") return true;
|
|
157
|
+
if(property=== "__dependents__") return dependents;
|
|
158
|
+
if(property=== "__reactorProxyTarget__") return data;
|
|
155
159
|
if (target instanceof Array) {
|
|
156
160
|
if (property === "toJSON") return function toJSON() {
|
|
157
161
|
return [...target];
|
|
@@ -160,15 +164,19 @@ const {observe} = (() => {
|
|
|
160
164
|
return JSON.stringify([...target]);
|
|
161
165
|
}
|
|
162
166
|
}
|
|
167
|
+
if(target instanceof Date) {
|
|
168
|
+
return Reflect.get(target,property);
|
|
169
|
+
}
|
|
163
170
|
let value = target[property];
|
|
164
171
|
const type = typeof (value);
|
|
165
172
|
if (CURRENTOBSERVER && typeof (property) !== "symbol" && type !== "function") {
|
|
166
173
|
const observers = dependents[property] ||= new Set();
|
|
167
174
|
observers.add(CURRENTOBSERVER)
|
|
168
175
|
}
|
|
176
|
+
if(value===undefined) return;
|
|
169
177
|
if (childReactors.includes(value) || (value && type !== "object") || typeof (property) === "symbol") {
|
|
170
|
-
//
|
|
171
|
-
if (type === "function" && [Date].includes(value)) value = value.bind(target)
|
|
178
|
+
// Dates and Promises must be bound to work with proxies
|
|
179
|
+
if (type === "function" && ([Date].includes(value) || property==="then")) value = value.bind(target)
|
|
172
180
|
return value;
|
|
173
181
|
}
|
|
174
182
|
if (value && type === "object") {
|
|
@@ -178,8 +186,14 @@ const {observe} = (() => {
|
|
|
178
186
|
target[property] = value;
|
|
179
187
|
return value;
|
|
180
188
|
},
|
|
181
|
-
set(target, property, value) {
|
|
189
|
+
async set(target, property, value) {
|
|
190
|
+
if(target instanceof Promise) {
|
|
191
|
+
console.warn(`Setting ${property} = ${value} on a Promise in Reactor`);
|
|
192
|
+
}
|
|
182
193
|
const type = typeof (value);
|
|
194
|
+
if(value && type==="object" && value instanceof Promise) {
|
|
195
|
+
value = await value;
|
|
196
|
+
}
|
|
183
197
|
if (target[property] !== value) {
|
|
184
198
|
if (value && type === "object") {
|
|
185
199
|
value = Reactor(value);
|
|
@@ -197,7 +211,7 @@ const {observe} = (() => {
|
|
|
197
211
|
});
|
|
198
212
|
return proxy;
|
|
199
213
|
}
|
|
200
|
-
return
|
|
214
|
+
return data;
|
|
201
215
|
}
|
|
202
216
|
|
|
203
217
|
class VariableEvent {
|
|
@@ -209,6 +223,9 @@ const {observe} = (() => {
|
|
|
209
223
|
const createVarsProxy = (vars, component, constructor) => {
|
|
210
224
|
return new Proxy(vars, {
|
|
211
225
|
get(target, property) {
|
|
226
|
+
if(target instanceof Date) {
|
|
227
|
+
return Reflect.get(target,property);
|
|
228
|
+
}
|
|
212
229
|
let {value} = target[property] || {};
|
|
213
230
|
if (typeof (value) === "function") return value.bind(target);
|
|
214
231
|
return value;
|
|
@@ -221,16 +238,22 @@ const {observe} = (() => {
|
|
|
221
238
|
if (event.defaultPrevented) delete target[property].value;
|
|
222
239
|
return true;
|
|
223
240
|
}
|
|
224
|
-
const
|
|
241
|
+
const variable = target[property],
|
|
242
|
+
{type, value, shared, exported, constant, reactive, remote} = variable;
|
|
225
243
|
if (constant) throw new TypeError(`${property}:${type} is a constant`);
|
|
244
|
+
newValue = type.validate ? type.validate(newValue,target[property]) : coerce(newValue,type);
|
|
226
245
|
const newtype = typeof (newValue),
|
|
227
246
|
typetype = typeof (type);
|
|
228
|
-
if (newValue == null || type === "any" || newtype === type || (typetype === "function" && newValue && newtype === "object" && newValue instanceof type)) {
|
|
247
|
+
if (newValue == null || type === "any" || (newtype === type && typetype==="string") || (typetype === "function" && (newValue && newtype === "object" && newValue instanceof type) || variable.validityState?.valid)) {
|
|
229
248
|
if (value !== newValue) {
|
|
230
249
|
event.oldValue = value;
|
|
231
250
|
target[property].value = reactive ? Reactor(newValue) : newValue; // do first to prevent loops
|
|
232
251
|
target.postEvent.value("change", event);
|
|
233
|
-
if (event.defaultPrevented)
|
|
252
|
+
if (event.defaultPrevented) {
|
|
253
|
+
target[property].value = value;
|
|
254
|
+
} else if(remote) {
|
|
255
|
+
handleRemote({variable,remote,reactive},true);
|
|
256
|
+
}
|
|
234
257
|
}
|
|
235
258
|
return true;
|
|
236
259
|
}
|
|
@@ -245,22 +268,21 @@ const {observe} = (() => {
|
|
|
245
268
|
});
|
|
246
269
|
}
|
|
247
270
|
const createObserver = (domNode, framed) => {
|
|
248
|
-
const
|
|
271
|
+
const mobserver = new MutationObserver((mutations) => {
|
|
249
272
|
mutations.forEach((mutation) => {
|
|
273
|
+
const target = mutation.target;
|
|
250
274
|
if (mutation.type === "attributes") {
|
|
251
275
|
//if (framed) debugger;
|
|
252
276
|
const name = mutation.attributeName,
|
|
253
|
-
target = mutation.target,
|
|
254
277
|
value = target.getAttribute(name);
|
|
255
278
|
if (framed && name === "message" && target instanceof IFrameElement) {
|
|
256
|
-
if (value) console.log("message", value);
|
|
279
|
+
//if (value) console.log("message", value);
|
|
257
280
|
target.removeAttribute(name);
|
|
258
281
|
target.dispatchEvent(new CustomEvent("message", {detail: JSON.parse(value)}))
|
|
259
282
|
}
|
|
260
283
|
if (target.observedAttributes && target.observedAttributes.includes(name)) {
|
|
261
284
|
if (value !== mutation.oldValue) {
|
|
262
|
-
target.
|
|
263
|
-
if (target.attributeChangedCallback) target.attributeChangedCallback(name, value, mutation.oldValue);
|
|
285
|
+
target.setVariableValue(name, value);
|
|
264
286
|
}
|
|
265
287
|
}
|
|
266
288
|
} else if (mutation.type === "childList") {
|
|
@@ -270,11 +292,13 @@ const {observe} = (() => {
|
|
|
270
292
|
for (const target of mutation.addedNodes) {
|
|
271
293
|
if (target.connectedCallback) target.connectedCallback();
|
|
272
294
|
}
|
|
295
|
+
} else if(mutation.type === "characterData") {
|
|
296
|
+
if(target.characterDataMutationCallback) target.characterDataMutationCallback(target,mutation.oldValue,target.textContent);
|
|
273
297
|
}
|
|
274
298
|
});
|
|
275
299
|
});
|
|
276
|
-
|
|
277
|
-
return
|
|
300
|
+
mobserver.observe(domNode, {subtree: true, childList: true});
|
|
301
|
+
return mobserver;
|
|
278
302
|
}
|
|
279
303
|
const querySelectorAll = (node, selector) => {
|
|
280
304
|
const nodes = [...node.querySelectorAll(selector)],
|
|
@@ -291,41 +315,49 @@ const {observe} = (() => {
|
|
|
291
315
|
nodes.push(root, ...getNodes(root.shadowRoot))
|
|
292
316
|
} else {
|
|
293
317
|
for (const node of root.childNodes) {
|
|
318
|
+
if (node.tagName === "SCRIPT") continue;
|
|
294
319
|
if (node.nodeType === Node.TEXT_NODE && node.nodeValue?.includes("${")) {
|
|
295
320
|
node.template ||= node.nodeValue;
|
|
296
321
|
nodes.push(node);
|
|
297
322
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
298
|
-
let skip;
|
|
299
|
-
if (node.getAttribute("type") === "radio") nodes.push(node);
|
|
323
|
+
let skip, pushed;
|
|
300
324
|
[...node.attributes].forEach((attr) => {
|
|
301
325
|
if (attr.value.includes("${")) {
|
|
302
326
|
attr.template ||= attr.value;
|
|
327
|
+
pushed = true;
|
|
303
328
|
nodes.push(node);
|
|
304
329
|
} else if (attr.name.includes(":") || attr.name.startsWith("l-")) {
|
|
305
330
|
skip = attr.name.includes("l-for:");
|
|
331
|
+
pushed = true;
|
|
306
332
|
nodes.push(node)
|
|
307
333
|
}
|
|
308
334
|
})
|
|
335
|
+
if (!pushed && node.getAttribute("type") === "radio") nodes.push(node);
|
|
309
336
|
if (!skip && !node.shadowRoot) nodes.push(...getNodes(node));
|
|
310
337
|
}
|
|
311
338
|
}
|
|
312
339
|
}
|
|
313
340
|
return nodes;
|
|
314
341
|
}
|
|
315
|
-
const
|
|
316
|
-
|
|
342
|
+
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
|
343
|
+
const resolveNodeOrText = (node, component, safe) => {
|
|
344
|
+
const type = typeof (node),
|
|
345
|
+
template = type === "string" ? node.trim() : node.template;
|
|
346
|
+
if (template) {
|
|
317
347
|
try {
|
|
318
|
-
let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(
|
|
319
|
-
value = node.nodeType===Node.TEXT_NODE || !safe ? value : Lightview.escapeHTML(value);
|
|
320
|
-
|
|
348
|
+
let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(template) + "` }")(component.varsProxy);
|
|
349
|
+
value = node.nodeType === Node.TEXT_NODE || !safe ? value : Lightview.escapeHTML(value);
|
|
350
|
+
if (type === "string") return value==="undefined" ? undefined : value;
|
|
351
|
+
node.nodeValue = value == "null" || value == "undefined" ? "" : value;
|
|
321
352
|
} catch (e) {
|
|
322
|
-
console.warn(e);
|
|
353
|
+
//console.warn(e);
|
|
323
354
|
if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
|
|
355
|
+
return undefined;
|
|
324
356
|
}
|
|
325
357
|
}
|
|
326
358
|
return node?.nodeValue;
|
|
327
359
|
}
|
|
328
|
-
const render =
|
|
360
|
+
const render = (hasTemplate, render) => {
|
|
329
361
|
let observer;
|
|
330
362
|
if (hasTemplate) {
|
|
331
363
|
if (observer) observer.cancel();
|
|
@@ -348,37 +380,43 @@ const {observe} = (() => {
|
|
|
348
380
|
addListener(node, "click", anchorHandler);
|
|
349
381
|
})
|
|
350
382
|
}
|
|
351
|
-
const
|
|
383
|
+
const bound = new WeakSet();
|
|
384
|
+
const bindInput = (input, variableName, component, value) => {
|
|
385
|
+
if (bound.has(input)) return;
|
|
386
|
+
bound.add(input);
|
|
352
387
|
const inputtype = input.tagName === "SELECT" || input.tagName === "TEXTAREA" ? "text" : input.getAttribute("type"),
|
|
353
|
-
type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype)
|
|
354
|
-
deflt = input.getAttribute("default");
|
|
388
|
+
type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype);
|
|
355
389
|
value ||= input.getAttribute("value");
|
|
356
|
-
let variable = component.vars[
|
|
390
|
+
let variable = component.vars[variableName] || {type};
|
|
357
391
|
if (type !== variable.type) {
|
|
358
392
|
if (variable.type === "any" || variable.type === "unknown") variable.type = type;
|
|
359
|
-
else throw new TypeError(`Attempt to bind <input name="${
|
|
393
|
+
else throw new TypeError(`Attempt to bind <input name="${variableName}" type="${type}"> to variable ${variableName}:${variable.type}`)
|
|
394
|
+
}
|
|
395
|
+
component.variables({[variableName]: type},{reactive:true});
|
|
396
|
+
if(inputtype!=="radio") {
|
|
397
|
+
if(value.includes("${")) input.attributes.value.value = "";
|
|
398
|
+
else component.setVariableValue(variableName, coerce(value,type));
|
|
360
399
|
}
|
|
361
|
-
component.variables({[name]: type});
|
|
362
|
-
component.setValue(name,value);
|
|
363
400
|
let eventname = "change";
|
|
364
401
|
if (input.tagName !== "SELECT" && (!inputtype || input.tagName === "TEXTAREA" || ["text", "number", "tel", "email", "url", "search", "password"].includes(inputtype))) {
|
|
365
402
|
eventname = "input";
|
|
366
403
|
}
|
|
367
|
-
|
|
368
|
-
event.stopImmediatePropagation();
|
|
369
|
-
|
|
370
|
-
let value = target.value;
|
|
404
|
+
const listener = (event) => {
|
|
405
|
+
if (event) event.stopImmediatePropagation();
|
|
406
|
+
let value = input.value;
|
|
371
407
|
if (inputtype === "checkbox") {
|
|
372
408
|
value = input.checked
|
|
373
|
-
} else if (
|
|
374
|
-
if (
|
|
375
|
-
|
|
376
|
-
|
|
409
|
+
} else if (input.tagName === "SELECT") {
|
|
410
|
+
if (input.hasAttribute("multiple")) {
|
|
411
|
+
const varvalue = component.varsProxy[variableName];
|
|
412
|
+
value = [...input.querySelectorAll("option")]
|
|
413
|
+
.filter((option) => option.selected || resolveNodeOrText(option.attributes.value || option.innerText, component) === value) //todo make sync comopat
|
|
377
414
|
.map((option) => option.getAttribute("value") || option.innerText);
|
|
378
415
|
}
|
|
379
416
|
}
|
|
380
|
-
component.varsProxy[
|
|
381
|
-
}
|
|
417
|
+
component.varsProxy[variableName] = coerce(value, type);
|
|
418
|
+
};
|
|
419
|
+
addListener(input, eventname, listener);
|
|
382
420
|
}
|
|
383
421
|
const tryParse = (value) => {
|
|
384
422
|
try {
|
|
@@ -388,22 +426,20 @@ const {observe} = (() => {
|
|
|
388
426
|
}
|
|
389
427
|
}
|
|
390
428
|
let reserved = {
|
|
391
|
-
any: {value: "any",constant: true},
|
|
392
|
-
boolean: {value: "boolean", constant: true},
|
|
393
|
-
string: {value: "string", constant: true},
|
|
394
|
-
number: {value: "number", constant: true},
|
|
395
|
-
object: {value: "object", constant: true},
|
|
396
429
|
observed: {value: true, constant: true},
|
|
397
430
|
reactive: {value: true, constant: true},
|
|
398
431
|
shared: {value: true, constant: true},
|
|
399
432
|
exported: {value: true, constant: true},
|
|
400
|
-
imported: {value: true, constant: true}
|
|
433
|
+
imported: {value: true, constant: true},
|
|
434
|
+
remote: {}
|
|
401
435
|
};
|
|
402
436
|
const createClass = (domElementNode, {observer, framed}) => {
|
|
403
437
|
const instances = new Set(),
|
|
404
438
|
dom = domElementNode.tagName === "TEMPLATE"
|
|
405
439
|
? domElementNode.content.cloneNode(true)
|
|
406
|
-
: domElementNode.cloneNode(true)
|
|
440
|
+
: domElementNode.cloneNode(true),
|
|
441
|
+
observedAttributes = [];
|
|
442
|
+
observedAttributes.add = function(name) { observedAttributes.includes(name) || observedAttributes.push(name); }
|
|
407
443
|
if (domElementNode.tagName === "TEMPLATE") domElementNode = domElementNode.cloneNode(true);
|
|
408
444
|
return class CustomElement extends HTMLElement {
|
|
409
445
|
static get instances() {
|
|
@@ -413,10 +449,18 @@ const {observe} = (() => {
|
|
|
413
449
|
constructor() {
|
|
414
450
|
super();
|
|
415
451
|
instances.add(this);
|
|
416
|
-
observer ||= createObserver(this, framed);
|
|
417
452
|
const currentComponent = this,
|
|
418
453
|
shadow = this.attachShadow({mode: "open"}),
|
|
419
454
|
eventlisteners = {};
|
|
455
|
+
// needs to be local to the instance
|
|
456
|
+
Object.defineProperty(this,"changeListener",{value:
|
|
457
|
+
function({variableName, value}) {
|
|
458
|
+
if (currentComponent.changeListener.targets.has(variableName)) {
|
|
459
|
+
value = typeof (value) === "string" || !value ? value : JSON.stringify(value);
|
|
460
|
+
if (value == null) removeComponentAttribute(currentComponent, variableName);
|
|
461
|
+
else setComponentAttribute(currentComponent, variableName, value);
|
|
462
|
+
}
|
|
463
|
+
}});
|
|
420
464
|
this.vars = {
|
|
421
465
|
...reserved,
|
|
422
466
|
addEventListener: {
|
|
@@ -431,9 +475,10 @@ const {observe} = (() => {
|
|
|
431
475
|
constant: true
|
|
432
476
|
},
|
|
433
477
|
postEvent: {
|
|
434
|
-
value: (eventName, event) => {
|
|
478
|
+
value: (eventName, event={}) => {
|
|
435
479
|
//event = {...event}
|
|
436
480
|
event.type = eventName;
|
|
481
|
+
event.target = currentComponent;
|
|
437
482
|
eventlisteners[eventName]?.forEach((f) => f(event));
|
|
438
483
|
},
|
|
439
484
|
type: "function",
|
|
@@ -443,6 +488,8 @@ const {observe} = (() => {
|
|
|
443
488
|
};
|
|
444
489
|
this.defaultAttributes = domElementNode.tagName === "TEMPLATE" ? domElementNode.attributes : dom.attributes;
|
|
445
490
|
this.varsProxy = createVarsProxy(this.vars, this, CustomElement);
|
|
491
|
+
this.changeListener.targets = new Set();
|
|
492
|
+
this.varsProxy.addEventListener("change", this.changeListener);
|
|
446
493
|
if (framed || CustomElement.lightviewFramed) this.variables({message: Object}, {exported: true});
|
|
447
494
|
["getElementById", "querySelector", "querySelectorAll"]
|
|
448
495
|
.forEach((fname) => {
|
|
@@ -473,138 +520,149 @@ const {observe} = (() => {
|
|
|
473
520
|
shadow = ctx.shadowRoot;
|
|
474
521
|
for (const attr of this.defaultAttributes) this.hasAttribute(attr.name) || this.setAttribute(attr.name, attr.value);
|
|
475
522
|
const scripts = shadow.querySelectorAll("script"),
|
|
476
|
-
promises = []
|
|
477
|
-
|
|
523
|
+
promises = [],
|
|
524
|
+
scriptpromises = [];
|
|
525
|
+
for (const script of [...scripts]) {
|
|
478
526
|
if (script.attributes.src?.value?.includes("/lightview.js")) continue;
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
527
|
+
const currentScript = document.createElement("script");
|
|
528
|
+
if (script.className !== "lightview" && !((script.attributes.type?.value || "").includes("lightview/"))) {
|
|
529
|
+
for (const attr of script.attributes) currentScript.setAttribute(attr.name,attr.value);
|
|
530
|
+
scriptpromises.push(new Promise((resolve) => {
|
|
531
|
+
currentScript.onload = () => resolve();
|
|
532
|
+
}))
|
|
533
|
+
shadow.appendChild(currentScript);
|
|
534
|
+
currentScript.remove();
|
|
535
|
+
continue;
|
|
536
|
+
};
|
|
537
|
+
const scriptid = Math.random() + "";
|
|
482
538
|
for (const attr of script.attributes) {
|
|
483
539
|
currentScript.setAttribute(attr.name, attr.name === "type" ? attr.value.replace("lightview/", "") : attr.value);
|
|
484
540
|
}
|
|
485
541
|
currentScript.classList.remove("lightview");
|
|
486
542
|
const text = script.innerHTML.replaceAll(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, "$1").replaceAll(/\r?\n/g, "");
|
|
487
|
-
currentScript.innerHTML = `
|
|
543
|
+
currentScript.innerHTML = `Object.getPrototypeOf(async function(){}).constructor('if(window["${scriptid}"]?.ctx) { const ctx = window["${scriptid}"].ctx; { with(ctx) { ${text}; } } }')().then(() => window["${scriptid}"]()); `;
|
|
488
544
|
let resolver;
|
|
489
|
-
promises.push(new Promise((resolve) => resolver = resolve));
|
|
490
545
|
window[scriptid] = () => {
|
|
491
546
|
delete window[scriptid];
|
|
492
547
|
currentScript.remove();
|
|
493
548
|
resolver();
|
|
494
549
|
}
|
|
495
550
|
window[scriptid].ctx = ctx.varsProxy;
|
|
496
|
-
|
|
551
|
+
promises.push(new Promise((resolve) => {
|
|
552
|
+
resolver = resolve;
|
|
553
|
+
// wait for all regular scripts to load first
|
|
554
|
+
Promise.all(scriptpromises).then(() => shadow.appendChild(currentScript));
|
|
555
|
+
}));
|
|
497
556
|
}
|
|
498
557
|
Promise.all(promises).then(() => {
|
|
499
558
|
const nodes = getNodes(ctx);
|
|
500
559
|
nodes.forEach((node) => {
|
|
501
560
|
if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
|
|
502
|
-
render(!!node.template,
|
|
561
|
+
render(!!node.template, () => resolveNodeOrText(node, this))
|
|
503
562
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
504
563
|
// resolve the value before all else;
|
|
505
|
-
const attr = node.attributes.value
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (eltype === "checkbox") {
|
|
518
|
-
if (coerce(value, "boolean") === true) {
|
|
519
|
-
node.setAttribute("checked", "");
|
|
520
|
-
node.checked = true;
|
|
521
|
-
} else {
|
|
522
|
-
node.removeAttribute("checked");
|
|
523
|
-
node.checked = false;
|
|
524
|
-
}
|
|
525
|
-
const vname = resolveNode(node.attributes.name, ctx);
|
|
526
|
-
if (vname) ctx.setValue(vname, node.checked, {coerceTo: "boolean"});
|
|
527
|
-
}
|
|
528
|
-
if (node.tagName === "SELECT") {
|
|
529
|
-
let values = [value];
|
|
530
|
-
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
531
|
-
[...node.querySelectorAll("option")].forEach((option) => {
|
|
532
|
-
if (option.hasAttribute("value")) {
|
|
533
|
-
if (values.includes(resolveNode(option.attributes.value, ctx))) {
|
|
534
|
-
option.setAttribute("selected", "");
|
|
535
|
-
option.selected = true;
|
|
536
|
-
}
|
|
537
|
-
} else if (option.innerText.trim() === value) {
|
|
538
|
-
option.setAttribute("selected", "");
|
|
539
|
-
option.selected = true;
|
|
564
|
+
const attr = node.attributes.value,
|
|
565
|
+
template = attr?.template;
|
|
566
|
+
if (attr && template) {
|
|
567
|
+
//render(!!template, () => {
|
|
568
|
+
let value = resolveNodeOrText(attr, this),
|
|
569
|
+
eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx) : null;
|
|
570
|
+
const template = attr.template;
|
|
571
|
+
if (template) {
|
|
572
|
+
if (/\$\{[a-zA-z_]+\}/g.test(template)) {
|
|
573
|
+
const name = template.substring(2, template.length - 1);
|
|
574
|
+
if(!this.vars[name] || this.vars[name].reactive) {
|
|
575
|
+
bindInput(node, name, this, value);
|
|
540
576
|
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const {name, value} = attr;
|
|
548
|
-
if (name === "type") {
|
|
549
|
-
if (value === "radio") {
|
|
550
|
-
const name = resolveNode(node.attributes.name, ctx);
|
|
551
|
-
for (const vname of this.getVariableNames()) {
|
|
552
|
-
if (vname === name) {
|
|
553
|
-
render(true, () => {
|
|
554
|
-
const name = resolveNode(node.attributes.name, ctx),
|
|
555
|
-
varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
|
|
556
|
-
if (varvalue == resolveNode(node.attributes.value, ctx)) {
|
|
577
|
+
}
|
|
578
|
+
observe(() => {
|
|
579
|
+
const value = resolveNodeOrText(template, ctx);
|
|
580
|
+
if(value!==undefined) {
|
|
581
|
+
if (eltype === "checkbox") {
|
|
582
|
+
if (coerce(value, "boolean") === true) {
|
|
557
583
|
node.setAttribute("checked", "");
|
|
558
584
|
node.checked = true;
|
|
559
585
|
} else {
|
|
560
586
|
node.removeAttribute("checked");
|
|
561
587
|
node.checked = false;
|
|
562
588
|
}
|
|
563
|
-
})
|
|
564
|
-
|
|
565
|
-
|
|
589
|
+
} else if (node.tagName === "SELECT") {
|
|
590
|
+
let values = [value];
|
|
591
|
+
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
592
|
+
[...node.querySelectorAll("option")].forEach(async (option) => {
|
|
593
|
+
if (option.hasAttribute("value")) {
|
|
594
|
+
if (values.includes(resolveNodeOrText(option.attributes.value, ctx))) {
|
|
595
|
+
option.setAttribute("selected", "");
|
|
596
|
+
option.selected = true;
|
|
597
|
+
}
|
|
598
|
+
} else if (values.includes(resolveNodeOrText(option.innerText, ctx))) {
|
|
599
|
+
option.setAttribute("selected", "");
|
|
600
|
+
option.selected = true;
|
|
601
|
+
}
|
|
602
|
+
})
|
|
603
|
+
} else if (eltype!=="radio") {
|
|
604
|
+
attr.value = value;
|
|
605
|
+
}
|
|
566
606
|
}
|
|
567
|
-
}
|
|
607
|
+
});
|
|
568
608
|
}
|
|
609
|
+
}
|
|
610
|
+
[...node.attributes].forEach(async (attr) => {
|
|
611
|
+
if (attr.name === "value" && attr.template) return;
|
|
612
|
+
const {name, value} = attr,
|
|
613
|
+
vname = node.attributes.name?.value;
|
|
614
|
+
if (name === "type" && value=="radio" && vname) {
|
|
615
|
+
bindInput(node, vname, this);
|
|
616
|
+
render(true, () => {
|
|
617
|
+
const varvalue = Function("context", "with(context) { return `${" + vname + "}` }")(ctx.varsProxy);
|
|
618
|
+
if (node.attributes.value.value == varvalue) {
|
|
619
|
+
node.setAttribute("checked", "");
|
|
620
|
+
node.checked = true;
|
|
621
|
+
} else {
|
|
622
|
+
node.removeAttribute("checked");
|
|
623
|
+
node.checked = false;
|
|
624
|
+
}
|
|
625
|
+
});
|
|
569
626
|
}
|
|
570
627
|
|
|
571
628
|
const [type, ...params] = name.split(":");
|
|
572
629
|
if (type === "") { // name is :something
|
|
573
|
-
render(!!attr.template,
|
|
574
|
-
const value = attr.value
|
|
575
|
-
elvalue = resolveNode(node.attributes.value, ctx),
|
|
576
|
-
eltype = resolveNode(node.attributes.type, ctx),
|
|
577
|
-
elname = resolveNode(node.attributes.name, ctx);
|
|
630
|
+
render(!!attr.template, () => {
|
|
631
|
+
const value = attr.value;
|
|
578
632
|
if (params[0]) {
|
|
579
633
|
if (value === "true") node.setAttribute(params[0], "")
|
|
580
634
|
else node.removeAttribute(params[0]);
|
|
581
|
-
} else
|
|
582
|
-
|
|
583
|
-
|
|
635
|
+
} else {
|
|
636
|
+
const elvalue = node.attributes.value ? resolveNodeOrText(node.attributes.value, ctx) : null,
|
|
637
|
+
eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx) : null;
|
|
638
|
+
if (eltype === "checkbox" || node.tagName === "OPTION") {
|
|
639
|
+
if (elvalue === "true") node.setAttribute("checked", "")
|
|
640
|
+
else node.removeAttribute("checked");
|
|
641
|
+
}
|
|
584
642
|
}
|
|
585
643
|
})
|
|
586
644
|
} else if (type === "l-on") {
|
|
587
645
|
let listener;
|
|
588
|
-
render(!!attr.template,
|
|
589
|
-
const value =
|
|
646
|
+
render(!!attr.template, () => {
|
|
647
|
+
const value = resolveNodeOrText(attr, this);
|
|
590
648
|
if (listener) node.removeEventListener(params[0], listener);
|
|
591
649
|
listener = this[value] || window[value] || Function(value);
|
|
592
650
|
addListener(node, params[0], listener);
|
|
593
651
|
})
|
|
594
652
|
} else if (type === "l-if") {
|
|
595
|
-
render(!!attr.template,
|
|
596
|
-
const value =
|
|
653
|
+
render(!!attr.template, () => {
|
|
654
|
+
const value = resolveNodeOrText(attr, this);
|
|
597
655
|
node.style.setProperty("display", value === "true" ? "revert" : "none");
|
|
598
656
|
})
|
|
599
657
|
} else if (type === "l-for") {
|
|
600
658
|
node.template ||= node.innerHTML;
|
|
601
|
-
render(!!attr.template,
|
|
659
|
+
render(!!attr.template, () => {
|
|
602
660
|
const [what = "each", vname = "item", index = "index", array = "array", after = false] = params,
|
|
603
|
-
value =
|
|
661
|
+
value = resolveNodeOrText(attr, this),
|
|
604
662
|
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
605
663
|
target = what === "each" ? coerced : Object[what](coerced),
|
|
606
|
-
html = target.reduce((html, item, i, target) => {
|
|
607
|
-
return html += Function("vars","context", "with(vars) { with(context) { return `" + node.template + "` }}")(
|
|
664
|
+
html = target.reduce( (html, item, i, target) => {
|
|
665
|
+
return html += Function("vars", "context", "with(vars) { with(context) { return `" + node.template + "` }}")(
|
|
608
666
|
ctx.varsProxy,
|
|
609
667
|
{
|
|
610
668
|
[vname]: item,
|
|
@@ -625,42 +683,33 @@ const {observe} = (() => {
|
|
|
625
683
|
else node.appendChild(parsed.body.firstChild);
|
|
626
684
|
}
|
|
627
685
|
})
|
|
628
|
-
} else
|
|
629
|
-
render(!!attr.template,
|
|
686
|
+
} else {
|
|
687
|
+
render(!!attr.template, () => {
|
|
688
|
+
resolveNodeOrText(attr, this);
|
|
689
|
+
})
|
|
630
690
|
}
|
|
631
691
|
})
|
|
632
692
|
}
|
|
633
693
|
})
|
|
634
694
|
shadow.normalize();
|
|
635
|
-
observer
|
|
636
|
-
|
|
695
|
+
observer ||= createObserver(ctx, framed);
|
|
696
|
+
observer.observe(ctx, {attributeOldValue: true, subtree:true, characterData:true, characterDataOldValue:true});
|
|
697
|
+
ctx.vars.postEvent.value("connected");
|
|
637
698
|
})
|
|
638
699
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
//Object.defineProperty(this, "adoptedCallback", {configurable: true, writable: true, value});
|
|
700
|
+
adoptedCallback(callback) {
|
|
701
|
+
this.vars.postEvent.value("adopted");
|
|
702
|
+
super.adoptedCallback();
|
|
643
703
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
//Object.defineProperty(this, "connectedCallback", {configurable: true, writable: true, value});
|
|
704
|
+
disconnectedCallback() {
|
|
705
|
+
this.vars.postEvent.value("disconnected");
|
|
706
|
+
super.disconnectedCallback();
|
|
648
707
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
this.attributeChangedCallback = callback;
|
|
652
|
-
//Object.defineProperty(this, "attributeChangedCallback", {configurable: true, writable: true, value});
|
|
708
|
+
get observedAttributes() {
|
|
709
|
+
return CustomElement.observedAttributes;
|
|
653
710
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
Object.defineProperty(this, "disconnectedCallback", {
|
|
657
|
-
configurable: true,
|
|
658
|
-
writable: true,
|
|
659
|
-
value: () => {
|
|
660
|
-
value();
|
|
661
|
-
super.disconnectedCallback(callback);
|
|
662
|
-
}
|
|
663
|
-
});
|
|
711
|
+
static get observedAttributes() {
|
|
712
|
+
return observedAttributes;
|
|
664
713
|
}
|
|
665
714
|
|
|
666
715
|
getVariableNames() {
|
|
@@ -669,17 +718,17 @@ const {observe} = (() => {
|
|
|
669
718
|
})
|
|
670
719
|
}
|
|
671
720
|
|
|
672
|
-
|
|
721
|
+
setVariableValue(variableName, value, {coerceTo = typeof (value)} = {}) {
|
|
673
722
|
if (!this.isConnected) {
|
|
674
723
|
instances.delete(this);
|
|
675
724
|
return false;
|
|
676
725
|
}
|
|
677
726
|
let {type} = this.vars[variableName] || {};
|
|
678
727
|
if (type) {
|
|
679
|
-
value = coerce(value, type);
|
|
680
728
|
if (this.varsProxy[variableName] !== value) {
|
|
681
729
|
const variable = this.vars[variableName];
|
|
682
730
|
if (variable.shared) {
|
|
731
|
+
value = type.validate ? type.validate(value,variable) : coerce(value,coerceTo);
|
|
683
732
|
const event = new VariableEvent({
|
|
684
733
|
variableName: variableName,
|
|
685
734
|
value: value,
|
|
@@ -698,11 +747,11 @@ const {observe} = (() => {
|
|
|
698
747
|
return false;
|
|
699
748
|
}
|
|
700
749
|
|
|
701
|
-
|
|
750
|
+
getVariableValue(variableName) {
|
|
702
751
|
return this.vars[variableName]?.value;
|
|
703
752
|
}
|
|
704
753
|
|
|
705
|
-
variables(variables, {observed, reactive, shared, exported, imported} = {}) { // options = {observed,reactive,shared,exported,imported}
|
|
754
|
+
variables(variables, {observed, reactive, shared, exported, imported, remote} = {}) { // options = {observed,reactive,shared,exported,imported}
|
|
706
755
|
const addEventListener = this.varsProxy.addEventListener;
|
|
707
756
|
if (variables !== undefined) {
|
|
708
757
|
Object.entries(variables)
|
|
@@ -712,6 +761,7 @@ const {observe} = (() => {
|
|
|
712
761
|
variable.value = this.hasAttribute(key) ? coerce(this.getAttribute(key), variable.type) : variable.value;
|
|
713
762
|
variable.observed = observed;
|
|
714
763
|
variable.imported = imported;
|
|
764
|
+
if(variable.observed) this.observedAttributes.add(key);
|
|
715
765
|
}
|
|
716
766
|
if (reactive) {
|
|
717
767
|
variable.reactive = true;
|
|
@@ -720,21 +770,20 @@ const {observe} = (() => {
|
|
|
720
770
|
if (shared) {
|
|
721
771
|
variable.shared = true;
|
|
722
772
|
addEventListener("change", ({variableName, value}) => {
|
|
723
|
-
if (this.vars[variableName]?.shared)
|
|
724
|
-
this.siblings.forEach((instance) => instance.setValue(variableName, value))
|
|
725
|
-
}
|
|
773
|
+
if (this.vars[variableName]?.shared) this.siblings.forEach((instance) => instance.setVariableValue(variableName, value))
|
|
726
774
|
})
|
|
727
775
|
}
|
|
728
776
|
if (exported) {
|
|
729
777
|
variable.exported = true;
|
|
730
778
|
// in case the export goes up to an iframe
|
|
731
779
|
if (variable.value != null) setComponentAttribute(this, key, variable.value);
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
})
|
|
780
|
+
this.changeListener.targets.add(key);
|
|
781
|
+
}
|
|
782
|
+
if (remote) {
|
|
783
|
+
variable.remote = remote;
|
|
784
|
+
handleRemote({variable, remote, reactive});
|
|
737
785
|
}
|
|
786
|
+
if(type.validate) type.validate(undefined,variable);
|
|
738
787
|
});
|
|
739
788
|
}
|
|
740
789
|
return Object.entries(this.vars)
|
|
@@ -763,24 +812,143 @@ const {observe} = (() => {
|
|
|
763
812
|
}
|
|
764
813
|
}
|
|
765
814
|
}
|
|
766
|
-
|
|
815
|
+
|
|
816
|
+
const remoteProxy = ({json, variable,remote, reactive}) => {
|
|
817
|
+
const type = typeof (remote);
|
|
818
|
+
return new Proxy(json, {
|
|
819
|
+
get(target,property) {
|
|
820
|
+
if(property==="__remoteProxytarget__") return json;
|
|
821
|
+
return target[property];
|
|
822
|
+
},
|
|
823
|
+
async set(target, property, value) {
|
|
824
|
+
if(value && typeof(value)==="object" && value instanceof Promise) value = await value;
|
|
825
|
+
const oldValue = target[property];
|
|
826
|
+
if (oldValue !== value) {
|
|
827
|
+
let remotevalue;
|
|
828
|
+
if (type === "string") {
|
|
829
|
+
const href = new URL(remote,window.location.href).href;
|
|
830
|
+
remotevalue = patch({target,property,value,oldValue},href,variable);
|
|
831
|
+
} else if(remote && type==="object") {
|
|
832
|
+
let href;
|
|
833
|
+
if(remote.path) href = new URL(remote.path,window.location.href).href;
|
|
834
|
+
if(!remote.patch) {
|
|
835
|
+
if(!href) throw new Error(`A remote path is required is no put function is provided for remote data`)
|
|
836
|
+
remote.patch = patch;
|
|
837
|
+
}
|
|
838
|
+
remotevalue = remote.patch({target,property,value,oldValue},href,variable);
|
|
839
|
+
}
|
|
840
|
+
if(remotevalue) {
|
|
841
|
+
await remotevalue.then((newjson) => {
|
|
842
|
+
if (newjson && typeof (newjson) === "object" && reactive) {
|
|
843
|
+
const target = variable.value?.__reactorProxyTarget__ ? json : variable.value;
|
|
844
|
+
Object.entries(newjson).forEach(([key,newValue]) => {
|
|
845
|
+
if(target[key]!==newValue) target[key] = newValue;
|
|
846
|
+
})
|
|
847
|
+
Object.keys(target).forEach((key) => {
|
|
848
|
+
if(!(key in newjson)) delete target[key];
|
|
849
|
+
});
|
|
850
|
+
if(variable.value?.__reactorProxyTarget__) {
|
|
851
|
+
const dependents = variable.value.__dependents__,
|
|
852
|
+
observers = dependents[property] || [];
|
|
853
|
+
[...observers].forEach((f) => {
|
|
854
|
+
if (f.cancelled) dependents[property].delete(f);
|
|
855
|
+
else f();
|
|
856
|
+
})
|
|
857
|
+
}
|
|
858
|
+
} else {
|
|
859
|
+
variable.value = json;
|
|
860
|
+
}
|
|
861
|
+
})
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
866
|
+
})
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const patch = ({target,property,value,oldValue},href,variable) => {
|
|
870
|
+
return fetch(href, {
|
|
871
|
+
method: "PATCH",
|
|
872
|
+
body: JSON.stringify({property,value,oldValue}),
|
|
873
|
+
headers: {
|
|
874
|
+
"Content-Type": "application/json"
|
|
875
|
+
}
|
|
876
|
+
}).then((response) => {
|
|
877
|
+
if (response.status < 400) return response.json();
|
|
878
|
+
})
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const get = (href,variable) => {
|
|
882
|
+
return fetch(href)
|
|
883
|
+
.then((response) => {
|
|
884
|
+
if (response.status < 400) return response.json();
|
|
885
|
+
})
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
const put = (href,variable) => {
|
|
889
|
+
return fetch(href, {
|
|
890
|
+
method: "PUT",
|
|
891
|
+
body: JSON.stringify(variable.value),
|
|
892
|
+
headers: {
|
|
893
|
+
"Content-Type": "application/json"
|
|
894
|
+
}
|
|
895
|
+
}).then((response) => {
|
|
896
|
+
if (response.status === 200) return response.json();
|
|
897
|
+
})
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const handleRemote = async ({variable, remote, reactive},doput) => {
|
|
901
|
+
const type = typeof (remote);
|
|
902
|
+
let value;
|
|
903
|
+
if (type === "string") {
|
|
904
|
+
const href = new URL(remote,window.location.href).href;
|
|
905
|
+
value = (doput
|
|
906
|
+
? put(href,variable)
|
|
907
|
+
: get(href,variable));
|
|
908
|
+
if(variable.value===undefined) variable.value = value;
|
|
909
|
+
} else if (remote && type === "object") {
|
|
910
|
+
let href;
|
|
911
|
+
if(remote.path) href = new URL(remote.path,window.location.href).href;
|
|
912
|
+
if(!remote.get || !remote.put) {
|
|
913
|
+
if(!href) throw new Error(`A remote path is required is no put function is provided for remote data`)
|
|
914
|
+
if(!remote.get) remote.get = get;
|
|
915
|
+
if(!remote.put) remote.put = put;
|
|
916
|
+
}
|
|
917
|
+
value = (doput
|
|
918
|
+
? remote.put(href,variable)
|
|
919
|
+
: remote.get(href,variable));
|
|
920
|
+
if(remote.ttl && !doput && !remote.intervalId) {
|
|
921
|
+
remote.intervalId = setInterval(async () => {
|
|
922
|
+
await handleRemote({variable, remote, reactive});
|
|
923
|
+
})
|
|
924
|
+
}
|
|
925
|
+
if(variable.value===undefined) variable.value = value;
|
|
926
|
+
}
|
|
927
|
+
if(value) {
|
|
928
|
+
variable.value = await value.then((json) => {
|
|
929
|
+
if (json && typeof (json) === "object" && reactive) {
|
|
930
|
+
return remoteProxy({json, variable,remote, reactive})
|
|
931
|
+
} else {
|
|
932
|
+
return json;
|
|
933
|
+
}
|
|
934
|
+
})
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const createComponent = (name, node, {framed, observer} = {}) => {
|
|
767
939
|
let ctor = customElements.get(name);
|
|
768
940
|
if (ctor) {
|
|
769
|
-
if (framed && !ctor.lightviewFramed)
|
|
770
|
-
|
|
771
|
-
} else {
|
|
772
|
-
console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
|
|
773
|
-
}
|
|
941
|
+
if (framed && !ctor.lightviewFramed) ctor.lightviewFramed = true;
|
|
942
|
+
else console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
|
|
774
943
|
return ctor;
|
|
775
944
|
}
|
|
776
945
|
ctor = createClass(node, {observer, framed});
|
|
777
946
|
customElements.define(name, ctor);
|
|
778
|
-
Lightview.customElements.set(name,ctor);
|
|
947
|
+
Lightview.customElements.set(name, ctor);
|
|
779
948
|
return ctor;
|
|
780
949
|
}
|
|
781
950
|
Lightview.customElements = new Map();
|
|
782
951
|
Lightview.createComponent = createComponent;
|
|
783
|
-
//Object.defineProperty(Lightview, "createComponent", {writable: true, configurable: true, value: createComponent})
|
|
784
952
|
const importLink = async (link, observer) => {
|
|
785
953
|
const url = (new URL(link.getAttribute("href"), window.location.href)),
|
|
786
954
|
as = link.getAttribute("as") || getNameFromPath(url.pathname);
|
|
@@ -792,12 +960,12 @@ const {observe} = (() => {
|
|
|
792
960
|
dom = parser.parseFromString(html, "text/html"),
|
|
793
961
|
unhide = !!dom.head.querySelector('meta[name="l-unhide"]'),
|
|
794
962
|
links = dom.head.querySelectorAll('link[href$=".html"][rel=module]');
|
|
795
|
-
for(const childlink of links) {
|
|
963
|
+
for (const childlink of links) {
|
|
796
964
|
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);
|
|
965
|
+
childurl = new URL(href, url.href);
|
|
966
|
+
childlink.setAttribute("href", childurl.href);
|
|
967
|
+
if (link.hasAttribute("crossorigin")) childlink.setAttribute("crossorigin", link.getAttribute("crossorigin"))
|
|
968
|
+
await importLink(childlink, observer);
|
|
801
969
|
}
|
|
802
970
|
if (unhide) dom.body.removeAttribute("hidden");
|
|
803
971
|
createComponent(as, dom.body, {observer});
|
|
@@ -807,7 +975,7 @@ const {observe} = (() => {
|
|
|
807
975
|
const importLinks = async () => {
|
|
808
976
|
const observer = createObserver(document.body);
|
|
809
977
|
for (const link of [...document.querySelectorAll(`link[href$=".html"][rel=module]`)]) {
|
|
810
|
-
await importLink(link,observer);
|
|
978
|
+
await importLink(link, observer);
|
|
811
979
|
}
|
|
812
980
|
}
|
|
813
981
|
|
|
@@ -875,21 +1043,19 @@ const {observe} = (() => {
|
|
|
875
1043
|
postMessage({type: "setAttribute", argsList: ["height", height + 20]});
|
|
876
1044
|
}
|
|
877
1045
|
resize();
|
|
878
|
-
onresize(document.body, () =>
|
|
879
|
-
resize();
|
|
880
|
-
});
|
|
1046
|
+
onresize(document.body, () => resize());
|
|
881
1047
|
return
|
|
882
1048
|
}
|
|
883
1049
|
if (type === "setAttribute") {
|
|
884
1050
|
const [name, value] = [...argsList],
|
|
885
1051
|
variable = document.body.vars[name];
|
|
886
|
-
if (variable && variable.imported) document.body.
|
|
1052
|
+
if (variable && variable.imported) document.body.setVariableValue(name, value);
|
|
887
1053
|
return;
|
|
888
1054
|
}
|
|
889
1055
|
if (type === "removeAttribute") {
|
|
890
1056
|
const [name] = argsList[0],
|
|
891
1057
|
variable = document.body.vars[name];
|
|
892
|
-
if (variable && variable.imported) document.body.
|
|
1058
|
+
if (variable && variable.imported) document.body.setVariableValue(name, undefined);
|
|
893
1059
|
|
|
894
1060
|
}
|
|
895
1061
|
});
|
|
@@ -927,9 +1093,7 @@ const {observe} = (() => {
|
|
|
927
1093
|
}
|
|
928
1094
|
if (type === "setAttribute") {
|
|
929
1095
|
const [name, value] = [...argsList];
|
|
930
|
-
if (iframe.getAttribute(name) !== value + "")
|
|
931
|
-
iframe.setAttribute(name, value);
|
|
932
|
-
}
|
|
1096
|
+
if (iframe.getAttribute(name) !== value + "") iframe.setAttribute(name, value);
|
|
933
1097
|
return;
|
|
934
1098
|
}
|
|
935
1099
|
if (type === "removeAttribute") {
|
|
@@ -977,7 +1141,7 @@ const {observe} = (() => {
|
|
|
977
1141
|
addListener(document, "DOMContentLoaded", (event) => loader(callback));
|
|
978
1142
|
}
|
|
979
1143
|
Lightview.whenFramed = whenFramed;
|
|
980
|
-
|
|
1144
|
+
|
|
981
1145
|
if (window.location === window.parent.location || !(window.parent instanceof Window) || window.parent !== window) {
|
|
982
1146
|
// loads for unframed content
|
|
983
1147
|
// CodePen mucks with window.parent
|
|
@@ -985,4 +1149,4 @@ const {observe} = (() => {
|
|
|
985
1149
|
}
|
|
986
1150
|
|
|
987
1151
|
return {observe}
|
|
988
|
-
})();
|
|
1152
|
+
})();
|