@thomas-siegfried/tapout 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1206 -0
  3. package/dist/applyBindings.d.ts +10 -0
  4. package/dist/applyBindings.d.ts.map +1 -0
  5. package/dist/applyBindings.js +261 -0
  6. package/dist/applyBindings.js.map +1 -0
  7. package/dist/arrayToDomMapping.d.ts +14 -0
  8. package/dist/arrayToDomMapping.d.ts.map +1 -0
  9. package/dist/arrayToDomMapping.js +219 -0
  10. package/dist/arrayToDomMapping.js.map +1 -0
  11. package/dist/attributeInterpolationMarkup.d.ts +6 -0
  12. package/dist/attributeInterpolationMarkup.d.ts.map +1 -0
  13. package/dist/attributeInterpolationMarkup.js +70 -0
  14. package/dist/attributeInterpolationMarkup.js.map +1 -0
  15. package/dist/bindingContext.d.ts +38 -0
  16. package/dist/bindingContext.d.ts.map +1 -0
  17. package/dist/bindingContext.js +138 -0
  18. package/dist/bindingContext.js.map +1 -0
  19. package/dist/bindingEvent.d.ts +33 -0
  20. package/dist/bindingEvent.d.ts.map +1 -0
  21. package/dist/bindingEvent.js +144 -0
  22. package/dist/bindingEvent.js.map +1 -0
  23. package/dist/bindingProvider.d.ts +38 -0
  24. package/dist/bindingProvider.d.ts.map +1 -0
  25. package/dist/bindingProvider.js +145 -0
  26. package/dist/bindingProvider.js.map +1 -0
  27. package/dist/bindings.d.ts +2 -0
  28. package/dist/bindings.d.ts.map +1 -0
  29. package/dist/bindings.js +862 -0
  30. package/dist/bindings.js.map +1 -0
  31. package/dist/compareArrays.d.ts +13 -0
  32. package/dist/compareArrays.d.ts.map +1 -0
  33. package/dist/compareArrays.js +91 -0
  34. package/dist/compareArrays.js.map +1 -0
  35. package/dist/componentBinding.d.ts +4 -0
  36. package/dist/componentBinding.d.ts.map +1 -0
  37. package/dist/componentBinding.js +240 -0
  38. package/dist/componentBinding.js.map +1 -0
  39. package/dist/componentDecorator.d.ts +22 -0
  40. package/dist/componentDecorator.d.ts.map +1 -0
  41. package/dist/componentDecorator.js +32 -0
  42. package/dist/componentDecorator.js.map +1 -0
  43. package/dist/components.d.ts +34 -0
  44. package/dist/components.d.ts.map +1 -0
  45. package/dist/components.js +256 -0
  46. package/dist/components.js.map +1 -0
  47. package/dist/computed.d.ts +54 -0
  48. package/dist/computed.d.ts.map +1 -0
  49. package/dist/computed.js +376 -0
  50. package/dist/computed.js.map +1 -0
  51. package/dist/configure.d.ts +4 -0
  52. package/dist/configure.d.ts.map +1 -0
  53. package/dist/configure.js +48 -0
  54. package/dist/configure.js.map +1 -0
  55. package/dist/controlFlowBindings.d.ts +2 -0
  56. package/dist/controlFlowBindings.d.ts.map +1 -0
  57. package/dist/controlFlowBindings.js +151 -0
  58. package/dist/controlFlowBindings.js.map +1 -0
  59. package/dist/core.d.ts +29 -0
  60. package/dist/core.d.ts.map +1 -0
  61. package/dist/core.js +27 -0
  62. package/dist/core.js.map +1 -0
  63. package/dist/decorators.d.ts +40 -0
  64. package/dist/decorators.d.ts.map +1 -0
  65. package/dist/decorators.js +276 -0
  66. package/dist/decorators.js.map +1 -0
  67. package/dist/dependencyDetection.d.ts +21 -0
  68. package/dist/dependencyDetection.d.ts.map +1 -0
  69. package/dist/dependencyDetection.js +53 -0
  70. package/dist/dependencyDetection.js.map +1 -0
  71. package/dist/disposable.d.ts +12 -0
  72. package/dist/disposable.d.ts.map +1 -0
  73. package/dist/disposable.js +29 -0
  74. package/dist/disposable.js.map +1 -0
  75. package/dist/domData.d.ts +6 -0
  76. package/dist/domData.d.ts.map +1 -0
  77. package/dist/domData.js +40 -0
  78. package/dist/domData.js.map +1 -0
  79. package/dist/domNodeDisposal.d.ts +7 -0
  80. package/dist/domNodeDisposal.d.ts.map +1 -0
  81. package/dist/domNodeDisposal.js +78 -0
  82. package/dist/domNodeDisposal.js.map +1 -0
  83. package/dist/effects.d.ts +13 -0
  84. package/dist/effects.d.ts.map +1 -0
  85. package/dist/effects.js +24 -0
  86. package/dist/effects.js.map +1 -0
  87. package/dist/event.d.ts +38 -0
  88. package/dist/event.d.ts.map +1 -0
  89. package/dist/event.js +138 -0
  90. package/dist/event.js.map +1 -0
  91. package/dist/expressionRewriting.d.ts +22 -0
  92. package/dist/expressionRewriting.d.ts.map +1 -0
  93. package/dist/expressionRewriting.js +169 -0
  94. package/dist/expressionRewriting.js.map +1 -0
  95. package/dist/extenders.d.ts +17 -0
  96. package/dist/extenders.d.ts.map +1 -0
  97. package/dist/extenders.js +75 -0
  98. package/dist/extenders.js.map +1 -0
  99. package/dist/filters.d.ts +5 -0
  100. package/dist/filters.d.ts.map +1 -0
  101. package/dist/filters.js +81 -0
  102. package/dist/filters.js.map +1 -0
  103. package/dist/index.d.ts +45 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +35 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/interpolationMarkup.d.ts +6 -0
  108. package/dist/interpolationMarkup.d.ts.map +1 -0
  109. package/dist/interpolationMarkup.js +110 -0
  110. package/dist/interpolationMarkup.js.map +1 -0
  111. package/dist/memoization.d.ts +7 -0
  112. package/dist/memoization.d.ts.map +1 -0
  113. package/dist/memoization.js +59 -0
  114. package/dist/memoization.js.map +1 -0
  115. package/dist/namespacedBindings.d.ts +7 -0
  116. package/dist/namespacedBindings.d.ts.map +1 -0
  117. package/dist/namespacedBindings.js +81 -0
  118. package/dist/namespacedBindings.js.map +1 -0
  119. package/dist/observable.d.ts +13 -0
  120. package/dist/observable.d.ts.map +1 -0
  121. package/dist/observable.js +39 -0
  122. package/dist/observable.js.map +1 -0
  123. package/dist/observableArray.d.ts +58 -0
  124. package/dist/observableArray.d.ts.map +1 -0
  125. package/dist/observableArray.js +328 -0
  126. package/dist/observableArray.js.map +1 -0
  127. package/dist/options.d.ts +14 -0
  128. package/dist/options.d.ts.map +1 -0
  129. package/dist/options.js +12 -0
  130. package/dist/options.js.map +1 -0
  131. package/dist/selectExtensions.d.ts +4 -0
  132. package/dist/selectExtensions.d.ts.map +1 -0
  133. package/dist/selectExtensions.js +67 -0
  134. package/dist/selectExtensions.js.map +1 -0
  135. package/dist/slotBinding.d.ts +2 -0
  136. package/dist/slotBinding.d.ts.map +1 -0
  137. package/dist/slotBinding.js +63 -0
  138. package/dist/slotBinding.js.map +1 -0
  139. package/dist/subscribable.d.ts +57 -0
  140. package/dist/subscribable.d.ts.map +1 -0
  141. package/dist/subscribable.js +211 -0
  142. package/dist/subscribable.js.map +1 -0
  143. package/dist/tasks.d.ts +8 -0
  144. package/dist/tasks.d.ts.map +1 -0
  145. package/dist/tasks.js +68 -0
  146. package/dist/tasks.js.map +1 -0
  147. package/dist/templateEngine.d.ts +37 -0
  148. package/dist/templateEngine.d.ts.map +1 -0
  149. package/dist/templateEngine.js +60 -0
  150. package/dist/templateEngine.js.map +1 -0
  151. package/dist/templateRendering.d.ts +8 -0
  152. package/dist/templateRendering.d.ts.map +1 -0
  153. package/dist/templateRendering.js +322 -0
  154. package/dist/templateRendering.js.map +1 -0
  155. package/dist/templateRewriting.d.ts +7 -0
  156. package/dist/templateRewriting.d.ts.map +1 -0
  157. package/dist/templateRewriting.js +63 -0
  158. package/dist/templateRewriting.js.map +1 -0
  159. package/dist/templateSources.d.ts +19 -0
  160. package/dist/templateSources.d.ts.map +1 -0
  161. package/dist/templateSources.js +110 -0
  162. package/dist/templateSources.js.map +1 -0
  163. package/dist/utils.d.ts +8 -0
  164. package/dist/utils.d.ts.map +1 -0
  165. package/dist/utils.js +100 -0
  166. package/dist/utils.js.map +1 -0
  167. package/dist/utilsDom.d.ts +8 -0
  168. package/dist/utilsDom.d.ts.map +1 -0
  169. package/dist/utilsDom.js +81 -0
  170. package/dist/utilsDom.js.map +1 -0
  171. package/dist/virtualElements.d.ts +12 -0
  172. package/dist/virtualElements.d.ts.map +1 -0
  173. package/dist/virtualElements.js +141 -0
  174. package/dist/virtualElements.js.map +1 -0
  175. package/dist/wireParams.d.ts +6 -0
  176. package/dist/wireParams.d.ts.map +1 -0
  177. package/dist/wireParams.js +39 -0
  178. package/dist/wireParams.js.map +1 -0
  179. package/package.json +55 -0
  180. package/src/applyBindings.ts +372 -0
  181. package/src/arrayToDomMapping.ts +300 -0
  182. package/src/attributeInterpolationMarkup.ts +91 -0
  183. package/src/bindingContext.ts +207 -0
  184. package/src/bindingEvent.ts +198 -0
  185. package/src/bindingProvider.ts +260 -0
  186. package/src/bindings.ts +963 -0
  187. package/src/compareArrays.ts +122 -0
  188. package/src/componentBinding.ts +318 -0
  189. package/src/componentDecorator.ts +62 -0
  190. package/src/components.ts +367 -0
  191. package/src/computed.ts +439 -0
  192. package/src/configure.ts +52 -0
  193. package/src/controlFlowBindings.ts +206 -0
  194. package/src/core.ts +60 -0
  195. package/src/decorators.ts +407 -0
  196. package/src/dependencyDetection.ts +76 -0
  197. package/src/disposable.ts +36 -0
  198. package/src/domData.ts +42 -0
  199. package/src/domNodeDisposal.ts +94 -0
  200. package/src/effects.ts +29 -0
  201. package/src/event.ts +173 -0
  202. package/src/expressionRewriting.ts +219 -0
  203. package/src/extenders.ts +102 -0
  204. package/src/filters.ts +91 -0
  205. package/src/index.ts +150 -0
  206. package/src/interpolationMarkup.ts +130 -0
  207. package/src/memoization.ts +71 -0
  208. package/src/namespacedBindings.ts +132 -0
  209. package/src/observable.ts +48 -0
  210. package/src/observableArray.ts +397 -0
  211. package/src/options.ts +25 -0
  212. package/src/selectExtensions.ts +68 -0
  213. package/src/slotBinding.ts +84 -0
  214. package/src/subscribable.ts +266 -0
  215. package/src/tasks.ts +79 -0
  216. package/src/templateEngine.ts +108 -0
  217. package/src/templateRendering.ts +399 -0
  218. package/src/templateRewriting.ts +94 -0
  219. package/src/templateSources.ts +134 -0
  220. package/src/utils.ts +123 -0
  221. package/src/utilsDom.ts +87 -0
  222. package/src/virtualElements.ts +153 -0
  223. package/src/wireParams.ts +49 -0
