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/package.json
CHANGED
package/repl_hold.html
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Lightview:REPL</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css">
|
|
9
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
10
|
+
<div id="content" style="flex:auto;min-height:100%;width:750px;max-width:750px;height:100%;border:1px solid;padding:10px;margin:0 auto">
|
|
11
|
+
<div style="display:flex;flex-direction:column;">
|
|
12
|
+
<div id="tabs" style="flex-grow:0;width:100%;border:1px;padding-bottom:5px;text-align:center">
|
|
13
|
+
<span style="padding-right:10px"><label for="headhtml" l-on:click="${onTabClick}">HTML Head</label><input for="headhtml" value="${headhtmlPinned}" type="checkbox" l-on:click="${onPinClick}"></span>
|
|
14
|
+
<span style="padding-right:10px"><label for="bodyhtml" l-on:click="${onTabClick}">HTML Body</label><input for="bodyhtml" value="${bodyhtmlPinned}" type="checkbox" l-on:click="${onPinClick}" checked></span>
|
|
15
|
+
<!--span style="padding-right:10px"><label for="markdown" l-on:click="${onTabClick}">Markdown (Body)</label><input for="markdown" value="${markdownPinned}" type="checkbox" l-on:click="${onPinClick}"></span-->
|
|
16
|
+
<span style="padding-right:10px"><label for="css" l-on:click="${onTabClick}">Style</label><input for="css" value="${cssPinned}" type="checkbox" l-on:click="${onPinClick}"></span>
|
|
17
|
+
<span style="padding-right:10px"><label for="script" l-on:click="${onTabClick}">Script</label><input for="script" value="${scriptPinned}" type="checkbox" l-on:click="${onPinClick}"></span>
|
|
18
|
+
<span style="padding-right:10px"><label for="preview" l-on:click="${onTabClick}">Preview</label><input for="preview" value="${previewPinned}" type="checkbox" l-on:click="${onPinClick}"></span>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div id="headhtml" style="display:none;margin:10px" class="language-html"><head><slot name="head"></slot></head></div>
|
|
22
|
+
<div id="bodyhtml" style="margin:10px" class="language-html"><body><slot name="body"></slot></body></div>
|
|
23
|
+
<!--textarea id="markdown" style="margin-right:2px;display:none">${markdown}</textarea-->
|
|
24
|
+
<div id="css" style="display:none;margin:10px" class="language-css"><style><slot name="css"></slot></style></div>
|
|
25
|
+
<div id="script" style="display:none;margin:10px" class="language-javascript"><script><slot name="script"></slot></script></div>
|
|
26
|
+
<iframe id="preview" style="max-width:99%;width:99%;margin:10px;display:none" src="./blank.html"></iframe>
|
|
27
|
+
</div>
|
|
28
|
+
<div style="width:100%;text-align:center">
|
|
29
|
+
<div style="padding:5px">${source}</div>
|
|
30
|
+
<button l-on:click="${doSave}">Save</button>
|
|
31
|
+
<button l-if="${!source.trim().startsWith('http')}" l-on:click="${doReset}">Reset</button>
|
|
32
|
+
</div>
|
|
33
|
+
<style id="style">
|
|
34
|
+
label:hover {
|
|
35
|
+
text-decoration: underline
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
38
|
+
<script id="lightview">
|
|
39
|
+
(document.currentComponent||(document.currentComponent=document.body)).mount = async function() {
|
|
40
|
+
const {CodeJar} = await import("https://cdn.jsdelivr.net/npm/codejar@3.6.0/codejar.min.js");
|
|
41
|
+
const turndownService = new TurndownService({
|
|
42
|
+
headingStyle: "atx",
|
|
43
|
+
codeBlockStyle: "fenced",
|
|
44
|
+
emDelimiter: "*"
|
|
45
|
+
});
|
|
46
|
+
turndownService.keep(() => true);
|
|
47
|
+
|
|
48
|
+
const {html, css, script} = await import("../types.js");
|
|
49
|
+
self.variables({
|
|
50
|
+
onTabClick: "function",
|
|
51
|
+
onPinClick: "function",
|
|
52
|
+
doSave: "function",
|
|
53
|
+
doReset: "function"
|
|
54
|
+
});
|
|
55
|
+
self.variables({
|
|
56
|
+
wysiwygPinned: "boolean",
|
|
57
|
+
bodyhtmlPinned: "boolean",
|
|
58
|
+
bodyhtml: html,
|
|
59
|
+
markdownPinned: "boolean",
|
|
60
|
+
markdown: "string",
|
|
61
|
+
cssPinned: "boolean",
|
|
62
|
+
cssText: css,
|
|
63
|
+
scriptPinned: "boolean",
|
|
64
|
+
scriptText: script,
|
|
65
|
+
headhtmlPinned: "boolean",
|
|
66
|
+
headhtml: html,
|
|
67
|
+
previewPinned: "boolean",
|
|
68
|
+
source: "string",
|
|
69
|
+
}, {reactive});
|
|
70
|
+
self.variables({
|
|
71
|
+
src: "string",
|
|
72
|
+
}, {imported});
|
|
73
|
+
|
|
74
|
+
bodyhtmlPinned = true;
|
|
75
|
+
|
|
76
|
+
const loadFromFile = async () => {
|
|
77
|
+
const html = await fs.readFile(url.pathname, {encoding: "utf8"});
|
|
78
|
+
source = `IndexedDB://${url.hostname + "_repl"}${url.pathname}`;
|
|
79
|
+
return html;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const loadFromServer = async () => {
|
|
83
|
+
const response = await fetch(url.href);
|
|
84
|
+
const html = await response.text();
|
|
85
|
+
source = url.href;
|
|
86
|
+
return html;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const parseFullHTML = (fullHTML) => {
|
|
90
|
+
const parser = new DOMParser(),
|
|
91
|
+
fragment = parser.parseFromString(fullHTML, "text/html"),
|
|
92
|
+
body_el = fragment?.querySelector("body"),
|
|
93
|
+
style_el = fragment?.querySelector("style"),
|
|
94
|
+
script_el = body_el?.querySelector("script");
|
|
95
|
+
if (style_el) {
|
|
96
|
+
cssText = style_el?.innerHTML.trim();
|
|
97
|
+
style_el.remove();
|
|
98
|
+
} else {
|
|
99
|
+
cssText = "";
|
|
100
|
+
}
|
|
101
|
+
if (script_el) {
|
|
102
|
+
scriptText = script_el?.innerHTML.trim(),
|
|
103
|
+
script_el.remove();
|
|
104
|
+
} else {
|
|
105
|
+
scriptText = "";
|
|
106
|
+
}
|
|
107
|
+
headhtml = (fragment?.head.innerHTML || "").trim();
|
|
108
|
+
bodyhtml = body_el?.innerHTML.trim();
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
let fs,
|
|
112
|
+
url;
|
|
113
|
+
if (src) {
|
|
114
|
+
url = new URL(src, window.location.href);
|
|
115
|
+
fs = new LightningFS(url.hostname + "_repl").promises;
|
|
116
|
+
try {
|
|
117
|
+
parseFullHTML(await loadFromFile());
|
|
118
|
+
} catch (e) {
|
|
119
|
+
try {
|
|
120
|
+
parseFullHTML(await loadFromServer());
|
|
121
|
+
} catch (e) {
|
|
122
|
+
fullHTML = e.message;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
url = new URL(window.location.href);
|
|
127
|
+
fs = new LightningFS(url.hostname + "_repl").promises;
|
|
128
|
+
bodyhtml = "";
|
|
129
|
+
headhtml = "";
|
|
130
|
+
scriptText = "";
|
|
131
|
+
cssText = "";
|
|
132
|
+
}
|
|
133
|
+
;
|
|
134
|
+
|
|
135
|
+
// initialize variables
|
|
136
|
+
markdown = "";
|
|
137
|
+
|
|
138
|
+
const tabs = [...self.querySelectorAll("label[for]")]
|
|
139
|
+
.map((label) => {
|
|
140
|
+
const id = label.getAttribute("for");
|
|
141
|
+
return [id, self.getElementById(id), label];
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const showTab = (targetid) => {
|
|
145
|
+
tabs.forEach(([id, el, label]) => {
|
|
146
|
+
if (id === targetid || self.varsProxy[`${id}Pinned`]) {
|
|
147
|
+
el.style.display = "block";
|
|
148
|
+
} else if (!self.varsProxy[`${id}Pinned`]) {
|
|
149
|
+
el.style.display = "none";
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const hideTab = (targetid) => {
|
|
155
|
+
tabs.forEach(([id, el, label]) => {
|
|
156
|
+
if (id === targetid) el.style.display = "none";
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
onTabClick = (event) => {
|
|
161
|
+
showTab(event.target.getAttribute("for"));
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
onPinClick = (event) => {
|
|
165
|
+
const id = event.target.getAttribute("for"),
|
|
166
|
+
checked = self.varsProxy[`${id}Pinned`] = event.target.checked;
|
|
167
|
+
if (checked) onTabClick(event);
|
|
168
|
+
else hideTab(id);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
doSave = async () => {
|
|
172
|
+
const parts = url.pathname.split("/");
|
|
173
|
+
let dir = "";
|
|
174
|
+
parts.shift();
|
|
175
|
+
parts.pop();
|
|
176
|
+
for (const part of parts) {
|
|
177
|
+
dir = dir + "/" + part;
|
|
178
|
+
try {
|
|
179
|
+
await fs.mkdir(dir);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
if (e.message === "EEXIST") break;
|
|
182
|
+
throw e;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
fs.writeFile(url.pathname, doPreview(), {encoding: "utf8"}, () => {
|
|
186
|
+
});
|
|
187
|
+
source = `IndexedDB://${url.hostname + "_repl"}${url.pathname}`;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
doReset = async () => {
|
|
191
|
+
try {
|
|
192
|
+
await fs.unlink(url.pathname);
|
|
193
|
+
} catch (e) {
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
if (src) {
|
|
197
|
+
try {
|
|
198
|
+
parseFullHTML(await loadFromServer());
|
|
199
|
+
doPreview();
|
|
200
|
+
} catch (e) {
|
|
201
|
+
previewEl.innerHTML = fullHTML = e.message;
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
bodyhtml = "";
|
|
205
|
+
headhtml = "";
|
|
206
|
+
scriptText = "";
|
|
207
|
+
cssText = "";
|
|
208
|
+
previewEl.innerHTML = "";
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const doPreview = () => {
|
|
213
|
+
// not quite write, createComponent needs to be reworked to handle a head and import its links
|
|
214
|
+
/*const template = document.createElement("template");
|
|
215
|
+
template.innerHTML = bodyhtml +
|
|
216
|
+
"<style>" + cssText + "</style>" +
|
|
217
|
+
'<script id="lightview">' + scriptText + "<" + "/script>";
|
|
218
|
+
const component = window.customElements.get("x-preview");
|
|
219
|
+
if (component) {
|
|
220
|
+
component.setTemplateNode(template);
|
|
221
|
+
} else {
|
|
222
|
+
Lightview.createComponent("x-preview", template);
|
|
223
|
+
}
|
|
224
|
+
previewEl.innerHTML = "<x-preview></x-preview>";
|
|
225
|
+
return "<html><head>" + headhtml + "</head><body>" + template.innerHTML + "<body></html>"*/
|
|
226
|
+
const str =
|
|
227
|
+
"<html>" +
|
|
228
|
+
"<head>" + headhtml.replace("./lightview.js",new URL("./lightview.js",window.location.href).href) +" </head>" +
|
|
229
|
+
"<body>" + bodyhtml +
|
|
230
|
+
"<style>" + cssText + "</style>" +
|
|
231
|
+
'<script id="lightview">debugger;' + scriptText + "<" + "/script>" +
|
|
232
|
+
"</body>" +
|
|
233
|
+
"</html>",
|
|
234
|
+
blob = new Blob([str], {type : 'text/html'}),
|
|
235
|
+
newurl = window.URL.createObjectURL(blob);
|
|
236
|
+
previewEl.src = newurl;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const tabsEl = self.getElementById("tabs"),
|
|
240
|
+
headhtmlEl = document.body.querySelector('[slot="head"]'),
|
|
241
|
+
bodyhtmlEl = document.body.querySelector('[slot="body"]'),
|
|
242
|
+
markdownEl = self.getElementById("markdown"),
|
|
243
|
+
cssEl = document.body.querySelector('[slot="css"]'),
|
|
244
|
+
scriptEl = document.body.querySelector('[slot="script"]'),
|
|
245
|
+
previewEl = self.getElementById("preview");
|
|
246
|
+
|
|
247
|
+
const highlight = (el,...args) => {
|
|
248
|
+
hljs.highlightElement(el,...args);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if(headhtmlEl) {
|
|
252
|
+
headhtmlEl.className = "language-html";
|
|
253
|
+
const bodyJar = CodeJar(headhtmlEl, highlight);
|
|
254
|
+
bodyJar.updateCode(headhtml);
|
|
255
|
+
bodyJar.onUpdate(code => {
|
|
256
|
+
headhtml = code;
|
|
257
|
+
doPreview();
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if(bodyhtmlEl) {
|
|
262
|
+
bodyhtmlEl.className = "language-html";
|
|
263
|
+
const bodyJar = CodeJar(bodyhtmlEl, highlight);
|
|
264
|
+
bodyJar.updateCode(bodyhtml);
|
|
265
|
+
bodyJar.onUpdate(code => {
|
|
266
|
+
bodyhtml = code;
|
|
267
|
+
doPreview();
|
|
268
|
+
})
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if(cssEl) {
|
|
272
|
+
cssEl.className = "language-css";
|
|
273
|
+
const bodyJar = CodeJar(cssEl, highlight);
|
|
274
|
+
bodyJar.updateCode(cssText);
|
|
275
|
+
bodyJar.onUpdate(code => {
|
|
276
|
+
cssText = code;
|
|
277
|
+
doPreview();
|
|
278
|
+
})
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if(scriptEl) {
|
|
282
|
+
scriptEl.className = "language-javascript";
|
|
283
|
+
const bodyJar = CodeJar(scriptEl, highlight);
|
|
284
|
+
bodyJar.updateCode(scriptText);
|
|
285
|
+
bodyJar.onUpdate(code => {
|
|
286
|
+
scriptText = code;
|
|
287
|
+
doPreview();
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
doPreview();
|
|
292
|
+
|
|
293
|
+
/*let prevmarkdown; // prevents indirect recursion
|
|
294
|
+
observe(() => {
|
|
295
|
+
const text = turndownService.turndown(bodyhtml).trim();
|
|
296
|
+
if (text && text !== prevtext) {
|
|
297
|
+
markdown = markdownEl.innerHTML = prevmarkdown = text;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
let prevbodyhtml; // prevents indirect recursion
|
|
302
|
+
observe(() => {
|
|
303
|
+
const html = marked.parse(markdown).trim();
|
|
304
|
+
if (html && html !== prevbodyhtml) {
|
|
305
|
+
bodyhtml = bodyhtmlEl.innerText = prevbodyhtml = html;
|
|
306
|
+
}
|
|
307
|
+
});*/
|
|
308
|
+
|
|
309
|
+
const initEditor = () => {
|
|
310
|
+
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
self.addEventListener("mounted", () => {
|
|
314
|
+
initEditor();
|
|
315
|
+
doPreview();
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
</script>
|
|
319
|
+
</body>
|
|
320
|
+
</html>
|
package/test/basic.html
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<title>Basic</title>
|
|
6
6
|
<template id="x-test" name="joe" open="true" count=1 children='["mary"]' l-on:click="${bump}">
|
|
7
7
|
|
|
8
|
+
<input id="iuntyped" value="${iuntyped}">
|
|
8
9
|
<input id="idatetime" type="datetime" value="${idatetime}">
|
|
9
10
|
|
|
10
11
|
<span id="children">${children}</span>
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
<span id="age">${age}</span>
|
|
19
20
|
<span id="hamburger">${hamburger}</span>
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
<input id="itext" type="text" value="${itext}">
|
|
23
24
|
<input id="itel" type="tel" value="${itel}">
|
|
24
25
|
<input id="iemail" type="email" value="${iemail}">
|
|
@@ -35,8 +36,16 @@
|
|
|
35
36
|
<input id="icheckbox" type="checkbox" value="${icheckbox}">
|
|
36
37
|
</template>
|
|
37
38
|
<script>
|
|
38
|
-
document.getElementById("x-test").mount = (self) => {
|
|
39
|
+
document.getElementById("x-test").mount = async (self) => {
|
|
39
40
|
// debugger;
|
|
41
|
+
self.variables(
|
|
42
|
+
{
|
|
43
|
+
shared:"function"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
constant: (await import("../types.js")).shared
|
|
47
|
+
}
|
|
48
|
+
);
|
|
40
49
|
self.variables({name: "string", open: "boolean", count: "number", children: Array}, {imported, reactive});
|
|
41
50
|
self.variables({color: "string", checked: "boolean", age: "number", hamburger: Array}, {
|
|
42
51
|
exported,
|
|
@@ -52,7 +61,8 @@
|
|
|
52
61
|
counter = 0;
|
|
53
62
|
myshare = 1;
|
|
54
63
|
|
|
55
|
-
self.addEventListener("
|
|
64
|
+
self.addEventListener("mounted", ({target}) => {
|
|
65
|
+
debugger;
|
|
56
66
|
iuntyped = "test";
|
|
57
67
|
itext = "test";
|
|
58
68
|
itel = "test";
|
|
@@ -80,8 +90,9 @@
|
|
|
80
90
|
});
|
|
81
91
|
}
|
|
82
92
|
</script>
|
|
83
|
-
<script src="../
|
|
93
|
+
<script src="../lightview.js"></script>
|
|
84
94
|
<script>
|
|
95
|
+
Lightview.createInputVariables = true;
|
|
85
96
|
Lightview.createComponent("x-test",document.getElementById("x-test"));
|
|
86
97
|
</script>
|
|
87
98
|
</head>
|
package/test/basic.test.mjs
CHANGED
|
@@ -164,7 +164,7 @@ describe('Lightview - Variables', () => {
|
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
test('untyped input - iuntyped should be "test"', async () => {
|
|
167
|
-
const result = await page.evaluate(
|
|
167
|
+
const result = await page.evaluate(() => {
|
|
168
168
|
const el = document.getElementById("test"),
|
|
169
169
|
result = el.getElementById("iuntyped")
|
|
170
170
|
return result.getAttribute("value");
|
package/test/extended.html
CHANGED
package/types.js
CHANGED
|
@@ -17,6 +17,41 @@ function reviver(property,value) {
|
|
|
17
17
|
if(value==="@NaN") return NaN;
|
|
18
18
|
return value;
|
|
19
19
|
}
|
|
20
|
+
const deepEqual = (a,b,matchType=deepEqual.LEFT, seen=new Set()) => {
|
|
21
|
+
if(matchType===deepEqual.RIGHT) return deepEqual(b,a,deepEqual.LEFT,seen);
|
|
22
|
+
if(matchType===deepEqual.COMMUTATIVE) return deepEqual(a,b,deepEqual.LEFT) && deepEqual(b,a,deepEqual.LEFT);
|
|
23
|
+
if(a===b) return true;
|
|
24
|
+
const type = typeof(a);
|
|
25
|
+
if(type==="function" || type!==typeof(b) || (a && !b) || (b && !a)) return false;
|
|
26
|
+
if(type==="number" && isNaN(a) && isNaN(b)) return true;
|
|
27
|
+
if(a && type==="object") {
|
|
28
|
+
if(seen.has(a)) return true;
|
|
29
|
+
seen.add(a);
|
|
30
|
+
if(a.constructor!==b.constructor || a.length!==b.length || a.size!==b.size) return false;
|
|
31
|
+
if(a instanceof Date) a.getTime() === b.getTime();
|
|
32
|
+
if(a instanceof RegExp) return a.toString() === b.toString();
|
|
33
|
+
if(a instanceof Set) {
|
|
34
|
+
for(const avalue of [...a]) {
|
|
35
|
+
if(![...b].some((bvalue) => deepEqual(avalue,bvalue,matchType,seen))) return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if(a instanceof Map) {
|
|
40
|
+
for(const [key,value] of [...a]) {
|
|
41
|
+
if(!deepEqual(b.get(key),value,matchType,seen)) return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
for(const key in a) {
|
|
46
|
+
if(!deepEqual(a[key],b[key],matchType,seen)) return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
deepEqual.LEFT = 1;
|
|
53
|
+
deepEqual.COMMUTATIVE = 2;
|
|
54
|
+
deepEqual.RIGHT = 3;
|
|
20
55
|
|
|
21
56
|
function ValidityState(options) {
|
|
22
57
|
if(!this || !(this instanceof ValidityState)) return new ValidityState(options);
|
|
@@ -245,11 +280,11 @@ const validateNumber = function(value,variable) {
|
|
|
245
280
|
variable.validityState = ValidityState({typeMismatch:true,value});
|
|
246
281
|
} else if(isNaN(result) && !this.allowNaN) {
|
|
247
282
|
variable.validityState = ValidityState({badInput:true,value});
|
|
248
|
-
} else if(this.min!=null && result<this.min) {
|
|
283
|
+
} else if(this.min!=null && result<this.min && !(this.min===-Infinity && isNaN(result))) {
|
|
249
284
|
variable.validityState = ValidityState({rangeUnderflow:true,value});
|
|
250
|
-
} else if(this.max!=null && result>this.max) {
|
|
285
|
+
} else if(this.max!=null && result>this.max && !(this.max===Infinity && isNaN(result))) {
|
|
251
286
|
variable.validityState = ValidityState({rangeOverflow:true,value});
|
|
252
|
-
} else if(this.step
|
|
287
|
+
} else if(this.step!=null && (result % this.step)!==0) {
|
|
253
288
|
variable.validityState = ValidityState({rangeUnderflow:true,value});
|
|
254
289
|
} else {
|
|
255
290
|
variable.validityState = ValidityState({valid:true});
|
|
@@ -258,7 +293,7 @@ const validateNumber = function(value,variable) {
|
|
|
258
293
|
}
|
|
259
294
|
return this.whenInvalid(variable,value);
|
|
260
295
|
}
|
|
261
|
-
const number = ({coerce=false,required = false,whenInvalid = ifInvalid,min=-Infinity,max=Infinity,step
|
|
296
|
+
const number = ({coerce=false,required = false,whenInvalid = ifInvalid,min=-Infinity,max=Infinity,step,allowNaN = true,...rest}={}) => {
|
|
262
297
|
if(typeof(coerce)!=="boolean") throw new TypeError(`coerce, ${JSON.stringify(coerce)}, must be a boolean`);
|
|
263
298
|
if(typeof(required)!=="boolean") throw new TypeError(`required, ${JSON.stringify(required)}, must be a boolean`);
|
|
264
299
|
if(typeof(whenInvalid)!=="function") throw new TypeError(`whenInvalid, ${whenInvalid}, must be a function`);
|
|
@@ -484,51 +519,89 @@ const put = (href,variable) => {
|
|
|
484
519
|
}
|
|
485
520
|
|
|
486
521
|
const handleRemote = async ({variable, functionalType, config=functionalType, component},doput) => {
|
|
487
|
-
const type = typeof (config);
|
|
488
522
|
let value;
|
|
489
|
-
if
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
: config.get(href,variable));
|
|
507
|
-
if(config.ttl && !doput && !config.intervalId) {
|
|
508
|
-
config.intervalId = setInterval(async () => {
|
|
509
|
-
await handleRemote({variable, config, component});
|
|
510
|
-
//schedule();
|
|
511
|
-
},config.ttl);
|
|
512
|
-
}
|
|
513
|
-
if(variable.value===undefined) variable.value = value;
|
|
523
|
+
if(!config.path) config.path = `./${variable.name}`;
|
|
524
|
+
if(config.path.endsWith("/")) config.path = `${config.path}${variable.name}`;
|
|
525
|
+
const href = new URL(config.path,new URL(config.base.replace("blob:","")).href).href;
|
|
526
|
+
if(!config.get || !config.put) {
|
|
527
|
+
if(!href) throw new Error(`A remote path is required if no put function is provided for remote data`)
|
|
528
|
+
if(!config.get) config.get = get;
|
|
529
|
+
if(!config.put && variable.reactive) config.put = put;
|
|
530
|
+
}
|
|
531
|
+
let areequal;
|
|
532
|
+
value = (doput
|
|
533
|
+
? (areequal = deepEqual(variable.value,config.previousValue,deepEqual.COMMUTATIVE)) ? variable.value : config.put(href,variable)
|
|
534
|
+
: config.get(href,variable));
|
|
535
|
+
if(config.ttl && !doput && !config.intervalId) {
|
|
536
|
+
config.intervalId = setInterval(async () => {
|
|
537
|
+
await handleRemote({variable, config, component});
|
|
538
|
+
//schedule();
|
|
539
|
+
},config.ttl);
|
|
514
540
|
}
|
|
515
|
-
if(value)
|
|
516
|
-
|
|
541
|
+
if(variable.value===undefined) variable.value = value;
|
|
542
|
+
if(value && !areequal) {
|
|
543
|
+
const json = config.previousValue = await value;
|
|
517
544
|
if (json && typeof (json) === "object" && variable.reactive) {
|
|
518
|
-
|
|
545
|
+
component.setVariableValue(variable.name,remoteProxy({json, variable,config, component}));
|
|
546
|
+
// variable.value = remoteProxy({json, variable,config, component});
|
|
519
547
|
} else {
|
|
520
548
|
component.setVariableValue(variable.name,json);
|
|
521
549
|
}
|
|
522
550
|
}
|
|
523
551
|
}
|
|
524
552
|
|
|
525
|
-
const remote = (config) => {
|
|
553
|
+
const remote = (config,base=document.baseURI||window.location.href) => {
|
|
554
|
+
if(typeof(config)==="string") config = {path:config};
|
|
555
|
+
config.base = base;
|
|
526
556
|
return {
|
|
527
|
-
config,
|
|
557
|
+
config:{...config},
|
|
528
558
|
handleRemote
|
|
529
559
|
}
|
|
530
560
|
}
|
|
531
561
|
|
|
562
|
+
const shared = () => {
|
|
563
|
+
return {
|
|
564
|
+
init({variable, component}) {
|
|
565
|
+
const name = variable.name,
|
|
566
|
+
set = variable.set || (() => {});
|
|
567
|
+
variable.shared = true;
|
|
568
|
+
variable.set = function(newValue) {
|
|
569
|
+
set(newValue);
|
|
570
|
+
if(this.shared) { // still shared
|
|
571
|
+
component.siblings.forEach((instance) => {
|
|
572
|
+
const svariable = instance.vars[name];
|
|
573
|
+
if(svariable?.shared) {
|
|
574
|
+
instance.setVariableValue(name, newValue);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// initialize
|
|
580
|
+
component.siblings.forEach((instance) => {
|
|
581
|
+
if(instance===component) return;
|
|
582
|
+
const svariable = instance.vars[name];
|
|
583
|
+
if(svariable?.shared) {
|
|
584
|
+
if(variable.value==null) {
|
|
585
|
+
if(svariable.value!=null) variable.value = instance.vars[name].value
|
|
586
|
+
} else instance.setVariableValue(name, variable.value);
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const observed = () => {
|
|
594
|
+
return {
|
|
595
|
+
init({variable, component}) {
|
|
596
|
+
if(variable.observed) return;
|
|
597
|
+
const name = variable.name;
|
|
598
|
+
variable.value = component.hasAttribute(name) ? coerce(component.getAttribute(name), variable.type) : variable.value;
|
|
599
|
+
variable.observed = true;
|
|
600
|
+
component.observedAttributes.add(name);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
532
605
|
const remoteGenerator = handleRemote;
|
|
533
606
|
|
|
534
|
-
export {ValidityState,any,array,boolean,duration,number,object,remote,remoteGenerator,string,symbol,reviver,html,css,script}
|
|
607
|
+
export {ValidityState,any,array,boolean,duration,number,object,remote,shared,observed,remoteGenerator,string,symbol,reviver,html,css,script}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<head>
|
|
3
|
-
<title>Lightview:Chart:Example</title>
|
|
4
|
-
<link href="./chart.html" rel="module">
|
|
5
|
-
<script src="../../lightview.js"></script>
|
|
6
|
-
</head>
|
|
7
|
-
<body>
|
|
8
|
-
<l-chart id="myPieChart" type="PieChart" style="height:500px;" title="How Much Pizza I Ate Last Night">
|
|
9
|
-
{
|
|
10
|
-
options: { },
|
|
11
|
-
columns: [
|
|
12
|
-
{label: "Topping", type: "string"},
|
|
13
|
-
{label: "Slices", type: "number"}
|
|
14
|
-
],
|
|
15
|
-
rows: [
|
|
16
|
-
["Mushrooms", 3],
|
|
17
|
-
["Onions", 1],
|
|
18
|
-
["Olives", 1],
|
|
19
|
-
["Zucchini", 1],
|
|
20
|
-
["Pepperoni", 2]
|
|
21
|
-
]
|
|
22
|
-
}
|
|
23
|
-
</l-chart>
|
|
24
|
-
<script>
|
|
25
|
-
const el = document.getElementById("myPieChart");
|
|
26
|
-
el.addEventListener("mounted",() => {
|
|
27
|
-
chart = el.chart;
|
|
28
|
-
chart.addRow(["Anchovies",1]);
|
|
29
|
-
});
|
|
30
|
-
</script>
|
|
31
|
-
</body>
|
|
32
|
-
</html>
|