lightview 1.7.2-b → 1.8.2
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 +15 -16
- package/docs/CNAME +1 -0
- package/docs/api.html +674 -0
- package/docs/blank.html +10 -0
- package/docs/comparedto.html +89 -0
- package/docs/components/chart-repl.html +69 -0
- package/docs/components/chart.html +17 -0
- package/{components → docs/components}/components.js +31 -11
- package/docs/components/contents.html +17 -0
- package/docs/components/gantt-repl.html +61 -0
- package/docs/components/gantt.html +42 -0
- package/docs/components/gauge-repl.html +66 -0
- package/docs/components/gauge.html +20 -0
- package/docs/components/orgchart-repl.html +64 -0
- package/docs/components/orgchart.html +41 -0
- package/docs/components/repl-as-src.html +17 -0
- package/docs/components/repl-repl.html +95 -0
- package/docs/components/repl.html +527 -0
- package/docs/components/timeline-repl.html +72 -0
- package/docs/components/timeline.html +44 -0
- package/docs/components.html +14 -0
- package/docs/css/highlightjs.min.css +9 -0
- package/docs/css/tutorial.css +35 -0
- package/docs/examples/anchor.html +11 -0
- package/{components/chart/example.html → docs/examples/chart.html} +6 -4
- package/docs/examples/counter.html +26 -0
- package/{examples → docs/examples}/counter.test.mjs +0 -0
- package/docs/examples/counter2.html +26 -0
- package/{examples → docs/examples}/directives.html +20 -18
- package/{examples → docs/examples}/foreign.html +1 -1
- package/docs/examples/forgeinform.html +98 -0
- package/docs/examples/form.html +61 -0
- package/{examples → docs/examples}/gauge.html +4 -2
- package/{examples → docs/examples}/invalid-template-literals.html +6 -4
- package/{examples → docs/examples}/medium/remote.html +1 -1
- package/docs/examples/message.html +18 -0
- package/{examples → docs/examples}/nested.html +2 -2
- package/docs/examples/object-bound-form.html +34 -0
- package/{examples → docs/examples}/remote-server.js +0 -0
- package/docs/examples/remote.html +34 -0
- package/{examples → docs/examples}/remote.json +0 -0
- package/{examples → docs/examples}/scratch.html +1 -1
- package/docs/examples/sensors/index.html +44 -0
- package/{examples → docs/examples}/sensors/sensor-server.js +0 -0
- package/docs/examples/shared.html +41 -0
- package/{examples → docs/examples}/template.html +1 -1
- package/{examples → docs/examples}/timeline.html +2 -2
- package/docs/examples/todo.html +40 -0
- package/docs/examples/top.html +10 -0
- package/{examples → docs/examples}/types.html +1 -1
- package/{examples → docs/examples}/xor.html +22 -20
- package/docs/examples.html +25 -0
- package/docs/index.html +44 -0
- package/docs/javascript/codejar.min.js +8 -0
- package/docs/javascript/highlightjs.min.js +1173 -0
- package/docs/javascript/isomorphic-git.js +9 -0
- package/docs/javascript/json5.min.js +1 -0
- package/docs/javascript/lightning-fs.js +1 -0
- package/docs/javascript/lightview.js +1285 -0
- package/docs/javascript/marked.min.js +6 -0
- package/docs/javascript/peerjs.min.js +70 -0
- package/docs/javascript/turndown.js +973 -0
- package/docs/javascript/types.js +606 -0
- package/docs/javascript/utils.js +45 -0
- package/docs/lightview.html +63 -0
- package/docs/old_index.html +965 -0
- package/docs/old_index.md +1132 -0
- package/docs/slidein.html +51 -0
- package/docs/tutorial/0-getting-started.html +67 -0
- package/docs/tutorial/1-intro-to-variables.html +103 -0
- package/docs/tutorial/10-template-components.html +80 -0
- package/docs/tutorial/11-linked-components.html +76 -0
- package/docs/tutorial/12-imported-components.html +67 -0
- package/docs/tutorial/13-input-binding.html +94 -0
- package/docs/tutorial/14-automatic-variable-creation.html +74 -0
- package/docs/tutorial/15-form-binding.html +110 -0
- package/docs/tutorial/16-if-directive.html +60 -0
- package/docs/tutorial/17-loop-directives.html +83 -0
- package/docs/tutorial/18-sanitizing-and-escaping-input.html +79 -0
- package/docs/tutorial/2-imported-and-exported-variables.html +80 -0
- package/docs/tutorial/3-data-types.html +89 -0
- package/docs/tutorial/4-extended-data-types.html +83 -0
- package/docs/tutorial/5-extended-functional-types.html +96 -0
- package/docs/tutorial/5.1-extended-functional-types.html +79 -0
- package/docs/tutorial/5.2-extended-functional-types.html +70 -0
- package/docs/tutorial/6-conventional-javascript.html +75 -0
- package/docs/tutorial/7-monitoring-with-observers.html +107 -0
- package/docs/tutorial/8-event-listeners.html +65 -0
- package/docs/tutorial/9-intro-to-components.html +91 -0
- package/docs/tutorial/contents.html +32 -0
- package/docs/tutorial/my-component.html +29 -0
- package/docs/tutorial/remote-value.json +4 -0
- package/docs/websiterepl.html +46 -0
- package/lightview.js +499 -363
- package/lightview.min.js +1 -0
- package/lightview_good.js +1267 -0
- package/lightview_optimized.js +1274 -0
- package/package.json +7 -2
- package/repl_hold.html +320 -0
- package/test/basic.html +29 -17
- package/test/basic.test.mjs +1 -11
- package/test/extended.html +16 -19
- package/types.js +118 -36
- package/unsplash.key +1 -0
- package/components/chart/chart.html +0 -15
- package/components/chart.html +0 -81
- package/components/gantt/example.html +0 -27
- package/components/gantt/gantt.html +0 -34
- package/components/gauge/example.html +0 -28
- package/components/gauge/guage.html +0 -19
- package/components/gauge.html +0 -57
- package/components/timeline.html +0 -81
- package/examples/chart.html +0 -66
- package/examples/counter.html +0 -24
- package/examples/forgeinform.html +0 -96
- package/examples/form.html +0 -59
- package/examples/message.html +0 -12
- package/examples/object-bound-form.html +0 -32
- package/examples/remote.html +0 -32
- package/examples/sensors/index.html +0 -30
- package/examples/todo.html +0 -38
- package/examples/top.html +0 -10
package/lightview.js
CHANGED
|
@@ -28,10 +28,12 @@ SOFTWARE.
|
|
|
28
28
|
imported(x) => exported(x) => reactive(x) => remote(x,{path:".
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
var currentComponent = window.currentComponent = null;
|
|
32
|
+
if(document.body) currentComponent = window.currentComponent = document.currentComponent = document.body;
|
|
33
|
+
var Lightview = window.Lightview = { };
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
let CURRENTOBSERVER;
|
|
35
|
+
var {observe} = (() => {
|
|
36
|
+
let CURRENTOBSERVER;//, CURRENTNODE;
|
|
35
37
|
const parser = new DOMParser();
|
|
36
38
|
|
|
37
39
|
const templateSanitizer = (string) => {
|
|
@@ -44,19 +46,19 @@ const {observe} = (() => {
|
|
|
44
46
|
Lightview.sanitizeTemplate = templateSanitizer;
|
|
45
47
|
|
|
46
48
|
const escaper = document.createElement('textarea');
|
|
47
|
-
|
|
49
|
+
const escapeHTML = html => {
|
|
48
50
|
escaper.textContent = html;
|
|
49
51
|
return escaper.innerHTML;
|
|
50
52
|
}
|
|
51
53
|
Lightview.escapeHTML = escapeHTML;
|
|
52
54
|
|
|
53
|
-
const isArrowFunction =
|
|
55
|
+
const isArrowFunction = f => typeof(f)==="function" && (f+"").match(/\(*.*\)*\s*=>/g);
|
|
54
56
|
|
|
55
|
-
const getTemplateVariableName =
|
|
56
|
-
if(template && /^\$\{[a-zA-
|
|
57
|
+
const getTemplateVariableName = template => {
|
|
58
|
+
if(template && !template.includes("[") && /^\$\{[a-zA-z_0-9.]*\}$/g.test(template)) return template.substring(2, template.length - 1);
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
const walk = (target,path,depth=path.length-1,create) => {
|
|
61
|
+
const walk = (target,path,{depth=path.length-1,create}={}) => {
|
|
60
62
|
for(let i=0;i<=depth;i++) {
|
|
61
63
|
target = (target[path[i]]==null && create ? target[path[i]] = (typeof(create)==="function" ? Object.create(create.prototype) : {}) : target[path[i]]);
|
|
62
64
|
if(target===undefined) return;
|
|
@@ -66,12 +68,14 @@ const {observe} = (() => {
|
|
|
66
68
|
|
|
67
69
|
const addListener = (node, eventName, callback, self) => {
|
|
68
70
|
node.addEventListener(eventName, (event) => {
|
|
69
|
-
|
|
71
|
+
// todo shopuld self be currentComponent or component
|
|
72
|
+
if(self) Object.defineProperty(event,"self",{value:self});
|
|
70
73
|
callback(event);
|
|
71
74
|
});
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
// imports a component based on an anchor and replace content of a target node with an instance of the component
|
|
78
|
+
const anchorHandler = async event => {
|
|
75
79
|
event.preventDefault();
|
|
76
80
|
const target = event.target;
|
|
77
81
|
if (target === event.currentTarget) {
|
|
@@ -83,14 +87,15 @@ const {observe} = (() => {
|
|
|
83
87
|
})
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
|
-
const getNameFromPath =
|
|
90
|
+
const getNameFromPath = path => {
|
|
87
91
|
const file = path.split("/").pop(),
|
|
88
|
-
name = file.split(".")[0]
|
|
89
|
-
|
|
92
|
+
name = file.split(".")[0],
|
|
93
|
+
parts = name.split("-");
|
|
94
|
+
if (name.includes("-") && parts[0].length>=1) return name;
|
|
90
95
|
return "l-" + name;
|
|
91
96
|
}
|
|
92
97
|
const observe = (f, thisArg, argsList = []) => {
|
|
93
|
-
|
|
98
|
+
let observer = (...args) => {
|
|
94
99
|
if(observer.cancelled) return;
|
|
95
100
|
CURRENTOBSERVER = observer;
|
|
96
101
|
try {
|
|
@@ -100,25 +105,27 @@ const {observe} = (() => {
|
|
|
100
105
|
}
|
|
101
106
|
CURRENTOBSERVER = null;
|
|
102
107
|
}
|
|
103
|
-
observer.cancel = () =>
|
|
108
|
+
observer.cancel = () => {
|
|
109
|
+
observer.cancelled = true;
|
|
110
|
+
return observer = null;
|
|
111
|
+
}
|
|
104
112
|
observer();
|
|
105
113
|
return observer;
|
|
106
114
|
}
|
|
107
115
|
const coerce = (value, toType) => {
|
|
108
|
-
if (
|
|
116
|
+
if (["null","undefined"].includes(value + "") || toType==="any") return value;
|
|
109
117
|
const type = typeof (value);
|
|
110
118
|
if (type === toType) return value;
|
|
111
|
-
if (toType === "number")
|
|
119
|
+
if (toType === "number") {
|
|
120
|
+
value = value + "";
|
|
121
|
+
if(value==="NaN") return NaN;
|
|
122
|
+
const result = parseFloat(value);
|
|
123
|
+
if(isNaN(result)) throw new TypeError(`Unable to coerce '${value}' to 'number'`)
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
112
126
|
if (toType === "boolean") {
|
|
113
|
-
if (["on", "checked", "selected"].includes(value)) return true;
|
|
114
|
-
if (
|
|
115
|
-
try {
|
|
116
|
-
const parsed = JSON.parse(value + "");
|
|
117
|
-
if (typeof (parsed) === "boolean") return parsed;
|
|
118
|
-
return [1, "on", "checked", "selected"].includes(parsed);
|
|
119
|
-
} catch (e) {
|
|
120
|
-
throw new TypeError(`Unable to convert ${value} into 'boolean'`);
|
|
121
|
-
}
|
|
127
|
+
if ([1,"on", "checked", "selected","true"].includes(value)) return true;
|
|
128
|
+
if ([null,"",0,"false"].includes(value)) return false;
|
|
122
129
|
}
|
|
123
130
|
if (toType === "string") return value + "";
|
|
124
131
|
const isfunction = typeof (toType) === "function";
|
|
@@ -139,7 +146,7 @@ const {observe} = (() => {
|
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
if (!Array.isArray(parsed)) {
|
|
142
|
-
throw new TypeError(`Expected
|
|
149
|
+
throw new TypeError(`Expected Array for parsed data ${parsed}:${typeof(parsed)}`)
|
|
143
150
|
}
|
|
144
151
|
instance.push(...parsed);
|
|
145
152
|
} else if (instance instanceof Date) {
|
|
@@ -147,55 +154,47 @@ const {observe} = (() => {
|
|
|
147
154
|
} else {
|
|
148
155
|
Object.assign(instance, JSON.parse(value));
|
|
149
156
|
}
|
|
150
|
-
if (toType !== Date) {
|
|
151
|
-
Object.defineProperty(instance, "constructor", {
|
|
152
|
-
configurable: true,
|
|
153
|
-
writable: true,
|
|
154
|
-
value: toType.prototype.constructor || toType
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
157
|
return instance;
|
|
158
158
|
}
|
|
159
159
|
return JSON.parse(value);
|
|
160
160
|
} catch (e) {
|
|
161
|
-
throw new TypeError(`Unable to
|
|
161
|
+
throw new TypeError(`Unable to coerce '${value}:${type}' to '${isfunction ? toType.name : type}'`);
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
-
throw new TypeError(`Unable to coerce ${value} to ${toType}`)
|
|
165
|
+
throw new TypeError(`Unable to coerce '${value}:${type}' to '${toType}'`)
|
|
166
166
|
}
|
|
167
|
-
const Reactor =
|
|
167
|
+
const Reactor = data => {
|
|
168
168
|
if (data && typeof (data) === "object") {
|
|
169
169
|
if (data.__isReactor__) return data;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
let dependents = {};
|
|
171
|
+
const childReactors = new Set(),
|
|
172
|
+
//nodes = new Set(),
|
|
173
|
+
{proxy,revoke} = Proxy.revocable(data, {
|
|
173
174
|
get(target, property) {
|
|
174
175
|
if (property === "__isReactor__") return true;
|
|
175
|
-
if(property=== "
|
|
176
|
-
|
|
176
|
+
if (property === "revoke") return () => {
|
|
177
|
+
dependents = {};
|
|
178
|
+
childReactors.forEach((reactor) => reactor.revoke());
|
|
179
|
+
revoke();
|
|
180
|
+
};
|
|
177
181
|
if (target instanceof Array) {
|
|
178
|
-
if (property === "toJSON") return function toJSON() { return [...target]
|
|
179
|
-
if (property === "toString") return function toString() { return JSON.stringify([...target])
|
|
182
|
+
if (property === "toJSON") return function toJSON() { return [...target] }
|
|
183
|
+
if (property === "toString") return function toString() { return JSON.stringify([...target]) }
|
|
184
|
+
}
|
|
185
|
+
if(target instanceof Date) {
|
|
186
|
+
const value = data[property];
|
|
187
|
+
if(typeof(value)==="function") return value.bind(data);
|
|
180
188
|
}
|
|
181
|
-
if(target instanceof Date) return Reflect.get(target,property);
|
|
182
189
|
let value = target[property];
|
|
190
|
+
if(typeof (property) === "symbol") return value;
|
|
183
191
|
const type = typeof (value);
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
observers.add(CURRENTOBSERVER)
|
|
187
|
-
}
|
|
192
|
+
if (type === "function") return ([Date].includes(value) || property==="then") ? value.bind(target) : value;
|
|
193
|
+
if (CURRENTOBSERVER) (dependents[property] ||= new Set()).add(CURRENTOBSERVER)
|
|
188
194
|
if(value===undefined) return;
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return value;
|
|
193
|
-
}
|
|
194
|
-
if (value && type === "object") {
|
|
195
|
-
value = Reactor(value);
|
|
196
|
-
childReactors.push(value);
|
|
197
|
-
}
|
|
198
|
-
target[property] = value;
|
|
195
|
+
if (value==null || type !== "object" || childReactors.has(value)) return value;
|
|
196
|
+
value = target[property] = Reactor(value);
|
|
197
|
+
childReactors.add(value);
|
|
199
198
|
return value;
|
|
200
199
|
},
|
|
201
200
|
async set(target, property, value) {
|
|
@@ -207,11 +206,10 @@ const {observe} = (() => {
|
|
|
207
206
|
if (target[property] !== value) {
|
|
208
207
|
if (value && type === "object") {
|
|
209
208
|
value = Reactor(value);
|
|
210
|
-
childReactors.
|
|
209
|
+
childReactors.add(value);
|
|
211
210
|
}
|
|
212
211
|
target[property] = value;
|
|
213
|
-
|
|
214
|
-
[...observers].forEach((f) => {
|
|
212
|
+
[...dependents[property] || []].forEach((f) => { // handle dependent observers
|
|
215
213
|
if (f.cancelled) dependents[property].delete(f);
|
|
216
214
|
else f();
|
|
217
215
|
})
|
|
@@ -219,28 +217,26 @@ const {observe} = (() => {
|
|
|
219
217
|
return true;
|
|
220
218
|
}
|
|
221
219
|
});
|
|
220
|
+
//Object.entries(proxy).forEach(([key,value]) => proxy[key] = value);
|
|
222
221
|
return proxy;
|
|
223
222
|
}
|
|
224
223
|
return data;
|
|
225
224
|
}
|
|
226
225
|
|
|
227
|
-
class VariableEvent {
|
|
228
|
-
constructor(config) {
|
|
229
|
-
Object.assign(this, config);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
226
|
const createVarsProxy = (vars, component, constructor) => {
|
|
234
|
-
|
|
227
|
+
const {proxy,revoke} = Proxy.revocable(vars, {
|
|
235
228
|
get(target, property) {
|
|
229
|
+
if(property==="self") return component;
|
|
230
|
+
if(property === "revoke") return revoke;
|
|
236
231
|
if(target instanceof Date) return Reflect.get(target,property);
|
|
232
|
+
if(typeof(property)==="symbol") return target[property];
|
|
237
233
|
let {value,get} = target[property] || {};
|
|
238
234
|
if(get) return target[property].value = get.call(target[property]);
|
|
239
235
|
if (typeof (value) === "function") return value.bind(target);
|
|
240
236
|
return value;
|
|
241
237
|
},
|
|
242
238
|
set(target, property, newValue) {
|
|
243
|
-
const event =
|
|
239
|
+
const event = {variableName: property, value: newValue};
|
|
244
240
|
if (target[property] === undefined) {
|
|
245
241
|
target[property] = {type: "any", value: newValue}; // should we allow this, do first to prevent loops
|
|
246
242
|
target.postEvent.value("change", event);
|
|
@@ -250,7 +246,14 @@ const {observe} = (() => {
|
|
|
250
246
|
const variable = target[property],
|
|
251
247
|
{value, shared, exported, constant, reactive, remote} = variable;
|
|
252
248
|
let type = variable.type;
|
|
253
|
-
if (constant)
|
|
249
|
+
if (constant) {
|
|
250
|
+
const error = new TypeError(`${property}:${type} is a constant`);
|
|
251
|
+
if(Lightview.renderErrors) {
|
|
252
|
+
variable.value = error.toString();
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
254
257
|
if(newValue!=null || type.required) newValue = type.validate ? type.validate(newValue,target[property]) : coerce(newValue,type);
|
|
255
258
|
const newtype = typeof (newValue),
|
|
256
259
|
typetype = typeof (type);
|
|
@@ -260,24 +263,36 @@ const {observe} = (() => {
|
|
|
260
263
|
(typetype === "function" && !type.validate && (newValue && newtype === "object" && newValue instanceof type) || variable.validityState?.valid)) {
|
|
261
264
|
if (value !== newValue) {
|
|
262
265
|
event.oldValue = value;
|
|
263
|
-
|
|
266
|
+
variable.value = reactive && !variable.__isReactor__ ? Reactor(newValue) : newValue; // do first to prevent loops
|
|
264
267
|
target.postEvent.value("change", event);
|
|
265
268
|
if (event.defaultPrevented) target[property].value = value;
|
|
266
|
-
else if(remote && (variable.reactive || remote.put)) remote.handleRemote({variable,config:remote
|
|
269
|
+
else if(remote && ((variable.reactive && variable?.remote?.config.put!==false) || remote.put)) remote.handleRemote({variable,config: variable?.remote?.config},true);
|
|
267
270
|
else if(variable.set) variable.set(newValue);
|
|
268
271
|
}
|
|
269
272
|
return true;
|
|
270
273
|
}
|
|
271
274
|
if (typetype === "function" && newValue && newtype === "object") {
|
|
272
|
-
|
|
275
|
+
const error = new TypeError(`Can't assign instance of '${newValue.constructor.name}' to variable '${property}:${type.name.replace("bound ", "")}'`)
|
|
276
|
+
if(Lightview.renderErrors) {
|
|
277
|
+
variable.value = error.toString();
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
const error = new TypeError(`Can't assign '${typeof (newValue)} ${newtype === "string" ? '"' + newValue + '"' : newValue}' to variable '${property}:${typetype === "function" ? type.name.replace("bound ", "") : type} ${type.required ? "required" : ""}'`)
|
|
283
|
+
if(Lightview.renderErrors) {
|
|
284
|
+
variable.value = error.toString();
|
|
285
|
+
return true;
|
|
273
286
|
}
|
|
274
|
-
throw
|
|
287
|
+
throw error;
|
|
275
288
|
},
|
|
276
289
|
keys() {
|
|
277
290
|
return [...Object.keys(vars)];
|
|
278
291
|
}
|
|
279
292
|
});
|
|
293
|
+
return proxy;
|
|
280
294
|
}
|
|
295
|
+
// this is a DOM observer, not an observer of the observer programming paradigm
|
|
281
296
|
const createObserver = (domNode, framed) => {
|
|
282
297
|
const mobserver = new MutationObserver((mutations) => {
|
|
283
298
|
mutations.forEach((mutation) => {
|
|
@@ -296,6 +311,7 @@ const {observe} = (() => {
|
|
|
296
311
|
}
|
|
297
312
|
} else if (mutation.type === "childList") {
|
|
298
313
|
for (const target of mutation.removedNodes) {
|
|
314
|
+
//target.lightviewProxies?.forEach((proxy) => proxy.forgetNode(target));
|
|
299
315
|
if (target.disconnectedCallback) target.disconnectedCallback();
|
|
300
316
|
}
|
|
301
317
|
for (const target of mutation.addedNodes) {
|
|
@@ -318,77 +334,109 @@ const {observe} = (() => {
|
|
|
318
334
|
}
|
|
319
335
|
return nodes;
|
|
320
336
|
}
|
|
321
|
-
const getNodes = (root) => {
|
|
322
|
-
const nodes = new Set();
|
|
337
|
+
const getNodes = (root,href,nodes = new Set()) => {
|
|
323
338
|
if (root.shadowRoot) {
|
|
324
339
|
nodes.add(root);
|
|
325
|
-
getNodes(root.shadowRoot
|
|
340
|
+
getNodes(root.shadowRoot,href,nodes);
|
|
326
341
|
} else {
|
|
327
342
|
for (const node of root.childNodes) {
|
|
328
|
-
if (node.
|
|
329
|
-
if (node.nodeType === Node.TEXT_NODE && node.nodeValue?.includes("${")) {
|
|
343
|
+
if (node.nodeType === Node.TEXT_NODE && (node.template || node.nodeValue?.includes("${"))) {
|
|
330
344
|
node.template ||= node.nodeValue;
|
|
331
345
|
nodes.add(node);
|
|
332
346
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
333
347
|
let skip;
|
|
334
|
-
|
|
335
|
-
if (attr.value.includes("${")) {
|
|
348
|
+
for(const attr of node.attributes) {
|
|
349
|
+
if (attr.template || attr.value.includes("${")) {
|
|
336
350
|
attr.template ||= attr.value;
|
|
337
351
|
nodes.add(node);
|
|
338
|
-
} else if (attr.name.
|
|
352
|
+
} else if (attr.name.startsWith("l-") || attr.name.includes(":")) {
|
|
339
353
|
skip = attr.name.includes("l-for:");
|
|
340
|
-
nodes.add(node)
|
|
354
|
+
nodes.add(node);
|
|
341
355
|
}
|
|
342
|
-
}
|
|
356
|
+
}
|
|
343
357
|
if (node.getAttribute("type") === "radio") nodes.add(node);
|
|
344
|
-
if (!skip && !node.shadowRoot) getNodes(node
|
|
358
|
+
if (!skip && !node.shadowRoot) getNodes(node,href,nodes);
|
|
345
359
|
}
|
|
346
360
|
}
|
|
347
361
|
}
|
|
348
362
|
return nodes;
|
|
349
363
|
}
|
|
350
364
|
|
|
351
|
-
|
|
365
|
+
// populate a string literal template and turn objects into strings
|
|
366
|
+
const populateTemplate = (strings,...values) => {
|
|
367
|
+
values = values.map((value) => {
|
|
368
|
+
const type = typeof(value);
|
|
369
|
+
try {
|
|
370
|
+
if(value && type==="object") return JSON.stringify(value);
|
|
371
|
+
return value;
|
|
372
|
+
} catch(e) {
|
|
373
|
+
return e.toString();
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
return strings.reduce((result,string,index) => index < values.length ? result += string + values[index] : result + string,"");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const resolveNodeOrText = (node, component, safe,extras={},skipSetAttribute) => {
|
|
352
380
|
const type = typeof (node),
|
|
353
381
|
template = type === "string" ? node.trim() : node.template;
|
|
382
|
+
extras.populateTemplate = populateTemplate;
|
|
354
383
|
if (template) {
|
|
355
384
|
const name = getTemplateVariableName(template);
|
|
356
385
|
try {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
386
|
+
const parts = name ? name.split(".") : null;
|
|
387
|
+
let value;
|
|
388
|
+
value = (parts
|
|
389
|
+
? (value = walk(extras,parts)) || (value = walk(component.varsProxy,parts)) || (value == null ? component[name] : value)
|
|
390
|
+
: Function("context", "extras", "with(context) { with(extras) { return populateTemplate`" + (safe ? template : Lightview.sanitizeTemplate(template)) + "` } }")(component.varsProxy,extras));
|
|
360
391
|
//let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(template) + "` }")(component.varsProxy);
|
|
361
392
|
if(typeof(value)==="function") return value;
|
|
362
393
|
value = (name || node.nodeType === Node.TEXT_NODE || safe ? value : Lightview.escapeHTML(value));
|
|
363
394
|
if (type === "string") return value==="undefined" ? undefined : value;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
node.nodeValue = value
|
|
395
|
+
|
|
396
|
+
if(!(skipSetAttribute && node.nodeType===Node.ATTRIBUTE_NODE)) {
|
|
397
|
+
//requestAnimationFrame(() => {
|
|
398
|
+
if(name) node.nodeValue = value==null ? "" : typeof(value)==="string" ? value : JSON.stringify(value);
|
|
399
|
+
else node.nodeValue = value == "null" || value == "undefined" ? "" : value;
|
|
400
|
+
//})
|
|
368
401
|
}
|
|
369
402
|
return value;
|
|
370
403
|
} catch (e) {
|
|
371
404
|
//console.warn(e);
|
|
372
|
-
if (!e.message.includes("defined"))
|
|
405
|
+
if (!e.message.includes("defined")) {
|
|
406
|
+
if(Lightview.renderErrors) return e.message;
|
|
407
|
+
throw e;
|
|
408
|
+
} // actually looking for undefined or not defined, but different browsers spell or quote differently
|
|
373
409
|
return undefined;
|
|
374
410
|
}
|
|
375
411
|
}
|
|
376
412
|
return node?.nodeValue;
|
|
377
413
|
}
|
|
378
|
-
const inputTypeToType =
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
414
|
+
const inputTypeToType = inputType => {
|
|
415
|
+
const map = {
|
|
416
|
+
text:"string",
|
|
417
|
+
tel:"string",
|
|
418
|
+
email:"string",
|
|
419
|
+
url:"string",
|
|
420
|
+
search:"string",
|
|
421
|
+
radio:"string",
|
|
422
|
+
color:"string",
|
|
423
|
+
password:"string",
|
|
424
|
+
number:"number",
|
|
425
|
+
range:"number",
|
|
426
|
+
datetime:Date,
|
|
427
|
+
checkbox:"boolean"
|
|
428
|
+
}
|
|
429
|
+
return map[inputType] || "any";
|
|
385
430
|
}
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
addListener(
|
|
390
|
-
}
|
|
431
|
+
const enableAnchors = node => {
|
|
432
|
+
for(const anode of node.querySelectorAll('a[href$=".html"][target^="#"]')) {
|
|
433
|
+
anode.removeEventListener("click", anchorHandler);
|
|
434
|
+
addListener(anode, "click", anchorHandler);
|
|
435
|
+
}
|
|
391
436
|
}
|
|
437
|
+
Object.defineProperty(Lightview,"enableAnchors",{value:enableAnchors});
|
|
438
|
+
//Lightview.enableAnchors = enableAnchors;
|
|
439
|
+
|
|
392
440
|
const bound = new WeakSet();
|
|
393
441
|
const bindInput = (input, variableName, component, value, object) => {
|
|
394
442
|
if (bound.has(input)) return;
|
|
@@ -397,22 +445,30 @@ const {observe} = (() => {
|
|
|
397
445
|
nameparts = variableName.split(".");
|
|
398
446
|
let type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype);
|
|
399
447
|
const variable = walk(component.vars,nameparts) || {type};
|
|
400
|
-
if(type==="any") type = variable.type;
|
|
401
|
-
if(value==null) value = input.
|
|
448
|
+
if(type==="any") type = variable?.type.type || variable?.type;
|
|
449
|
+
if(inputtype==="checkbox" && value==null) value = input.checked;
|
|
450
|
+
if(value==null) {
|
|
451
|
+
const avalue = input.getAttribute("value");
|
|
452
|
+
if(avalue) value = avalue;
|
|
453
|
+
}
|
|
402
454
|
if(object && nameparts.length>1) {
|
|
403
|
-
const [root,...path] = nameparts
|
|
404
|
-
|
|
405
|
-
|
|
455
|
+
const [root,...path] = nameparts,
|
|
456
|
+
key = path[path.length-1];
|
|
457
|
+
object = walk(object,path,{depth:path.length-2,create:true});
|
|
406
458
|
object[key] = coerce(value,type);
|
|
407
459
|
} else {
|
|
408
|
-
if (type !== variable.type) {
|
|
409
|
-
if (variable.type === "any" || variable.type === "unknown") variable.type = type;
|
|
410
|
-
else throw new TypeError(`Attempt to bind <input name="${variableName}" type="${type}"> to variable ${variableName}:${variable.type}`)
|
|
411
|
-
}
|
|
412
460
|
const existing = component.vars[variableName];
|
|
413
|
-
if(
|
|
461
|
+
if(existing) {
|
|
462
|
+
existingtype = existing?.type.type || existing?.type;
|
|
463
|
+
if(existingtype!==type) throw new TypeError(`Attempt to bind <${input.tagName} name="${variableName}" type="${type}"> to variable ${variableName}:${existing.type}`)
|
|
464
|
+
existing.reactive = true;
|
|
465
|
+
} else if(Lightview.createInputVariables) {
|
|
466
|
+
component.variables({[variableName]: type},{reactive,set:typeof(value)==="string" && value.startsWith("${") ? "" : value});
|
|
467
|
+
} else {
|
|
468
|
+
throw new TypeError(`Attempt to bind undefined variable ${variableName} to <${input.tagName} type="${type}>`)
|
|
469
|
+
}
|
|
414
470
|
if(inputtype!=="radio") {
|
|
415
|
-
if(typeof(value)==="string" && value.includes("${")) input.
|
|
471
|
+
if(typeof(value)==="string" && value.includes("${")) input.setAttribute("value","");
|
|
416
472
|
else component.setVariableValue(variableName, coerce(value,type));
|
|
417
473
|
}
|
|
418
474
|
}
|
|
@@ -434,32 +490,22 @@ const {observe} = (() => {
|
|
|
434
490
|
}
|
|
435
491
|
if(object) {
|
|
436
492
|
const [root,...path] = nameparts;
|
|
437
|
-
object = walk(object,nameparts,path.length-2,true);
|
|
493
|
+
object = walk(object,nameparts,{depth:path.length-2,create:true});
|
|
438
494
|
} else {
|
|
439
|
-
object = walk(component.varsProxy,nameparts,nameparts.length-2,true);
|
|
495
|
+
object = walk(component.varsProxy,nameparts,{depth:nameparts.length-2,create:true});
|
|
440
496
|
}
|
|
441
497
|
const key = nameparts[nameparts.length-1];
|
|
442
498
|
object[key] = coerce(value,type);
|
|
443
499
|
};
|
|
444
500
|
addListener(input, eventname, listener,component);
|
|
445
501
|
}
|
|
446
|
-
const tryParse =
|
|
502
|
+
const tryParse = value => {
|
|
447
503
|
try {
|
|
448
504
|
return JSON.parse(value);
|
|
449
505
|
} catch (e) {
|
|
450
506
|
return value;
|
|
451
507
|
}
|
|
452
508
|
}
|
|
453
|
-
const observed = () => {
|
|
454
|
-
return {
|
|
455
|
-
init({variable, component}) {
|
|
456
|
-
const name = variable.name;
|
|
457
|
-
variable.value = component.hasAttribute(name) ? coerce(component.getAttribute(name), variable.type) : variable.value;
|
|
458
|
-
variable.observed = true;
|
|
459
|
-
component.observedAttributes.add(variable.name);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
509
|
const reactive = () => {
|
|
464
510
|
return {
|
|
465
511
|
init({variable, component}) {
|
|
@@ -468,30 +514,21 @@ const {observe} = (() => {
|
|
|
468
514
|
}
|
|
469
515
|
}
|
|
470
516
|
}
|
|
471
|
-
const shared = () => {
|
|
472
|
-
return {
|
|
473
|
-
init({variable, component}) {
|
|
474
|
-
variable.shared = true;
|
|
475
|
-
/*addEventListener("change", ({variableName, value}) => {
|
|
476
|
-
if (variableName===variable.name && component.vars[variableName]?.shared) component.siblings.forEach((instance) => instance.setVariableValue(variableName, value))
|
|
477
|
-
})*/
|
|
478
|
-
variable.set = function(newValue) {
|
|
479
|
-
if(component.vars[this.name]?.shared) component.siblings.forEach((instance) => instance.setVariableValue(this.name, newValue));
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
517
|
const exported = () => {
|
|
485
518
|
return {
|
|
486
519
|
init({variable, component}) {
|
|
487
|
-
const name = variable.name
|
|
520
|
+
const name = variable.name,
|
|
521
|
+
set = variable.set || (() => {});
|
|
488
522
|
variable.exported = true;
|
|
489
523
|
variable.set = (newValue) => {
|
|
490
|
-
|
|
491
|
-
|
|
524
|
+
set(newValue);
|
|
525
|
+
if(variable.exported) { // still exported
|
|
526
|
+
if(variable.value==null) {
|
|
492
527
|
removeComponentAttribute(component, name);
|
|
493
528
|
} else {
|
|
494
|
-
|
|
529
|
+
const type = typeof(newValue);
|
|
530
|
+
// note, using isArray below will not always work for proxied items, so using instanceOf
|
|
531
|
+
newValue = type === "string" ? newValue : (type==="object" && newValue instanceof Array) ? JSON.stringify([...newValue]) : JSON.stringify(newValue);
|
|
495
532
|
setComponentAttribute(component, name, newValue);
|
|
496
533
|
}
|
|
497
534
|
}
|
|
@@ -503,26 +540,23 @@ const {observe} = (() => {
|
|
|
503
540
|
const imported = () => {
|
|
504
541
|
return {
|
|
505
542
|
init({variable, component}) {
|
|
506
|
-
const name = variable.name;
|
|
543
|
+
const name = variable.name.toLowerCase();
|
|
544
|
+
let value = component.hasAttribute(name) ? component.getAttribute(name) : null;
|
|
545
|
+
if((variable.type==="boolean" || variable.type.type==="boolean") && value==="") value = true;
|
|
507
546
|
variable.imported = true;
|
|
508
|
-
variable.value =
|
|
547
|
+
variable.value = value!=null ? coerce(value, variable.type) : variable.value;
|
|
548
|
+
if(variable.set) {
|
|
549
|
+
variable.set(variable.value);
|
|
550
|
+
}
|
|
509
551
|
}
|
|
510
552
|
}
|
|
511
553
|
}
|
|
512
554
|
|
|
513
555
|
let reserved = {
|
|
514
|
-
observed: {
|
|
515
|
-
constant: true,
|
|
516
|
-
value: observed
|
|
517
|
-
},
|
|
518
556
|
reactive: {
|
|
519
557
|
constant: true,
|
|
520
558
|
value: reactive
|
|
521
559
|
},
|
|
522
|
-
shared: {
|
|
523
|
-
constant: true,
|
|
524
|
-
value: shared
|
|
525
|
-
},
|
|
526
560
|
exported: {
|
|
527
561
|
constant: true,
|
|
528
562
|
value: exported
|
|
@@ -531,24 +565,55 @@ const {observe} = (() => {
|
|
|
531
565
|
constant: true,
|
|
532
566
|
value: imported
|
|
533
567
|
}
|
|
534
|
-
}
|
|
535
|
-
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// patches relative references to use the passed in href as base
|
|
571
|
+
const patchElementURIs = (node,tagName,attributeName,href) => { // attributeName will always be href or src
|
|
572
|
+
if(node) {
|
|
573
|
+
for(const child of node.querySelectorAll(tagName)) {
|
|
574
|
+
let value = child.getAttribute(attributeName);
|
|
575
|
+
if (value) child.setAttribute(attributeName, new URL(value, href).href);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// forces re-evaluation of scripts and links
|
|
580
|
+
const forceLoadElement = node => {
|
|
581
|
+
if((node.getAttribute("src")||"").includes("/lightview.js")) return;
|
|
582
|
+
const el = document.createElement(node.tagName.toLowerCase());
|
|
583
|
+
for(const attr of node.attributes) el.setAttribute(attr.name,attr.value);
|
|
584
|
+
el.innerHTML = node.innerHTML;
|
|
585
|
+
node.after(el);
|
|
586
|
+
node.remove();
|
|
587
|
+
}
|
|
588
|
+
const createClass = (domElementNode, {observer, framed, href= window.location.href.replace("blob:",""), mount}) => {
|
|
536
589
|
const instances = new Set(),
|
|
537
|
-
dom = domElementNode.tagName === "TEMPLATE"
|
|
538
|
-
? domElementNode.content.cloneNode(true)
|
|
539
|
-
: domElementNode.cloneNode(true),
|
|
540
590
|
observedAttributes = [];
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
591
|
+
let dom;
|
|
592
|
+
observedAttributes.add = function(name) { observedAttributes.includes(name) || observedAttributes.push(name) }
|
|
593
|
+
const cls = class CustomElement extends HTMLElement {
|
|
544
594
|
static get instances() {
|
|
545
595
|
return instances;
|
|
546
596
|
}
|
|
597
|
+
static setTemplateNode(node) {
|
|
598
|
+
dom = node.tagName === "TEMPLATE"
|
|
599
|
+
? document.createElement("div")
|
|
600
|
+
: node.cloneNode(true);
|
|
601
|
+
if(node.tagName === "TEMPLATE") {
|
|
602
|
+
dom.innerHTML = node.innerHTML;
|
|
603
|
+
for(const attr of node.attributes) dom.setAttribute(attr.name,attr.value);
|
|
604
|
+
}
|
|
605
|
+
patchElementURIs(dom.head,"link","href",href);
|
|
606
|
+
patchElementURIs(dom.head,"script","src",href);
|
|
607
|
+
patchElementURIs(dom.body||dom,"link","href",href);
|
|
608
|
+
patchElementURIs(dom.body||dom,"script","src",href);
|
|
609
|
+
dom.mount = node.mount || node.body?.mount;
|
|
610
|
+
}
|
|
547
611
|
constructor() {
|
|
548
612
|
super();
|
|
613
|
+
this.componentBaseURI = href;
|
|
614
|
+
currentComponent = window.currentComponent = document.currentComponent = this;
|
|
549
615
|
instances.add(this);
|
|
550
|
-
const
|
|
551
|
-
shadow = this.attachShadow({mode: "open"}),
|
|
616
|
+
const shadow = this.attachShadow({mode: "open"}),
|
|
552
617
|
eventlisteners = {};
|
|
553
618
|
this.vars = {
|
|
554
619
|
...reserved,
|
|
@@ -560,7 +625,7 @@ const {observe} = (() => {
|
|
|
560
625
|
addEventListener: {
|
|
561
626
|
value: (eventName, listener) => {
|
|
562
627
|
const listeners = eventlisteners[eventName] ||= new Set();
|
|
563
|
-
|
|
628
|
+
listeners.forEach((f) => {
|
|
564
629
|
if (listener + "" === f + "") listeners.delete(f);
|
|
565
630
|
})
|
|
566
631
|
eventlisteners[eventName].add(listener);
|
|
@@ -580,19 +645,48 @@ const {observe} = (() => {
|
|
|
580
645
|
},
|
|
581
646
|
self: {value: currentComponent, type: CustomElement, constant: true}
|
|
582
647
|
};
|
|
583
|
-
this.defaultAttributes = domElementNode.tagName === "TEMPLATE" ? domElementNode.attributes : dom.attributes;
|
|
584
648
|
this.varsProxy = createVarsProxy(this.vars, this, CustomElement);
|
|
585
649
|
if (framed || CustomElement.lightviewFramed) this.variables({message: Object}, {exported});
|
|
586
|
-
["getElementById", "querySelector"
|
|
650
|
+
["getElementById", "querySelector"] //, "querySelectorAll"
|
|
651
|
+
.forEach((fname) => {
|
|
652
|
+
const f = this[fname];
|
|
653
|
+
Object.defineProperty(this, fname, {
|
|
654
|
+
configurable: true,
|
|
655
|
+
writable: true,
|
|
656
|
+
value: (...args) => { return (f ? f.call(this,...args) : null) || this.shadowRoot[fname](...args) }
|
|
657
|
+
})
|
|
658
|
+
});
|
|
659
|
+
["querySelectorAll"]
|
|
587
660
|
.forEach((fname) => {
|
|
661
|
+
const f = this[fname];
|
|
588
662
|
Object.defineProperty(this, fname, {
|
|
589
663
|
configurable: true,
|
|
590
664
|
writable: true,
|
|
591
|
-
value: (...args) => this.shadowRoot[fname](...args)
|
|
665
|
+
value: (...args) => { return [...f.call(this,...args),...this.shadowRoot[fname](...args)] }
|
|
592
666
|
})
|
|
593
667
|
});
|
|
594
|
-
[...dom.childNodes].forEach((child) =>
|
|
595
|
-
|
|
668
|
+
[...dom.head?.childNodes||[]].forEach((child) => {
|
|
669
|
+
const clone = child.cloneNode(true);
|
|
670
|
+
document.head.appendChild(clone);
|
|
671
|
+
if(["LINK","SCRIPT"].includes(clone.tagName)) forceLoadElement(clone);
|
|
672
|
+
});
|
|
673
|
+
const body = dom.body || dom;
|
|
674
|
+
for(const child of body.childNodes) {
|
|
675
|
+
if(child.tagName && customElements.get(child.tagName.toLowerCase())) {
|
|
676
|
+
const node = document.createElement(child.tagName);
|
|
677
|
+
for(const attr of child.attributes) node.setAttribute(attr.name,attr.value);
|
|
678
|
+
//currentComponent = window.currentComponent = document.currentComponent = node;
|
|
679
|
+
shadow.appendChild(node);
|
|
680
|
+
} else {
|
|
681
|
+
const clone = child.cloneNode(true);
|
|
682
|
+
shadow.appendChild(clone);
|
|
683
|
+
if(["LINK","SCRIPT"].includes(clone.tagName)) forceLoadElement(clone);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
//forceLoadElements(shadow,"link");
|
|
687
|
+
//forceLoadElements(shadow,"script");
|
|
688
|
+
//importStyleSheets(shadow,this);
|
|
689
|
+
enableAnchors(shadow);
|
|
596
690
|
}
|
|
597
691
|
|
|
598
692
|
get siblings() {
|
|
@@ -607,117 +701,139 @@ const {observe} = (() => {
|
|
|
607
701
|
instances.delete(this);
|
|
608
702
|
}
|
|
609
703
|
|
|
610
|
-
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
.
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const timeout = setTimeout(() => resolve(),500);
|
|
630
|
-
currentScript.onload = () => {
|
|
631
|
-
clearTimeout(timeout);
|
|
632
|
-
currentScript.remove();
|
|
633
|
-
resolve();
|
|
634
|
-
}
|
|
635
|
-
})
|
|
636
|
-
continue;
|
|
637
|
-
};
|
|
638
|
-
const scriptid = Math.random() + "";
|
|
639
|
-
for (const attr of script.attributes) {
|
|
640
|
-
currentScript.setAttribute(attr.name, attr.name === "type" ? attr.value.replace("lightview/", "") : attr.value);
|
|
704
|
+
connectedCallback() {
|
|
705
|
+
const node = dom.body || dom;
|
|
706
|
+
for(const attr of node.attributes) {
|
|
707
|
+
if(!this.hasAttribute(attr.name)) this.setAttribute(attr.name,attr.value);
|
|
708
|
+
}
|
|
709
|
+
if(mount ||= dom.mount||this.mount) {
|
|
710
|
+
const script = document.createElement("script");
|
|
711
|
+
document.currentComponent = this;
|
|
712
|
+
script.innerHTML = `with(document.currentComponent.varsProxy) {
|
|
713
|
+
${typeof(lightviewDebug)!=="undefined" && lightviewDebug===true ? "debugger;" : ""}
|
|
714
|
+
const component = document.currentComponent;
|
|
715
|
+
(async () => { await (${mount}).call(self,self);
|
|
716
|
+
component.compile(); })();
|
|
717
|
+
};`;
|
|
718
|
+
this.appendChild(script);
|
|
719
|
+
script.remove();
|
|
720
|
+
for(const child of this.querySelectorAll('link[rel="stylesheet"][export]')) { // ].forEach(async (child) =>
|
|
721
|
+
child.remove();
|
|
722
|
+
this.appendChild(child);
|
|
641
723
|
}
|
|
642
|
-
|
|
643
|
-
currentScript.innerHTML = `Object.getPrototypeOf(async function(){}).constructor('if(window["${scriptid}"]?.ctx) { const ctx = window["${scriptid}"].ctx; { with(ctx) { ${text}; } } }')().then(() => window["${scriptid}"]()); `;
|
|
644
|
-
await new Promise((resolve) => {
|
|
645
|
-
window[scriptid] = () => {
|
|
646
|
-
delete window[scriptid];
|
|
647
|
-
currentScript.remove();
|
|
648
|
-
script.remove();
|
|
649
|
-
resolve();
|
|
650
|
-
}
|
|
651
|
-
window[scriptid].ctx = ctx.varsProxy;
|
|
652
|
-
shadow.appendChild(currentScript);
|
|
653
|
-
})
|
|
724
|
+
window.currentComponent = document.currentComponent = null;
|
|
654
725
|
}
|
|
726
|
+
}
|
|
727
|
+
compile() {
|
|
655
728
|
// Promise.all(promises).then(() => {
|
|
656
|
-
const
|
|
657
|
-
|
|
729
|
+
const shadow = this.shadowRoot,
|
|
730
|
+
nodes = getNodes(this,href),
|
|
731
|
+
processNodes = (nodes,{object,extras}={}) => { //rootName
|
|
658
732
|
nodes.forEach((node) => {
|
|
659
733
|
if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
|
|
660
|
-
observe(() => resolveNodeOrText(node,
|
|
734
|
+
const observer = observe(((node,ctx) => () => resolveNodeOrText(node, ctx,true,extras))(node,this));
|
|
735
|
+
node.observers ||= new Set();
|
|
736
|
+
node.observers.add(observer);
|
|
737
|
+
if(node.parentElement?.tagName==="TEXTAREA") {
|
|
738
|
+
const name = getTemplateVariableName(node.template);
|
|
739
|
+
if (name) {
|
|
740
|
+
const nameparts = name.split(".");
|
|
741
|
+
if(extras && extras[nameparts[0]]) object = extras[nameparts[0]];
|
|
742
|
+
if(!this.vars[nameparts[0]] || this.vars[nameparts[0]].reactive || object) {
|
|
743
|
+
bindInput(node.parentElement, name, this, resolveNodeOrText(node.template, this,true,extras), object);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
661
747
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
748
|
+
if(node.tagName==="FORM") {
|
|
749
|
+
const value = node.getAttribute("value"),
|
|
750
|
+
name = getTemplateVariableName(value);
|
|
751
|
+
if(name) {
|
|
752
|
+
node.addEventListener("submit",(event) => {
|
|
753
|
+
if(!event.target.hasAttribute("action")) event.preventDefault();
|
|
754
|
+
const object = {};
|
|
755
|
+
[...node.querySelectorAll("input[name]"),...node.querySelectorAll("textarea[name]")]
|
|
756
|
+
.forEach((input) => {
|
|
757
|
+
const eltype = input.getAttribute("type"),
|
|
758
|
+
path = input.getAttribute("name").split("."),
|
|
759
|
+
property = path[path.length-1],
|
|
760
|
+
target = walk(object,path,{depth:path.length-2,create:true})
|
|
761
|
+
if(["radio","checkbox"].includes(eltype)) {
|
|
762
|
+
target[property] = input.checked;
|
|
763
|
+
} else {
|
|
764
|
+
target[property] = input.value;
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
this.varsProxy[name] = object;
|
|
768
|
+
})
|
|
769
|
+
}
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
662
772
|
// resolve the value before all else;
|
|
663
773
|
const attr = node.attributes.value,
|
|
664
774
|
template = attr?.template;
|
|
665
775
|
if (attr && template) {
|
|
666
776
|
//let value = resolveNodeOrText(attr, this),
|
|
667
777
|
// ;
|
|
668
|
-
const eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type,
|
|
669
|
-
|
|
778
|
+
const eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, this, false,extras) : null,
|
|
779
|
+
aname = node.getAttribute("name"),
|
|
780
|
+
template = attr.template;// || (rootName && aname ? `\${${rootName}.${name}}` : null);
|
|
670
781
|
if (template) {
|
|
671
782
|
const name = getTemplateVariableName(template);
|
|
672
783
|
if (name) {
|
|
673
784
|
const nameparts = name.split(".");
|
|
674
|
-
if(
|
|
675
|
-
object = node.extras[nameparts[0]];
|
|
676
|
-
}
|
|
785
|
+
if(extras && extras[nameparts[0]]) object = extras[nameparts[0]];
|
|
677
786
|
if(!this.vars[nameparts[0]] || this.vars[nameparts[0]].reactive || object) {
|
|
678
|
-
bindInput(node, name, this, resolveNodeOrText(attr, this,false,
|
|
787
|
+
bindInput(node, name, this, resolveNodeOrText(attr, this,false,extras), object);
|
|
679
788
|
}
|
|
680
789
|
}
|
|
681
|
-
observe(() => {
|
|
682
|
-
const value = resolveNodeOrText(template, ctx,false,
|
|
790
|
+
const observer = observe(((node,ctx,template) => () => {
|
|
791
|
+
const value = resolveNodeOrText(template, ctx,false,extras);
|
|
683
792
|
if(value!==undefined) {
|
|
684
793
|
if (eltype === "checkbox") {
|
|
685
794
|
if (coerce(value, "boolean") === true) {
|
|
686
795
|
node.setAttribute("checked", "");
|
|
687
|
-
node.checked = true;
|
|
796
|
+
//node.checked = true; // do we need to do this and set attribute
|
|
688
797
|
} else {
|
|
689
798
|
node.removeAttribute("checked");
|
|
690
|
-
node.checked = false;
|
|
799
|
+
//node.checked = false;
|
|
691
800
|
}
|
|
692
801
|
} else if (node.tagName === "SELECT") {
|
|
693
|
-
|
|
694
|
-
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
802
|
+
const values = node.hasAttribute("multiple") ? coerce(value, Array) : [value];
|
|
695
803
|
[...node.querySelectorAll("option")].forEach(async (option) => {
|
|
696
804
|
if (option.hasAttribute("value")) {
|
|
697
|
-
if (values.includes(resolveNodeOrText(option.attributes.value, ctx,false,
|
|
805
|
+
if (values.includes(resolveNodeOrText(option.attributes.value, ctx,false,extras))) {
|
|
698
806
|
option.setAttribute("selected", "");
|
|
699
|
-
option.selected = true;
|
|
807
|
+
//option.selected = true;
|
|
700
808
|
}
|
|
701
|
-
} else if (values.includes(resolveNodeOrText(option.innerText, ctx,false,
|
|
809
|
+
} else if (values.includes(resolveNodeOrText(option.innerText, ctx,false,extras))) {
|
|
702
810
|
option.setAttribute("selected", "");
|
|
703
|
-
option.selected = true;
|
|
811
|
+
//option.selected = true;
|
|
704
812
|
}
|
|
705
813
|
})
|
|
706
814
|
} else if (eltype!=="radio") {
|
|
815
|
+
//attr.value = typeof(value)==="string" ? value : JSON.stringify(value);
|
|
816
|
+
let avalue = typeof(value)==="string" ? value : value.toString ? value.toString() : JSON.stringify(value);
|
|
817
|
+
if(avalue.startsWith('"')) avalue = avalue.substring(1);
|
|
818
|
+
if(avalue.endsWith('"')) avalue = avalue.substring(0,avalue.length-1);
|
|
707
819
|
attr.value = value;
|
|
820
|
+
if(node.tagName==="INPUT") node.value = value;
|
|
708
821
|
}
|
|
709
822
|
}
|
|
710
|
-
});
|
|
823
|
+
})(node,this,template));
|
|
824
|
+
node.observers ||= new Set();
|
|
825
|
+
node.observers.add(observer);
|
|
711
826
|
}
|
|
712
827
|
}
|
|
713
|
-
|
|
714
|
-
if (attr.name === "value" && attr.template)
|
|
828
|
+
for(const attr of node.attributes) {
|
|
829
|
+
if (attr.name === "value" && attr.template) continue;
|
|
715
830
|
const {name, value} = attr,
|
|
716
831
|
vname = node.attributes.name?.value;
|
|
717
|
-
if (
|
|
832
|
+
if (value.includes("${")) attr.template = value;
|
|
833
|
+
if (name === "type" && value == "radio" && vname) {
|
|
718
834
|
bindInput(node, vname, this, undefined, object);
|
|
719
|
-
observe(() => {
|
|
720
|
-
const varvalue =
|
|
835
|
+
const observer = observe(((node,ctx) => () => {
|
|
836
|
+
const varvalue = Function("context", "with(context) { return `${" + vname + "}` }")(ctx.varsProxy);
|
|
721
837
|
if (node.attributes.value.value == varvalue) {
|
|
722
838
|
node.setAttribute("checked", "");
|
|
723
839
|
node.checked = true;
|
|
@@ -725,117 +841,122 @@ const {observe} = (() => {
|
|
|
725
841
|
node.removeAttribute("checked");
|
|
726
842
|
node.checked = false;
|
|
727
843
|
}
|
|
728
|
-
});
|
|
844
|
+
})(node,this));
|
|
845
|
+
node.observers ||= new Set();
|
|
846
|
+
node.observers.add(observer);
|
|
729
847
|
}
|
|
730
|
-
|
|
731
848
|
const [type, ...params] = name.split(":");
|
|
732
849
|
if (type === "") { // name is :something
|
|
733
|
-
observe(() => {
|
|
850
|
+
const observer = observe(((node,attr,ctx)=>() => {
|
|
734
851
|
const value = attr.value;
|
|
735
852
|
if (params[0]) {
|
|
736
853
|
if (value === "true") node.setAttribute(params[0], "")
|
|
737
854
|
else node.removeAttribute(params[0]);
|
|
738
855
|
} else {
|
|
739
|
-
const elvalue = node.attributes.value ? resolveNodeOrText(node.attributes.value, ctx,false,
|
|
740
|
-
eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx,false,
|
|
856
|
+
const elvalue = node.attributes.value ? resolveNodeOrText(node.attributes.value, ctx, false, extras) : null,
|
|
857
|
+
eltype = node.attributes.type ? resolveNodeOrText(node.attributes.type, ctx, false, extras) : null;
|
|
741
858
|
if (eltype === "checkbox" || node.tagName === "OPTION") {
|
|
742
859
|
if (elvalue === true) node.setAttribute("checked", "")
|
|
743
860
|
else node.removeAttribute("checked");
|
|
744
861
|
}
|
|
745
862
|
}
|
|
746
|
-
})
|
|
863
|
+
})(node,attr,this));
|
|
864
|
+
node.observers ||= new Set();
|
|
865
|
+
node.observers.add(observer);
|
|
747
866
|
} else if (type === "l-on") {
|
|
748
867
|
let listener;
|
|
749
|
-
observe(() => {
|
|
750
|
-
const value = resolveNodeOrText(attr,
|
|
868
|
+
const observer = observe(((node,attr,ctx) => () => {
|
|
869
|
+
const value = resolveNodeOrText(attr, ctx, true, extras,true);
|
|
751
870
|
if (listener) node.removeEventListener(params[0], listener);
|
|
752
871
|
listener = null;
|
|
753
|
-
if(typeof(value)==="function") {
|
|
872
|
+
if (typeof (value) === "function") {
|
|
754
873
|
listener = value;
|
|
755
874
|
} else {
|
|
756
875
|
try {
|
|
757
876
|
listener = Function("return " + value)();
|
|
758
|
-
} catch(e) {
|
|
877
|
+
} catch (e) {
|
|
759
878
|
|
|
760
879
|
}
|
|
761
880
|
}
|
|
762
|
-
if(listener) addListener(node, params[0], listener,ctx);
|
|
763
|
-
})
|
|
881
|
+
if (listener) addListener(node, params[0], listener, ctx);
|
|
882
|
+
})(node,attr,this));
|
|
883
|
+
node.observers ||= new Set();
|
|
884
|
+
node.observers.add(observer);
|
|
764
885
|
} else if (type === "l-if") {
|
|
765
|
-
observe(() => {
|
|
766
|
-
const value = resolveNodeOrText(attr,
|
|
767
|
-
node.style.setProperty("display", value == true ? "revert" : "none");
|
|
768
|
-
})
|
|
886
|
+
const observer = observe(((node,attr,ctx) => () => {
|
|
887
|
+
const value = resolveNodeOrText(attr, ctx, true, extras);
|
|
888
|
+
node.style.setProperty("display", value == true || value === "true" ? "revert" : "none");
|
|
889
|
+
})(node,attr,this));
|
|
890
|
+
node.observers ||= new Set();
|
|
891
|
+
node.observers.add(observer);
|
|
769
892
|
} else if (type === "l-for") {
|
|
770
|
-
node.template ||= node.innerHTML;
|
|
771
|
-
node.clone ||= node.cloneNode(true);
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
893
|
+
//node.template ||= node.innerHTML;
|
|
894
|
+
//node.clone ||= node.cloneNode(true);
|
|
895
|
+
let clone = node.cloneNode(true),
|
|
896
|
+
observer = observe(((node,attr,ctx) => () => {
|
|
897
|
+
const [what = "each", vname = "item", index = "index", array = "array", after = false] = params;
|
|
898
|
+
/*if (!window.lightviewDebug) {
|
|
899
|
+
requestAnimationFrame(() => {
|
|
900
|
+
if (after) node.style.setProperty("display", "none")
|
|
901
|
+
else node.innerHTML = "";
|
|
902
|
+
})
|
|
903
|
+
}*/
|
|
904
|
+
const value = resolveNodeOrText(attr, ctx, false, extras,true),
|
|
905
|
+
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
906
|
+
target = what === "each" ? coerced : Object[what](value),
|
|
907
|
+
children = target.reduce((children, item, i, target) => {
|
|
908
|
+
const child = clone.cloneNode(true),
|
|
909
|
+
extras = {
|
|
910
|
+
[vname]: item,
|
|
911
|
+
[index]: i,
|
|
912
|
+
[array]: target
|
|
913
|
+
},
|
|
914
|
+
nodes = getNodes(child, href);
|
|
915
|
+
processNodes(nodes,{extras});
|
|
916
|
+
children.push(...child.childNodes);
|
|
917
|
+
return children;
|
|
918
|
+
}, []);
|
|
919
|
+
//requestAnimationFrame(() => {
|
|
920
|
+
if(children.length===0) {
|
|
921
|
+
node.innerHTML = ""
|
|
795
922
|
} else {
|
|
796
|
-
|
|
923
|
+
children.forEach((child,i) => {
|
|
924
|
+
if (after) node.parentElement.insertBefore(child, node);
|
|
925
|
+
else if(node?.childNodes[i]) {
|
|
926
|
+
const old = node.childNodes[i];
|
|
927
|
+
if(old.observers) {
|
|
928
|
+
old.observers.forEach((observer) => observer.cancel())
|
|
929
|
+
old.observers.clear();
|
|
930
|
+
old.observers = null;
|
|
931
|
+
}
|
|
932
|
+
node.replaceChild(child,old);
|
|
933
|
+
}
|
|
934
|
+
else node.appendChild(child);
|
|
935
|
+
})
|
|
936
|
+
while(node.childNodes.length>children.length) node.lastChild.remove();
|
|
797
937
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
} else if(attr.template) {
|
|
809
|
-
observe(() => {
|
|
810
|
-
resolveNodeOrText(attr, this,false,node.extras);
|
|
811
|
-
})
|
|
938
|
+
// });
|
|
939
|
+
})(node,attr,this,extras));
|
|
940
|
+
node.observers ||= new Set();
|
|
941
|
+
node.observers.add(observer);
|
|
942
|
+
} else if (attr.template) {
|
|
943
|
+
const observer = observe(((node,attr,ctx,extras) => () => {
|
|
944
|
+
resolveNodeOrText(attr, ctx, false, extras);
|
|
945
|
+
})(node,attr,this,extras));
|
|
946
|
+
node.observers ||= new Set();
|
|
947
|
+
node.observers.add(observer);
|
|
812
948
|
}
|
|
813
|
-
}
|
|
949
|
+
}
|
|
814
950
|
}
|
|
815
951
|
})
|
|
816
952
|
};
|
|
817
|
-
nodes.forEach((node) => {
|
|
818
|
-
if(node.tagName==="FORM") {
|
|
819
|
-
const value = node.getAttribute("value"),
|
|
820
|
-
name = getTemplateVariableName(value);
|
|
821
|
-
if(name) {
|
|
822
|
-
const childnodes = [...nodes].filter((childnode) => node!==childnode && node.contains(childnode));
|
|
823
|
-
childnodes.forEach((node) => nodes.delete(node));
|
|
824
|
-
const variable = ctx.vars[name] ||= {type: "object", reactive:true, value: Reactor({})};
|
|
825
|
-
if(variable.type !== "object" || !variable.reactive || !variable.value || typeof(variable.value)!=="object") {
|
|
826
|
-
throw new TypeError(`Can't bind form ${node.getAttribute("id")} to non-object variable ${name}`);
|
|
827
|
-
}
|
|
828
|
-
processNodes(childnodes,variable.value);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
})
|
|
832
953
|
processNodes(nodes);
|
|
833
954
|
shadow.normalize();
|
|
834
|
-
observer ||= createObserver(
|
|
835
|
-
observer.observe(
|
|
955
|
+
observer ||= createObserver(this, framed);
|
|
956
|
+
observer.observe(this, {attributeOldValue: true, subtree:true, characterData:true, characterDataOldValue:true});
|
|
836
957
|
if(this.hasAttribute("l-unhide")) this.removeAttribute("hidden");
|
|
837
958
|
//ctx.vars.postEvent.value("connected");
|
|
838
|
-
this.dispatchEvent(new Event("
|
|
959
|
+
this.dispatchEvent(new Event("mounted"));
|
|
839
960
|
// })
|
|
840
961
|
}
|
|
841
962
|
adoptedCallback(callback) {
|
|
@@ -856,6 +977,13 @@ const {observe} = (() => {
|
|
|
856
977
|
.filter(name => !(name in reserved) && !["self", "addEventListener", "postEvent","observe"].includes(name))
|
|
857
978
|
}
|
|
858
979
|
|
|
980
|
+
getVariableData() {
|
|
981
|
+
return this.getVariableNames().reduce((data,name) => {
|
|
982
|
+
data[name] = this.getVariableValue(name);
|
|
983
|
+
return data;
|
|
984
|
+
},{})
|
|
985
|
+
}
|
|
986
|
+
|
|
859
987
|
getVariable(name) {
|
|
860
988
|
return this.vars[name] ? {...this.vars[name]} : undefined;
|
|
861
989
|
}
|
|
@@ -871,11 +999,11 @@ const {observe} = (() => {
|
|
|
871
999
|
const variable = this.vars[variableName];
|
|
872
1000
|
if (variable.shared) {
|
|
873
1001
|
value = type.validate ? type.validate(value,variable) : coerce(value,coerceTo);
|
|
874
|
-
const event =
|
|
1002
|
+
const event = {
|
|
875
1003
|
variableName: variableName,
|
|
876
1004
|
value: value,
|
|
877
1005
|
oldValue: variable.value
|
|
878
|
-
}
|
|
1006
|
+
};
|
|
879
1007
|
variable.value = value;
|
|
880
1008
|
this.vars.postEvent.value("change", event);
|
|
881
1009
|
if (event.defaultPrevented) variable.value = value;
|
|
@@ -893,29 +1021,31 @@ const {observe} = (() => {
|
|
|
893
1021
|
return this.vars[variableName]?.value;
|
|
894
1022
|
}
|
|
895
1023
|
|
|
896
|
-
variables(variables, {remote, constant,set,...rest} = {}) { // options = {observed,reactive,shared,exported,imported}
|
|
897
|
-
const options = {remote, constant
|
|
898
|
-
addEventListener = this.varsProxy.addEventListener;
|
|
1024
|
+
variables(variables, {remote, constant, set,...rest} = {}) { // options = {observed,reactive,shared,exported,imported}
|
|
1025
|
+
const options = {remote, constant, ...rest};
|
|
899
1026
|
if (variables !== undefined) {
|
|
900
1027
|
Object.entries(variables)
|
|
901
1028
|
.forEach(([key, type]) => {
|
|
902
1029
|
if(isArrowFunction(type)) type = type();
|
|
903
1030
|
const variable = this.vars[key] ||= {name: key, type};
|
|
904
1031
|
if(set!==undefined && constant!==undefined) throw new TypeError(`${key} has the constant value ${constant} and can't be set to ${set}`);
|
|
905
|
-
variable.value = set;
|
|
1032
|
+
if(set!==undefined) variable.value = set;
|
|
906
1033
|
if(constant!==undefined) {
|
|
1034
|
+
if(remote || rest.imported || rest.observed) throw new TypeError(`${key} can't be a constant and also remote, imported or observed`)
|
|
907
1035
|
variable.constant = true;
|
|
908
1036
|
variable.value = constant;
|
|
909
1037
|
}
|
|
910
1038
|
if (remote) {
|
|
911
|
-
|
|
912
|
-
variable.remote = remote;
|
|
913
|
-
remote.
|
|
1039
|
+
const type = typeof(remote);
|
|
1040
|
+
if(type==="function") variable.remote = remote(`./${key}`);
|
|
1041
|
+
else if(this.vars.remote.value && type==="string") variable.remote = this.vars.remote.value(remote);
|
|
1042
|
+
else throw new TypeError("Attempt to use type 'remote' without importing 'remote' from types.js")
|
|
1043
|
+
variable.remote.handleRemote({variable, config:variable.remote.config,component:this});
|
|
914
1044
|
}
|
|
915
1045
|
// todo: handle custom functional types, remote should actually be handled this way
|
|
916
1046
|
Object.entries(rest).forEach(([type,f]) => {
|
|
917
1047
|
const functionalType = variable[type] = typeof(f)==="function" ? f() : f;
|
|
918
|
-
if(functionalType.init) functionalType.init({variable,options,component:this});
|
|
1048
|
+
if(functionalType.init) functionalType.init({variable,options,component:this,coerce});
|
|
919
1049
|
if((rest.get!==undefined || rest.set!==undefined) && constant!==undefined) throw new TypeError(`${key} has the constant value ${constant} and can't have a getter or setter`);
|
|
920
1050
|
variable.set != functionalType.set;
|
|
921
1051
|
variable.get != functionalType.get;
|
|
@@ -930,16 +1060,18 @@ const {observe} = (() => {
|
|
|
930
1060
|
}, {});
|
|
931
1061
|
}
|
|
932
1062
|
}
|
|
1063
|
+
cls.setTemplateNode(domElementNode);
|
|
1064
|
+
return cls;
|
|
933
1065
|
}
|
|
934
1066
|
|
|
935
|
-
const createComponent = (name, node, {framed, observer, href} = {}) => {
|
|
1067
|
+
const createComponent = (name, node, {framed, observer, href, mount} = {}) => {
|
|
936
1068
|
let ctor = customElements.get(name);
|
|
937
1069
|
if (ctor) {
|
|
938
1070
|
if (framed && !ctor.lightviewFramed) ctor.lightviewFramed = true;
|
|
939
1071
|
else console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
|
|
940
1072
|
return ctor;
|
|
941
1073
|
}
|
|
942
|
-
ctor = createClass(node, {observer, framed, href});
|
|
1074
|
+
ctor = createClass(node, {observer, framed, href, mount});
|
|
943
1075
|
customElements.define(name, ctor);
|
|
944
1076
|
Lightview.customElements.set(name, ctor);
|
|
945
1077
|
return ctor;
|
|
@@ -947,40 +1079,46 @@ const {observe} = (() => {
|
|
|
947
1079
|
Lightview.customElements = new Map();
|
|
948
1080
|
Lightview.createComponent = createComponent;
|
|
949
1081
|
const importLink = async (link, observer) => {
|
|
950
|
-
const url = (new URL(link.getAttribute("href"),
|
|
1082
|
+
const url = (new URL(link.getAttribute("href"),document.baseURI)),
|
|
951
1083
|
as = link.getAttribute("as") || getNameFromPath(url.pathname);
|
|
952
|
-
if (url.
|
|
1084
|
+
if (url.origin !== window.location.origin && !link.getAttribute("crossorigin")) {
|
|
953
1085
|
throw new URIError(`importLink:HTML imports must be from same domain: ${url.hostname}!=${location.hostname} unless 'crossorigin' attribute is set.`)
|
|
954
1086
|
}
|
|
955
1087
|
if (!customElements.get(as)) {
|
|
956
1088
|
const html = await (await fetch(url.href)).text(),
|
|
957
1089
|
dom = parser.parseFromString(html, "text/html"),
|
|
958
|
-
unhide = !!dom.head.querySelector('meta[name="l-unhide"]')
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
const href = childlink.getAttribute("href"),
|
|
962
|
-
childurl = new URL(href, url.href);
|
|
963
|
-
childlink.setAttribute("href", childurl.href);
|
|
1090
|
+
unhide = !!dom.head.querySelector('meta[name="l-unhide"]');
|
|
1091
|
+
for (const childlink of dom.head.querySelectorAll('link[href]')) {
|
|
1092
|
+
childlink.setAttribute("href", new URL(childlink.getAttribute("href"), url.href).href);
|
|
964
1093
|
if (link.hasAttribute("crossorigin")) childlink.setAttribute("crossorigin", link.getAttribute("crossorigin"))
|
|
965
|
-
await importLink(childlink, observer);
|
|
1094
|
+
if(childlink.getAttribute("href").endsWith(".html") && childlink.getAttribute("rel")==="module") await importLink(childlink, observer);
|
|
1095
|
+
}
|
|
1096
|
+
currentComponent = window.currentComponent = document.currentComponent = dom.body;
|
|
1097
|
+
currentComponent.componentBaseURI = url.href;
|
|
1098
|
+
const lvscript = dom.getElementById("lightview");
|
|
1099
|
+
if(lvscript) {
|
|
1100
|
+
const script = document.createElement("script");
|
|
1101
|
+
script.innerHTML = lvscript.innerHTML;
|
|
1102
|
+
document.body.appendChild(script);
|
|
1103
|
+
script.remove();
|
|
966
1104
|
}
|
|
1105
|
+
currentComponent = window.currentComponent = document.currentComponent = null;
|
|
1106
|
+
createComponent(as, dom, {observer,href:url.href});
|
|
967
1107
|
if (unhide) dom.body.removeAttribute("hidden");
|
|
968
|
-
createComponent(as, dom.body, {observer,href:url.href});
|
|
969
1108
|
}
|
|
970
1109
|
return {as};
|
|
971
1110
|
}
|
|
972
1111
|
const importLinks = async () => {
|
|
973
1112
|
const observer = createObserver(document.body);
|
|
974
|
-
for (const link of
|
|
1113
|
+
for (const link of document.querySelectorAll(`link[href$=".html"][rel=module]`)) {
|
|
975
1114
|
await importLink(link, observer);
|
|
976
1115
|
}
|
|
977
1116
|
}
|
|
978
1117
|
|
|
979
1118
|
const bodyAsComponent = ({as = "x-body", unhide, framed} = {}) => {
|
|
980
|
-
const parent = document.body.parentElement;
|
|
981
1119
|
createComponent(as, document.body, {framed});
|
|
982
1120
|
const component = document.createElement(as);
|
|
983
|
-
|
|
1121
|
+
document.body.parentElement.replaceChild(component, document.body);
|
|
984
1122
|
Object.defineProperty(document, "body", {
|
|
985
1123
|
enumerable: true, configurable: true, get() {
|
|
986
1124
|
return component;
|
|
@@ -992,10 +1130,10 @@ const {observe} = (() => {
|
|
|
992
1130
|
const postMessage = (data, target = window.parent) => {
|
|
993
1131
|
if (postMessage.enabled) {
|
|
994
1132
|
if (target instanceof HTMLIFrameElement) {
|
|
995
|
-
data = {...data, href:
|
|
1133
|
+
data = {...data, href: document.baseURI};
|
|
996
1134
|
target.contentWindow.postMessage(JSON.stringify(data), "*");
|
|
997
1135
|
} else {
|
|
998
|
-
data = {...data, iframeId: document.lightviewId, href:
|
|
1136
|
+
data = {...data, iframeId: document.lightviewId, href: document.baseURI};
|
|
999
1137
|
target.postMessage(JSON.stringify(data), "*");
|
|
1000
1138
|
}
|
|
1001
1139
|
}
|
|
@@ -1018,11 +1156,11 @@ const {observe} = (() => {
|
|
|
1018
1156
|
resizeObserver.observe(node);
|
|
1019
1157
|
};
|
|
1020
1158
|
|
|
1021
|
-
const url = new URL(document.currentScript.getAttribute("src"),
|
|
1159
|
+
const url = new URL(document.currentScript.getAttribute("src"), document.baseURI);
|
|
1022
1160
|
let domContentLoadedEvent;
|
|
1023
1161
|
if (!domContentLoadedEvent) addListener(window, "DOMContentLoaded", (event) => domContentLoadedEvent = event);
|
|
1024
1162
|
let OBSERVER;
|
|
1025
|
-
const loader = async
|
|
1163
|
+
const loader = async whenFramed => {
|
|
1026
1164
|
await importLinks();
|
|
1027
1165
|
const unhide = !!document.querySelector('meta[name="l-unhide"]'),
|
|
1028
1166
|
isolated = !!document.querySelector('meta[name="l-isolate"]'),
|
|
@@ -1043,20 +1181,14 @@ const {observe} = (() => {
|
|
|
1043
1181
|
onresize(document.body, () => resize());
|
|
1044
1182
|
return
|
|
1045
1183
|
}
|
|
1046
|
-
if (
|
|
1047
|
-
|
|
1048
|
-
|
|
1184
|
+
if (["setAttribute","removeAttribute"].includes(type)) {
|
|
1185
|
+
let [name, value] = [...argsList];
|
|
1186
|
+
const variable = document.body.vars[name];
|
|
1187
|
+
if(type==="removeAttribute") value = undefined;
|
|
1049
1188
|
if (variable && variable.imported) document.body.setVariableValue(name, value);
|
|
1050
|
-
return;
|
|
1051
|
-
}
|
|
1052
|
-
if (type === "removeAttribute") {
|
|
1053
|
-
const [name] = argsList[0],
|
|
1054
|
-
variable = document.body.vars[name];
|
|
1055
|
-
if (variable && variable.imported) document.body.setVariableValue(name, undefined);
|
|
1056
|
-
|
|
1057
1189
|
}
|
|
1058
1190
|
});
|
|
1059
|
-
const url = new URL(
|
|
1191
|
+
const url = new URL(document.baseURI);
|
|
1060
1192
|
document.lightviewId = url.searchParams.get("id");
|
|
1061
1193
|
postMessage({type: "DOMContentLoaded"})
|
|
1062
1194
|
}
|
|
@@ -1070,7 +1202,7 @@ const {observe} = (() => {
|
|
|
1070
1202
|
iframe = document.getElementById(iframeId);
|
|
1071
1203
|
if (iframe) {
|
|
1072
1204
|
if (type === "DOMContentLoaded") {
|
|
1073
|
-
postMessage({type: "framed", href:
|
|
1205
|
+
postMessage({type: "framed", href: document.baseURI}, iframe);
|
|
1074
1206
|
Object.defineProperty(domContentLoadedEvent, "currentTarget", {
|
|
1075
1207
|
enumerable: false,
|
|
1076
1208
|
configurable: true,
|
|
@@ -1127,9 +1259,11 @@ const {observe} = (() => {
|
|
|
1127
1259
|
}
|
|
1128
1260
|
}
|
|
1129
1261
|
};
|
|
1130
|
-
const observer = OBSERVER = new MutationObserver(mutationCallback)
|
|
1131
|
-
|
|
1132
|
-
|
|
1262
|
+
const observer = OBSERVER = new MutationObserver(mutationCallback);
|
|
1263
|
+
// iframe = document.getElementById("myframe");
|
|
1264
|
+
for(const iframe of document.body.querySelectorAll("iframe")) {
|
|
1265
|
+
observer.observe(iframe, {attributes: true, attributeOldValue: true});
|
|
1266
|
+
}
|
|
1133
1267
|
}
|
|
1134
1268
|
}
|
|
1135
1269
|
}
|
|
@@ -1137,8 +1271,10 @@ const {observe} = (() => {
|
|
|
1137
1271
|
// loads for framed content
|
|
1138
1272
|
addListener(document, "DOMContentLoaded", (event) => loader(callback));
|
|
1139
1273
|
}
|
|
1140
|
-
Lightview
|
|
1141
|
-
|
|
1274
|
+
Object.defineProperty(Lightview,"whenFramed",{value:whenFramed});
|
|
1275
|
+
//Lightview.whenFramed = whenFramed;
|
|
1276
|
+
Lightview.loader = loader;
|
|
1277
|
+
//debugger;
|
|
1142
1278
|
if (window.location === window.parent.location || !(window.parent instanceof Window) || window.parent !== window) {
|
|
1143
1279
|
// loads for unframed content
|
|
1144
1280
|
// CodePen mucks with window.parent
|