lightview 1.8.1-b → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +15 -16
  2. package/docs/CNAME +1 -0
  3. package/docs/api.html +674 -0
  4. package/docs/blank.html +10 -0
  5. package/docs/comparedto.html +89 -0
  6. package/docs/components/chart-repl.html +69 -0
  7. package/{components/chart → docs/components}/chart.html +2 -2
  8. package/{components → docs/components}/components.js +3 -3
  9. package/docs/components/contents.html +17 -0
  10. package/docs/components/gantt-repl.html +61 -0
  11. package/{components/gantt → docs/components}/gantt.html +3 -3
  12. package/docs/components/gauge-repl.html +66 -0
  13. package/{components/gauge → docs/components}/gauge.html +2 -2
  14. package/docs/components/orgchart-repl.html +64 -0
  15. package/{components/orgchart → docs/components}/orgchart.html +2 -2
  16. package/docs/components/repl-as-src.html +17 -0
  17. package/docs/components/repl-repl.html +95 -0
  18. package/docs/components/repl.html +527 -0
  19. package/docs/components/timeline-repl.html +72 -0
  20. package/{components/timeline → docs/components}/timeline.html +2 -2
  21. package/docs/components.html +14 -0
  22. package/docs/css/highlightjs.min.css +9 -0
  23. package/docs/css/tutorial.css +35 -0
  24. package/docs/examples/anchor.html +11 -0
  25. package/{examples → docs/examples}/chart.html +2 -2
  26. package/{examples → docs/examples}/counter.html +1 -1
  27. package/{examples → docs/examples}/counter.test.mjs +0 -0
  28. package/{examples → docs/examples}/counter2.html +1 -1
  29. package/{examples → docs/examples}/directives.html +1 -1
  30. package/{examples → docs/examples}/foreign.html +1 -1
  31. package/{examples → docs/examples}/forgeinform.html +1 -1
  32. package/{examples → docs/examples}/form.html +1 -1
  33. package/{examples → docs/examples}/gauge.html +2 -2
  34. package/{examples → docs/examples}/invalid-template-literals.html +1 -1
  35. package/{examples → docs/examples}/medium/remote.html +1 -1
  36. package/{examples → docs/examples}/message.html +0 -0
  37. package/{examples → docs/examples}/nested.html +1 -1
  38. package/{examples → docs/examples}/object-bound-form.html +0 -0
  39. package/{examples → docs/examples}/remote-server.js +0 -0
  40. package/{examples → docs/examples}/remote.html +2 -2
  41. package/{examples → docs/examples}/remote.json +0 -0
  42. package/{examples → docs/examples}/scratch.html +1 -1
  43. package/docs/examples/sensors/index.html +44 -0
  44. package/{examples → docs/examples}/sensors/sensor-server.js +0 -0
  45. package/{examples → docs/examples}/shared.html +0 -0
  46. package/{examples → docs/examples}/template.html +1 -1
  47. package/{examples → docs/examples}/timeline.html +2 -2
  48. package/docs/examples/todo.html +40 -0
  49. package/docs/examples/top.html +10 -0
  50. package/{examples → docs/examples}/types.html +1 -1
  51. package/{examples → docs/examples}/xor.html +1 -1
  52. package/docs/examples.html +25 -0
  53. package/docs/index.html +44 -0
  54. package/docs/javascript/codejar.min.js +8 -0
  55. package/docs/javascript/highlightjs.min.js +1173 -0
  56. package/docs/javascript/isomorphic-git.js +9 -0
  57. package/docs/javascript/json5.min.js +1 -0
  58. package/docs/javascript/lightning-fs.js +1 -0
  59. package/docs/javascript/lightview.js +1285 -0
  60. package/docs/javascript/marked.min.js +6 -0
  61. package/docs/javascript/peerjs.min.js +70 -0
  62. package/docs/javascript/turndown.js +973 -0
  63. package/docs/javascript/types.js +606 -0
  64. package/docs/javascript/utils.js +45 -0
  65. package/docs/lightview.html +63 -0
  66. package/docs/old_index.html +965 -0
  67. package/docs/old_index.md +1132 -0
  68. package/docs/slidein.html +51 -0
  69. package/docs/tutorial/0-getting-started.html +67 -0
  70. package/docs/tutorial/1-intro-to-variables.html +103 -0
  71. package/docs/tutorial/10-template-components.html +80 -0
  72. package/docs/tutorial/11-linked-components.html +76 -0
  73. package/docs/tutorial/12-imported-components.html +67 -0
  74. package/docs/tutorial/13-input-binding.html +94 -0
  75. package/docs/tutorial/14-automatic-variable-creation.html +74 -0
  76. package/docs/tutorial/15-form-binding.html +110 -0
  77. package/docs/tutorial/16-if-directive.html +60 -0
  78. package/docs/tutorial/17-loop-directives.html +83 -0
  79. package/docs/tutorial/18-sanitizing-and-escaping-input.html +79 -0
  80. package/docs/tutorial/2-imported-and-exported-variables.html +80 -0
  81. package/docs/tutorial/3-data-types.html +89 -0
  82. package/docs/tutorial/4-extended-data-types.html +83 -0
  83. package/docs/tutorial/5-extended-functional-types.html +96 -0
  84. package/docs/tutorial/5.1-extended-functional-types.html +79 -0
  85. package/docs/tutorial/5.2-extended-functional-types.html +70 -0
  86. package/docs/tutorial/6-conventional-javascript.html +75 -0
  87. package/docs/tutorial/7-monitoring-with-observers.html +107 -0
  88. package/docs/tutorial/8-event-listeners.html +65 -0
  89. package/docs/tutorial/9-intro-to-components.html +91 -0
  90. package/docs/tutorial/contents.html +32 -0
  91. package/docs/tutorial/my-component.html +29 -0
  92. package/docs/tutorial/remote-value.json +4 -0
  93. package/docs/websiterepl.html +46 -0
  94. package/lightview.js +430 -340
  95. package/lightview.min.js +1 -0
  96. package/lightview_good.js +1267 -0
  97. package/lightview_optimized.js +1274 -0
  98. package/package.json +1 -1
  99. package/repl_hold.html +320 -0
  100. package/test/basic.html +15 -4
  101. package/test/basic.test.mjs +1 -1
  102. package/test/extended.html +1 -1
  103. package/types.js +109 -36
  104. package/components/chart/example.html +0 -32
  105. package/components/chart.html +0 -83
  106. package/components/gantt/example.html +0 -22
  107. package/components/gauge/example.html +0 -28
  108. package/components/gauge.html +0 -60
  109. package/components/orgchart/example.html +0 -25
  110. package/components/repl/code-editor.html +0 -64
  111. package/components/repl/editor.html +0 -37
  112. package/components/repl/editorjs-inline-tool/index.js +0 -3
  113. package/components/repl/editorjs-inline-tool/inline-tools.js +0 -28
  114. package/components/repl/editorjs-inline-tool/tool.js +0 -175
  115. package/components/repl/repl-with-wysiwyg.html +0 -355
  116. package/components/repl/repl.html +0 -345
  117. package/components/repl/sup.js +0 -44
  118. package/components/repl/wysiwyg-repl.html +0 -258
  119. package/components/timeline/example.html +0 -33
  120. package/components/timeline.html +0 -81
  121. package/examples/anchor.html +0 -11
  122. package/examples/sensors/index.html +0 -30
  123. package/examples/todo.html +0 -38
  124. package/examples/top.html +0 -10
  125. package/sites/client.html +0 -48
  126. package/sites/index.html +0 -247
