lightview 1.8.2 → 2.0.0
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/.agent/workflows/daisyui-component-migration.md +155 -0
- 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/lightview.js.backup +793 -0
- 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/docs/old_index.html
DELETED
|
@@ -1,965 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<link rel="stylesheet" href="//codepen.io/CodePenTemplates/pen/GNoNGy.css" />
|
|
6
|
-
<link rel="stylesheet" href="//codepen.io/CodePenTemplates/pen/XNXpgM.css" />
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<section class="markdown-body">
|
|
10
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
11
|
-
<base target="_tab">
|
|
12
|
-
<style>
|
|
13
|
-
.markdown-body { margin:opx; padding: 0px; max-width: 100%; }
|
|
14
|
-
</style>
|
|
15
|
-
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
16
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/styles/default.min.css">
|
|
17
|
-
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/highlight.min.js"></script>
|
|
18
|
-
<script src="https://kit.fontawesome.com/aee33b06ff.js" crossorigin="anonymous"></script>
|
|
19
|
-
<div style="position:fixed;min-width:100%;opacity:1;background:white;margin-top:0px;height:1.50em;z-index:10;"><a href="https://lightview.dev">lightview.dev</a> v1.8.1b (BETA)</div>
|
|
20
|
-
|
|
21
|
-
<div id="TOC" style="position:fixed;top:3em;max-height:97%;height:97%;opacity:1;max-width:275px;">
|
|
22
|
-
<div id="header" "font-size:125%;font-weight:bold;">
|
|
23
|
-
<span id="toggle-button" style="display:none;float:right;font-weight:bold;margin-top:0px"><<</span>
|
|
24
|
-
</div>
|
|
25
|
-
<div class="toc" style="border:1px solid grey;margin-right:5px;border-radius:5px;overflow-x:hidden;overflow-y:auto;background:whitesmoke">
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
<div id="content" style="float:right;padding-top:0px;padding-right:10px;max-height:100vh;overflow:auto;opacity:1;position:relative;left:10px;top:3px">
|
|
29
|
-
|
|
30
|
-
<h3 id="introduction-to-lightview">Introduction to Lightview</h3>
|
|
31
|
-
<p>Small, simple, powerful web UI and micro front end creation ...</p>
|
|
32
|
-
<p>Great ideas from Svelte, React, Vue and Riot plus more combined into one small tool: 7.5K (minified/gzipped)</p></p>
|
|
33
|
-
<p style="width:99%;">
|
|
34
|
-
A ToDo demo does not get much simpler than this:
|
|
35
|
-
<span style="float:right">
|
|
36
|
-
<i class="fa-brands fa-codepen"></i>
|
|
37
|
-
<a href="https://codepen.io/anywhichway/details/ExQPYZL">lightview-todo</a>
|
|
38
|
-
</span>
|
|
39
|
-
</p>
|
|
40
|
-
<pre height="100"><code src="./examples/todo.html"></code></pre>
|
|
41
|
-
|
|
42
|
-
<h4 id="what-you-get">What You Get</h4>
|
|
43
|
-
<ol>
|
|
44
|
-
<li><p>Single file and <a href="#local-templates" target=_self>template</a> components.</p>
|
|
45
|
-
</li>
|
|
46
|
-
<li><p><a href="#sandboxed-components">Sandboxed remote components</a> and micro front ends</a>.</p>
|
|
47
|
-
</li>
|
|
48
|
-
<li><p><a href="#unit-testing">Unit testable</a> components and a <a href="#debugging">debug mode</a> for using standard JavaScript debuggers</a>. </p>
|
|
49
|
-
</li>
|
|
50
|
-
<li><p>No pre-deployment transpilation/compilation required. </p>
|
|
51
|
-
</li>
|
|
52
|
-
<li><p>Svelte like variable usage, i.e. write your state modifying code like normal code.</p>
|
|
53
|
-
</li>
|
|
54
|
-
<li><p>Extended variable type declarations including <code>min</code>, <code>max</code> and <code>step</code> on <code>number</code> or limits on <code>string</code> and <code>array</code> lengths.</p>
|
|
55
|
-
</li>
|
|
56
|
-
<li><p><a href="#variables">TypeScript like</a> runtime type checking of variables in components.</p>
|
|
57
|
-
</li>
|
|
58
|
-
<li><p>Automatic server retrieval and update of variables declared as <code>remote</code>.</p>
|
|
59
|
-
</li>
|
|
60
|
-
<li><p>Automatic import, export, cross-component sync, or reactive response to attributes/props/variables. See <a href="#super-variable">superVariable</a>.</p>
|
|
61
|
-
</li>
|
|
62
|
-
<li><p><a href="#auto-binding-inputs-and-forms">Automatic input field variable creation and binding</a>.</p>
|
|
63
|
-
</li>
|
|
64
|
-
<li><p><a href="#attribute-directives">Attribute directives</a> like <code>l-if</code>, and a single powerful <code>l-for</code> that handles array and object keys, values, and entries.</p>
|
|
65
|
-
</li>
|
|
66
|
-
<li><p>Reactive string template literals for content and attribute value replacement.</p>
|
|
67
|
-
</li>
|
|
68
|
-
<li><p>No virtual DOM. The Lightview dependency tracker laser targets just those nodes that need updates.</p>
|
|
69
|
-
</li>
|
|
70
|
-
<li><p>SPA, and MPA friendly ... somewhat SEO friendly and short steps away from fully SEO friendly.</p>
|
|
71
|
-
</li>
|
|
72
|
-
<li><p>A <a href="components">component library</a> including charts and gauges that work in Markdown files.</p>
|
|
73
|
-
</li>
|
|
74
|
-
<li><p>Lots of live <a href="#examples">editable examples</a>.</p>
|
|
75
|
-
<p>If you like what you get on the front-end with <code>Lightview</code>, check out our back-end reactive framework <code>Watchlight</code> at <a href="https://watchlight.dev">https://watchlight.dev</a>.</p>
|
|
76
|
-
</li>
|
|
77
|
-
</ol>
|
|
78
|
-
<h4 id="usage">Usage</h4>
|
|
79
|
-
<p>Install it from <a href="https://www.npmjs.com/package/lightview">NPMJS</a>. Or, visit the repository on <a href="https://www.github.com/anywhichway/lightview">GitHub</a>.
|
|
80
|
-
Note, we will actively iterate on CodePen and the NPM or GitHub versions may not be the most recent. If you want the bleeding edge
|
|
81
|
-
<a href="https://000686818.codepen.website/lightview.js">download it from CodePen</a> and include it in your build pipeline.</p>
|
|
82
|
-
<p>It should be evident from the todo example above, Lightview components are just HTML files similar to Riot or Vue's SFCs. And, for
|
|
83
|
-
the most part, the code you write looks like regular JavaScript ... we modeled this on Svelte.</p>
|
|
84
|
-
<p>The script blocks in these files are set to type <code>lightview/module</code> and inside them the variable <code>self</code> is bound to the component.</p>
|
|
85
|
-
<p>The HTML in the files is rendered in a shadow DOM node and the style blocks and scripts are isolated to that shadow DOM.</p>
|
|
86
|
-
<p>You use the components in other files by inserting the Lightview script in the head of the file where you want ot use the component and load the components
|
|
87
|
-
using <code>link</code> tags, e.g.</p>
|
|
88
|
-
<pre><code><head>
|
|
89
|
-
<<span class="hljs-meta">link</span> href=<span class="hljs-string">"../components/gantt/gantt.html"</span> rel=<span class="hljs-string">"module"</span>>
|
|
90
|
-
<script src=<span class="hljs-string">"./lightview.js"</span>></script>
|
|
91
|
-
</head>
|
|
92
|
-
<body>
|
|
93
|
-
<l-gantt id=<span class="hljs-string">"myChart"</span> style=<span class="hljs-string">"height:500px;"</span> <span class="hljs-meta">title</span>=<span class="hljs-string">"Research Project"</span> hidden l-unhide>
|
|
94
|
-
{
|
|
95
|
-
<span class="hljs-meta">options</span>: { },
|
|
96
|
-
rows: [
|
|
97
|
-
[<span class="hljs-string">'Research'</span>, <span class="hljs-string">'Find sources'</span>,<span class="hljs-string">"2015-01-01"</span>, <span class="hljs-string">"2015-01-05"</span>, <span class="hljs-meta">null</span>, 100, <span class="hljs-meta">null</span>],
|
|
98
|
-
[<span class="hljs-string">'Write'</span>, <span class="hljs-string">'Write paper'</span>,<span class="hljs-meta">null</span>,<span class="hljs-string">"2015-01-09"</span>, <span class="hljs-string">"3d"</span>, 25, <span class="hljs-string">'Research,Outline'</span>],
|
|
99
|
-
[<span class="hljs-string">'Cite'</span>, <span class="hljs-string">'Create bibliography'</span>,<span class="hljs-meta">null</span>, <span class="hljs-string">"2015-01-07"</span>,<span class="hljs-string">"1d"</span> , 20, <span class="hljs-string">'Research'</span>],
|
|
100
|
-
[<span class="hljs-string">'Complete'</span>, <span class="hljs-string">'Hand in paper'</span>, <span class="hljs-meta">null</span>, <span class="hljs-string">"2015-01-10"</span>, <span class="hljs-string">"1d"</span> , 0, <span class="hljs-string">'Cite,Write'</span>],
|
|
101
|
-
[<span class="hljs-string">'Outline'</span>, <span class="hljs-string">'Outline paper'</span>, <span class="hljs-meta">null</span>, <span class="hljs-string">"2015-01-06"</span>, <span class="hljs-string">"1d"</span> , 100, <span class="hljs-string">'Research'</span>]
|
|
102
|
-
]
|
|
103
|
-
}
|
|
104
|
-
</l-gantt>
|
|
105
|
-
</body>
|
|
106
|
-
</code></pre>
|
|
107
|
-
<p>You can do the same in a Markdown file, just leave out the <code>head</code> and <code>body</code> tags. You can see the <a href="examples/markdown.md">source of a Markdown file</a>, or <a href="examples/markdown.html">see it rendered</a>.</p>
|
|
108
|
-
<h3 id="api">API</h3>
|
|
109
|
-
<p><a id="variables"></a></p>
|
|
110
|
-
<h4 id="variables">Variables</h4>
|
|
111
|
-
<p>Much of the power of Lightview comes from its expressive variable declarations.</p>
|
|
112
|
-
<p>You can use the normal declarations <code>var</code>, <code>let</code>, and <code>const</code> for data that only needs to be present
|
|
113
|
-
within a script block. For other data, you need to declare variables using <code>self.variables(options)</code>.</p>
|
|
114
|
-
<pre><code class="language-javascript">Object <span class="hljs-keyword">self</span>.variables(
|
|
115
|
-
{[<span class="hljs-symbol">name:</span>string]<span class="hljs-symbol">:dataType</span><span class="hljs-symbol">:string</span>, [ ...]},
|
|
116
|
-
[{[functionalType]<span class="hljs-symbol">:boolean|string|object</span>, ...}]
|
|
117
|
-
);
|
|
118
|
-
</code></pre>
|
|
119
|
-
<p>The available <code>dataTypes</code> are <code>"any"</code>, <code>"array"</code>, <code>"boolean"</code>, <code>"number"</code>, <code>"object"</code>, <code>"string"</code>. You can also provide any object constructor like, <code>Array</code>.</p>
|
|
120
|
-
<p><code>FunctionalTypes</code> apply behaviors to variables. The available <code>functionalTypes</code> are <code>shared</code>, <code>reactive</code>, <code>imported</code>, <code>exported</code>, <code>observed</code>, <code>remote</code>, <code>set</code>, <code>constant</code>. These are reserved words and, with the exception of <code>set</code> and <code>constant</code>, are themselves functions that configure variable behavior.</p>
|
|
121
|
-
<p>If a varibable is <code>required</code>, <code>set</code> or <code>constant</code> DO NOT need to be present. The <code>required</code> is enforced when attempts are made to set the variable.</p>
|
|
122
|
-
<p><code>set</code> can take any value that is consistent with the <code>dataType</code> of the variable(s) it is used with.</p>
|
|
123
|
-
<p><code>constant</code> can take any value that is consistent with the <code>dataType</code> of the variable(s) it is used with.</p>
|
|
124
|
-
<p><code>set</code> and <code>constant</code> can't be used together.</p>
|
|
125
|
-
<p>See <a href="#more-on-remote-variables">More On Remote Variables</a> for details on the <code>string</code> and <code>object</code> value configuration options for <code>remote</code>.</p>
|
|
126
|
-
<p>As a result of the use of the variable definition approach, Lightview variable declarations look very much like <code>TypeScript</code>.</p>
|
|
127
|
-
<p><a id="super-variable"></a>
|
|
128
|
-
You would probably never make a variable this powerful, but the below illustrates the use of most of the types of variables.</p>
|
|
129
|
-
<pre><code class="language-javascript">self.variables({superVariable:<span class="hljs-string">"number"</span>}, { set:<span class="hljs-number">1</span>, imported, exported, shared, reactive, observed, remote:remote(<span class="hljs-string">"./superVariable"</span>)});
|
|
130
|
-
|
|
131
|
-
observe(<span class="hljs-function"><span class="hljs-params">()</span> =></span> {
|
|
132
|
-
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"superVariable changed to:"</span>,superVariable);
|
|
133
|
-
});
|
|
134
|
-
</script>
|
|
135
|
-
</code></pre>
|
|
136
|
-
<p><strong><em>Note</em></strong>: <code>lightview/module</code> code is sensitive to the use/lack of of semi-colons. When in doubt, terminate your statements with semi-colons. Also,
|
|
137
|
-
since since Lightview compiles your code into an asynchronous function, top level <code>await</code> is supported. </p>
|
|
138
|
-
<p>Runtime type checking will be applied to ensure <code>superVariable</code> is always a number or can be coerced to a number. There is an example for <a href="#type-checking">type checking</a>.</p>
|
|
139
|
-
<p>It will initially be set to 1 and then an attempt to import a value from the attributes of the component will be made. If no attribute by the name of the
|
|
140
|
-
variable exists, the value will remain 1. Any time it changes, its value will be <code>exported</code> back up to the attributes. The value will also be <code>shared</code> with all instances of the
|
|
141
|
-
same component and <code>reactive</code> HTML will be re-rendered. Also see the example <a href="#sharing-state">Sharing State</a>.</p>
|
|
142
|
-
<p>Reactive HTML is any HTML, including attribute values (with the exception of the <code>name</code> attribute on input elements), that contains a reference or references to a variable inside a template literal.</p>
|
|
143
|
-
<p>Because <code>superVariable</code> is <code>observed</code> you can also call <code>addEventListener(callback)</code> to register a listener that will be called any time the attribute <code>superVariable</code> changes. Attributes that are <code>observed</code> are automatically <code>imported</code>. If your needs are simple, you can just rely on <code>reactive</code> rather than define a callback using <code>addEventListener</code>.</p>
|
|
144
|
-
<p>Because you declared <code>superVariable</code> as <code>reactive</code> and wrapped a function referencing it in <code>observe</code>, the function will be called every time <code>superVariable</code> changes.</p>
|
|
145
|
-
<p>And, because you declared <code>superVariable</code> as <code>remote</code> at the relative URL <code>./superVariable</code>, its value will be retrieved from the server. Also,
|
|
146
|
-
since you made it <code>reactive</code>, changes will be sent to the server as <code>PATCH</code> requests.</p>
|
|
147
|
-
<h5 id="extended-type-definitions">Extended Type Definitions</h5>
|
|
148
|
-
<p>There are extended type definitions that use the symbolic names of all the basic types, e.g. <code>string</code> vs <code>"string"</code>. These extended types can be used like their string named
|
|
149
|
-
counterparts except they do not automatically coerce, or they can be used as functions for more sophisticated type checking, e.g. the basic types always attempt coercion, with extended types this is turned off by default, although you can turn in on. The declaration of <code>superVariable</code> above, might look like this:</p>
|
|
150
|
-
<pre><code class="language-javascript"><span class="hljs-keyword">const</span> {<span class="hljs-built_in">number</span>} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
151
|
-
self.variables(
|
|
152
|
-
{<span class="hljs-attr">superVariable</span>:<span class="hljs-built_in">number</span>}, <span class="hljs-comment">// note, number is not quoted</span>
|
|
153
|
-
{<span class="hljs-attr">imported</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">exported</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">shared</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">reactive</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">observed</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">remote</span>:<span class="hljs-string">"./superVariable"</span>}
|
|
154
|
-
);
|
|
155
|
-
<span class="hljs-keyword">await</span> superVariable;
|
|
156
|
-
</code></pre>
|
|
157
|
-
<p>Or, if you want to coerce and constrain the value to a range, like this:</p>
|
|
158
|
-
<pre><code class="language-javascript"><span class="hljs-keyword">const</span> {<span class="hljs-built_in">number</span>} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
159
|
-
self.variables(
|
|
160
|
-
{<span class="hljs-attr">superVariable</span>:<span class="hljs-built_in">number</span>({<span class="hljs-attr">coerce</span>:<span class="hljs-literal">true</span>,<span class="hljs-attr">min</span>:<span class="hljs-number">0</span>,<span class="hljs-attr">max</span>:<span class="hljs-number">1000</span>})}, <span class="hljs-comment">// note, how number is used as a function</span>
|
|
161
|
-
{<span class="hljs-attr">imported</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">exported</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">shared</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">reactive</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">observed</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">remote</span>:remote(<span class="hljs-string">"./superVariable"</span>)}
|
|
162
|
-
);
|
|
163
|
-
<span class="hljs-keyword">await</span> superVariable;
|
|
164
|
-
</code></pre>
|
|
165
|
-
<p>An example showing errors should help:</p>
|
|
166
|
-
<pre><code><span class="hljs-keyword">const</span> {<span class="hljs-built_in">string</span>,<span class="hljs-built_in">number</span>} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
167
|
-
self.variables({<span class="hljs-attr">name</span>:<span class="hljs-built_in">string</span>({<span class="hljs-attr">minlength</span>:<span class="hljs-number">2</span>,<span class="hljs-attr">maxlength</span>:<span class="hljs-number">20</span>,<span class="hljs-attr">required</span>:<span class="hljs-literal">true</span>,<span class="hljs-attr">default</span>:<span class="hljs-string">"anonymous"</span>}),<span class="hljs-attr">age</span>:<span class="hljs-built_in">number</span>});
|
|
168
|
-
<span class="hljs-built_in">console</span>.log(name); <span class="hljs-comment">// will log "anonymous"</span>
|
|
169
|
-
<span class="hljs-keyword">try</span> {
|
|
170
|
-
name = <span class="hljs-string">"J"</span>; <span class="hljs-comment">// will throw an error since the value is too short</span>
|
|
171
|
-
} <span class="hljs-keyword">catch</span>(e) {
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
<span class="hljs-keyword">try</span> {
|
|
175
|
-
name = <span class="hljs-literal">null</span>; <span class="hljs-comment">// will throw an error since a value is required</span>
|
|
176
|
-
} <span class="hljs-keyword">catch</span>(e) {
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
<span class="hljs-keyword">try</span> {
|
|
180
|
-
age = <span class="hljs-string">"10"</span>; <span class="hljs-comment">// will throw an error since the value is not a number</span>
|
|
181
|
-
} <span class="hljs-keyword">catch</span>(e) {
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
name = <span class="hljs-string">"joe"</span>; <span class="hljs-comment">// will succeed</span>
|
|
186
|
-
age = <span class="hljs-number">10</span>; <span class="hljs-comment">// will succeed</span>
|
|
187
|
-
|
|
188
|
-
</code></pre>
|
|
189
|
-
<p>The extended types include:</p>
|
|
190
|
-
<p><strong><code>any({required?:boolean,whenInvalid?:function,default?:any})</code></strong></p>
|
|
191
|
-
<p><strong><code>array({coerce?:boolean,required?:boolean,whenInvalid?:function,minlength?:number,maxlength?:number,default?:Array})</code></strong></p>
|
|
192
|
-
<ul>
|
|
193
|
-
<li><code>minlength</code> defaults to <code>0</code></li>
|
|
194
|
-
<li><code>maxlength</code> defaults to <code>Infinity</code></li>
|
|
195
|
-
</ul>
|
|
196
|
-
<p><strong><code>boolean({coerce?:boolean,required?:boolean,whenInvalid?:function,default?:boolean})</code></strong></p>
|
|
197
|
-
<p><strong><code>number({coerce?:boolean,required?:boolean,whenInvalid?:function,min?:number,max?:number,step?:number,allowNaN?:boolean,default?:number})</code></strong></p>
|
|
198
|
-
<ul>
|
|
199
|
-
<li><code>min</code> defaults to <code>-Infinity</code></li>
|
|
200
|
-
<li><code>max</code> defaults to <code>Infinity</code></li>
|
|
201
|
-
<li><code>step</code> defaults to <code>1</code></li>
|
|
202
|
-
<li><code>allowNaN</code> defaults to <code>true</code></li>
|
|
203
|
-
</ul>
|
|
204
|
-
<p><strong><code>object({coerce?:boolean,required?:boolean,whenInvalid?:function,default?:object})</code></strong></p>
|
|
205
|
-
<p><strong><code>remote(string|object)</code></strong></p>
|
|
206
|
-
<p>Remote must be imported from <code>types.js</code> and SHOULD be called with a configuration.
|
|
207
|
-
See <a href="#more-on-remote-variables">More On Remote Variables</a>.</p>
|
|
208
|
-
<p><strong><code>string({coerce?:boolean,required?:boolean,whenInvalid?:function,minlength?:number,maxlength?:number,pattern?:RegExp,default?:string})</code></strong></p>
|
|
209
|
-
<ul>
|
|
210
|
-
<li><code>minlength</code> defaults to <code>0</code></li>
|
|
211
|
-
<li><code>maxlength</code> defaults to <code>Infinity</code></li>
|
|
212
|
-
<li><code>pattern</code> ensures the value matches the RegExp prior to assignment</li>
|
|
213
|
-
</ul>
|
|
214
|
-
<p><strong><code>symbol({coerce?:boolean,required?:boolean,whenInvalid?:function,default?:symbol})</code></strong></p>
|
|
215
|
-
<p>All extended types have a default <code>whenInvalid</code> parameter which throws an error when an attempt to set the varibale to an invalid value is made. A custom function
|
|
216
|
-
can be passed in that swallows the error and returns the existing value for the variable, or <code>undefined</code>, or some other value, for example:</p>
|
|
217
|
-
<pre><code class="language-javascript">const whenInvalid = <span class="hljs-function"><span class="hljs-params">(variable)</span> =></span> {
|
|
218
|
-
<span class="hljs-keyword">return</span> variable.value;
|
|
219
|
-
}
|
|
220
|
-
</code></pre>
|
|
221
|
-
<p>you could even go ahead and make the assignment but log a warning:</p>
|
|
222
|
-
<pre><code class="language-javascript"><span class="hljs-keyword">const</span> whenInvalid = <span class="hljs-function">(<span class="hljs-params">variable,invalidValue</span>) =></span> {
|
|
223
|
-
<span class="hljs-built_in">console</span>.warn(<span class="hljs-string">`Assigning <span class="hljs-subst">${variable.name}</span>:<span class="hljs-subst">${variable.<span class="hljs-keyword">type</span>.name||variable.<span class="hljs-keyword">type</span>}</span> invalid value <span class="hljs-subst">${invalidValue}</span>);
|
|
224
|
-
return newValue;
|
|
225
|
-
}</span>
|
|
226
|
-
</code></pre>
|
|
227
|
-
<h5 id="more-on-remote-variables">More On Remote Variables</h5>
|
|
228
|
-
<p>Below is an example of how simple it can be to set up remote sensor monitoring using remote variables. This example is talking to a simulator running on a Cloudflare Worker. The guages themselves are available in the Lightview <a href="components">component library</a>.</p>
|
|
229
|
-
<pre height="140"><code src="./examples/sensors/index.html" contentonly></code></pre>
|
|
230
|
-
|
|
231
|
-
<iframe width="100%" height="225px" src="examples/sensors/index.html"></iframe>
|
|
232
|
-
|
|
233
|
-
<p>The easiest way to configure remote variables is to provide the absolute or relative unique URL to access the variable value, e.g.</p>
|
|
234
|
-
<pre><code class="language-javascript">const {remote} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
235
|
-
self.variables(
|
|
236
|
-
{sensor1:<span class="hljs-built_in">object</span>}, {remote:remote(<span class="hljs-string">"./sensors/sensor1"</span>)}
|
|
237
|
-
);
|
|
238
|
-
<span class="hljs-keyword">await</span> sensor1;
|
|
239
|
-
</code></pre>
|
|
240
|
-
<p><strong>Note</strong>: You MAY need to await the remote variable after it is declared. Future use, e.g. in template literals, will NEVER need to be awaited.</p>
|
|
241
|
-
<p>If you do not call <code>remote</code> during your variable declaration, the assumed path to the variable will be the current file path plus the variable name, e.g.</p>
|
|
242
|
-
<pre><code>const {remote} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
243
|
-
self.variables({sensor1:<span class="hljs-built_in">object</span>}, {remote});
|
|
244
|
-
</code></pre>
|
|
245
|
-
<p>is the same as</p>
|
|
246
|
-
<pre><code>const {remote} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
247
|
-
self.variables({sensor1:<span class="hljs-built_in">object</span>}, {remote:remote(<span class="hljs-string">"./sensor1"</span>)});
|
|
248
|
-
</code></pre>
|
|
249
|
-
<p>If you call <code>remote</code> with a path that is terminates by a slash, then the variable name is appended to the path.</p>
|
|
250
|
-
<pre><code>const {remote} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
251
|
-
self.variables({sensor1:<span class="hljs-built_in">object</span>}, {remote:remote(<span class="hljs-string">"https://mysite.com/sensors/"</span>)});
|
|
252
|
-
</code></pre>
|
|
253
|
-
<p>is the same as:</p>
|
|
254
|
-
<pre><code>const {remote} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
255
|
-
self.variables({sensor1:<span class="hljs-built_in">object</span>}, {remote:remote(<span class="hljs-string">"https://mysite.com/sensors/sensor1"</span>)});
|
|
256
|
-
</code></pre>
|
|
257
|
-
<p>In some cases you may have an existing application that does not provide an easily addressable unique URL for each variable, in this case you can provide a configuration object providing a <code>get</code> method
|
|
258
|
-
(as well as <code>patch</code> and <code>put</code> if the variable is reactive and sending updates to the server), along with an optional <code>path</code> and <code>ttl</code>.</p>
|
|
259
|
-
<p>The <code>get</code> method should have the signature <code>get(path,variable)</code>. You can use the <code>path</code>, the variable definition contained in <code>variable</code>, and any variables within the closure of your method to create a URL and do your own <code>fetch</code>. Your <code>patch</code> method must parse the <code>fetch</code> response and return a Promise for JSON.</p>
|
|
260
|
-
<p>The <code>patch</code> method should have the signature <code>patch({target,property,value,oldValue},path,variable)</code>. (Currently, remotely patched variables must be objects, in the future <code>{value,oldValue}</code> will also be legal for primitive variables).</p>
|
|
261
|
-
<p>You can use data from the <code>target</code> object along with the <code>path</code>, the variable definition contained in <code>variable</code>, and any variables within the closure of your method to create a URL and do your own <code>fetch</code>. Your <code>patch</code> method must parse the <code>fetch</code> response and return a Promise for JSON.</p>
|
|
262
|
-
<p>The <code>put</code> method should have the signature <code>put(target,path,variable)</code>. You can use data from the <code>target</code> object along with the <code>path</code>, the variable definition contained in <code>variable</code>, and any variables within the closure of your method to create a URL and do your own <code>fetch</code>. Your <code>put</code> method must parse the <code>fetch</code> response and return a Promise for JSON which is the current state of the variable on the server.</p>
|
|
263
|
-
<p>The <code>ttl</code> is the number of milliseconds between server polls to refresh data. If you do not wish to poll the server, you could also implement <code>get</code> so that it establishes a websocket connection and update your variables in realtime.</p>
|
|
264
|
-
<p>Here is an example of a custom remote variable configuration for polling sensor data:</p>
|
|
265
|
-
<pre><code class="language-javacript"><span class="hljs-keyword">const</span> {remote} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
266
|
-
self.variables(
|
|
267
|
-
{ <span class="hljs-attr">sensor1</span>:<span class="hljs-built_in">object</span>, <span class="hljs-attr">sensor2</span>:<span class="hljs-built_in">object</span> },
|
|
268
|
-
{ <span class="hljs-attr">remote</span>:
|
|
269
|
-
remote({
|
|
270
|
-
<span class="hljs-attr">path</span>: <span class="hljs-string">"./sensors/"</span>,
|
|
271
|
-
<span class="hljs-attr">ttl</span>: <span class="hljs-number">10000</span>, <span class="hljs-comment">// get new data every 10 seconds</span>
|
|
272
|
-
<span class="hljs-function"><span class="hljs-title">get</span>(<span class="hljs-params">path,variable</span>)</span> {
|
|
273
|
-
<span class="hljs-comment">// create a normalized full path to the sensor data</span>
|
|
274
|
-
<span class="hljs-keyword">const</span> href = <span class="hljs-keyword">new</span> URL(path + <span class="hljs-built_in">object</span>.id,<span class="hljs-built_in">window</span>.location.href).href;
|
|
275
|
-
<span class="hljs-keyword">return</span> fetch(href)
|
|
276
|
-
.then(<span class="hljs-function">(<span class="hljs-params">response</span>) =></span> {
|
|
277
|
-
<span class="hljs-keyword">if</span>(response.status===<span class="hljs-number">200</span>) <span class="hljs-keyword">return</span> response.json();
|
|
278
|
-
})
|
|
279
|
-
}
|
|
280
|
-
})
|
|
281
|
-
}
|
|
282
|
-
);
|
|
283
|
-
<span class="hljs-keyword">await</span> sensor1;
|
|
284
|
-
<span class="hljs-keyword">await</span> sensor2;
|
|
285
|
-
</code></pre>
|
|
286
|
-
<p>Here is partial example of a custom remote variable configuration for streaming data over a websocket:</p>
|
|
287
|
-
<pre><code class="language-javacript"><span class="hljs-keyword">const</span> {remote} = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"./types.js"</span>);
|
|
288
|
-
<span class="hljs-comment">// use these in the UI so that it automatically updates</span>
|
|
289
|
-
self.variables({<span class="hljs-attr">sensor1</span>:<span class="hljs-built_in">object</span>,<span class="hljs-attr">sensor2</span>:<span class="hljs-built_in">object</span>},{reactive});
|
|
290
|
-
<span class="hljs-comment">// use a variable to hold the websocket</span>
|
|
291
|
-
self.variables(
|
|
292
|
-
{ <span class="hljs-attr">ws</span>: <span class="hljs-built_in">object</span> },
|
|
293
|
-
{ <span class="hljs-attr">remote</span>:
|
|
294
|
-
remote({
|
|
295
|
-
<span class="hljs-attr">path</span>: <span class="hljs-string">"./sensors"</span>,
|
|
296
|
-
<span class="hljs-attr">ttl</span>: <span class="hljs-number">10000</span>, <span class="hljs-comment">// get new data every 10 seconds</span>
|
|
297
|
-
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-title">get</span>(<span class="hljs-params">path,variable</span>)</span> {
|
|
298
|
-
<span class="hljs-comment">// only create one socket</span>
|
|
299
|
-
<span class="hljs-keyword">if</span>(!ws) {
|
|
300
|
-
<span class="hljs-comment">// create a normalized full path to the sensor data</span>
|
|
301
|
-
<span class="hljs-keyword">const</span> href = <span class="hljs-keyword">new</span> URL(path,<span class="hljs-built_in">window</span>.location.href).href.replace(<span class="hljs-string">"https://"</span>,<span class="hljs-string">"wss://"</span>);
|
|
302
|
-
ws = <span class="hljs-keyword">new</span> WebSocket(href);
|
|
303
|
-
<span class="hljs-comment">// do websocketty stuff, ideally in a more robust way than this!</span>
|
|
304
|
-
ws.onmessage = <span class="hljs-function">(<span class="hljs-params">event</span>) =></span> {
|
|
305
|
-
<span class="hljs-keyword">const</span> {sensorName,value} = event.data;
|
|
306
|
-
<span class="hljs-comment">// assumes sensor1 and sensor2 are the names</span>
|
|
307
|
-
self.setVariableValue(sensorName,value);
|
|
308
|
-
}
|
|
309
|
-
<span class="hljs-comment">// end websockety stuff</span>
|
|
310
|
-
<span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.resolve(ws); <span class="hljs-comment">// you must return a Promise for the socket</span>
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
}
|
|
314
|
-
);
|
|
315
|
-
<span class="hljs-keyword">await</span> ws;
|
|
316
|
-
</code></pre>
|
|
317
|
-
<p>Since using remote variables requires running a custom server, it is not possible to demonstrate on this CodePen hosted site. Below is the source code for a very basic custom NodeJS server that will respond appropriately to remote variable requests and updates for data stored in JSON files. The full demo can be found in the <a href="https://github.com/anywhichway/lightview">GitHub repository</a>.</p>
|
|
318
|
-
<pre height="100"><code src="./examples/remote-server.js" contentonly></code></pre>
|
|
319
|
-
|
|
320
|
-
<h4 id="functions">Functions</h4>
|
|
321
|
-
<p>We already introduced <code>observe</code> above. There are several more functions available with Lightview and components created using Lightview.</p>
|
|
322
|
-
<h5 id="lightview">Lightview</h5>
|
|
323
|
-
<p><strong><code>Class Lightview.createComponent(name:string, node:HTMLElement [, {framed:boolean, observer:MutationObsever}])</code></strong></p>
|
|
324
|
-
<ul>
|
|
325
|
-
<li>Creates a component, i.e. <code>customElement</code>, using the <code>node</code> contents as a template.</li>
|
|
326
|
-
<li>See the example <a href="#local-templates">Local Templates</a>.</li>
|
|
327
|
-
</ul>
|
|
328
|
-
<p><strong><code>HTMLCustomElement Lightview.bodyAsComponent([{as = "x-body", unhide:boolean, framed:boolean}])</code></strong></p>
|
|
329
|
-
<ul>
|
|
330
|
-
<li>This function treats the body of an HTML document as a component and is typically called in a <code>DOMContentLoaded</code> event handler.</li>
|
|
331
|
-
<li>A shorthand way of using this is to just passs the query string ``?as=x-body` to the Lightview loading script in the head of your document.</li>
|
|
332
|
-
<li><strong>Note</strong>, once a body is turned into a componet, its contents are in a <code>shadoDOM</code>, so using <code>document.getElementById</code> will not work. Instead,
|
|
333
|
-
use <code>document.body.getElementById</code>. All components implement <code>getElementById</code>.</li>
|
|
334
|
-
<li>The use of this method or the query string approach is ignored when a component is loaded as a subcomponet. Hence, you can use it to support
|
|
335
|
-
the creation of unit testable components.</li>
|
|
336
|
-
<li>See the example <a href="reactive-variables-and-encapsultated-style">Reactive Variables and Encapsultated Style</a>.</li>
|
|
337
|
-
</ul>
|
|
338
|
-
<p><strong><code>void Lightview.whenFramed(callback:function [, {isolated:boolean}])</code></strong></p>
|
|
339
|
-
<ul>
|
|
340
|
-
<li>Invokes <code>callback</code> when a component file detects it is being loaded in an <code>iframe</code>. </li>
|
|
341
|
-
<li>If <code>isolated</code> is set to true, then there will be no communication with the parent window. Otherwise, message handling is automatically implemented.</li>
|
|
342
|
-
<li>See the example <a href="#sandboxed-components">Sandboxed Components and Micro Front Ends</a>.</li>
|
|
343
|
-
</ul>
|
|
344
|
-
<h5 id="lifecycle-event-handlers">Lifecycle Event Handlers</h5>
|
|
345
|
-
<p><strong><code>self.addEventListener(eventName:string,callback:function)</code></strong></p>
|
|
346
|
-
<ul>
|
|
347
|
-
<li><p>Adds the <code>callback</code> to be invoked when the <code>eventName</code> occurs.</p>
|
|
348
|
-
</li>
|
|
349
|
-
<li><p>This is actually just the standard <code>HTMLElement.addEventLister</code>, as such, it is availabled as a method on the element when created with <code>document.create</code>.</p>
|
|
350
|
-
</li>
|
|
351
|
-
<li><p>Valid <code>eventNames</code> include:</p>
|
|
352
|
-
<ul>
|
|
353
|
-
<li><code>adopted</code> which will be invoked when a component is adopted by a document with the callback as <code>callback({type:"adopted",target:component})</code>.</li>
|
|
354
|
-
<li><code>connected</code> which will be invoked when a component is added to a document DOM with the callback as <code>callback({type:"connected",target:component})</code>.</li>
|
|
355
|
-
</ul>
|
|
356
|
-
<blockquote>
|
|
357
|
-
<p>In order to prevent blocking, a number of Lightview component initialization functions are asynchronous. As a result, when a component is connected by the DOM, there may still be initialization work in flight. Once the Lightview <code>connected</code> event has fired, you can be sure all initialization work is complete.</p>
|
|
358
|
-
</blockquote>
|
|
359
|
-
<blockquote>
|
|
360
|
-
<p>If you encouter errors that say custom methods on your components are not available to other components or external scripts, then try wrapping the code that accesses these methods in a <code>connected</code> event listener.</p>
|
|
361
|
-
</blockquote>
|
|
362
|
-
<ul>
|
|
363
|
-
<li><code>disconnected</code> which will be invoked when a component is removed from a DOM with the callback as <code>callback({type:"disconnected",target:component})</code>.</li>
|
|
364
|
-
</ul>
|
|
365
|
-
</li>
|
|
366
|
-
</ul>
|
|
367
|
-
<p><strong><code>addEventListener(eventName:string,callback:function)</code></strong></p>
|
|
368
|
-
<ul>
|
|
369
|
-
<li><p>Available in the context of <code>lightview/script</code>. Adds the <code>callback</code> to be invoked when the <code>eventName</code> occurs.</p>
|
|
370
|
-
</li>
|
|
371
|
-
<li><p>This is a special <code>addEventListener</code> that deals with data and events internal to the component.</p>
|
|
372
|
-
</li>
|
|
373
|
-
<li><p>Not preceded by <code>self.</code>. Just call directly. This is the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/message_event">same way it works for web workers</a>.</p>
|
|
374
|
-
</li>
|
|
375
|
-
<li><p>Valid <code>eventNames</code> include:</p>
|
|
376
|
-
<ul>
|
|
377
|
-
<li><code>change</code> which will be invoked when variable values change with the callback as <code>callback({type:"change",target:component,variableName:string,value:any,oldValue:any})</code>.</li>
|
|
378
|
-
</ul>
|
|
379
|
-
</li>
|
|
380
|
-
<li><p>See the example <a href="#sharing-xor-state">Sharing Exclusive Or State</a>.</p>
|
|
381
|
-
</li>
|
|
382
|
-
</ul>
|
|
383
|
-
<h5 id="component-variable-access">Component Variable Access</h5>
|
|
384
|
-
<p>You should be careful not to overload and shadow these functions by redefining them on your component.</p>
|
|
385
|
-
<p><strong><code>Array self.getVariable()</code></strong></p>
|
|
386
|
-
<ul>
|
|
387
|
-
<li>Returns an a copy of the internal structure of a variable or <code>undefined</code>. See <code>self.variables</code> below.</li>
|
|
388
|
-
</ul>
|
|
389
|
-
<p><strong><code>Array self.getVariableNames()</code></strong></p>
|
|
390
|
-
<ul>
|
|
391
|
-
<li>Returns an array of names of the currently defined variables for a component.</li>
|
|
392
|
-
</ul>
|
|
393
|
-
<p><strong><code>any self.getVariableValue(variableName:string)</code></strong></p>
|
|
394
|
-
<ul>
|
|
395
|
-
<li>Gets the current value of <code>variableName</code>. Returns <code>undefined</code> if the variable does not exist.</li>
|
|
396
|
-
</ul>
|
|
397
|
-
<p><strong><code>boolean self.setVariableValue(variableName:string, value:any[, {coerceTo:string|function}])</code></strong></p>
|
|
398
|
-
<ul>
|
|
399
|
-
<li>Sets a value for a <code>variableName</code>. Returns <code>true</code> if the variable already existed and <code>false</code> if not.</li>
|
|
400
|
-
<li>If the variable already existed, the existing type is used and <code>coerceTo</code> is ignored.</li>
|
|
401
|
-
<li>If the variable is created, the type is infered from the value or set to <code>coerceTo</code> (in case you want to use <code>any</code>).</li>
|
|
402
|
-
</ul>
|
|
403
|
-
<p><strong><code>object self.variables({[variableName]:variableType,...}[,...]})</code></strong></p>
|
|
404
|
-
<ul>
|
|
405
|
-
<li>Used to declare variables as described in the <a href="#variables">Variables</a> section above.</li>
|
|
406
|
-
<li>Returns an <code>object</code>, the keys of which are variable names with the values being copies of the internal structure of the variable, e.g.</li>
|
|
407
|
-
</ul>
|
|
408
|
-
<pre><code class="language-javascript">self.variables({v1:<span class="hljs-string">"string"</span>},{imported,shared});
|
|
409
|
-
/* <span class="hljs-keyword">returns</span>
|
|
410
|
-
{
|
|
411
|
-
v1: {<span class="hljs-literal">name</span>: <span class="hljs-string">"v1"</span>, <span class="hljs-built_in">type</span>: <span class="hljs-string">"string"</span>, imported:<span class="hljs-keyword">true</span>, <span class="hljs-literal">shared</span>:<span class="hljs-keyword">true</span>}
|
|
412
|
-
}
|
|
413
|
-
*/
|
|
414
|
-
self.variables({v2:<span class="hljs-string">"number"</span>},{exported,reactive});
|
|
415
|
-
/* <span class="hljs-keyword">returns</span>
|
|
416
|
-
{
|
|
417
|
-
v2: {<span class="hljs-literal">name</span>: <span class="hljs-string">"v2"</span>, value:<span class="hljs-number">2</span>, <span class="hljs-built_in">type</span>: <span class="hljs-string">"number"</span>, exported:<span class="hljs-keyword">true</span>, reactive:<span class="hljs-keyword">true</span>}
|
|
418
|
-
}
|
|
419
|
-
*/
|
|
420
|
-
</code></pre>
|
|
421
|
-
<h5 id="other-component-methods">Other Component Methods</h5>
|
|
422
|
-
<p>Components are DOM nodes with a <code>shadowRoot</code> and have the standard DOM node capability, e.g. <code>getQuerySelector</code>. They also implement <code>getElementById</code> (which is normally only on a <code>document</code>).</p>
|
|
423
|
-
<p><a id="examples"></a></p>
|
|
424
|
-
<h3 id="examples-of-use">Examples of Use</h3>
|
|
425
|
-
<p>All of the code examples on this site with a Reset button and LIVE label can be edited directly on the site. Or, you can open them in CodePen using the link above the example toward the right margin.</p>
|
|
426
|
-
<p>Some examples are not implemented as Pens and can't be edited.</p>
|
|
427
|
-
<h4 id="reactive-variables-and-encapsultated-style">Reactive Variables and Encapsultated Style</h4>
|
|
428
|
-
<p>A basic example of Lightview to show how simple reactive UI development can be. </p>
|
|
429
|
-
<p>If you declare variables as reactive, with the exception of the <code>name</code> attribute for input elements, any HTML referencing the variables will be automatically re-rendered. If you inspect the content of the rendered view, you will also see that this particular page has turned the body into a self rendering component called 'x-body'.</p>
|
|
430
|
-
<p>The <code>name</code> attribute is excluded because making dynamic substitutions for <code>name</code> can result in VERY obscure code.</p>
|
|
431
|
-
<p> <button onclick="resetPen('QWaOyXQ')">Reset</button>
|
|
432
|
-
<span style="float:right">
|
|
433
|
-
<i class="fa-brands fa-codepen"></i>
|
|
434
|
-
<a href="https://codepen.io/anywhichway/details/QWaOyXQ">lightview-counter</a>
|
|
435
|
-
</span></p>
|
|
436
|
-
<ul>
|
|
437
|
-
<li>Try modifying to put the count outside the button and change the button color.</li>
|
|
438
|
-
<li>Try adding a reactive variable <code>color</code>, setting it to "red", and putting <code>background: ${color}</code> in the style.</li>
|
|
439
|
-
</ul>
|
|
440
|
-
<p class="codepen" data-height="390" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="QWaOyXQ" data-editable="true" data-user="anywhichway" style="height: 390px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
441
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/QWaOyXQ">
|
|
442
|
-
hcx-counter</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
443
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
444
|
-
</p>
|
|
445
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
446
|
-
|
|
447
|
-
<h4 id="auto-binding-inputs-and-forms">Auto Binding Inputs And Forms</h4>
|
|
448
|
-
<p>Lightview can automatically create variables based on form input field names and types.</p>
|
|
449
|
-
<p>Any time you use a string literal in the <code>value</code> attribute of form inputs, Lightview checks to see if the entirety of
|
|
450
|
-
the literal is a declared or undeclared variable. If it is undeclared, a reactive variable is automatically created. The
|
|
451
|
-
variables are then bound to the form input. For radio buttons, it just checks to see if the <code>name</code> attribute matches a variable. (This
|
|
452
|
-
is one of the reasons the <code>name</code> attribute on inputs can't also be a dynamically bound variable.)</p>
|
|
453
|
-
<p>Note how the only variable declared in the Pen below is <code>color</code>, because it is used outside the context of an input value in the style attribute.</p>
|
|
454
|
-
<p> <button onclick="resetPen('ExobKwx')">Reset</button>
|
|
455
|
-
<span style="float:right">
|
|
456
|
-
<i class="fa-brands fa-codepen"></i>
|
|
457
|
-
<a href="https://codepen.io/anywhichway/details/ExobKwx">lightview-form</a>
|
|
458
|
-
</span></p>
|
|
459
|
-
<p class="codepen" data-height="1070" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="ExobKwx" data-editable="true" data-user="anywhichway" style="height: 1070px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
460
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/ExobKwx">
|
|
461
|
-
hcx-form</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
462
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
463
|
-
</p>
|
|
464
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
465
|
-
|
|
466
|
-
<p>You can bind or autobind an object to the <code>value</code> attribute of a form and then bind the inputs to properties on the object using dot notation paths. Also note in this example the use of <code>observe</code> in the demo instrumentation rather than a <code>change</code> event listener.</p>
|
|
467
|
-
<p class="codepen" data-height="604.7999267578125" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="XWZmpwX" data-editable="true" data-user="anywhichway" style="height: 604.7999267578125px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
468
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/XWZmpwX">
|
|
469
|
-
lightview-form</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
470
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
471
|
-
</p>
|
|
472
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
473
|
-
|
|
474
|
-
<h4 id="attribute-directives">Attribute Directives</h4>
|
|
475
|
-
<p>Lightview supports <code>l-if</code>, <code>l-for</code>, and <code>:</code> (to handle custom boolean attributes).</p>
|
|
476
|
-
<p><button onclick="resetPen('WNdXgrw')">Reset</button>
|
|
477
|
-
<span style="float:right">
|
|
478
|
-
<i class="fa-brands fa-codepen"></i>
|
|
479
|
-
<a href="https://codepen.io/anywhichway/details/WNdXgrw">lightview-directives</a>
|
|
480
|
-
</span></p>
|
|
481
|
-
<p class="codepen" data-height="1375" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="WNdXgrw" data-editable="true" data-user="anywhichway" style="height: 1375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
482
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/WNdXgrw">
|
|
483
|
-
lightview--directives</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
484
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
485
|
-
</p>
|
|
486
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
487
|
-
|
|
488
|
-
<h4 id="targeted-anchorhref-imports">Targeted Anchor/HREF Imports</h4>
|
|
489
|
-
<p>Anchor elements that include an element <code>id</code> as a target can be used to import components and place them at the target. For security,
|
|
490
|
-
the current implementation requires the components be hosted on the same server as the requesting file or you must set the
|
|
491
|
-
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin">CORS attribute</a> <code>crossorigin</code> on your <code><a></code> element.
|
|
492
|
-
(This is not a typical location for the <code>crossorigin</code> attribute). </p>
|
|
493
|
-
<p>You MUST ABSOLUTELY TRUST the server from which you are loading anchor imported components. Components can navigate up out of the <code>shadowDom</code>
|
|
494
|
-
and modify other areas of a page. For more on components that are loaded across origins, see <a href="#link-imports-and-nested-components">Link imports and Nested Components</a>
|
|
495
|
-
and <a href="#sandboxed-components">Sandboxed Components</a>.</p>
|
|
496
|
-
<h5 id="same-origin-import">Same Origin Import</h5>
|
|
497
|
-
<p>Since importing through a Pen requires cross origin activity, there is no Pen, just this included example.</p>
|
|
498
|
-
<pre height="140"><code src="./examples/anchors.html"></code></pre>
|
|
499
|
-
|
|
500
|
-
<h5 id="cross-origin-import">Cross Origin Import</h5>
|
|
501
|
-
<p>Pens are hosted on a separate server, so the <code>crossorigin</code> attribute is used below.</p>
|
|
502
|
-
<p><button onclick="resetPen('BaJYXRE')">Reset</button>
|
|
503
|
-
<span style="float:right">
|
|
504
|
-
<i class="fa-brands fa-codepen"></i>
|
|
505
|
-
<a href="https://codepen.io/anywhichway/details/BaJYXRE">lightview-anchor-crossdomain</a>
|
|
506
|
-
</span></p>
|
|
507
|
-
<p class="codepen" data-height="250" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="BaJYXRE" data-editable="true" data-user="anywhichway" style="height: 250px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
508
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/BaJYXRE">
|
|
509
|
-
lightview-anchors</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
510
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
511
|
-
</p>
|
|
512
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
<p><a id="templates"></a></p>
|
|
516
|
-
<h4 id="local-templates">Local Templates</h4>
|
|
517
|
-
<p>Template tags can be used to define components in the same file where they are used. You can actually use any DOM node,
|
|
518
|
-
but that's for another time...</p>
|
|
519
|
-
<p><button onclick="resetPen('abEVNMZ')">Reset</button>
|
|
520
|
-
<span style="float:right">
|
|
521
|
-
<i class="fa-brands fa-codepen"></i>
|
|
522
|
-
<a href="https://codepen.io/anywhichway/details/abEVNMZ">lightview-template</a>
|
|
523
|
-
</span></p>
|
|
524
|
-
<p class="codepen" data-height="650" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="abEVNMZ" data-editable="true" data-user="anywhichway" style="height: 650px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
525
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/abEVNMZ">
|
|
526
|
-
lightview-template</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
527
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
528
|
-
</p>
|
|
529
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
530
|
-
|
|
531
|
-
<p><a name="link-imports-and-nested-components"></a></p>
|
|
532
|
-
<h4 id="link-imports-and-nested-components">Link Imports and Nested Components</h4>
|
|
533
|
-
<p>You can import components using the <code><link></code> tag in the head of your components. These will be recursively loaded by components that use the component with <code><link></code> imports.</p>
|
|
534
|
-
<p>If you are operating cross domain (which Pens frequently do, you must include the <code>crossorigin</code> <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin">CORS attribute</a>).
|
|
535
|
-
You MUST ABSOLUTELY TRUST the server from which you are loading linked components. Components can navigate up out of the <code>shadowDom</code> and modify other areas of a page. For secure
|
|
536
|
-
components in iframes see <a href="#sandboxed-components">Sandboxed Components</a>, they can also be nested.</p>
|
|
537
|
-
<p><button onclick="resetPen('ZEvrrjM')">Reset</button>
|
|
538
|
-
<span style="float:right">
|
|
539
|
-
<i class="fa-brands fa-codepen"></i>
|
|
540
|
-
<a href="https://codepen.io/anywhichway/details/ZEvrrjM">lightview-nested</a>
|
|
541
|
-
</span></p>
|
|
542
|
-
<p class="codepen" data-height="270" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="ZEvrrjM" data-editable="true" data-user="anywhichway" style="height: 270px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
543
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/ZEvrrjM">
|
|
544
|
-
lightview-nested</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
545
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
546
|
-
</p>
|
|
547
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
548
|
-
|
|
549
|
-
<p>The above Pen uses the following two remote files:</p>
|
|
550
|
-
<pre height="100"><code src="./examples/nested.html" contentonly></code></pre>
|
|
551
|
-
|
|
552
|
-
<pre height="100"><code src="./examples/message.html" contentonly></code></pre>
|
|
553
|
-
|
|
554
|
-
<p>In order to reduce network calls, a "compiler" that turns local links into templates in the files containing the links is under development.</p>
|
|
555
|
-
<p><a id="type-checking"></a></p>
|
|
556
|
-
<h4 id="type-checking">Type Checking</h4>
|
|
557
|
-
<p><button onclick="resetPen('GRyOgzj')">Reset</button>
|
|
558
|
-
<span style="float:right">
|
|
559
|
-
<i class="fa-brands fa-codepen"></i>
|
|
560
|
-
<a href="https://codepen.io/anywhichway/details/GRyOgzj">lightview-types</a>
|
|
561
|
-
</span></p>
|
|
562
|
-
<p class="codepen" data-height="1355" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="GRyOgzj" data-editable="true" data-user="anywhichway" style="height: 1355px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
563
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/GRyOgzj">
|
|
564
|
-
hcx-types</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
565
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
566
|
-
</p>
|
|
567
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
568
|
-
|
|
569
|
-
<p><a id="sharing-state"></a></p>
|
|
570
|
-
<h4 id="sharing-state-across-components-of-same-type">Sharing State Across Components Of Same Type</h4>
|
|
571
|
-
<p>Lightview can ensure that state is the same across all instances of the same component. This example also shows
|
|
572
|
-
how variables declared as <code>imported</code> can be used to map attributes to content.</p>
|
|
573
|
-
<p><button onclick="resetPen('NWXwNme')">Reset</button>
|
|
574
|
-
<span style="float:right">
|
|
575
|
-
<i class="fa-brands fa-codepen"></i>
|
|
576
|
-
<a href="https://codepen.io/anywhichway/details/NWXwNme">lightview-types</a>
|
|
577
|
-
</span></p>
|
|
578
|
-
<p class="codepen" data-height="770" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="NWXwNme" data-editable="true" data-user="anywhichway" style="height: 770px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
579
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/NWXwNme">
|
|
580
|
-
lightview-shared</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
581
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
582
|
-
</p>
|
|
583
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
584
|
-
|
|
585
|
-
<p><a id="sharing-xor-state"></a></p>
|
|
586
|
-
<h4 id="sharing-exclusive-or-state">Sharing Exclusive Or State</h4>
|
|
587
|
-
<p>If you had components that implemented audio streams, you would only want to play one at a time. This shows how to implement an XOR of state across components using checkboxes in place of radio buttons.</p>
|
|
588
|
-
<p><button onclick="resetPen('mdpqPZx')">Reset</button>
|
|
589
|
-
<span style="float:right">
|
|
590
|
-
<i class="fa-brands fa-codepen"></i>
|
|
591
|
-
<a href="https://codepen.io/anywhichway/details/mdpqPZx">lightview-xor</a>
|
|
592
|
-
</span></p>
|
|
593
|
-
<p class="codepen" data-height="1010" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="mdpqPZx" data-editable="true" data-user="anywhichway" style="height:1010px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
594
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/mdpqPZx">
|
|
595
|
-
lightview-xor</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
596
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
597
|
-
</p>
|
|
598
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
599
|
-
|
|
600
|
-
<p><a name="sandboxed-components"></a></p>
|
|
601
|
-
<h4 id="sandboxed-remote-components-and-micro-front-ends">Sandboxed Remote Components and Micro Front Ends</h4>
|
|
602
|
-
<p>Since components can contain JavaScript, loading them from another domain brings with it
|
|
603
|
-
security risks. Lightview can sandbox remote components in iframes. Communication can then
|
|
604
|
-
occur by getting and setting attributes. Lightview manages this for you. </p>
|
|
605
|
-
<p>We can't show you a Pen here because Pens do not work well with iframe communication.</p>
|
|
606
|
-
<pre height="850"><code src="./examples/foreign.html" scriptonly></code></pre>
|
|
607
|
-
|
|
608
|
-
<p>As shown below, you only need to add two lines of code to a remote component to enable it for iframe messaging.</p>
|
|
609
|
-
<pre><code class="language-html"> <script>
|
|
610
|
-
<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Lightview</span>.</span></span><span class="hljs-keyword">when</span><span class="hljs-constructor">Framed(({<span class="hljs-params">as</span>,<span class="hljs-params">unhide</span>,<span class="hljs-params">isolated</span>,<span class="hljs-params">enableFrames</span>,<span class="hljs-params">framed</span>})</span> => {
|
|
611
|
-
<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Lightview</span>.</span></span>body<span class="hljs-constructor">AsComponent({<span class="hljs-params">as</span>,<span class="hljs-params">unhide</span>,<span class="hljs-params">isolated</span>,<span class="hljs-params">enableFrames</span>,<span class="hljs-params">framed</span>})</span>;
|
|
612
|
-
})
|
|
613
|
-
</script>
|
|
614
|
-
</code></pre>
|
|
615
|
-
<p>The spread of the argument object above is done for clarity, you could just do this:</p>
|
|
616
|
-
<pre><code class="language-html"> <script>
|
|
617
|
-
<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Lightview</span>.</span></span><span class="hljs-keyword">when</span><span class="hljs-constructor">Framed((<span class="hljs-params">options</span>)</span> => <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Lightview</span>.</span></span>body<span class="hljs-constructor">AsComponent(<span class="hljs-params">options</span>)</span>);
|
|
618
|
-
</script>
|
|
619
|
-
</code></pre>
|
|
620
|
-
<p><strong><em>Note</em></strong>, because the iframe demo above is doubly nested, the demo iframe does not
|
|
621
|
-
automatically resize. However, the nested iframe does resize based on its inner component.
|
|
622
|
-
You can view the demo on a <a href="./remote.html">separate page</a> to get a cleaner picture.</p>
|
|
623
|
-
<p>Below is the code for the remote form, the code is the same as the form demo shown earlier
|
|
624
|
-
with just two lines of code and some styling added to iframe enable it. Plus, the addition of a Place Order button.</p>
|
|
625
|
-
<p>The automatically exported variable <code>message</code> is created in the component. Just assign values to it
|
|
626
|
-
like the <code>placeOrder</code> function below.</p>
|
|
627
|
-
<pre height="400"><code src="./examples/foreignform.html" contentonly></code></pre>
|
|
628
|
-
|
|
629
|
-
<p>The parent document requires a little more work because it must handle messages from the child.</p>
|
|
630
|
-
<p>You must this metatag to the parent document, <code><meta name="l-enableFrames"></code> and some event handlers.</p>
|
|
631
|
-
<pre height="850"><code src="./examples/foreign.html" contentonly></code></pre>
|
|
632
|
-
|
|
633
|
-
<p><a id="component-library"></a></p>
|
|
634
|
-
<h4 id="component-library">Component Library</h4>
|
|
635
|
-
<p>Lightview comes with a component library <a href="components">documented separately</a>. The initial release is <a href="https://developers.google.com/chart/">Goolge Chart</a> based.</p>
|
|
636
|
-
<p><a id="debugging"></a></p>
|
|
637
|
-
<h3 id="debugging">Debugging</h3>
|
|
638
|
-
<p>Your <code>lightview/module</code> script are re-compiled on the fly, so you can't set break points in them directly in your source. Instead,
|
|
639
|
-
insert a <code>debugger</code> command at the location you wish to start debugging. When the debugger stops at that point, you can
|
|
640
|
-
set breakpoints in the re-compiled source.</p>
|
|
641
|
-
<p>The source is likely to be shown as just one line by your browser debugger. If you are using Chrome, Edge, and Firefox you can click <code>{}</code> in the
|
|
642
|
-
bottom left of the source window to pretty-print the code.</p>
|
|
643
|
-
<p>If you set the variable <code>lightviewDebug</code> to <code>true</code> in a normal script at the top of your component file, then some additional assistance is provided,
|
|
644
|
-
i.e. DOM nodes with template literals are left in place when executing attribute directives.</p>
|
|
645
|
-
<pre><code class="language-javascript"> <span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="javascript"><span class="hljs-keyword">var</span> lightviewDebug=<span class="hljs-literal">true</span></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
|
|
646
|
-
</code></pre>
|
|
647
|
-
<p><a id="unit-testing"></a></p>
|
|
648
|
-
<h3 id="unit-testing">Unit Testing</h3>
|
|
649
|
-
<p>You can use <a href="https://github.com/puppeteer/puppeteer">Puppeteer</a> to unit test components. Below is a unit test for the counter button component when instantiated as a standalone file. As described earlier, if you provide the query string <code>?as=x-body</code> to the Lightview script in the head of a componenty definition file it can be instantiated directly. This script is ignored when loading the component from another file.</p>
|
|
650
|
-
<pre height="100"><code src="./examples/counter.test.mjs" contentonly></code></pre>
|
|
651
|
-
|
|
652
|
-
<p><a id="security"></a></p>
|
|
653
|
-
<h3 id="security">Security</h3>
|
|
654
|
-
<p>In addition to the cross-origin security issues discussed above, there are some security issues related to the use of
|
|
655
|
-
template literals to substitute values into HTML. This is particularly true if you allow un-sanitized user input as
|
|
656
|
-
values for variables. We strongly recommend against this for anything other than demos and prototypes. You should use
|
|
657
|
-
something like <a href="https://www.npmjs.com/package/dompurify">DOMPurify</a> or the new browser API
|
|
658
|
-
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Sanitizer/sanitize">Sanitize</a> when it becomes available
|
|
659
|
-
in order to santize your input prior to assigning values to Lightview variables.</p>
|
|
660
|
-
<p>This being said, Lightview has a small blunt mechanism for providing some level of protection:</p>
|
|
661
|
-
<ol>
|
|
662
|
-
<li>It "sanitizes" templates before attempting to resolve them by making suspicious code unparseable. The result is that the template will simply not be replaced in the component.</li>
|
|
663
|
-
<li>If the target node is an <code>HTMLElement</code> or <code>Attr</code>, it takes the result of the template interpolation and escapes all HTML characters
|
|
664
|
-
before inserting the value into the DOM. If the target node is a <code>TextNode</code>, no escaping is conducted because it is not needed. The DOM
|
|
665
|
-
will not try to treat the content of a text node like it is HTML, even if it looks just like HTML. Surprisingly, most
|
|
666
|
-
of the time, the target will be a <code>TextNode</code>.</li>
|
|
667
|
-
</ol>
|
|
668
|
-
<p>Here is the code:</p>
|
|
669
|
-
<pre><code class="language-javascript"> <span class="hljs-keyword">const</span> templateSanitizer = <span class="hljs-function">(<span class="hljs-params"><span class="hljs-built_in">string</span></span>) =></span> {
|
|
670
|
-
<span class="hljs-keyword">return</span> <span class="hljs-built_in">string</span>.replace(<span class="hljs-regexp">/function\s+/g</span>,<span class="hljs-string">""</span>)
|
|
671
|
-
.replace(<span class="hljs-regexp">/function\(/g</span>,<span class="hljs-string">""</span>)
|
|
672
|
-
.replace(<span class="hljs-regexp">/=\s*>/g</span>,<span class="hljs-string">""</span>)
|
|
673
|
-
.replace(<span class="hljs-regexp">/(while|do|for|alert)\s*\(/g</span>,<span class="hljs-string">""</span>)
|
|
674
|
-
.replace(<span class="hljs-regexp">/console\.[a-zA-Z$]+\s*\(/g</span>,<span class="hljs-string">""</span>);
|
|
675
|
-
}
|
|
676
|
-
Lightview.sanitizeTemplate = templateSanitizer;
|
|
677
|
-
|
|
678
|
-
<span class="hljs-keyword">const</span> escaper = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'textarea'</span>);
|
|
679
|
-
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">escapeHTML</span>(<span class="hljs-params">html</span>) </span>{
|
|
680
|
-
escaper.textContent = html;
|
|
681
|
-
<span class="hljs-keyword">return</span> escaper.innerHTML;
|
|
682
|
-
}
|
|
683
|
-
</code></pre>
|
|
684
|
-
<p>Any errors thrown by the santizier will be logged as warnings to the console and re-thrown.</p>
|
|
685
|
-
<p>If you need dynamic arrow function closures in your templates, you can replace <code>Lightview.sanitizeTemplate</code> with your own code at the
|
|
686
|
-
top of your component file:</p>
|
|
687
|
-
<pre><code class="language-html"> <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./lightview.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
|
|
688
|
-
<span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="javascript">
|
|
689
|
-
Lightview.sanitizeTemplate = <span class="hljs-function">(<span class="hljs-params">string</span>) =></span> {
|
|
690
|
-
<span class="hljs-keyword">return</span> string.replace(<span class="hljs-regexp">/function\s+/g</span>,<span class="hljs-string">""</span>)
|
|
691
|
-
.replace(<span class="hljs-regexp">/function\(/g</span>,<span class="hljs-string">""</span>)
|
|
692
|
-
.replace(<span class="hljs-regexp">/(while|do|for|alert)\s*\(/g</span>,<span class="hljs-string">""</span>)
|
|
693
|
-
.replace(<span class="hljs-regexp">/console\.[a-zA-Z$]+\s*\(/g</span>,<span class="hljs-string">""</span>);
|
|
694
|
-
}
|
|
695
|
-
</span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
|
|
696
|
-
</code></pre>
|
|
697
|
-
<p>Here is an example of Lightview sanitizing in action.</p>
|
|
698
|
-
<p><button onclick="resetPen('XWVELBv')">Reset</button>
|
|
699
|
-
<span style="float:right">
|
|
700
|
-
<i class="fa-brands fa-codepen"></i>
|
|
701
|
-
<a href="https://codepen.io/anywhichway/details/XWVELBv">lightview-invalid-template-literals</a>
|
|
702
|
-
</span></p>
|
|
703
|
-
<p class="codepen" data-height="900" data-theme-id="40735" data-default-tab="html,result" data-slug-hash="XWVELBv" data-editable="true" data-user="anywhichway" style="height: 900px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
|
704
|
-
<span>See the Pen <a href="https://codepen.io/anywhichway/pen/XWVELBv">
|
|
705
|
-
lightview-invalid-template-literals</a> by Simon Y. Blackwell (<a href="https://codepen.io/anywhichway">@anywhichway</a>)
|
|
706
|
-
on <a href="https://codepen.io">CodePen</a>.</span>
|
|
707
|
-
</p>
|
|
708
|
-
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
|
709
|
-
|
|
710
|
-
<p>Finally, Lightview may be flagged by software supply chain analysis programs for the use of <code>eval</code>. Ligthview actually uses
|
|
711
|
-
<code>Function</code>, which is slightly less risky, but still an issue. Hence, it deserves explanation. </p>
|
|
712
|
-
<p>Lightview makes use of the much maligned but very powerful <code>with</code> statement at four locations in its codebase. The <code>with</code>
|
|
713
|
-
statement is not typically considered a security risk, but its uninformed use can make for slow and obscure code.</p>
|
|
714
|
-
<p>Three of the locations where <code>with</code> is used are related to template interpolation. At the expense of making the code base
|
|
715
|
-
substantially larger or adding 3rd party dependencies, the use of <code>with</code> could be avoided for interpolation.</p>
|
|
716
|
-
<p>The fourth use of <code>with</code> is related to <code>lightview/module</code> script execution. The means by which Ligthview is able to support
|
|
717
|
-
what appears to be direct coding against variables is through the use of <code>with</code> and a <code>Proxy</code>. The <code>with</code> statement is
|
|
718
|
-
not valid in <code>strict mode</code> Javascript like modules. The dynamic <code>Function</code> by-passes this restriction for this very limited
|
|
719
|
-
place in which <code>with</code> is used. It would probably not be possible to implement Ligthview as without a pre-processor if this
|
|
720
|
-
approach were not taken, in which case <code>Svelte</code> would be the best option.</p>
|
|
721
|
-
<h3 id="license">License</h3>
|
|
722
|
-
<p>MIT License</p>
|
|
723
|
-
<p>Copyright (c) 2022 AnyWhichWay,LLC - Lightview Small, simple, powerful UI creation ...</p>
|
|
724
|
-
<p>Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
725
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
726
|
-
in the Software without restriction, including without limitation the rights
|
|
727
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
728
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
729
|
-
furnished to do so, subject to the following conditions:</p>
|
|
730
|
-
<p>The above copyright notice and this permission notice shall be included in all
|
|
731
|
-
copies or substantial portions of the Software.</p>
|
|
732
|
-
<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
733
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
734
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
735
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
736
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
737
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
738
|
-
SOFTWARE.</p>
|
|
739
|
-
<h3 id="change-history">Change History</h3>
|
|
740
|
-
<p>Reverse Chronological Order</p>
|
|
741
|
-
<p>If the day is <code>??</code>, then the version is currently under test on this site, but not yet commited to GitHub or updated on NPM.</p>
|
|
742
|
-
<p>2022-05-19 v1.8.1b Addressed issue with <code>textarea</code> inputs not binding properly. Link elements in the head of components are now imported when
|
|
743
|
-
a module link is imported. Eliminated need for script recompiles. Now, just set a <code>mount</code> function for the component. Much faster. Smaller codebase. Works better with syntax highlighters.</p>
|
|
744
|
-
<p>2022-05-11 v1.7.3b Standardized chart components so they all have a very similar API. Added additional charts <code>OrgChart</code> and <code>Timeline</code>.</p>
|
|
745
|
-
<p>2022-05-10 v1.7.2b Adjusted relative pathing for components.</p>
|
|
746
|
-
<p>2022-05-09 v1.7.1b Added support for object bound forms. Exposed <code>observe</code> for use in lightview/module scripts. Addressed issue related to single quotes causing lightview/module scripts to fail parsing. Improved <code>l-for</code> so that it is not destructive of HTML. Added <code>examples/todo.html</code>. Made <code>l-on</code> more restrictive. Value MUST now be in a template literal. Added <code>examples/markdown.md</code>.</p>
|
|
747
|
-
<p>2022-05-04 v1.6.6b Added unit tests. Simplified code for functional types.</p>
|
|
748
|
-
<p>2022-05-03 v1.6.5b Added Medium remote sensor article example.</p>
|
|
749
|
-
<p>2022-05-02 v1.6.4b Added default <code>whenInvalid</code> to all extened types. Fixed issue with extended boolean always return a valid state when not coercing. Added many unit tests.</p>
|
|
750
|
-
<p>2022-05-01 v1.6.3b GitHub repository README updates.</p>
|
|
751
|
-
<p>2022-05-01 v1.6.2b Added unit testing example and documentation. Added <code>self.getVariable</code>.</p>
|
|
752
|
-
<p>2022-04-30 v1.6.1b Renamed <code>variableType</code> to <code>dataType</code> and <code>variableKind</code> to <code>functionalType</code>. Moved functional type <code>remote</code> to the <code>types.js</code> file since it adds almost 1K of size to the core and may not be used by many developers. Fixed dates in the Change History (they were all set to 2021!).</p>
|
|
753
|
-
<p>2022-04-29 v1.5.1b Added extended type definitions, added chart and gauge components, modified lifecycle events to use event listeners.</p>
|
|
754
|
-
<p>2022-04-26 v1.4.10b Eliminated excess export of auto defined variables. Fixed unit test flaw for radio elements.</p>
|
|
755
|
-
<p>2022-04-25 v1.4.9b Added support for remote variables.</p>
|
|
756
|
-
<p>2022-04-8 v1.4.8b Addressed issue where inputs had multiple of the same event handler registered. Addressed
|
|
757
|
-
script tags getting processed for interpolation when they shouldn't. Added reset button to Pens. Fixed
|
|
758
|
-
invisible cursor issue in Pens. Fixed default selection for forms based on variables.</p>
|
|
759
|
-
<p>2022-04-7 v1.4.7b Documentation changes, addition of <code>any</code> and <code>object</code> constants, aditional sanitizing.</p>
|
|
760
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/highlight.min.js"></script>
|
|
761
|
-
<script>
|
|
762
|
-
const resetPen = (id) => {
|
|
763
|
-
const pen = document.querySelector(`iframe[src*="${id}"]`);
|
|
764
|
-
if(pen) pen.setAttribute("src",pen.getAttribute("src"));
|
|
765
|
-
}
|
|
766
|
-
</script>
|
|
767
|
-
<script type="module">
|
|
768
|
-
function fit() {
|
|
769
|
-
const examples = [...document.querySelectorAll("code[src]")];
|
|
770
|
-
examples.forEach(async (example) => {
|
|
771
|
-
const parent = example.parentElement,
|
|
772
|
-
src = example.getAttribute("src"),
|
|
773
|
-
url = new URL(src,window.location.href),
|
|
774
|
-
text = (await (await fetch(url.href)).text()).replace("<!DOCTYPE html>",""),
|
|
775
|
-
iframe = document.createElement("iframe"),
|
|
776
|
-
height = example.getAttribute("height") || example.parentElement.getAttribute("height");
|
|
777
|
-
if(height) iframe.setAttribute("height",height);
|
|
778
|
-
if(!example.hasAttribute("scriptonly")) {
|
|
779
|
-
example.textContent = text;
|
|
780
|
-
hljs.highlightElement(parent);
|
|
781
|
-
}
|
|
782
|
-
parent.style.setProperty("margin-right","10px");
|
|
783
|
-
if(example.hasAttribute("contentonly")) return;
|
|
784
|
-
if(example.parentElement!==parent) {
|
|
785
|
-
while(example.lastChild) example.lastChild.remove();
|
|
786
|
-
while(parent.firstChild) example.appendChild(parent.firstChild);
|
|
787
|
-
parent.appendChild(example);
|
|
788
|
-
}
|
|
789
|
-
hljs.highlightElement(example.parentElement);
|
|
790
|
-
iframe.setAttribute("src",src);
|
|
791
|
-
iframe.setAttribute("class","gh-fit");
|
|
792
|
-
iframe.setAttribute("style","width:100%;border-style:solid");
|
|
793
|
-
parent.insertAdjacentElement("afterend",iframe);
|
|
794
|
-
|
|
795
|
-
})
|
|
796
|
-
|
|
797
|
-
const iframes = [...document.querySelectorAll("iframe.gh-fit")];
|
|
798
|
-
|
|
799
|
-
iframes.forEach((ifrm) => {
|
|
800
|
-
const win = ifrm.contentWindow,
|
|
801
|
-
doc = win.document;
|
|
802
|
-
doc.addEventListener("DOMContentLoaded",() => {
|
|
803
|
-
const html = doc.documentElement,
|
|
804
|
-
body = doc.body;
|
|
805
|
-
|
|
806
|
-
if(body) {
|
|
807
|
-
body.style.overflowX = "auto";
|
|
808
|
-
body.style.overflowY = "auto";
|
|
809
|
-
}
|
|
810
|
-
if(html) {
|
|
811
|
-
html.style.overflowX = "auto";
|
|
812
|
-
html.style.overflowY = "auto";
|
|
813
|
-
var style = win.getComputedStyle(html)
|
|
814
|
-
ifrm.width = parseInt(style.getPropertyValue("width"));
|
|
815
|
-
ifrm.height = parseInt(style.getPropertyValue("height")) + 10;
|
|
816
|
-
}
|
|
817
|
-
})
|
|
818
|
-
|
|
819
|
-
})
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
//addEventListener("load", requestAnimationFrame.bind(this, fit))
|
|
823
|
-
function generateLinkMarkup(contentElement) {
|
|
824
|
-
const headings = [...contentElement.querySelectorAll('h3, h4, h5')]
|
|
825
|
-
const parsedHeadings = headings.map(heading => {
|
|
826
|
-
return {
|
|
827
|
-
title: heading.innerText.split(" ").map((word) => word[0].toUpperCase() + word.substring(1).toLowerCase()).join(" "),
|
|
828
|
-
depth: parseInt(heading.nodeName.replace(/\D/g,'')),
|
|
829
|
-
id: heading.getAttribute('id')
|
|
830
|
-
}
|
|
831
|
-
});
|
|
832
|
-
let html = "";
|
|
833
|
-
for(let i=0;i<parsedHeadings.length;i++) {
|
|
834
|
-
const heading = parsedHeadings[i];
|
|
835
|
-
if(i>0) {
|
|
836
|
-
if(heading.depth>parsedHeadings[i-1].depth) {
|
|
837
|
-
html+="<ul>"
|
|
838
|
-
} else if(heading.depth<parsedHeadings[i-1].depth) {
|
|
839
|
-
html+="</ul>"
|
|
840
|
-
}
|
|
841
|
-
html += `<li><a href="#${heading.id}" target="_self">${heading.title}</a></li>`;
|
|
842
|
-
} else {
|
|
843
|
-
html += `<li><a href="#${heading.id}" target="_self">${heading.title}</a></li>`;
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
return `<ul>${html}</ul>`;
|
|
847
|
-
}
|
|
848
|
-
const toc = document.querySelector(".toc");
|
|
849
|
-
if(toc) {
|
|
850
|
-
toc.innerHTML = generateLinkMarkup(document.body);
|
|
851
|
-
}
|
|
852
|
-
document.querySelectorAll('pre code').forEach((el) => {
|
|
853
|
-
hljs.highlightElement(el);
|
|
854
|
-
el.style.setProperty("margin-right","10px");
|
|
855
|
-
});
|
|
856
|
-
document.body.style = "overflow:hidden;height:100%;max-height:100%;margin-top:0px"
|
|
857
|
-
|
|
858
|
-
let touchstartX = 0,
|
|
859
|
-
touchendX = 0,
|
|
860
|
-
touchstartY = 0,
|
|
861
|
-
touchendY = 0,
|
|
862
|
-
x = 0,
|
|
863
|
-
y = 0;
|
|
864
|
-
const toggle = document.getElementById("TOC"),
|
|
865
|
-
header = document.getElementById("header"),
|
|
866
|
-
content = document.getElementById("content");
|
|
867
|
-
document.getElementById("toggle-button").style.setProperty("display","inline");
|
|
868
|
-
function handleGesture({event,right,left}={}) {
|
|
869
|
-
if (left && touchendX < touchstartX && Math.abs(touchstartY-touchendY)<100 && Math.abs(touchstartX-touchendX)>75) { left(); }
|
|
870
|
-
else if (right && touchendX > touchstartX && touchstartX<150) { right(); }
|
|
871
|
-
}
|
|
872
|
-
let opened;
|
|
873
|
-
function handleTOC(open) {
|
|
874
|
-
const previous = opened;
|
|
875
|
-
if(open===undefined) open = opened = !opened
|
|
876
|
-
else opened = open;
|
|
877
|
-
if(opened) {
|
|
878
|
-
toc.style.setProperty("max-width","");
|
|
879
|
-
toc.style.setProperty("overflow-y","auto");
|
|
880
|
-
toc.style.setProperty("max-height","calc(100% - 3em)");
|
|
881
|
-
toc.style.setProperty("height","calc(100% - 3em)");
|
|
882
|
-
header.style.setProperty("display","initial");
|
|
883
|
-
content.style.setProperty("margin-left",toc.clientWidth+10);
|
|
884
|
-
content.style.setProperty("max-width",`calc(100% - ${toc.clientWidth+40}px)`);
|
|
885
|
-
} else {
|
|
886
|
-
toc.style.setProperty("max-width","12px");
|
|
887
|
-
toc.style.setProperty("overflow-y","hidden");
|
|
888
|
-
toc.style.setProperty("max-height","97%");
|
|
889
|
-
toc.style.setProperty("height","97%");
|
|
890
|
-
header.style.display = "none";
|
|
891
|
-
content.style.setProperty("margin-left","");
|
|
892
|
-
content.style.setProperty("max-width",`calc(100% - ${48}px)`);
|
|
893
|
-
}
|
|
894
|
-
if(opened && previous!=undefined) {
|
|
895
|
-
setTimeout(()=> {
|
|
896
|
-
content.scrollTo({top:y+25});
|
|
897
|
-
},250);
|
|
898
|
-
} else if(!opened) {
|
|
899
|
-
setTimeout(()=> {
|
|
900
|
-
content.scrollTo({top:y-25});
|
|
901
|
-
},250);
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
content.style.setProperty("margin-left",toc.clientWidth+10);
|
|
905
|
-
toc.style.setProperty("max-width","12px");
|
|
906
|
-
handleTOC(true)
|
|
907
|
-
toc.addEventListener('touchstart', event => {
|
|
908
|
-
touchstartX = event.changedTouches[0].screenX
|
|
909
|
-
},{passive:true})
|
|
910
|
-
toc.addEventListener('touchend', event => {
|
|
911
|
-
touchendX = event.changedTouches[0].screenX
|
|
912
|
-
handleGesture({left:()=>handleTOC(false),right:()=>handleTOC(true)})
|
|
913
|
-
},{passive:true})
|
|
914
|
-
content.addEventListener("scroll",(event) => {
|
|
915
|
-
y = content.scrollTop;
|
|
916
|
-
},{passive:true})
|
|
917
|
-
content.addEventListener('touchstart', event => {
|
|
918
|
-
touchstartX = event.changedTouches[0].screenX
|
|
919
|
-
touchstartY = event.changedTouches[0].screenY
|
|
920
|
-
},{passive:true})
|
|
921
|
-
content.addEventListener('touchend', event => {
|
|
922
|
-
touchendX = event.changedTouches[0].screenX;
|
|
923
|
-
touchendY = event.changedTouches[0].screenY;
|
|
924
|
-
handleGesture({right:()=>handleTOC(true),left:()=>event.preventDefault()});
|
|
925
|
-
},{passive:true})
|
|
926
|
-
toggle.addEventListener("click",(event) => {
|
|
927
|
-
event.stopImmediatePropagation();
|
|
928
|
-
if(event.target.tagName==="A" || event.target.id==="toggle-button") handleTOC(false);
|
|
929
|
-
else if(!opened) handleTOC(true);
|
|
930
|
-
});
|
|
931
|
-
[...document.body.querySelectorAll('a[href^="#"]')].forEach((a) => {
|
|
932
|
-
a.setAttribute("target","_self");
|
|
933
|
-
})
|
|
934
|
-
fit();
|
|
935
|
-
</script>
|
|
936
|
-
<style>
|
|
937
|
-
a:target {
|
|
938
|
-
scroll-margin-top: 24px;
|
|
939
|
-
}
|
|
940
|
-
.toc {
|
|
941
|
-
overflow-wrap: break-word;
|
|
942
|
-
}
|
|
943
|
-
.toc ul {
|
|
944
|
-
margin-left: 5px;
|
|
945
|
-
margin-top: 0px;
|
|
946
|
-
margin-bottom: 0px;
|
|
947
|
-
list-style: none; /* This removes the list styling which are provided by default */
|
|
948
|
-
padding-left: 5px; /* Removes the front padding */
|
|
949
|
-
}
|
|
950
|
-
.toc ul li a {
|
|
951
|
-
text-decoration: none; /* Removes the underline from the link tags */
|
|
952
|
-
font-size: 80%
|
|
953
|
-
}
|
|
954
|
-
.toc ul li {
|
|
955
|
-
margin-left: 0px;
|
|
956
|
-
margin-top: 5px;
|
|
957
|
-
margin-bottom: 5px;
|
|
958
|
-
padding: 2px; /* Adds a little space around each <li> tag */
|
|
959
|
-
line-height: 80%
|
|
960
|
-
}
|
|
961
|
-
</style>
|
|
962
|
-
|
|
963
|
-
</section>
|
|
964
|
-
</body>
|
|
965
|
-
</html>
|