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