package/docs/api.html ADDED
@@ -0,0 +1,674 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Lightview:API</title>
6
+ <script src="./javascript/marked.min.js"></script>
7
+ <script src="./javascript/highlightjs.min.js"></script>
8
+ <script src="./javascript/utils.js"></script>
9
+ </head>
10
+ <body>
11
+ <div class="markdown">
12
+
13
+ ## API
14
+
15
+ Lightview has two APIs based on the context in which it is used:
16
+
17
+ 1. Public API
18
+
19
+ The public API is the one used in a normal JavaScript context. It is relatively limited in scope.
20
+
21
+ 2. Contextual API
22
+
23
+ The contextual API is only available from within scripts having an `id="lightview"`.
24
+
25
+ ### Public API
26
+
27
+ A `Lightview` object is available after you load the file `lightview.js` using the script &lt;script src="./lightview.js"></script&gt;
28
+
29
+ The path to `lightview.js` may vary based on where you install Lightview.
30
+
31
+ - `HTMLCustomElement Lightview.bodyAsComponent([{as = "x-body", unhide:boolean, framed:boolean}])`
32
+
33
+ - This function is rarely called directly and treats the body of an HTML document as a component. If it is called
34
+ directly, the place to do so is in a `DOMContentLoaded` event handler.
35
+ - A shorthand and most common way of using this is to just pass the query string ``?as=x-body` to the Lightview loading script in the head or close to
36
+ the top of the body in your document.
37
+ - Note, once a body is turned into a component, its contents are in a shadow DOM, so using `document.getElementById` will not work. Instead, use
38
+ `document.body.getElementById`. All components implement `getElementById`.
39
+ - 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
40
+ the creation of unit testable components.
41
+
42
+ See the tutorial section on [Intro To Components](./tutorial/9-intro-to-components.html).
43
+
44
+ <a id="createComponent"></a>
45
+ - `function Lightview.createComponent(tagName:string,template:HTMLElement)`
46
+
47
+ Creates a component and registers it as a custom element with the provided `tagName` based on the inner HTML
48
+ of the HTMLElement pointed to by `template`. The `template` is typically a
49
+ <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement" target="_tab">HTMLTemplateElement</a>,
50
+ but it can be almost any HTMLElement like a `<div>`. It is usually provided by using `document.getElementById`.
51
+
52
+ See the tutorial section on [Template Components](./tutorial/10-template-components.html).
53
+
54
+ <a id="createInputVariables"></a>
55
+ - `Lightview.createInputVariables:boolean`
56
+
57
+ A flag that can be set to `true` prior to defining and processing a component `mount` function. When `true`, any HTML input
58
+ elements with `value` attributes that resolve to a atomic string template literals automatically result in the creation
59
+ of reactive variables with a type consistent with the input elements.
60
+
61
+ See the tutorial section on [Automatic Variable Creation](./tutorial/14-automatic-variable-creation.html) or its predecessor
62
+ [Input Binding](./tutorial/input-binding.html).
63
+
64
+ <a id="escapeHTML"></a>
65
+ - `string Lightview.escapeHTML(possibleHTML:string)`
66
+
67
+ There are some security issues related to the use of template literals and un-sanitized user input as values for variables.
68
+
69
+ Wherever template resolution is completed the target node is an HTMLElement or Attr, it takes the result of the template
70
+ interpolation and escapes all HTML characters before inserting the value into the DOM. If the target node is a TextNode,
71
+ no escaping is conducted because it is not needed. The DOM will not try to treat the content of a text node like it is HTML, even if it looks like HTML.
72
+ Surprisingly, most of the time, the target will be a TextNode.
73
+
74
+ The simplest way to escape HTML is to set it into a textarea and then retrieve it again. This is the mechanism employed by Lightview.
75
+
76
+ ```javascript
77
+ const escaper = document.createElement('textarea');
78
+ const escapeHTML = html => {
79
+ escaper.textContent = html;
80
+ return escaper.innerHTML;
81
+ }
82
+ Lightview.escapeHTML = escapeHTML;
83
+ ```
84
+
85
+ You can override this mechanism by assigning a new value to `Lightview.escapeHTML`, perhaps based on <a href="https://github.com/cure53/DOMPurify" target="_tab">DOMPurify</a>
86
+ or the new browser <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API" target="_tab">Sanitizer API</a> when it becomes widely available
87
+
88
+ See the tutorial section [Sanitizing and Escaping HTML](./tutorial/18-sanitizing-and-escaping-input.html).
89
+
90
+ <a id="renderErrors"></a>
91
+ - `Lightview.renderErrors:boolean`
92
+
93
+ A flag that can be set to `true` prior to defining and processing a component `mount` function. When `true`, any errors that
94
+ result from string literal template resolution in HTML are rendered into the HTML. Otherwise, the errors are swallowed and the string
95
+ literal template remains in the HTML.
96
+
97
+ See the example code about half-way down tutorial section [Introduction to Variables](./tutorial/1-intro-to-variables.html).
98
+
99
+ - `Lightview.whenFramed:function`
100
+
101
+ <a id="sanitizeTemplate"></a>
102
+ - `string Lightview.sanitizeTemplate(template:string)`
103
+
104
+ There are some security issues related to the use of template literals to substitute values into HTML. Lightview has a small blunt mechanism for
105
+ providing protection. It "sanitizes" templates before attempting to resolve them by making suspicious code unparseable. The result is that the template will simply
106
+ not be replaced in the component. If resolution is successful and the target node is an HTMLElement or Attr, it takes the result of the template
107
+ interpolation and escapes all HTML characters before inserting the value into the DOM (see Lightview.escapeHTML). If the target node is a TextNode,
108
+ no escaping is conducted because it is not needed. The DOM will not try to treat the content of a text node like it is HTML, even if it looks like HTML.
109
+ Surprisingly, most of the time, the target will be a TextNode. Here is the code:
110
+
111
+ ```javascript
112
+ const templateSanitizer = (string) => {
113
+ return string.replace(/function\s+/g,"")
114
+ .replace(/function\(/g,"")
115
+ .replace(/=\s*>/g,"")
116
+ .replace(/(while|do|for|alert)\s*\(/g,"")
117
+ .replace(/console\.[a-zA-Z$]+\s*\(/g,"");
118
+ };
119
+ Lightview.sanitizeTemplate = templateSanitizer;
120
+ ```
121
+
122
+ If you need dynamic arrow function closures in your templates, you can replace Lightview.sanitizeTemplate with your own code at the top of
123
+ your component file:
124
+
125
+ ```javascript
126
+ Lightview.sanitizeTemplate = (string) => {
127
+ return string.replace(/function\s+/g,"")
128
+ .replace(/function\(/g,"")
129
+ .replace(/(while|do|for|alert)\s*\(/g,"")
130
+ .replace(/console\.[a-zA-Z$]+\s*\(/g,"");
131
+ }
132
+ ```
133
+
134
+ You can use something like <a href="https://github.com/cure53/DOMPurify" target="_tab">DOMPurify</a> or the new browser
135
+ <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API" target="_tab">Sanitizer API</a> when it becomes
136
+ widely available in order to implement and provide an alternate sanitizer if you wish. Just assign it to `Lightview.sanitizeTemplate`.
137
+
138
+ See the tutorial section [Sanitizing and Escaping HTML](./tutorial/18-sanitizing-and-escaping-input.html).
139
+
140
+ <a id="whenFramed"></a>
141
+
142
+ - `void Lightview.whenFramed(callback:function [, {isolated:boolean}])`
143
+
144
+ - Invokes `callback` when a component file detects it is being loaded in an iframe.
145
+ - If `isolated` is set to true, then there will be no communication with the parent window. Otherwise, basic message
146
+ handling is automatically implemented in the child. The parent document will still need to implement handling. Basic
147
+ child message handling includes notification of component level attribute changes and content area scroll/display size.
148
+
149
+
150
+ ### Contextual API
151
+
152
+ Once again, the contextual API of Lightview is only available from within scripts having an `id="lightview"`.
153
+
154
+ The API consists of:
155
+
156
+ - contextual variables, e.g. `currentComponent` and `self`
157
+ - built-in properties and methods on components
158
+ - global constants that effectively become new reserved words, e.g. `reactive`
159
+ - capabilities that are imported from the file `types.js`
160
+
161
+
162
+ ### Contextual Variables
163
+
164
+ - `currentComponent:HTMLElement`
165
+
166
+ - `self`
167
+
168
+ ### Built-in Properties and Methods on Components
169
+
170
+ - `component.addEventListener(eventName:string,callback:function)`
171
+
172
+ Adds the callback to be invoked when the `eventName` occurs.
173
+
174
+ This is actually just the standard `HTMLElement.addEventLister`.
175
+
176
+ Valid eventNames beyond those supported natively include:
177
+
178
+ - "adopted" which will be invoked when a component is adopted by a document with the callback as callback({type:"adopted",target:component}).
179
+ - "connected" which will be invoked when a component is added to a document DOM with the callback as callback({type:"connected",target:component}).
180
+ - "mounted" which will be invoked when the <a href="#mount">mount</a> function described below has executed.
181
+ In order to prevent blocking, a number of Lightview component initialization functions are asynchronous. As a result, when a component
182
+ is connected by the DOM, there may still be initialization work in flight. Once the Lightview "mounted"" event has fired, you can be sure
183
+ all initialization work is complete so long as any custom asychnronous code inside of `mount` has been awaited.
184
+
185
+ If you encouter errors that say custom methods on your components are not available to other components or external scripts,
186
+ then try wrapping the code that accesses these methods in a "mounted" event listener.
187
+
188
+ - "disconnected" which will be invoked when a component is removed from a DOM with the callback as callback({type:"disconnected",target:component}).
189
+
190
+ <a id="variable"></a>
191
+ #### Methods Handling Variables
192
+ - `void this.variables({variableName:dataType[,...]},{functionalType:boolean|string|object,...[,...}})`
193
+
194
+ In addition to `var`, `let`, and `const`, Lightview supports the creation of variables using the method `variables`
195
+ available on all components implemented using Lightview. These variables can have both a data types and multiple
196
+ functional types. A "functional type" is one that assigns default behavior to a variable. See the sections
197
+ Global Constants and Imported Capabilities for more detail.
198
+
199
+ The `dataType` can be the string name of one of the primitive data types in JavaScript plus the value "any". It can
200
+ also be a reference to a constructor (e.g. Array) or an extended data type imported from "types.js".
201
+ See [Imported Capabilities](#imported-capabilities) below.
202
+
203
+ A `functionalType` can be one of the [Global Constants](#global-constants) defined below, or an extended functional type
204
+ imported from "types.js". See [Imported Capabilities](#imported-capabilities) below. A variable can have more than one functional type.
205
+
206
+ See the tutorial starting with [Intro To Variables](./tutorial/1-intro-to-variables.html).
207
+
208
+ ### Component Variable Access
209
+
210
+ You should be careful not to overload and shadow these functions by redefining them on your component.
211
+
212
+ - `Array this.getVariable()`
213
+
214
+ Returns a copy of the internal structure of a variable or `undefined`. See `this.variables` below.
215
+
216
+ - `Array this.getVariableNames()`
217
+
218
+ Returns an array of names of the currently defined variables for a component.
219
+
220
+ - `any this.getVariableValue(variableName:string)`
221
+
222
+ Gets the current value of variableName. Returns undefined if the variable does not exist.
223
+
224
+ - `boolean this.setVariableValue(variableName:string, value:any[, {coerceTo:string|function}])`
225
+
226
+ Sets a value for a `variableName`. Returns `true` if the variable already existed and `false` if not.
227
+
228
+ If the variable already existed, the existing type is used and `coerceTo` is ignored.
229
+
230
+ If the variable is created, the type is infered from the `value` or coerced value if `coerceTo` is provided.
231
+
232
+ - `object this.variables({[variableName]:variableType,...}[,{functionalType:any,...]})`
233
+
234
+ Used to declare variables.
235
+
236
+ Returns an object, the keys of which are variable names with the values being copies of the internal structure of the variable, e.g.
237
+
238
+ ```javascript
239
+ this.variables({v1:"string"},{imported,shared});
240
+ /* returns
241
+ {
242
+ v1: {name: "v1", type: "string", imported:true, shared:true}
243
+ }
244
+ */
245
+ self.variables({v2:"number"},{exported,reactive});
246
+ /* returns
247
+ {
248
+ v2: {name: "v2", value:2, type: "number", exported:true, reactive:true}
249
+ }
250
+ */
251
+ ```
252
+
253
+ #### Other Component Methods
254
+
255
+ <a id="mount"></a>
256
+ - `void mount(event:Event)`
257
+
258
+ A method implemented by the application developer to provide the variable creation and primary logic for the component
259
+ in the context of a script with `id="lightview"`. This is the methos that calls the above documented component methods and
260
+ uses the below documented functions and constants.
261
+
262
+ If the method is implemented as a regular function, `this` will be bound to the component when it executes.
263
+
264
+ If the method is implemented as an arrow function, the property `self` will be available when the function executes.
265
+
266
+ Components are HTML elements with a `shadowRoot` and have the standard `HTMLElement` properties and capability, e.g. getQuerySelector.
267
+ They also implement `getElementById` (which is normally only on a document). CHECK THIS, does shado Root provie? If sho, delete capabilty
268
+
269
+ ### Lightview Script Capability
270
+
271
+ A script with `id="lightview"` contains the following functions, variables, constants, and importable capability.
272
+
273
+ #### Functions
274
+
275
+ - `void addEventListener(evantName:string,callback:function)`
276
+
277
+ Like ??? in service workers, `addEventListener` is available directly in the top level of a script, although it will
278
+ always be invoked in the context of a `mount` function.
279
+
280
+ There is only one valid `eventName`, "change". The `callback` will be invoked every time a variable value changes with
281
+ an event of the form `{variableName:string,previousValue:any,value:any}`.
282
+
283
+ #### Variables
284
+
285
+ - `currentComponent:HTMLElement`
286
+
287
+ The currently processing component. In some contexts it is necessary to assign the value `document.body` like this:
288
+
289
+ ```javascript
290
+ (currentComponent||=document.body).mount = function() { ... }
291
+ ```
292
+
293
+ Doing the above will always be safe. It is necessary when `ligthview.js?as=x-body` is loaded in the head section of a file
294
+ rather than as the first script in the body section.
295
+
296
+ #### Constants
297
+
298
+ <a id="exported"></a>
299
+ - `const exported:boolean = true`
300
+
301
+ A functional type that automatically promotes the variable value to a component attribute of the same name when the value changes.
302
+
303
+ See the tutorial section [Imported and Exported Variables](./tutorial/2-imported-and-exported-variables.html).
304
+
305
+
306
+ <a id="imported"></a>
307
+ - `const imported:boolean = true`
308
+
309
+ A functional type that automatically imports the initial value for a variable from a component attribute by the same name.
310
+
311
+ See the tutorial section [Imported and Exported Variables](./tutorial/2-imported-and-exported-variables.html).
312
+
313
+
314
+ <a id="reactive"></a>
315
+ - `const reactive:boolean = true`
316
+
317
+ A functional type that automatically updates string template literals in HTML that reference variables of its type. The
318
+ template literals can be atomic references or computations, e.g. `${myVar}` or `${myVar + 1}`.
319
+
320
+ See the tutorial section [Intro to Variables](./tutorial/1-intro-to-variables.html).
321
+
322
+ #### Imported Capabilities
323
+
324
+ Imported capabilities take the form of functions that provide extended data types or extended functional types. The capabilities
325
+ must be imported from the file "types.js".
326
+
327
+ Extended data types consistently differ from standard data types in 5 ways.
328
+
329
+ 1. They are declared using a symbolic or functional form rather than quoted for, e.g. `{myVar:number}` or `{myVarv:number({min:0})` vs {myVar:"number"}`
330
+ 1. They do not automatically coerce values to their type. Coercion must be enabled using `{myVar:number({min:0,coerce:true})`
331
+ 1. They support default values if there is an attempt to assign `null` or `undefined`.
332
+ 1. They support required values. Although they may be in an initial state of `null` or `undefined`, they can't be set to `null` or `undefined` later.
333
+ 1. They have a default whenInvalid parameter which throws an error when an attempt to set the variable to an invalid value is made.
334
+ A custom function can be passed in that swallows the error and returns the existing value for the variable value, or undefined, or some other value. The
335
+ function is passed a copy of the internal structure of the variable, for example:
336
+
337
+ ```javascript
338
+ const whenInvalid = (variable) => {
339
+ return variable.value;
340
+ }
341
+ self.variables({myVar:number({min:0,whenInvalid}));
342
+ ```
343
+ you could even go ahead and make the assignment but log a warning:
344
+
345
+ ```javascript
346
+ const whenInvalid = (variable,invalidValue) => {
347
+ console.warn(`Assigning ${variable.name}:${variable.type.name||variable.type} invalid value ${invalidValue});
348
+ return newValue;
349
+ }
350
+ ```
351
+ <a id="any"></a>
352
+ - `object any({required?:boolean,whenInvalid?:function,default?:any})`
353
+
354
+ The extended data type `any` provides no more capability than "any", other than that common to
355
+ all extended data types: strict type validation,
356
+ optional `whenInvalid` handling, being required or not, and a default value.
357
+
358
+ <a id="array"></a>
359
+ - `object array({coerce?:boolean,required?:boolean,whenInvalid?:function,minlength?:number,maxlength?:number,default?:Array})`
360
+
361
+ An extended data type for the base data type `Array`.
362
+
363
+ - `minlength` defaults to `0`
364
+ - `maxlength` defaults to `Infinity`
365
+
366
+ <a id="boolean"></a>
367
+ - `object boolean({coerce?:boolean,required?:boolean,whenInvalid?:function,default?:boolean})`
368
+
369
+ An extended data type that provides just common extended data type capability.
370
+
371
+ <a id="duration"></a>
372
+ - `object duration:function`
373
+
374
+ An extended data type to represent time duration, e.g. `1m` a minute or `1M` a month. Variables of this type will
375
+ automatically coerce to milliseconds when used in math formulas, e.g.
376
+
377
+ ```javascript
378
+ this.variables({d:duration});
379
+ d = "1m 2h 3m";
380
+ const future = new Date(Date.now() + d);
381
+ ```
382
+
383
+ Note, you can't use just any string like it is a duration, `Date.now() + "1m 2h 3m"` will not work because the
384
+ JavaScript engine just thinks "1m 2h 3m" is a string.
385
+
386
+ Durations are always a positive of negative number followed by a string suffix denoting the time increment:
387
+
388
+ - ms = milliseconds
389
+ - s = seconds
390
+ - m = minutes
391
+ - h = hours
392
+ - d = days
393
+ - w = weeks
394
+ - M = months
395
+ - q = quarters
396
+ - y = years
397
+
398
+ <a id="number"></a>
399
+ - `object number({coerce?:boolean,required?:boolean,whenInvalid?:function,min?:number,max?:number,step?:number,allowNaN?:boolean,default?:number})`
400
+
401
+ An extended data type.
402
+
403
+ - `min` defaults to `-Infinity`
404
+ - `max` defaults to `Infinity`
405
+ - `step` defaults to `1`
406
+ - `allowNaN` defaults to `true`
407
+
408
+ <a id="object"></a>
409
+ - `object object({coerce?:boolean,required?:boolean,whenInvalid?:function,default?:object})`
410
+
411
+ An extended data type that provides just common extended data type capability.
412
+
413
+ <a id="observed"></a>
414
+ - `object observed:function`
415
+
416
+ An extended functional type that automatically updates the value for a variable based on a component attribute by the same name every
417
+ time the component attribute changes.
418
+
419
+ Although provided as a function to be consistent with other extended types, there is no need to call the function
420
+ since it takes no configuration data.
421
+
422
+ <a id="remote"></a>
423
+ - `object remote(source:string|object)`
424
+
425
+ An extended functional type that can GET and PATCH variable values automatically.
426
+
427
+ Variables of functional type `remote` are automatically `reactive` and will attempt to put changes back to the URL from
428
+ which their current state is retrieved.
429
+
430
+ If `source` is a string it should be an absolute or relative path to access the variable on a server.
431
+
432
+ If `source` is an `object` it should have the surface:
433
+
434
+ ```javascript
435
+ {
436
+ path:string,
437
+ get:function,
438
+ patch:function,
439
+ put:function,
440
+ ttl:number, // milliseconds
441
+ }
442
+ ```
443
+
444
+ The easiest way to configure remote variables is to provide the absolute or relative unique URL to access the variable value, e.g.
445
+
446
+ ```javascript
447
+ const {remote} = await import("./types.js");
448
+ self.variables(
449
+ {sensor1:object}, {remote:"./sensors/sensor1"}
450
+ );
451
+ await sensor1;
452
+ ```
453
+
454
+ which is shorthand for
455
+
456
+ ```javascript
457
+ const {remote} = await import("./types.js");
458
+ self.variables(
459
+ {sensor1:object}, {remote:remote("./sensors/sensor1"})
460
+ );
461
+ await sensor1;
462
+ ```
463
+
464
+ Note: 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.
465
+
466
+ If you do not provide a value or call `remote` with a configuration object during your variable declaration, the assumed path to the variable
467
+ will be the current file path plus the variable name, e.g.
468
+
469
+ ```javascript
470
+ const {remote} = await import("./types.js");
471
+ self.variables({sensor1:object}, {remote});
472
+ ```
473
+
474
+ is the same as
475
+
476
+ ```javascript
477
+ const {remote} = await import("./types.js");
478
+ self.variables({sensor1:object}, {remote("./sensor1")});
479
+ ```
480
+
481
+ If you use remote with a path that is terminated by a slash, then the variable name is appended to the path.
482
+
483
+ ```javascript
484
+ const {remote} = await import("./types.js");
485
+ self.variables({sensor1:object}, {remote:"https://mysite.com/sensors/"});
486
+ ```
487
+
488
+ is the same as:
489
+
490
+ ```javascript
491
+ const {remote} = await import("./types.js");
492
+ self.variables({sensor1:object}, {remote:"https://mysite.com/sensors/sensor1"});
493
+ ```
494
+
495
+ This allows you to define multiple remote variables at the same time:
496
+
497
+ ```javascript
498
+ const {remote} = await import("./types.js");
499
+ self.variables({sensor1:object,sensor2:object}, {remote:"https://mysite.com/sensors/"});
500
+ ```
501
+
502
+ In some cases, you may have an existing application that does not provide an easily addressable unique URL for each variable,
503
+ in this case you can provide a configuration object providing a `get` method (as well as `patch`if the variable is
504
+ reactive and sending updates to the server), along with an optional `path` and `ttl`.
505
+
506
+ If your variable is `reactive` but not expected to send updates to the server, then you will need to do this:
507
+
508
+ ```javascript
509
+ const {remote} = await import("./types.js");
510
+ self.variables({sensor1:object}, {remote:remote({path:"https://mysite.com/sensors/sensor1",patch:()=>{})})});
511
+ ```
512
+
513
+ The `get` method should have the signature `get(path,variable)`. You can use the `path`, the variable definition contained
514
+ in `variable`, and any variables within the closure of your method to create a URL and do your own fetch that returns
515
+ a Promise for JSON.
516
+
517
+ Your `patch` method, if provided, must parse the `PATCH` response and return a Promise for JSON.
518
+
519
+ The patch method should have the signature `patch({target,property,value,oldValue},path,variable)`. (Currently,
520
+ remotely patched variables must be objects, in the future {value,oldValue} will also be legal for primitive variables).
521
+
522
+ You can use data from the `target` object along with the `path`, the variable definition contained in `variable`, and any
523
+ variables within the closure of your method to create a URL and do your own fetch.
524
+
525
+ Your patch method must parse the fetch response and return a Promise for JSON. If your sever does not return the current
526
+ value of the variable in response to `PATCH`, you may need to implement your `patch` method in a manner that follows the `PATCH`
527
+ request with a `GET` request.
528
+
529
+ The `ttl` is the number of milliseconds between server polls to refresh data. If you do not wish to poll the server,
530
+ you could also implement `get` so that it establishes a websocket connection and update your variables in realtime. If you do
531
+ not provide a `ttl`, no polling will occur.
532
+
533
+ Here is an example of a custom remote variable configuration for polling sensor data with error handling:
534
+
535
+ ```javascript
536
+ const {remote} = await import("./types.js");
537
+ self.variables(
538
+ { sensor1:object, sensor2:object },
539
+ { remote:
540
+ remote({
541
+ path: "./sensors/",
542
+ ttl: 10000, // get new data every 10 seconds
543
+ get(path,variable) {
544
+ // create a normalized full path to the sensor data
545
+ const href = new URL(path + object.id,window.location.href).href;
546
+ return fetch(href)
547
+ .then((response) => {
548
+ if(response.status===200) return response.json();
549
+ })
550
+ }
551
+ })
552
+ }
553
+ );
554
+ await sensor1;
555
+ await sensor2;
556
+ ```
557
+
558
+ Here is partial example of a custom remote variable configuration for streaming data over a websocket:
559
+
560
+ ```javascript
561
+ const {remote} = await import("./types.js");
562
+ // use these in the UI so that it automatically updates
563
+ self.variables({sensor1:object,sensor2:object},{reactive});
564
+ // use a variable to hold the websocket
565
+ self.variables(
566
+ { ws: object },
567
+ { remote:
568
+ remote({
569
+ path: "./sensors",
570
+ ttl: 10000, // get new data every 10 seconds
571
+ async get(path,variable) {
572
+ // only create one socket
573
+ if(!ws) {
574
+ // create a normalized full path to the sensor data
575
+ const href = new URL(path,window.location.href).href.replace("https://","wss://");
576
+ ws = new WebSocket(href);
577
+ // do websocketty stuff, ideally in a more robust way than this!
578
+ ws.onmessage = (event) => {
579
+ const {sensorName,value} = event.data;
580
+ // assumes sensor1 and sensor2 are the names
581
+ self.setVariableValue(sensorName,value);
582
+ }
583
+ // end websockety stuff
584
+ return Promise.resolve(ws); // you must return a Promise for the socket
585
+ }
586
+ })
587
+ }
588
+ );
589
+ await ws;
590
+ ```
591
+
592
+ Since using remote variables requires running a custom server. Below is the source code for a very basic custom
593
+ NodeJS server that will respond appropriately to remote variable requests and updates for data stored in JSON files.
594
+
595
+ ```javascript
596
+ const http = require("http"),
597
+ fs = require("fs"),
598
+ host = 'localhost',
599
+ port = 8000,
600
+ requestListener = async function (req, res) {
601
+ const path = `.${req.url}`;
602
+ res.setHeader("Access-Control-Allow-Origin","*");
603
+ res.setHeader("Access-Control-Allow-Methods", "*");
604
+ res.setHeader("Access-Control-Allow-Headers", "*");
605
+ res.setHeader("Content-Type", "application/json");
606
+ if(req.method==="OPTIONS") {
607
+ res.end();
608
+ return;
609
+ }
610
+ if(req.method==="GET") {
611
+ console.log("GET",req.url);
612
+ res.write(fs.readFileSync(path));
613
+ res.end();
614
+ return;
615
+ }
616
+ const buffers = [];
617
+ for await(const chunk of req) {
618
+ buffers.push(chunk);
619
+ }
620
+ const data = JSON.parse(Buffer.concat(buffers).toString());
621
+ console.log(req.method,req.url,data);
622
+ if(req.method==="PUT") {
623
+ const string = JSON.stringify(data);
624
+ fs.writeFileSync(path,string);
625
+ res.write(string);
626
+ res.end();
627
+ return;
628
+ }
629
+ if(req.method==="PATCH") {
630
+ const {property,value,oldValue} = data,
631
+ json = JSON.parse(fs.readFileSync(path));
632
+ // optimistic "lock", do not update unless value is same as when last retrieved
633
+ if(property!==undefined && json[property]===oldValue) { // probably need a deepEqual for production use
634
+ json[property] = value;
635
+ fs.writeFileSync(path,JSON.stringify(json))
636
+ }
637
+ // just returning what was sent, in production use would probably get real data readings
638
+ res.write(JSON.stringify(json));
639
+ res.end();
640
+ return;
641
+ },
642
+ server = http.createServer(requestListener);
643
+ server.listen(port, host, () => {
644
+ console.log(`Server is running on http://${host}:${port}`);
645
+ });
646
+ ```
647
+
648
+ See the tutorial [Extended Functional Type: Remote](./tutorial/5-extended-functional-types.html).
649
+
650
+ <a id="shared"></a>
651
+ - `object shared:function`
652
+
653
+ Lightview can ensure that state is the same across all instances of the same component. Declaring a variable
654
+ as shared synchrnoizes its value across all instances of the component.
655
+
656
+ Although provided as a function to be consistent with other extended types, there is no need to call the function
657
+ since it takes no configuration data.
658
+
659
+ See the tutorial [Extended Functional Type: Shared](./tutorial/5.1-extended-functional-types.html).
660
+
661
+ <a id="string"></a>
662
+ - `object string({coerce?:boolean,required?:boolean,whenInvalid?:function,minlength?:number,maxlength?:number,pattern?:RegExp,default?:string})`
663
+
664
+ - `minlength` defaults to `0`
665
+ - `maxlength` `defaults to `Infinity`
666
+ - `pattern` ensures the value matches the RegExp prior to assignment
667
+
668
+ </div>
669
+
670
+ <script>
671
+ processMarkdown();
672
+ </script>
673
+ </body>
674
+ </html>
@@ -0,0 +1,10 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Blank</title>
6
+ </head>
7
+ <body>
8
+
9
+ </body>
10
+ </html>