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