@@ -0,0 +1,862 @@
1
+ import { unwrapObservable } from './utils.js';
2
+ import { parseHtmlFragment } from './utilsDom.js';
3
+ import { bindingHandlers } from './bindingProvider.js';
4
+ import { allowedVirtualElementBindings, virtualFirstChild, virtualNextSibling, virtualSetChildren, virtualEmptyNode } from './virtualElements.js';
5
+ import { addDisposeCallback, removeNode } from './domNodeDisposal.js';
6
+ import { twoWayBindings, writeValueToProperty } from './expressionRewriting.js';
7
+ import { Computed, PureComputed } from './computed.js';
8
+ import { Observable } from './observable.js';
9
+ import { ignore, isInitial, getDependenciesCount } from './dependencyDetection.js';
10
+ import { bindingEvent } from './bindingEvent.js';
11
+ import * as selectExtensions from './selectExtensions.js';
12
+ // ---- Shared DOM Utilities ----
13
+ function triggerEvent(element, eventType) {
14
+ const EventCtor = (element.ownerDocument?.defaultView)?.Event ?? Event;
15
+ element.dispatchEvent(new EventCtor(eventType, { bubbles: true, cancelable: true }));
16
+ }
17
+ function setTextContent(element, textContent) {
18
+ let value = unwrapObservable(textContent);
19
+ if (value === null || value === undefined)
20
+ value = '';
21
+ const innerTextNode = virtualFirstChild(element);
22
+ if (!innerTextNode || innerTextNode.nodeType !== 3 || virtualNextSibling(innerTextNode)) {
23
+ const doc = element.ownerDocument ?? element;
24
+ virtualSetChildren(element, [doc.createTextNode(String(value))]);
25
+ }
26
+ else {
27
+ innerTextNode.data = String(value);
28
+ }
29
+ }
30
+ function emptyDomNode(node) {
31
+ while (node.firstChild) {
32
+ removeNode(node.firstChild);
33
+ }
34
+ }
35
+ function setHtml(node, html) {
36
+ html = unwrapObservable(html);
37
+ if (node.nodeType === 8) {
38
+ if (html != null) {
39
+ const parsedNodes = parseHtmlFragment('' + html, node.ownerDocument ?? undefined);
40
+ virtualSetChildren(node, parsedNodes);
41
+ }
42
+ else {
43
+ virtualEmptyNode(node);
44
+ }
45
+ }
46
+ else {
47
+ emptyDomNode(node);
48
+ if (html === null || html === undefined)
49
+ return;
50
+ const el = node;
51
+ el.innerHTML = typeof html === 'string' ? html : String(html);
52
+ }
53
+ }
54
+ const CSS_CLASS_REGEX = /\S+/g;
55
+ const CLASSES_WRITTEN_KEY = '__tapout__cssValue';
56
+ function toggleDomNodeCssClass(node, classNames, shouldHaveClass) {
57
+ if (!classNames)
58
+ return;
59
+ const matches = classNames.match(CSS_CLASS_REGEX);
60
+ if (!matches)
61
+ return;
62
+ if (typeof node.classList === 'object') {
63
+ const fn = shouldHaveClass ? 'add' : 'remove';
64
+ for (const cls of matches) {
65
+ node.classList[fn](cls);
66
+ }
67
+ }
68
+ else if (typeof node.className?.baseVal === 'string') {
69
+ // SVG element
70
+ toggleClassPropertyString(node.className, 'baseVal', matches, shouldHaveClass);
71
+ }
72
+ else {
73
+ toggleClassPropertyString(node, 'className', matches, shouldHaveClass);
74
+ }
75
+ }
76
+ function toggleClassPropertyString(obj, prop, classNames, shouldHaveClass) {
77
+ const current = (obj[prop] || '').match(CSS_CLASS_REGEX)?.slice() ?? [];
78
+ for (const cls of classNames) {
79
+ const idx = current.indexOf(cls);
80
+ if (shouldHaveClass && idx === -1) {
81
+ current.push(cls);
82
+ }
83
+ else if (!shouldHaveClass && idx !== -1) {
84
+ current.splice(idx, 1);
85
+ }
86
+ }
87
+ obj[prop] = current.join(' ');
88
+ }
89
+ function setElementName(element, name) {
90
+ element.name = name;
91
+ }
92
+ // ---- Task 17: One-way display bindings ----
93
+ const textHandler = {
94
+ init() {
95
+ return { controlsDescendantBindings: true };
96
+ },
97
+ update(node, valueAccessor) {
98
+ setTextContent(node, valueAccessor());
99
+ },
100
+ };
101
+ const htmlHandler = {
102
+ init() {
103
+ return { controlsDescendantBindings: true };
104
+ },
105
+ update(node, valueAccessor) {
106
+ setHtml(node, valueAccessor());
107
+ },
108
+ };
109
+ const visibleHandler = {
110
+ update(node, valueAccessor) {
111
+ const el = node;
112
+ const value = unwrapObservable(valueAccessor());
113
+ const isCurrentlyVisible = el.style.display !== 'none';
114
+ if (value && !isCurrentlyVisible) {
115
+ el.style.display = '';
116
+ }
117
+ else if (!value && isCurrentlyVisible) {
118
+ el.style.display = 'none';
119
+ }
120
+ },
121
+ };
122
+ const hiddenHandler = {
123
+ update(node, valueAccessor) {
124
+ visibleHandler.update(node, () => !unwrapObservable(valueAccessor()), {}, undefined, {});
125
+ },
126
+ };
127
+ // ---- Task 18: Attribute bindings ----
128
+ const attrHandler = {
129
+ update(node, valueAccessor) {
130
+ const el = node;
131
+ const value = unwrapObservable(valueAccessor());
132
+ if (!value)
133
+ return;
134
+ for (const attrName of Object.keys(value)) {
135
+ let attrValue = unwrapObservable(value[attrName]);
136
+ const prefixLen = attrName.indexOf(':');
137
+ const namespace = 'lookupNamespaceURI' in el && prefixLen > 0
138
+ ? el.lookupNamespaceURI(attrName.substring(0, prefixLen))
139
+ : null;
140
+ const toRemove = attrValue === false || attrValue === null || attrValue === undefined;
141
+ if (toRemove) {
142
+ namespace
143
+ ? el.removeAttributeNS(namespace, attrName)
144
+ : el.removeAttribute(attrName);
145
+ }
146
+ else {
147
+ const strValue = String(attrValue);
148
+ namespace
149
+ ? el.setAttributeNS(namespace, attrName, strValue)
150
+ : el.setAttribute(attrName, strValue);
151
+ }
152
+ if (attrName === 'name') {
153
+ setElementName(el, toRemove ? '' : String(attrValue));
154
+ }
155
+ }
156
+ },
157
+ };
158
+ const classHandler = {
159
+ update(node, valueAccessor) {
160
+ const el = node;
161
+ const value = String(unwrapObservable(valueAccessor()) ?? '').trim();
162
+ const previousValue = el[CLASSES_WRITTEN_KEY];
163
+ toggleDomNodeCssClass(el, previousValue, false);
164
+ el[CLASSES_WRITTEN_KEY] = value;
165
+ toggleDomNodeCssClass(el, value, true);
166
+ },
167
+ };
168
+ const cssHandler = {
169
+ update(node, valueAccessor) {
170
+ const el = node;
171
+ const value = unwrapObservable(valueAccessor());
172
+ if (value !== null && typeof value === 'object') {
173
+ for (const className of Object.keys(value)) {
174
+ const shouldHaveClass = !!unwrapObservable(value[className]);
175
+ toggleDomNodeCssClass(el, className, shouldHaveClass);
176
+ }
177
+ }
178
+ else {
179
+ classHandler.update(node, valueAccessor, {}, undefined, {});
180
+ }
181
+ },
182
+ };
183
+ const styleHandler = {
184
+ update(node, valueAccessor) {
185
+ const el = node;
186
+ const value = unwrapObservable(valueAccessor() || {});
187
+ for (const styleName of Object.keys(value)) {
188
+ let styleValue = unwrapObservable(value[styleName]);
189
+ if (styleValue === null || styleValue === undefined || styleValue === false) {
190
+ styleValue = '';
191
+ }
192
+ if (/^--/.test(styleName)) {
193
+ el.style.setProperty(styleName, String(styleValue));
194
+ }
195
+ else {
196
+ const camelCased = styleName.replace(/-(\w)/g, (_all, letter) => letter.toUpperCase());
197
+ const previousStyle = el.style[camelCased];
198
+ el.style[camelCased] = String(styleValue);
199
+ if (String(styleValue) !== previousStyle &&
200
+ el.style[camelCased] === previousStyle &&
201
+ !isNaN(styleValue)) {
202
+ el.style[camelCased] = styleValue + 'px';
203
+ }
204
+ }
205
+ }
206
+ },
207
+ };
208
+ // ---- Task 19: Form state bindings ----
209
+ const enableHandler = {
210
+ update(node, valueAccessor) {
211
+ const el = node;
212
+ const value = unwrapObservable(valueAccessor());
213
+ if (value && el.disabled) {
214
+ el.removeAttribute('disabled');
215
+ }
216
+ else if (!value && !el.disabled) {
217
+ el.disabled = true;
218
+ }
219
+ },
220
+ };
221
+ const disableHandler = {
222
+ update(node, valueAccessor) {
223
+ enableHandler.update(node, () => !unwrapObservable(valueAccessor()), {}, undefined, {});
224
+ },
225
+ };
226
+ let uniqueNameIndex = 0;
227
+ const uniqueNameHandler = {
228
+ init(node, valueAccessor) {
229
+ if (valueAccessor()) {
230
+ const name = 'tap_unique_' + (++uniqueNameIndex);
231
+ setElementName(node, name);
232
+ }
233
+ },
234
+ };
235
+ // ---- Task 20: Event bindings ----
236
+ const eventHandler = {
237
+ init(node, valueAccessor, allBindings, viewModel, bindingContext) {
238
+ const el = node;
239
+ const eventsToHandle = (valueAccessor() || {});
240
+ for (const eventName of Object.keys(eventsToHandle)) {
241
+ el.addEventListener(eventName, function (event) {
242
+ const handlerFunction = valueAccessor()[eventName];
243
+ if (!handlerFunction)
244
+ return;
245
+ let handlerReturnValue;
246
+ try {
247
+ const data = bindingContext.$data;
248
+ handlerReturnValue = handlerFunction.call(data, data, event);
249
+ }
250
+ finally {
251
+ if (handlerReturnValue !== true) {
252
+ event.preventDefault();
253
+ }
254
+ }
255
+ const bubble = allBindings.get(eventName + 'Bubble') !== false;
256
+ if (!bubble) {
257
+ event.stopPropagation();
258
+ }
259
+ });
260
+ }
261
+ },
262
+ };
263
+ function makeEventHandlerShortcut(eventName) {
264
+ return {
265
+ init(node, valueAccessor, allBindings, viewModel, bindingContext) {
266
+ const newValueAccessor = () => {
267
+ const result = {};
268
+ result[eventName] = valueAccessor();
269
+ return result;
270
+ };
271
+ return eventHandler.init(node, newValueAccessor, allBindings, viewModel, bindingContext);
272
+ },
273
+ };
274
+ }
275
+ const clickHandler = makeEventHandlerShortcut('click');
276
+ const submitHandler = {
277
+ init(node, valueAccessor, _allBindings, _viewModel, bindingContext) {
278
+ if (typeof valueAccessor() !== 'function') {
279
+ throw new Error('The value for a submit binding must be a function');
280
+ }
281
+ node.addEventListener('submit', function (event) {
282
+ let handlerReturnValue;
283
+ const value = valueAccessor();
284
+ try {
285
+ handlerReturnValue = value.call(bindingContext.$data, node);
286
+ }
287
+ finally {
288
+ if (handlerReturnValue !== true) {
289
+ event.preventDefault();
290
+ }
291
+ }
292
+ });
293
+ },
294
+ };
295
+ // ---- Task 21: value binding ----
296
+ const valueHandler = {
297
+ init(node, valueAccessor, allBindings) {
298
+ const el = node;
299
+ const tagName = (el.tagName || '').toLowerCase();
300
+ const isInputElement = tagName === 'input';
301
+ if (isInputElement && (el.type === 'checkbox' || el.type === 'radio')) {
302
+ checkedValueHandler.update(node, valueAccessor, allBindings, undefined, {});
303
+ return;
304
+ }
305
+ let eventsToCatch = [];
306
+ const requestedEventsToCatch = allBindings.get('valueUpdate');
307
+ let elementValueBeforeEvent = null;
308
+ if (requestedEventsToCatch) {
309
+ if (typeof requestedEventsToCatch === 'string') {
310
+ eventsToCatch = [requestedEventsToCatch];
311
+ }
312
+ else {
313
+ eventsToCatch = [...new Set(requestedEventsToCatch)];
314
+ }
315
+ const changeIdx = eventsToCatch.indexOf('change');
316
+ if (changeIdx >= 0)
317
+ eventsToCatch.splice(changeIdx, 1);
318
+ }
319
+ const valueUpdateHandler = () => {
320
+ elementValueBeforeEvent = null;
321
+ const modelValue = valueAccessor();
322
+ const elementValue = selectExtensions.readValue(el);
323
+ writeValueToProperty(modelValue, allBindings, 'value', elementValue);
324
+ };
325
+ for (const eventName of eventsToCatch) {
326
+ let handler = valueUpdateHandler;
327
+ if (eventName.startsWith('after')) {
328
+ const actualHandler = () => {
329
+ elementValueBeforeEvent = selectExtensions.readValue(el);
330
+ setTimeout(valueUpdateHandler, 0);
331
+ };
332
+ el.addEventListener(eventName.substring(5), actualHandler);
333
+ continue;
334
+ }
335
+ el.addEventListener(eventName, handler);
336
+ }
337
+ let updateFromModelComputed = null;
338
+ const updateFromModel = () => {
339
+ let newValue = unwrapObservable(valueAccessor());
340
+ const elementValue = selectExtensions.readValue(el);
341
+ if (elementValueBeforeEvent !== null && newValue === elementValueBeforeEvent) {
342
+ setTimeout(updateFromModel, 0);
343
+ return;
344
+ }
345
+ const valueHasChanged = newValue !== elementValue;
346
+ if (valueHasChanged || elementValue === undefined) {
347
+ if (tagName === 'select') {
348
+ const allowUnset = allBindings.get('valueAllowUnset');
349
+ selectExtensions.writeValue(el, newValue, !!allowUnset);
350
+ if (!allowUnset && newValue !== selectExtensions.readValue(el)) {
351
+ ignore(valueUpdateHandler);
352
+ }
353
+ }
354
+ else {
355
+ selectExtensions.writeValue(el, newValue);
356
+ }
357
+ }
358
+ };
359
+ if (tagName === 'select') {
360
+ el.addEventListener('change', () => {
361
+ if (updateFromModelComputed)
362
+ valueUpdateHandler();
363
+ });
364
+ bindingEvent.subscribe(node, bindingEvent.childrenComplete, () => {
365
+ if (!updateFromModelComputed) {
366
+ updateFromModelComputed = new Computed(updateFromModel);
367
+ addDisposeCallback(node, () => updateFromModelComputed.dispose());
368
+ }
369
+ else if (allBindings.get('valueAllowUnset')) {
370
+ updateFromModel();
371
+ }
372
+ else {
373
+ valueUpdateHandler();
374
+ }
375
+ }, null, { notifyImmediately: true });
376
+ }
377
+ else {
378
+ el.addEventListener('change', valueUpdateHandler);
379
+ const comp = new Computed(updateFromModel);
380
+ addDisposeCallback(node, () => comp.dispose());
381
+ }
382
+ },
383
+ update() { },
384
+ };
385
+ // ---- Task 22: textInput binding ----
386
+ const textInputHandler = {
387
+ init(node, valueAccessor, allBindings) {
388
+ const el = node;
389
+ let previousElementValue = el.value;
390
+ let timeoutHandle;
391
+ let elementValueBeforeEvent;
392
+ let ourUpdate = false;
393
+ const updateModel = (_event) => {
394
+ clearTimeout(timeoutHandle);
395
+ elementValueBeforeEvent = timeoutHandle = undefined;
396
+ const elementValue = el.value;
397
+ if (previousElementValue !== elementValue) {
398
+ previousElementValue = elementValue;
399
+ writeValueToProperty(valueAccessor(), allBindings, 'textInput', elementValue);
400
+ }
401
+ };
402
+ const deferUpdateModel = (_event) => {
403
+ if (!timeoutHandle) {
404
+ elementValueBeforeEvent = el.value;
405
+ timeoutHandle = setTimeout(updateModel, 4);
406
+ }
407
+ };
408
+ const updateView = () => {
409
+ let modelValue = unwrapObservable(valueAccessor());
410
+ if (modelValue === null || modelValue === undefined) {
411
+ modelValue = '';
412
+ }
413
+ if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) {
414
+ setTimeout(updateView, 4);
415
+ return;
416
+ }
417
+ if (el.value !== modelValue) {
418
+ ourUpdate = true;
419
+ el.value = modelValue;
420
+ ourUpdate = false;
421
+ previousElementValue = el.value;
422
+ }
423
+ };
424
+ el.addEventListener('input', updateModel);
425
+ el.addEventListener('change', updateModel);
426
+ el.addEventListener('blur', updateModel);
427
+ const viewComputed = new Computed(updateView);
428
+ addDisposeCallback(node, () => viewComputed.dispose());
429
+ },
430
+ };
431
+ const textinputAliasHandler = {
432
+ preprocess(value, _name, addBinding) {
433
+ addBinding('textInput', value);
434
+ },
435
+ };
436
+ // ---- Task 23: checked / checkedValue bindings ----
437
+ function addOrRemoveItem(array, value, included) {
438
+ const existingIndex = array.indexOf(value);
439
+ if (included && existingIndex < 0) {
440
+ array.push(value);
441
+ }
442
+ else if (!included && existingIndex >= 0) {
443
+ array.splice(existingIndex, 1);
444
+ }
445
+ }
446
+ const checkedHandler = {
447
+ after: ['value', 'attr'],
448
+ init(node, valueAccessor, allBindings) {
449
+ const el = node;
450
+ const isCheckbox = el.type === 'checkbox';
451
+ const isRadio = el.type === 'radio';
452
+ if (!isCheckbox && !isRadio)
453
+ return;
454
+ const checkedValue = new PureComputed(() => {
455
+ if (allBindings.has('checkedValue')) {
456
+ return unwrapObservable(allBindings.get('checkedValue'));
457
+ }
458
+ else if (useElementValue) {
459
+ if (allBindings.has('value')) {
460
+ return unwrapObservable(allBindings.get('value'));
461
+ }
462
+ else {
463
+ return el.value;
464
+ }
465
+ }
466
+ });
467
+ const rawValue = valueAccessor();
468
+ const valueIsArray = isCheckbox && (unwrapObservable(rawValue) instanceof Array);
469
+ const rawValueIsNonArrayObservable = !(valueIsArray && rawValue.push && rawValue.splice);
470
+ const useElementValue = isRadio || valueIsArray;
471
+ let oldElemValue = valueIsArray ? checkedValue.peek() : undefined;
472
+ if (isRadio && !el.name) {
473
+ uniqueNameHandler.init(node, () => true, {}, undefined, {});
474
+ }
475
+ function updateModel() {
476
+ const isChecked = el.checked;
477
+ let elemValue = checkedValue.peek();
478
+ if (isInitial())
479
+ return;
480
+ if (!isChecked && (isRadio || getDependenciesCount()))
481
+ return;
482
+ const modelValue = ignore(valueAccessor);
483
+ if (valueIsArray) {
484
+ const writableValue = rawValueIsNonArrayObservable
485
+ ? modelValue.peek()
486
+ : modelValue;
487
+ const saveOldValue = oldElemValue;
488
+ oldElemValue = elemValue;
489
+ if (saveOldValue !== elemValue) {
490
+ if (isChecked) {
491
+ addOrRemoveItem(writableValue, elemValue, true);
492
+ addOrRemoveItem(writableValue, saveOldValue, false);
493
+ }
494
+ }
495
+ else {
496
+ addOrRemoveItem(writableValue, elemValue, isChecked);
497
+ }
498
+ if (rawValueIsNonArrayObservable && isWritableObs(modelValue)) {
499
+ modelValue.set(writableValue);
500
+ }
501
+ }
502
+ else {
503
+ if (isCheckbox) {
504
+ if (elemValue === undefined) {
505
+ elemValue = isChecked;
506
+ }
507
+ else if (!isChecked) {
508
+ elemValue = undefined;
509
+ }
510
+ }
511
+ writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);
512
+ }
513
+ }
514
+ function updateView() {
515
+ const modelValue = unwrapObservable(valueAccessor());
516
+ const elemValue = checkedValue.peek();
517
+ if (valueIsArray) {
518
+ el.checked = modelValue != null &&
519
+ modelValue.indexOf(elemValue) >= 0;
520
+ oldElemValue = elemValue;
521
+ }
522
+ else if (isCheckbox && elemValue === undefined) {
523
+ el.checked = !!modelValue;
524
+ }
525
+ else {
526
+ el.checked = (checkedValue.peek() === modelValue);
527
+ }
528
+ }
529
+ const updateModelComputed = new Computed(updateModel);
530
+ addDisposeCallback(node, () => updateModelComputed.dispose());
531
+ el.addEventListener('click', updateModel);
532
+ const updateViewComputed = new Computed(updateView);
533
+ addDisposeCallback(node, () => updateViewComputed.dispose());
534
+ },
535
+ };
536
+ const checkedValueHandler = {
537
+ update(node, valueAccessor) {
538
+ node.value = unwrapObservable(valueAccessor());
539
+ },
540
+ };
541
+ // ---- Task 24: hasfocus / hasFocus bindings ----
542
+ const HASFOCUS_UPDATING = '__tapout_hasfocusUpdating';
543
+ const HASFOCUS_LAST_VALUE = '__tapout_hasfocusLastValue';
544
+ const hasfocusHandler = {
545
+ init(node, valueAccessor, allBindings) {
546
+ const el = node;
547
+ const handleElementFocusChange = (isFocused) => {
548
+ el[HASFOCUS_UPDATING] = true;
549
+ const ownerDoc = el.ownerDocument;
550
+ if (ownerDoc && 'activeElement' in ownerDoc) {
551
+ try {
552
+ isFocused = (ownerDoc.activeElement === el);
553
+ }
554
+ catch {
555
+ isFocused = false;
556
+ }
557
+ }
558
+ const modelValue = valueAccessor();
559
+ writeValueToProperty(modelValue, allBindings, 'hasfocus', isFocused, true);
560
+ el[HASFOCUS_LAST_VALUE] = isFocused;
561
+ el[HASFOCUS_UPDATING] = false;
562
+ };
563
+ const handleFocusIn = () => handleElementFocusChange(true);
564
+ const handleFocusOut = () => handleElementFocusChange(false);
565
+ el.addEventListener('focus', handleFocusIn);
566
+ el.addEventListener('focusin', handleFocusIn);
567
+ el.addEventListener('blur', handleFocusOut);
568
+ el.addEventListener('focusout', handleFocusOut);
569
+ el[HASFOCUS_LAST_VALUE] = false;
570
+ },
571
+ update(node, valueAccessor) {
572
+ const el = node;
573
+ const value = !!unwrapObservable(valueAccessor());
574
+ if (!el[HASFOCUS_UPDATING] && el[HASFOCUS_LAST_VALUE] !== value) {
575
+ if (value) {
576
+ el.focus();
577
+ }
578
+ else {
579
+ el.blur();
580
+ }
581
+ }
582
+ },
583
+ };
584
+ // ---- Task 25: selectedOptions binding ----
585
+ const selectedOptionsHandler = {
586
+ init(node, valueAccessor, allBindings) {
587
+ const el = node;
588
+ if ((el.tagName || '').toLowerCase() !== 'select') {
589
+ throw new Error('selectedOptions binding applies only to SELECT elements');
590
+ }
591
+ function updateFromView() {
592
+ const value = valueAccessor();
593
+ const valueToWrite = [];
594
+ const options = el.getElementsByTagName('option');
595
+ for (let i = 0; i < options.length; i++) {
596
+ if (options[i].selected) {
597
+ valueToWrite.push(selectExtensions.readValue(options[i]));
598
+ }
599
+ }
600
+ writeValueToProperty(value, allBindings, 'selectedOptions', valueToWrite);
601
+ }
602
+ function updateFromModel() {
603
+ const newValue = unwrapObservable(valueAccessor());
604
+ const previousScrollTop = el.scrollTop;
605
+ if (newValue && typeof newValue.length === 'number') {
606
+ const options = el.getElementsByTagName('option');
607
+ for (let i = 0; i < options.length; i++) {
608
+ const optEl = options[i];
609
+ const isSelected = newValue.indexOf(selectExtensions.readValue(optEl)) >= 0;
610
+ if (optEl.selected !== isSelected) {
611
+ optEl.selected = isSelected;
612
+ }
613
+ }
614
+ }
615
+ el.scrollTop = previousScrollTop;
616
+ }
617
+ let updateFromModelComputed = null;
618
+ bindingEvent.subscribe(node, bindingEvent.childrenComplete, () => {
619
+ if (!updateFromModelComputed) {
620
+ el.addEventListener('change', updateFromView);
621
+ updateFromModelComputed = new Computed(updateFromModel);
622
+ addDisposeCallback(node, () => updateFromModelComputed.dispose());
623
+ }
624
+ else {
625
+ updateFromView();
626
+ }
627
+ }, null, { notifyImmediately: true });
628
+ },
629
+ update() { },
630
+ };
631
+ // ---- Task 26: options binding ----
632
+ const CAPTION_PLACEHOLDER = {};
633
+ const optionsHandler = {
634
+ init(node) {
635
+ const el = node;
636
+ if ((el.tagName || '').toLowerCase() !== 'select') {
637
+ throw new Error('options binding applies only to SELECT elements');
638
+ }
639
+ while (el.options.length > 0) {
640
+ el.removeChild(el.options[0]);
641
+ }
642
+ return { controlsDescendantBindings: true };
643
+ },
644
+ update(node, valueAccessor, allBindings) {
645
+ const el = node;
646
+ function selectedOptions() {
647
+ return Array.from(el.options).filter(o => o.selected);
648
+ }
649
+ const selectWasPreviouslyEmpty = el.length === 0;
650
+ const multiple = el.multiple;
651
+ const previousScrollTop = (!selectWasPreviouslyEmpty && multiple) ? el.scrollTop : null;
652
+ let unwrappedArray = unwrapObservable(valueAccessor());
653
+ const valueAllowUnset = allBindings.get('valueAllowUnset') && allBindings.has('value');
654
+ const includeDestroyed = allBindings.get('optionsIncludeDestroyed');
655
+ let filteredArray;
656
+ let previousSelectedValues = [];
657
+ if (!valueAllowUnset) {
658
+ if (multiple) {
659
+ previousSelectedValues = selectedOptions().map(o => selectExtensions.readValue(o));
660
+ }
661
+ else if (el.selectedIndex >= 0) {
662
+ previousSelectedValues.push(selectExtensions.readValue(el.options[el.selectedIndex]));
663
+ }
664
+ }
665
+ if (unwrappedArray) {
666
+ if (!Array.isArray(unwrappedArray)) {
667
+ unwrappedArray = [unwrappedArray];
668
+ }
669
+ filteredArray = unwrappedArray.filter((item) => {
670
+ return includeDestroyed || item === undefined || item === null ||
671
+ !unwrapObservable(item?.['_destroy']);
672
+ });
673
+ if (allBindings.has('optionsCaption')) {
674
+ const captionValue = unwrapObservable(allBindings.get('optionsCaption'));
675
+ if (captionValue !== null && captionValue !== undefined) {
676
+ filteredArray.unshift(CAPTION_PLACEHOLDER);
677
+ }
678
+ }
679
+ }
680
+ function applyToObject(object, predicate, defaultValue) {
681
+ if (typeof predicate === 'function')
682
+ return predicate(object);
683
+ if (typeof predicate === 'string')
684
+ return object[predicate];
685
+ return defaultValue;
686
+ }
687
+ while (el.options.length > 0) {
688
+ el.removeChild(el.options[0]);
689
+ }
690
+ if (filteredArray) {
691
+ for (let i = 0; i < filteredArray.length; i++) {
692
+ const arrayEntry = filteredArray[i];
693
+ const option = el.ownerDocument.createElement('option');
694
+ if (arrayEntry === CAPTION_PLACEHOLDER) {
695
+ option.textContent = String(unwrapObservable(allBindings.get('optionsCaption')) ?? '');
696
+ selectExtensions.writeValue(option, undefined);
697
+ }
698
+ else {
699
+ const optionValue = applyToObject(arrayEntry, allBindings.get('optionsValue'), arrayEntry);
700
+ selectExtensions.writeValue(option, unwrapObservable(optionValue));
701
+ const optionText = applyToObject(arrayEntry, allBindings.get('optionsText'), optionValue);
702
+ option.textContent = String(unwrapObservable(optionText) ?? '');
703
+ }
704
+ el.appendChild(option);
705
+ if (previousSelectedValues.length) {
706
+ const isSelected = previousSelectedValues.indexOf(selectExtensions.readValue(option)) >= 0;
707
+ option.selected = isSelected;
708
+ }
709
+ if (allBindings.has('optionsAfterRender') && typeof allBindings.get('optionsAfterRender') === 'function') {
710
+ ignore(() => {
711
+ allBindings.get('optionsAfterRender')(option, arrayEntry !== CAPTION_PLACEHOLDER ? arrayEntry : undefined);
712
+ });
713
+ }
714
+ }
715
+ }
716
+ if (!valueAllowUnset) {
717
+ let selectionChanged;
718
+ if (multiple) {
719
+ selectionChanged = previousSelectedValues.length > 0 && selectedOptions().length < previousSelectedValues.length;
720
+ }
721
+ else {
722
+ selectionChanged = (previousSelectedValues.length > 0 && el.selectedIndex >= 0)
723
+ ? (selectExtensions.readValue(el.options[el.selectedIndex]) !== previousSelectedValues[0])
724
+ : (previousSelectedValues.length > 0 || el.selectedIndex >= 0);
725
+ }
726
+ if (selectionChanged) {
727
+ ignore(() => triggerEvent(el, 'change'));
728
+ }
729
+ }
730
+ if (valueAllowUnset || isInitial()) {
731
+ bindingEvent.notify(node, bindingEvent.childrenComplete);
732
+ }
733
+ },
734
+ };
735
+ function isWritableObs(value) {
736
+ if (value instanceof Observable)
737
+ return true;
738
+ if (value instanceof Computed)
739
+ return value.hasWriteFunction;
740
+ return false;
741
+ }
742
+ // ---- Key event bindings (keydown / keyup with key + modifier filtering) ----
743
+ const KEY_ALIASES = {
744
+ enter: 'Enter', tab: 'Tab', esc: 'Escape', escape: 'Escape',
745
+ space: ' ', delete: 'Delete', backspace: 'Backspace',
746
+ up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight',
747
+ };
748
+ const MODIFIER_KEYS = new Set(['ctrl', 'alt', 'shift', 'meta']);
749
+ const MODIFIER_PROPS = {
750
+ ctrl: 'ctrlKey', alt: 'altKey', shift: 'shiftKey', meta: 'metaKey',
751
+ };
752
+ function createKeyFilteredHandler(eventType, name) {
753
+ const parts = name.split('.');
754
+ const modifiers = [];
755
+ let keyName;
756
+ for (const part of parts) {
757
+ if (MODIFIER_KEYS.has(part)) {
758
+ modifiers.push(part);
759
+ }
760
+ else if (!keyName) {
761
+ keyName = KEY_ALIASES[part] ?? part;
762
+ }
763
+ }
764
+ return {
765
+ init(node, valueAccessor, allBindings, _viewModel, bindingContext) {
766
+ const el = node;
767
+ el.addEventListener(eventType, function (event) {
768
+ const ke = event;
769
+ if (keyName && ke.key !== keyName)
770
+ return;
771
+ for (const mod of modifiers) {
772
+ if (!ke[MODIFIER_PROPS[mod]])
773
+ return;
774
+ }
775
+ const callback = valueAccessor();
776
+ if (typeof callback !== 'function')
777
+ return;
778
+ let handlerReturnValue;
779
+ try {
780
+ const data = bindingContext.$data;
781
+ handlerReturnValue = callback.call(data, data, event);
782
+ }
783
+ finally {
784
+ if (handlerReturnValue !== true) {
785
+ event.preventDefault();
786
+ }
787
+ }
788
+ const bubble = allBindings.get(eventType + 'Bubble') !== false;
789
+ if (!bubble) {
790
+ event.stopPropagation();
791
+ }
792
+ });
793
+ },
794
+ };
795
+ }
796
+ const keydownHandler = makeEventHandlerShortcut('keydown');
797
+ keydownHandler.getNamespacedHandler = function (name, _ns, _namespacedName) {
798
+ return createKeyFilteredHandler('keydown', name);
799
+ };
800
+ const keyupHandler = makeEventHandlerShortcut('keyup');
801
+ keyupHandler.getNamespacedHandler = function (name, _ns, _namespacedName) {
802
+ return createKeyFilteredHandler('keyup', name);
803
+ };
804
+ const enterHandler = createKeyFilteredHandler('keydown', 'enter');
805
+ // ---- modal: toggles <dialog> showModal/close based on observable boolean ----
806
+ const modalHandler = {
807
+ init(element, valueAccessor) {
808
+ const dlg = element;
809
+ const value = unwrapObservable(valueAccessor());
810
+ if (value)
811
+ dlg.showModal();
812
+ },
813
+ update(element, valueAccessor) {
814
+ const dlg = element;
815
+ const value = unwrapObservable(valueAccessor());
816
+ if (value) {
817
+ if (!dlg.open)
818
+ dlg.showModal();
819
+ }
820
+ else {
821
+ if (dlg.open)
822
+ dlg.close();
823
+ }
824
+ },
825
+ };
826
+ // ---- Registration ----
827
+ bindingHandlers['text'] = textHandler;
828
+ bindingHandlers['html'] = htmlHandler;
829
+ bindingHandlers['visible'] = visibleHandler;
830
+ bindingHandlers['hidden'] = hiddenHandler;
831
+ bindingHandlers['attr'] = attrHandler;
832
+ bindingHandlers['class'] = classHandler;
833
+ bindingHandlers['css'] = cssHandler;
834
+ bindingHandlers['style'] = styleHandler;
835
+ bindingHandlers['enable'] = enableHandler;
836
+ bindingHandlers['disable'] = disableHandler;
837
+ bindingHandlers['uniqueName'] = uniqueNameHandler;
838
+ bindingHandlers['event'] = eventHandler;
839
+ bindingHandlers['click'] = clickHandler;
840
+ bindingHandlers['submit'] = submitHandler;
841
+ bindingHandlers['value'] = valueHandler;
842
+ bindingHandlers['textInput'] = textInputHandler;
843
+ bindingHandlers['textinput'] = textinputAliasHandler;
844
+ bindingHandlers['checked'] = checkedHandler;
845
+ bindingHandlers['checkedValue'] = checkedValueHandler;
846
+ bindingHandlers['hasfocus'] = hasfocusHandler;
847
+ bindingHandlers['hasFocus'] = hasfocusHandler;
848
+ bindingHandlers['selectedOptions'] = selectedOptionsHandler;
849
+ bindingHandlers['options'] = optionsHandler;
850
+ bindingHandlers['keydown'] = keydownHandler;
851
+ bindingHandlers['keyup'] = keyupHandler;
852
+ bindingHandlers['enter'] = enterHandler;
853
+ bindingHandlers['modal'] = modalHandler;
854
+ twoWayBindings['value'] = true;
855
+ twoWayBindings['textInput'] = true;
856
+ twoWayBindings['checked'] = true;
857
+ twoWayBindings['hasfocus'] = true;
858
+ twoWayBindings['hasFocus'] = 'hasfocus';
859
+ twoWayBindings['selectedOptions'] = true;
860
+ allowedVirtualElementBindings['text'] = true;
861
+ allowedVirtualElementBindings['html'] = true;
862
+ //# sourceMappingURL=bindings.js.map