lightview 1.4.8-b → 1.6.2-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/examples/counter.html +3 -10
- package/examples/counter.test.mjs +47 -0
- package/examples/directives.html +2 -2
- package/examples/foreign.html +36 -0
- package/examples/{remoteform.html → forgeinform.html} +3 -3
- package/examples/form.html +9 -15
- package/examples/gauge.html +16 -0
- package/examples/invalid-template-literals.html +45 -0
- package/examples/message.html +1 -1
- package/examples/remote-server.js +51 -0
- package/examples/remote.html +24 -28
- package/examples/remote.json +1 -0
- package/examples/sensors/index.html +30 -0
- package/examples/sensors/sensor-server.js +30 -0
- package/examples/template.html +33 -0
- package/examples/types.html +93 -0
- package/examples/xor.html +3 -3
- package/lightview.js +238 -223
- package/package.json +36 -36
- package/test/basic.html +29 -20
- package/test/basic.test.mjs +98 -21
- package/types.js +445 -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 && remote.put) {
|
|
255
|
+
remote.handleRemote({variable,config:remote.config,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,53 +315,48 @@ const {observe} = (() => {
|
|
|
291
315
|
nodes.push(root, ...getNodes(root.shadowRoot))
|
|
292
316
|
} else {
|
|
293
317
|
for (const node of root.childNodes) {
|
|
294
|
-
if(node.tagName==="SCRIPT") continue;
|
|
318
|
+
if (node.tagName === "SCRIPT") continue;
|
|
295
319
|
if (node.nodeType === Node.TEXT_NODE && node.nodeValue?.includes("${")) {
|
|
296
320
|
node.template ||= node.nodeValue;
|
|
297
321
|
nodes.push(node);
|
|
298
322
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
299
|
-
let skip;
|
|
300
|
-
if (node.getAttribute("type") === "radio") nodes.push(node);
|
|
323
|
+
let skip, pushed;
|
|
301
324
|
[...node.attributes].forEach((attr) => {
|
|
302
325
|
if (attr.value.includes("${")) {
|
|
303
326
|
attr.template ||= attr.value;
|
|
327
|
+
pushed = true;
|
|
304
328
|
nodes.push(node);
|
|
305
329
|
} else if (attr.name.includes(":") || attr.name.startsWith("l-")) {
|
|
306
330
|
skip = attr.name.includes("l-for:");
|
|
331
|
+
pushed = true;
|
|
307
332
|
nodes.push(node)
|
|
308
333
|
}
|
|
309
334
|
})
|
|
335
|
+
if (!pushed && node.getAttribute("type") === "radio") nodes.push(node);
|
|
310
336
|
if (!skip && !node.shadowRoot) nodes.push(...getNodes(node));
|
|
311
337
|
}
|
|
312
338
|
}
|
|
313
339
|
}
|
|
314
340
|
return nodes;
|
|
315
341
|
}
|
|
316
|
-
const
|
|
317
|
-
|
|
342
|
+
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
|
343
|
+
const resolveNodeOrText = (node, component, safe) => {
|
|
344
|
+
const type = typeof (node),
|
|
318
345
|
template = type === "string" ? node.trim() : node.template;
|
|
319
346
|
if (template) {
|
|
320
347
|
try {
|
|
321
348
|
let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(template) + "` }")(component.varsProxy);
|
|
322
|
-
value = node.nodeType===Node.TEXT_NODE || !safe ? value : Lightview.escapeHTML(value);
|
|
323
|
-
if(type==="string") return value;
|
|
324
|
-
node.nodeValue = value=="null" || value=="undefined" ? "" : value;
|
|
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;
|
|
325
352
|
} catch (e) {
|
|
326
|
-
console.warn(e);
|
|
353
|
+
//console.warn(e);
|
|
327
354
|
if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
|
|
355
|
+
return undefined;
|
|
328
356
|
}
|
|
329
357
|
}
|
|
330
358
|
return node?.nodeValue;
|
|
331
359
|
}
|
|
332
|
-
const render = (hasTemplate, render) => {
|
|
333
|
-
let observer;
|
|
334
|
-
if (hasTemplate) {
|
|
335
|
-
if (observer) observer.cancel();
|
|
336
|
-
observer = observe(render)
|
|
337
|
-
} else {
|
|
338
|
-
render();
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
360
|
const inputTypeToType = (inputType) => {
|
|
342
361
|
if (!inputType) return "any"
|
|
343
362
|
if (["text", "tel", "email", "url", "search", "radio", "color", "password"].includes(inputType)) return "string";
|
|
@@ -353,26 +372,28 @@ const {observe} = (() => {
|
|
|
353
372
|
})
|
|
354
373
|
}
|
|
355
374
|
const bound = new WeakSet();
|
|
356
|
-
const bindInput = (input, variableName, component,value) => {
|
|
357
|
-
if(bound.has(input)) return;
|
|
375
|
+
const bindInput = (input, variableName, component, value) => {
|
|
376
|
+
if (bound.has(input)) return;
|
|
358
377
|
bound.add(input);
|
|
359
378
|
const inputtype = input.tagName === "SELECT" || input.tagName === "TEXTAREA" ? "text" : input.getAttribute("type"),
|
|
360
|
-
type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype)
|
|
361
|
-
deflt = input.getAttribute("default");
|
|
379
|
+
type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype);
|
|
362
380
|
value ||= input.getAttribute("value");
|
|
363
381
|
let variable = component.vars[variableName] || {type};
|
|
364
382
|
if (type !== variable.type) {
|
|
365
383
|
if (variable.type === "any" || variable.type === "unknown") variable.type = type;
|
|
366
384
|
else throw new TypeError(`Attempt to bind <input name="${variableName}" type="${type}"> to variable ${variableName}:${variable.type}`)
|
|
367
385
|
}
|
|
368
|
-
component.variables({[variableName]: type});
|
|
369
|
-
if(inputtype!=="radio")
|
|
386
|
+
component.variables({[variableName]: type},{reactive:true});
|
|
387
|
+
if(inputtype!=="radio") {
|
|
388
|
+
if(value.includes("${")) input.attributes.value.value = "";
|
|
389
|
+
else component.setVariableValue(variableName, coerce(value,type));
|
|
390
|
+
}
|
|
370
391
|
let eventname = "change";
|
|
371
392
|
if (input.tagName !== "SELECT" && (!inputtype || input.tagName === "TEXTAREA" || ["text", "number", "tel", "email", "url", "search", "password"].includes(inputtype))) {
|
|
372
393
|
eventname = "input";
|
|
373
394
|
}
|
|
374
395
|
const listener = (event) => {
|
|
375
|
-
if(event) event.stopImmediatePropagation();
|
|
396
|
+
if (event) event.stopImmediatePropagation();
|
|
376
397
|
let value = input.value;
|
|
377
398
|
if (inputtype === "checkbox") {
|
|
378
399
|
value = input.checked
|
|
@@ -380,7 +401,7 @@ const {observe} = (() => {
|
|
|
380
401
|
if (input.hasAttribute("multiple")) {
|
|
381
402
|
const varvalue = component.varsProxy[variableName];
|
|
382
403
|
value = [...input.querySelectorAll("option")]
|
|
383
|
-
.filter((option) => option.selected || resolveNodeOrText(option.attributes.value||option.innerText,component)===value)
|
|
404
|
+
.filter((option) => option.selected || resolveNodeOrText(option.attributes.value || option.innerText, component) === value) //todo make sync comopat
|
|
384
405
|
.map((option) => option.getAttribute("value") || option.innerText);
|
|
385
406
|
}
|
|
386
407
|
}
|
|
@@ -396,11 +417,6 @@ const {observe} = (() => {
|
|
|
396
417
|
}
|
|
397
418
|
}
|
|
398
419
|
let reserved = {
|
|
399
|
-
any: {value: "any",constant: true},
|
|
400
|
-
boolean: {value: "boolean", constant: true},
|
|
401
|
-
string: {value: "string", constant: true},
|
|
402
|
-
number: {value: "number", constant: true},
|
|
403
|
-
object: {value: "object", constant: true},
|
|
404
420
|
observed: {value: true, constant: true},
|
|
405
421
|
reactive: {value: true, constant: true},
|
|
406
422
|
shared: {value: true, constant: true},
|
|
@@ -411,7 +427,9 @@ const {observe} = (() => {
|
|
|
411
427
|
const instances = new Set(),
|
|
412
428
|
dom = domElementNode.tagName === "TEMPLATE"
|
|
413
429
|
? domElementNode.content.cloneNode(true)
|
|
414
|
-
: domElementNode.cloneNode(true)
|
|
430
|
+
: domElementNode.cloneNode(true),
|
|
431
|
+
observedAttributes = [];
|
|
432
|
+
observedAttributes.add = function(name) { observedAttributes.includes(name) || observedAttributes.push(name); }
|
|
415
433
|
if (domElementNode.tagName === "TEMPLATE") domElementNode = domElementNode.cloneNode(true);
|
|
416
434
|
return class CustomElement extends HTMLElement {
|
|
417
435
|
static get instances() {
|
|
@@ -421,10 +439,18 @@ const {observe} = (() => {
|
|
|
421
439
|
constructor() {
|
|
422
440
|
super();
|
|
423
441
|
instances.add(this);
|
|
424
|
-
observer ||= createObserver(this, framed);
|
|
425
442
|
const currentComponent = this,
|
|
426
443
|
shadow = this.attachShadow({mode: "open"}),
|
|
427
444
|
eventlisteners = {};
|
|
445
|
+
// needs to be local to the instance
|
|
446
|
+
Object.defineProperty(this,"changeListener",{value:
|
|
447
|
+
function({variableName, value}) {
|
|
448
|
+
if (currentComponent.changeListener.targets.has(variableName)) {
|
|
449
|
+
value = typeof (value) === "string" || !value ? value : JSON.stringify(value);
|
|
450
|
+
if (value == null) removeComponentAttribute(currentComponent, variableName);
|
|
451
|
+
else setComponentAttribute(currentComponent, variableName, value);
|
|
452
|
+
}
|
|
453
|
+
}});
|
|
428
454
|
this.vars = {
|
|
429
455
|
...reserved,
|
|
430
456
|
addEventListener: {
|
|
@@ -439,9 +465,10 @@ const {observe} = (() => {
|
|
|
439
465
|
constant: true
|
|
440
466
|
},
|
|
441
467
|
postEvent: {
|
|
442
|
-
value: (eventName, event) => {
|
|
468
|
+
value: (eventName, event = {}) => {
|
|
443
469
|
//event = {...event}
|
|
444
470
|
event.type = eventName;
|
|
471
|
+
event.target = currentComponent;
|
|
445
472
|
eventlisteners[eventName]?.forEach((f) => f(event));
|
|
446
473
|
},
|
|
447
474
|
type: "function",
|
|
@@ -451,6 +478,8 @@ const {observe} = (() => {
|
|
|
451
478
|
};
|
|
452
479
|
this.defaultAttributes = domElementNode.tagName === "TEMPLATE" ? domElementNode.attributes : dom.attributes;
|
|
453
480
|
this.varsProxy = createVarsProxy(this.vars, this, CustomElement);
|
|
481
|
+
this.changeListener.targets = new Set();
|
|
482
|
+
this.varsProxy.addEventListener("change", this.changeListener);
|
|
454
483
|
if (framed || CustomElement.lightviewFramed) this.variables({message: Object}, {exported: true});
|
|
455
484
|
["getElementById", "querySelector", "querySelectorAll"]
|
|
456
485
|
.forEach((fname) => {
|
|
@@ -481,138 +510,148 @@ const {observe} = (() => {
|
|
|
481
510
|
shadow = ctx.shadowRoot;
|
|
482
511
|
for (const attr of this.defaultAttributes) this.hasAttribute(attr.name) || this.setAttribute(attr.name, attr.value);
|
|
483
512
|
const scripts = shadow.querySelectorAll("script"),
|
|
484
|
-
promises = []
|
|
485
|
-
|
|
513
|
+
promises = [],
|
|
514
|
+
scriptpromises = [];
|
|
515
|
+
for (const script of [...scripts]) {
|
|
486
516
|
if (script.attributes.src?.value?.includes("/lightview.js")) continue;
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
517
|
+
const currentScript = document.createElement("script");
|
|
518
|
+
if (script.className !== "lightview" && !((script.attributes.type?.value || "").includes("lightview/"))) {
|
|
519
|
+
for (const attr of script.attributes) currentScript.setAttribute(attr.name,attr.value);
|
|
520
|
+
scriptpromises.push(new Promise((resolve) => {
|
|
521
|
+
currentScript.onload = () => resolve();
|
|
522
|
+
}))
|
|
523
|
+
shadow.appendChild(currentScript);
|
|
524
|
+
currentScript.remove();
|
|
525
|
+
continue;
|
|
526
|
+
};
|
|
527
|
+
const scriptid = Math.random() + "";
|
|
490
528
|
for (const attr of script.attributes) {
|
|
491
529
|
currentScript.setAttribute(attr.name, attr.name === "type" ? attr.value.replace("lightview/", "") : attr.value);
|
|
492
530
|
}
|
|
493
531
|
currentScript.classList.remove("lightview");
|
|
494
532
|
const text = script.innerHTML.replaceAll(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, "$1").replaceAll(/\r?\n/g, "");
|
|
495
|
-
currentScript.innerHTML = `
|
|
533
|
+
currentScript.innerHTML = `Object.getPrototypeOf(async function(){}).constructor('if(window["${scriptid}"]?.ctx) { const ctx = window["${scriptid}"].ctx; { with(ctx) { ${text}; } } }')().then(() => window["${scriptid}"]()); `;
|
|
496
534
|
let resolver;
|
|
497
|
-
promises.push(new Promise((resolve) => resolver = resolve));
|
|
498
535
|
window[scriptid] = () => {
|
|
499
536
|
delete window[scriptid];
|
|
500
537
|
currentScript.remove();
|
|
501
538
|
resolver();
|
|
502
539
|
}
|
|
503
540
|
window[scriptid].ctx = ctx.varsProxy;
|
|
504
|
-
|
|
541
|
+
promises.push(new Promise((resolve) => {
|
|
542
|
+
resolver = resolve;
|
|
543
|
+
// wait for all regular scripts to load first
|
|
544
|
+
Promise.all(scriptpromises).then(() => shadow.appendChild(currentScript));
|
|
545
|
+
}));
|
|
505
546
|
}
|
|
506
547
|
Promise.all(promises).then(() => {
|
|
507
548
|
const nodes = getNodes(ctx);
|
|
508
549
|
nodes.forEach((node) => {
|
|
509
550
|
if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
|
|
510
|
-
|
|
551
|
+
observe(() => resolveNodeOrText(node, this));
|
|
511
552
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
512
553
|
// resolve the value before all else;
|
|
513
|
-
const attr = node.attributes.value
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
554
|
+
const attr = node.attributes.value,
|
|
555
|
+
template = attr?.template;
|
|
556
|
+
if (attr && template) {
|
|
557
|
+
let value = resolveNodeOrText(attr, this),
|
|
517
558
|
eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx) : null;
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
if(/\$\{[a-zA-z_]+\}/g.test(template)) {
|
|
521
|
-
const name = template.substring(2,template.length-1);
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
if (eltype === "checkbox") {
|
|
526
|
-
if (coerce(value, "boolean") === true) {
|
|
527
|
-
node.setAttribute("checked", "");
|
|
528
|
-
node.checked = true;
|
|
529
|
-
} else {
|
|
530
|
-
node.removeAttribute("checked");
|
|
531
|
-
node.checked = false;
|
|
532
|
-
}
|
|
533
|
-
const vname = resolveNodeOrText(node.attributes.name, ctx);
|
|
534
|
-
if (vname) ctx.setValue(vname, node.checked, {coerceTo: "boolean"});
|
|
535
|
-
}
|
|
536
|
-
if (node.tagName === "SELECT") {
|
|
537
|
-
let values = [value];
|
|
538
|
-
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
539
|
-
[...node.querySelectorAll("option")].forEach((option) => {
|
|
540
|
-
if (option.hasAttribute("value")) {
|
|
541
|
-
if (values.includes(resolveNodeOrText(option.attributes.value, ctx))) {
|
|
542
|
-
option.setAttribute("selected", "");
|
|
543
|
-
option.selected = true;
|
|
544
|
-
}
|
|
545
|
-
} else if (values.includes(resolveNodeOrText(option.innerText,ctx))) {
|
|
546
|
-
option.setAttribute("selected", "");
|
|
547
|
-
option.selected = true;
|
|
559
|
+
const template = attr.template;
|
|
560
|
+
if (template) {
|
|
561
|
+
if (/\$\{[a-zA-z_]+\}/g.test(template)) {
|
|
562
|
+
const name = template.substring(2, template.length - 1);
|
|
563
|
+
if(!this.vars[name] || this.vars[name].reactive) {
|
|
564
|
+
bindInput(node, name, this, value);
|
|
548
565
|
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
const {name, value} = attr;
|
|
556
|
-
if (name === "type") {
|
|
557
|
-
if (value === "radio") {
|
|
558
|
-
const name = resolveNodeOrText(node.attributes.name, ctx);
|
|
559
|
-
for (const vname of this.getVariableNames()) {
|
|
560
|
-
if (vname === name) {
|
|
561
|
-
render(true, () => {
|
|
562
|
-
const name = resolveNodeOrText(node.attributes.name, ctx),
|
|
563
|
-
varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
|
|
564
|
-
if (varvalue == resolveNodeOrText(node.attributes.value, ctx)) {
|
|
566
|
+
}
|
|
567
|
+
observe(() => {
|
|
568
|
+
const value = resolveNodeOrText(template, ctx);
|
|
569
|
+
if(value!==undefined) {
|
|
570
|
+
if (eltype === "checkbox") {
|
|
571
|
+
if (coerce(value, "boolean") === true) {
|
|
565
572
|
node.setAttribute("checked", "");
|
|
566
573
|
node.checked = true;
|
|
567
574
|
} else {
|
|
568
575
|
node.removeAttribute("checked");
|
|
569
576
|
node.checked = false;
|
|
570
577
|
}
|
|
571
|
-
})
|
|
572
|
-
|
|
573
|
-
|
|
578
|
+
} else if (node.tagName === "SELECT") {
|
|
579
|
+
let values = [value];
|
|
580
|
+
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
581
|
+
[...node.querySelectorAll("option")].forEach(async (option) => {
|
|
582
|
+
if (option.hasAttribute("value")) {
|
|
583
|
+
if (values.includes(resolveNodeOrText(option.attributes.value, ctx))) {
|
|
584
|
+
option.setAttribute("selected", "");
|
|
585
|
+
option.selected = true;
|
|
586
|
+
}
|
|
587
|
+
} else if (values.includes(resolveNodeOrText(option.innerText, ctx))) {
|
|
588
|
+
option.setAttribute("selected", "");
|
|
589
|
+
option.selected = true;
|
|
590
|
+
}
|
|
591
|
+
})
|
|
592
|
+
} else if (eltype!=="radio") {
|
|
593
|
+
attr.value = value;
|
|
594
|
+
}
|
|
574
595
|
}
|
|
575
|
-
}
|
|
596
|
+
});
|
|
576
597
|
}
|
|
598
|
+
}
|
|
599
|
+
[...node.attributes].forEach(async (attr) => {
|
|
600
|
+
if (attr.name === "value" && attr.template) return;
|
|
601
|
+
const {name, value} = attr,
|
|
602
|
+
vname = node.attributes.name?.value;
|
|
603
|
+
if (name === "type" && value=="radio" && vname) {
|
|
604
|
+
bindInput(node, vname, this);
|
|
605
|
+
observe(() => {
|
|
606
|
+
const varvalue = Function("context", "with(context) { return `${" + vname + "}` }")(ctx.varsProxy);
|
|
607
|
+
if (node.attributes.value.value == varvalue) {
|
|
608
|
+
node.setAttribute("checked", "");
|
|
609
|
+
node.checked = true;
|
|
610
|
+
} else {
|
|
611
|
+
node.removeAttribute("checked");
|
|
612
|
+
node.checked = false;
|
|
613
|
+
}
|
|
614
|
+
});
|
|
577
615
|
}
|
|
578
616
|
|
|
579
617
|
const [type, ...params] = name.split(":");
|
|
580
618
|
if (type === "") { // name is :something
|
|
581
|
-
|
|
582
|
-
const value = attr.value
|
|
583
|
-
elvalue = resolveNodeOrText(node.attributes.value, ctx),
|
|
584
|
-
eltype = resolveNodeOrText(node.attributes.type, ctx),
|
|
585
|
-
elname = resolveNodeOrText(node.attributes.name, ctx);
|
|
619
|
+
observe(() => {
|
|
620
|
+
const value = attr.value;
|
|
586
621
|
if (params[0]) {
|
|
587
622
|
if (value === "true") node.setAttribute(params[0], "")
|
|
588
623
|
else node.removeAttribute(params[0]);
|
|
589
|
-
} else
|
|
590
|
-
|
|
591
|
-
|
|
624
|
+
} else {
|
|
625
|
+
const elvalue = node.attributes.value ? resolveNodeOrText(node.attributes.value, ctx) : null,
|
|
626
|
+
eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx) : null;
|
|
627
|
+
if (eltype === "checkbox" || node.tagName === "OPTION") {
|
|
628
|
+
if (elvalue === "true") node.setAttribute("checked", "")
|
|
629
|
+
else node.removeAttribute("checked");
|
|
630
|
+
}
|
|
592
631
|
}
|
|
593
632
|
})
|
|
594
633
|
} else if (type === "l-on") {
|
|
595
634
|
let listener;
|
|
596
|
-
|
|
635
|
+
observe(() => {
|
|
597
636
|
const value = resolveNodeOrText(attr, this);
|
|
598
637
|
if (listener) node.removeEventListener(params[0], listener);
|
|
599
638
|
listener = this[value] || window[value] || Function(value);
|
|
600
639
|
addListener(node, params[0], listener);
|
|
601
640
|
})
|
|
602
641
|
} else if (type === "l-if") {
|
|
603
|
-
|
|
642
|
+
observe(() => {
|
|
604
643
|
const value = resolveNodeOrText(attr, this);
|
|
605
644
|
node.style.setProperty("display", value === "true" ? "revert" : "none");
|
|
606
645
|
})
|
|
607
646
|
} else if (type === "l-for") {
|
|
608
647
|
node.template ||= node.innerHTML;
|
|
609
|
-
|
|
648
|
+
observe(() => {
|
|
610
649
|
const [what = "each", vname = "item", index = "index", array = "array", after = false] = params,
|
|
611
650
|
value = resolveNodeOrText(attr, this),
|
|
612
651
|
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
613
652
|
target = what === "each" ? coerced : Object[what](coerced),
|
|
614
|
-
html = target.reduce((html, item, i, target) => {
|
|
615
|
-
return html += Function("vars","context", "with(vars) { with(context) { return `" + node.template + "` }}")(
|
|
653
|
+
html = target.reduce( (html, item, i, target) => {
|
|
654
|
+
return html += Function("vars", "context", "with(vars) { with(context) { return `" + node.template + "` }}")(
|
|
616
655
|
ctx.varsProxy,
|
|
617
656
|
{
|
|
618
657
|
[vname]: item,
|
|
@@ -633,61 +672,53 @@ const {observe} = (() => {
|
|
|
633
672
|
else node.appendChild(parsed.body.firstChild);
|
|
634
673
|
}
|
|
635
674
|
})
|
|
636
|
-
} else if
|
|
637
|
-
|
|
675
|
+
} else if(attr.template) {
|
|
676
|
+
observe(() => {
|
|
677
|
+
resolveNodeOrText(attr, this);
|
|
678
|
+
})
|
|
638
679
|
}
|
|
639
680
|
})
|
|
640
681
|
}
|
|
641
682
|
})
|
|
642
683
|
shadow.normalize();
|
|
643
|
-
observer
|
|
644
|
-
|
|
684
|
+
observer ||= createObserver(ctx, framed);
|
|
685
|
+
observer.observe(ctx, {attributeOldValue: true, subtree:true, characterData:true, characterDataOldValue:true});
|
|
686
|
+
ctx.vars.postEvent.value("connected");
|
|
645
687
|
})
|
|
646
688
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
this.adoptedCallback = value;
|
|
650
|
-
//Object.defineProperty(this, "adoptedCallback", {configurable: true, writable: true, value});
|
|
689
|
+
adoptedCallback(callback) {
|
|
690
|
+
this.vars.postEvent.value("adopted");
|
|
651
691
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
this.connectedCallback = callback;
|
|
655
|
-
//Object.defineProperty(this, "connectedCallback", {configurable: true, writable: true, value});
|
|
692
|
+
disconnectedCallback() {
|
|
693
|
+
this.vars.postEvent.value("disconnected");
|
|
656
694
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
this.attributeChangedCallback = callback;
|
|
660
|
-
//Object.defineProperty(this, "attributeChangedCallback", {configurable: true, writable: true, value});
|
|
695
|
+
get observedAttributes() {
|
|
696
|
+
return CustomElement.observedAttributes;
|
|
661
697
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
Object.defineProperty(this, "disconnectedCallback", {
|
|
665
|
-
configurable: true,
|
|
666
|
-
writable: true,
|
|
667
|
-
value: () => {
|
|
668
|
-
value();
|
|
669
|
-
super.disconnectedCallback(callback);
|
|
670
|
-
}
|
|
671
|
-
});
|
|
698
|
+
static get observedAttributes() {
|
|
699
|
+
return observedAttributes;
|
|
672
700
|
}
|
|
673
701
|
|
|
674
702
|
getVariableNames() {
|
|
675
|
-
return Object.keys(this.vars)
|
|
676
|
-
|
|
677
|
-
|
|
703
|
+
return Object.keys(this.vars)
|
|
704
|
+
.filter(name => !(name in reserved) && !["self", "addEventListener", "postEvent"].includes(name))
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
getVariable(name) {
|
|
708
|
+
return this.vars[name] ? {...this.vars[name]} : undefined;
|
|
678
709
|
}
|
|
679
710
|
|
|
680
|
-
|
|
711
|
+
setVariableValue(variableName, value, {coerceTo = typeof (value)} = {}) {
|
|
681
712
|
if (!this.isConnected) {
|
|
682
713
|
instances.delete(this);
|
|
683
714
|
return false;
|
|
684
715
|
}
|
|
685
716
|
let {type} = this.vars[variableName] || {};
|
|
686
717
|
if (type) {
|
|
687
|
-
value = coerce(value, type);
|
|
688
718
|
if (this.varsProxy[variableName] !== value) {
|
|
689
719
|
const variable = this.vars[variableName];
|
|
690
720
|
if (variable.shared) {
|
|
721
|
+
value = type.validate ? type.validate(value,variable) : coerce(value,coerceTo);
|
|
691
722
|
const event = new VariableEvent({
|
|
692
723
|
variableName: variableName,
|
|
693
724
|
value: value,
|
|
@@ -706,20 +737,29 @@ const {observe} = (() => {
|
|
|
706
737
|
return false;
|
|
707
738
|
}
|
|
708
739
|
|
|
709
|
-
|
|
740
|
+
getVariableValue(variableName) {
|
|
710
741
|
return this.vars[variableName]?.value;
|
|
711
742
|
}
|
|
712
743
|
|
|
713
|
-
variables(variables, {observed, reactive, shared, exported, imported} = {}) { // options = {observed,reactive,shared,exported,imported}
|
|
744
|
+
variables(variables, {observed, reactive, shared, exported, imported, remote, constant,set} = {}) { // options = {observed,reactive,shared,exported,imported}
|
|
714
745
|
const addEventListener = this.varsProxy.addEventListener;
|
|
715
746
|
if (variables !== undefined) {
|
|
716
747
|
Object.entries(variables)
|
|
717
748
|
.forEach(([key, type]) => {
|
|
718
749
|
const variable = this.vars[key] ||= {name: key, type};
|
|
750
|
+
if(set!==undefined && constant!==undefined) throw new TypeError(`${key} has the constant value ${constant} and can't be set to ${set}`);
|
|
751
|
+
variable.value = set;
|
|
752
|
+
if(constant!==undefined) {
|
|
753
|
+
variable.constant = true;
|
|
754
|
+
variable.value = constant;
|
|
755
|
+
}
|
|
719
756
|
if (observed || imported) {
|
|
720
757
|
variable.value = this.hasAttribute(key) ? coerce(this.getAttribute(key), variable.type) : variable.value;
|
|
721
|
-
variable.observed = observed;
|
|
722
758
|
variable.imported = imported;
|
|
759
|
+
if(variable.observed) {
|
|
760
|
+
variable.observed = observed;
|
|
761
|
+
this.observedAttributes.add(key);
|
|
762
|
+
}
|
|
723
763
|
}
|
|
724
764
|
if (reactive) {
|
|
725
765
|
variable.reactive = true;
|
|
@@ -728,21 +768,21 @@ const {observe} = (() => {
|
|
|
728
768
|
if (shared) {
|
|
729
769
|
variable.shared = true;
|
|
730
770
|
addEventListener("change", ({variableName, value}) => {
|
|
731
|
-
if (this.vars[variableName]?.shared)
|
|
732
|
-
this.siblings.forEach((instance) => instance.setValue(variableName, value))
|
|
733
|
-
}
|
|
771
|
+
if (this.vars[variableName]?.shared) this.siblings.forEach((instance) => instance.setVariableValue(variableName, value))
|
|
734
772
|
})
|
|
735
773
|
}
|
|
736
774
|
if (exported) {
|
|
737
775
|
variable.exported = true;
|
|
738
776
|
// in case the export goes up to an iframe
|
|
739
777
|
if (variable.value != null) setComponentAttribute(this, key, variable.value);
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
778
|
+
this.changeListener.targets.add(key);
|
|
779
|
+
}
|
|
780
|
+
if (remote) {
|
|
781
|
+
if(typeof(remote)==="function") remote = remote(`./${key}`);
|
|
782
|
+
variable.remote = remote;
|
|
783
|
+
remote.handleRemote({variable, config:remote.config, reactive,component:this});
|
|
745
784
|
}
|
|
785
|
+
if(type.validate) type.validate(variable.value,variable);
|
|
746
786
|
});
|
|
747
787
|
}
|
|
748
788
|
return Object.entries(this.vars)
|
|
@@ -751,44 +791,23 @@ const {observe} = (() => {
|
|
|
751
791
|
return result;
|
|
752
792
|
}, {});
|
|
753
793
|
}
|
|
754
|
-
|
|
755
|
-
constants(variables) {
|
|
756
|
-
if (variables !== undefined) {
|
|
757
|
-
Object.entries(variables)
|
|
758
|
-
.forEach(([key, value]) => {
|
|
759
|
-
const type = typeof (value) === "function" ? value : typeof (value),
|
|
760
|
-
variable = this.vars[key];
|
|
761
|
-
if (variable !== undefined) throw new TypeError(`${variable.constant ? "const" : "let"} ${key}:${variable.type} already declared.`);
|
|
762
|
-
if (value === undefined) throw new TypeError(`const ${key}:undefined must be initialized.`);
|
|
763
|
-
this.vars[key] = {type, value, constant: true};
|
|
764
|
-
})
|
|
765
|
-
}
|
|
766
|
-
return Object.entries(this.vars)
|
|
767
|
-
.reduce((result, [key, variable]) => {
|
|
768
|
-
if (variable.constant) result[key] = {...variable};
|
|
769
|
-
return result;
|
|
770
|
-
}, {});
|
|
771
|
-
}
|
|
772
794
|
}
|
|
773
795
|
}
|
|
774
|
-
|
|
796
|
+
|
|
797
|
+
const createComponent = (name, node, {framed, observer} = {}) => {
|
|
775
798
|
let ctor = customElements.get(name);
|
|
776
799
|
if (ctor) {
|
|
777
|
-
if (framed && !ctor.lightviewFramed)
|
|
778
|
-
|
|
779
|
-
} else {
|
|
780
|
-
console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
|
|
781
|
-
}
|
|
800
|
+
if (framed && !ctor.lightviewFramed) ctor.lightviewFramed = true;
|
|
801
|
+
else console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
|
|
782
802
|
return ctor;
|
|
783
803
|
}
|
|
784
804
|
ctor = createClass(node, {observer, framed});
|
|
785
805
|
customElements.define(name, ctor);
|
|
786
|
-
Lightview.customElements.set(name,ctor);
|
|
806
|
+
Lightview.customElements.set(name, ctor);
|
|
787
807
|
return ctor;
|
|
788
808
|
}
|
|
789
809
|
Lightview.customElements = new Map();
|
|
790
810
|
Lightview.createComponent = createComponent;
|
|
791
|
-
//Object.defineProperty(Lightview, "createComponent", {writable: true, configurable: true, value: createComponent})
|
|
792
811
|
const importLink = async (link, observer) => {
|
|
793
812
|
const url = (new URL(link.getAttribute("href"), window.location.href)),
|
|
794
813
|
as = link.getAttribute("as") || getNameFromPath(url.pathname);
|
|
@@ -800,12 +819,12 @@ const {observe} = (() => {
|
|
|
800
819
|
dom = parser.parseFromString(html, "text/html"),
|
|
801
820
|
unhide = !!dom.head.querySelector('meta[name="l-unhide"]'),
|
|
802
821
|
links = dom.head.querySelectorAll('link[href$=".html"][rel=module]');
|
|
803
|
-
for(const childlink of links) {
|
|
822
|
+
for (const childlink of links) {
|
|
804
823
|
const href = childlink.getAttribute("href"),
|
|
805
|
-
childurl = new URL(href,url.href);
|
|
806
|
-
childlink.setAttribute("href",childurl.href);
|
|
807
|
-
if(link.hasAttribute("crossorigin")) childlink.setAttribute("crossorigin",link.getAttribute("crossorigin"))
|
|
808
|
-
await importLink(childlink,observer);
|
|
824
|
+
childurl = new URL(href, url.href);
|
|
825
|
+
childlink.setAttribute("href", childurl.href);
|
|
826
|
+
if (link.hasAttribute("crossorigin")) childlink.setAttribute("crossorigin", link.getAttribute("crossorigin"))
|
|
827
|
+
await importLink(childlink, observer);
|
|
809
828
|
}
|
|
810
829
|
if (unhide) dom.body.removeAttribute("hidden");
|
|
811
830
|
createComponent(as, dom.body, {observer});
|
|
@@ -815,7 +834,7 @@ const {observe} = (() => {
|
|
|
815
834
|
const importLinks = async () => {
|
|
816
835
|
const observer = createObserver(document.body);
|
|
817
836
|
for (const link of [...document.querySelectorAll(`link[href$=".html"][rel=module]`)]) {
|
|
818
|
-
await importLink(link,observer);
|
|
837
|
+
await importLink(link, observer);
|
|
819
838
|
}
|
|
820
839
|
}
|
|
821
840
|
|
|
@@ -883,21 +902,19 @@ const {observe} = (() => {
|
|
|
883
902
|
postMessage({type: "setAttribute", argsList: ["height", height + 20]});
|
|
884
903
|
}
|
|
885
904
|
resize();
|
|
886
|
-
onresize(document.body, () =>
|
|
887
|
-
resize();
|
|
888
|
-
});
|
|
905
|
+
onresize(document.body, () => resize());
|
|
889
906
|
return
|
|
890
907
|
}
|
|
891
908
|
if (type === "setAttribute") {
|
|
892
909
|
const [name, value] = [...argsList],
|
|
893
910
|
variable = document.body.vars[name];
|
|
894
|
-
if (variable && variable.imported) document.body.
|
|
911
|
+
if (variable && variable.imported) document.body.setVariableValue(name, value);
|
|
895
912
|
return;
|
|
896
913
|
}
|
|
897
914
|
if (type === "removeAttribute") {
|
|
898
915
|
const [name] = argsList[0],
|
|
899
916
|
variable = document.body.vars[name];
|
|
900
|
-
if (variable && variable.imported) document.body.
|
|
917
|
+
if (variable && variable.imported) document.body.setVariableValue(name, undefined);
|
|
901
918
|
|
|
902
919
|
}
|
|
903
920
|
});
|
|
@@ -935,9 +952,7 @@ const {observe} = (() => {
|
|
|
935
952
|
}
|
|
936
953
|
if (type === "setAttribute") {
|
|
937
954
|
const [name, value] = [...argsList];
|
|
938
|
-
if (iframe.getAttribute(name) !== value + "")
|
|
939
|
-
iframe.setAttribute(name, value);
|
|
940
|
-
}
|
|
955
|
+
if (iframe.getAttribute(name) !== value + "") iframe.setAttribute(name, value);
|
|
941
956
|
return;
|
|
942
957
|
}
|
|
943
958
|
if (type === "removeAttribute") {
|
|
@@ -985,7 +1000,7 @@ const {observe} = (() => {
|
|
|
985
1000
|
addListener(document, "DOMContentLoaded", (event) => loader(callback));
|
|
986
1001
|
}
|
|
987
1002
|
Lightview.whenFramed = whenFramed;
|
|
988
|
-
|
|
1003
|
+
|
|
989
1004
|
if (window.location === window.parent.location || !(window.parent instanceof Window) || window.parent !== window) {
|
|
990
1005
|
// loads for unframed content
|
|
991
1006
|
// CodePen mucks with window.parent
|