@thomas-siegfried/tapout 0.0.1 → 0.0.3

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 (52) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1207 -1205
  3. package/decorator-config.md +220 -0
  4. package/dist/wireParams.d.ts.map +1 -1
  5. package/dist/wireParams.js +27 -4
  6. package/dist/wireParams.js.map +1 -1
  7. package/llms.txt +360 -0
  8. package/package.json +57 -55
  9. package/src/applyBindings.ts +372 -372
  10. package/src/arrayToDomMapping.ts +300 -300
  11. package/src/attributeInterpolationMarkup.ts +91 -91
  12. package/src/bindingContext.ts +207 -207
  13. package/src/bindingEvent.ts +198 -198
  14. package/src/bindingProvider.ts +260 -260
  15. package/src/bindings.ts +963 -963
  16. package/src/compareArrays.ts +122 -122
  17. package/src/componentBinding.ts +318 -318
  18. package/src/componentDecorator.ts +62 -62
  19. package/src/components.ts +367 -367
  20. package/src/computed.ts +439 -439
  21. package/src/configure.ts +52 -52
  22. package/src/controlFlowBindings.ts +206 -206
  23. package/src/core.ts +60 -60
  24. package/src/decorators.ts +407 -407
  25. package/src/dependencyDetection.ts +76 -76
  26. package/src/disposable.ts +36 -36
  27. package/src/domData.ts +42 -42
  28. package/src/domNodeDisposal.ts +94 -94
  29. package/src/effects.ts +29 -29
  30. package/src/event.ts +173 -173
  31. package/src/expressionRewriting.ts +219 -219
  32. package/src/extenders.ts +102 -102
  33. package/src/filters.ts +91 -91
  34. package/src/index.ts +150 -150
  35. package/src/interpolationMarkup.ts +130 -130
  36. package/src/memoization.ts +71 -71
  37. package/src/namespacedBindings.ts +132 -132
  38. package/src/observable.ts +48 -48
  39. package/src/observableArray.ts +397 -397
  40. package/src/options.ts +25 -25
  41. package/src/selectExtensions.ts +68 -68
  42. package/src/slotBinding.ts +84 -84
  43. package/src/subscribable.ts +266 -266
  44. package/src/tasks.ts +79 -79
  45. package/src/templateEngine.ts +108 -108
  46. package/src/templateRendering.ts +399 -399
  47. package/src/templateRewriting.ts +94 -94
  48. package/src/templateSources.ts +134 -134
  49. package/src/utils.ts +123 -123
  50. package/src/utilsDom.ts +87 -87
  51. package/src/virtualElements.ts +153 -153
  52. package/src/wireParams.ts +69 -49
@@ -1,372 +1,372 @@
1
- import { BindingContext, BINDING_INFO_KEY, SUBSCRIBABLE, DATA_DEPENDENCY } from './bindingContext.js';
2
- import type { AllBindingsAccessor } from './expressionRewriting.js';
3
- import { instance as providerInstance, getBindingHandler } from './bindingProvider.js';
4
- import type { BindingHandler } from './bindingProvider.js';
5
- import { Computed } from './computed.js';
6
- import { ignore } from './dependencyDetection.js';
7
- import { domDataGetOrSet } from './domData.js';
8
- import { addDisposeCallback } from './domNodeDisposal.js';
9
- import {
10
- virtualFirstChild,
11
- virtualNextSibling,
12
- allowedVirtualElementBindings,
13
- } from './virtualElements.js';
14
- import { bindingEvent, subscribeToBindingEvent } from './bindingEvent.js';
15
- import type { BindingInfo } from './bindingEvent.js';
16
- import { ensureConfigured } from './configure.js';
17
-
18
- interface SortedBinding {
19
- key: string;
20
- handler: BindingHandler;
21
- }
22
-
23
- // Elements whose descendants should not be recursively bound
24
- const NO_RECURSE: Record<string, boolean> = {
25
- script: true,
26
- textarea: true,
27
- template: true,
28
- };
29
-
30
- function tagNameLower(node: Node): string {
31
- return (node as Element).tagName ? (node as Element).tagName.toLowerCase() : '';
32
- }
33
-
34
- // ---- Helpers ----
35
-
36
- function evaluateValueAccessor(valueAccessor: unknown): unknown {
37
- return typeof valueAccessor === 'function' ? (valueAccessor as () => unknown)() : valueAccessor;
38
- }
39
-
40
- function makeValueAccessor(value: unknown): () => unknown {
41
- return () => value;
42
- }
43
-
44
- // ---- Topological Sort ----
45
-
46
- function topologicalSortBindings(
47
- bindings: Record<string, unknown>,
48
- ): SortedBinding[] {
49
- const result: SortedBinding[] = [];
50
- const considered: Record<string, boolean> = {};
51
- const cyclicStack: string[] = [];
52
-
53
- function pushBinding(bindingKey: string) {
54
- if (considered[bindingKey]) return;
55
-
56
- const handler = getBindingHandler(bindingKey);
57
- if (handler) {
58
- if (handler.after) {
59
- cyclicStack.push(bindingKey);
60
- for (const depKey of handler.after) {
61
- if (depKey in bindings) {
62
- if (cyclicStack.indexOf(depKey) !== -1) {
63
- throw new Error(
64
- 'Cannot combine the following bindings, because they have a cyclic dependency: ' +
65
- cyclicStack.join(', '),
66
- );
67
- }
68
- pushBinding(depKey);
69
- }
70
- }
71
- cyclicStack.pop();
72
- }
73
- result.push({ key: bindingKey, handler });
74
- }
75
- considered[bindingKey] = true;
76
- }
77
-
78
- for (const key of Object.keys(bindings)) {
79
- pushBinding(key);
80
- }
81
-
82
- return result;
83
- }
84
-
85
- // ---- Virtual Element Validation ----
86
-
87
- function validateVirtualElementBinding(bindingName: string): void {
88
- if (!allowedVirtualElementBindings[bindingName]) {
89
- throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements");
90
- }
91
- }
92
-
93
- // ---- Core: Apply Bindings to a Single Node ----
94
-
95
- function applyBindingsToNodeInternal(
96
- node: Node,
97
- sourceBindings: Record<string, () => unknown> | ((ctx: BindingContext, node: Node) => Record<string, () => unknown>) | null,
98
- bindingContext: BindingContext,
99
- ): { shouldBindDescendants: boolean; bindingContextForDescendants: BindingContext | false } {
100
- const bindingInfo = domDataGetOrSet(node, BINDING_INFO_KEY, {}) as BindingInfo;
101
-
102
- const alreadyBound = bindingInfo.alreadyBound;
103
- if (!sourceBindings) {
104
- if (alreadyBound) {
105
- throw new Error('You cannot apply bindings multiple times to the same element.');
106
- }
107
- bindingInfo.alreadyBound = true;
108
- }
109
- if (!alreadyBound) {
110
- bindingInfo.context = bindingContext;
111
- }
112
-
113
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
- let bindings: Record<string, any> | null = null;
115
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
- let bindingsUpdater: Computed<any> | null = null;
117
-
118
- if (sourceBindings && typeof sourceBindings !== 'function') {
119
- bindings = sourceBindings;
120
- } else {
121
- const provider = providerInstance;
122
- const getBindingsFn = provider.getBindingAccessors.bind(provider);
123
-
124
- const updater = new Computed(() => {
125
- const resolved = sourceBindings
126
- ? (sourceBindings as (ctx: BindingContext, n: Node) => Record<string, () => unknown>)(bindingContext, node)
127
- : getBindingsFn(node, bindingContext);
128
-
129
- if (resolved) {
130
- const sub = bindingContext[SUBSCRIBABLE];
131
- if (sub) sub.get();
132
- const dep = bindingContext[DATA_DEPENDENCY] as Computed<unknown> | undefined;
133
- if (dep) dep.get();
134
- }
135
- bindings = resolved;
136
- return resolved;
137
- });
138
-
139
- bindings = updater.peek() as typeof bindings;
140
-
141
- if (!bindings || !updater.isActive()) {
142
- if (updater.isActive()) {
143
- addDisposeCallback(node, () => updater.dispose());
144
- }
145
- bindingsUpdater = null;
146
- } else {
147
- bindingsUpdater = updater;
148
- addDisposeCallback(node, () => updater.dispose());
149
- }
150
- }
151
-
152
- let contextToExtend = bindingContext;
153
- let bindingHandlerThatControlsDescendants: string | undefined;
154
-
155
- if (bindings) {
156
- contextToExtend = subscribeToBindingEvent(node, bindings, bindingContext, evaluateValueAccessor);
157
-
158
- const getValueAccessor: (key: string) => () => unknown = bindingsUpdater
159
- ? (bindingKey) => () => {
160
- const current = bindingsUpdater!.get() as Record<string, unknown> | null;
161
- return evaluateValueAccessor(current?.[bindingKey]);
162
- }
163
- : (bindingKey) => bindings![bindingKey] as () => unknown;
164
-
165
- const allBindings: AllBindingsAccessor = {
166
- get(key: string): unknown {
167
- return bindings![key] ? evaluateValueAccessor(getValueAccessor(key)) : undefined;
168
- },
169
- has(key: string): boolean {
170
- return key in bindings!;
171
- },
172
- };
173
-
174
- const orderedBindings = topologicalSortBindings(bindings);
175
-
176
- for (const { key: bindingKey, handler } of orderedBindings) {
177
- if (node.nodeType === 8) {
178
- validateVirtualElementBinding(bindingKey);
179
- }
180
-
181
- try {
182
- if (typeof handler.init === 'function') {
183
- const initFn = handler.init;
184
- ignore(() => {
185
- const initResult = initFn(
186
- node,
187
- getValueAccessor(bindingKey),
188
- allBindings,
189
- contextToExtend.$data,
190
- contextToExtend,
191
- );
192
- if (initResult && initResult.controlsDescendantBindings) {
193
- if (bindingHandlerThatControlsDescendants !== undefined) {
194
- throw new Error(
195
- 'Multiple bindings (' + bindingHandlerThatControlsDescendants + ' and ' + bindingKey +
196
- ') are trying to control descendant bindings of the same element.',
197
- );
198
- }
199
- bindingHandlerThatControlsDescendants = bindingKey;
200
- }
201
- });
202
- }
203
-
204
- if (typeof handler.update === 'function') {
205
- const updateFn = handler.update;
206
- const updateComputed = new Computed(() => {
207
- updateFn(
208
- node,
209
- getValueAccessor(bindingKey),
210
- allBindings,
211
- contextToExtend.$data,
212
- contextToExtend,
213
- );
214
- });
215
- if (updateComputed.isActive()) {
216
- addDisposeCallback(node, () => updateComputed.dispose());
217
- }
218
- }
219
- } catch (ex) {
220
- const err = ex as Error;
221
- err.message =
222
- 'Unable to process binding "' + bindingKey + ': ' + bindings[bindingKey] +
223
- '"\nMessage: ' + err.message;
224
- throw err;
225
- }
226
- }
227
- }
228
-
229
- const shouldBindDescendants = bindingHandlerThatControlsDescendants === undefined;
230
- return {
231
- shouldBindDescendants,
232
- bindingContextForDescendants: shouldBindDescendants && contextToExtend,
233
- };
234
- }
235
-
236
- // ---- Tree Walk ----
237
-
238
- function applyBindingsToDescendantsInternal(
239
- bindingContext: BindingContext,
240
- elementOrVirtualElement: Node,
241
- ): void {
242
- let nextInQueue = virtualFirstChild(elementOrVirtualElement);
243
-
244
- if (nextInQueue) {
245
- const provider = providerInstance;
246
- const preprocessNode = provider.preprocessNode;
247
-
248
- if (preprocessNode) {
249
- let currentChild: Node | null = nextInQueue;
250
- while (currentChild) {
251
- const next = virtualNextSibling(currentChild);
252
- preprocessNode.call(provider, currentChild);
253
- currentChild = next;
254
- }
255
- nextInQueue = virtualFirstChild(elementOrVirtualElement);
256
- }
257
-
258
- let currentChild: Node | null = nextInQueue;
259
- while (currentChild) {
260
- const next = virtualNextSibling(currentChild);
261
- applyBindingsToNodeAndDescendantsInternal(bindingContext, currentChild);
262
- currentChild = next;
263
- }
264
- }
265
-
266
- bindingEvent.notify(elementOrVirtualElement, bindingEvent.childrenComplete);
267
- }
268
-
269
- function applyBindingsToNodeAndDescendantsInternal(
270
- bindingContext: BindingContext,
271
- nodeVerified: Node,
272
- ): void {
273
- let bindingContextForDescendants: BindingContext | false = bindingContext;
274
-
275
- const isElement = nodeVerified.nodeType === 1;
276
-
277
- const shouldApplyBindings = isElement || providerInstance.nodeHasBindings(nodeVerified);
278
- if (shouldApplyBindings) {
279
- const result = applyBindingsToNodeInternal(nodeVerified, null, bindingContext);
280
- bindingContextForDescendants = result.bindingContextForDescendants;
281
- }
282
-
283
- if (bindingContextForDescendants && !NO_RECURSE[tagNameLower(nodeVerified)]) {
284
- applyBindingsToDescendantsInternal(bindingContextForDescendants, nodeVerified);
285
- }
286
- }
287
-
288
- // ---- Utility: Wrap raw bindings as accessors ----
289
-
290
- function makeBindingAccessors(
291
- bindings: Record<string, unknown> | ((ctx: BindingContext, node: Node) => Record<string, unknown>),
292
- context: BindingContext,
293
- node: Node,
294
- ): Record<string, () => unknown> {
295
- if (typeof bindings === 'function') {
296
- const boundFn = (bindings as (ctx: BindingContext, node: Node) => Record<string, unknown>).bind(null, context, node);
297
- const initial = ignore(boundFn);
298
- const result: Record<string, () => unknown> = {};
299
- for (const key of Object.keys(initial)) {
300
- result[key] = () => boundFn()[key];
301
- }
302
- return result;
303
- }
304
- const result: Record<string, () => unknown> = {};
305
- for (const key of Object.keys(bindings)) {
306
- result[key] = makeValueAccessor(bindings[key]);
307
- }
308
- return result;
309
- }
310
-
311
- // ---- Public Helpers ----
312
-
313
- function getBindingContext(
314
- viewModelOrBindingContext: unknown,
315
- extendContextCallback?: (self: BindingContext, parentContext: BindingContext | undefined, dataItem: unknown) => void,
316
- ): BindingContext {
317
- if (viewModelOrBindingContext instanceof BindingContext) {
318
- return viewModelOrBindingContext;
319
- }
320
- return new BindingContext(viewModelOrBindingContext, undefined, undefined, extendContextCallback);
321
- }
322
-
323
- // ---- Public API ----
324
-
325
- export function applyBindings(
326
- viewModelOrBindingContext: unknown,
327
- rootNode: Node,
328
- extendContextCallback?: (self: BindingContext, parentContext: BindingContext | undefined, dataItem: unknown) => void,
329
- ): void {
330
- ensureConfigured();
331
- if (!rootNode || (rootNode.nodeType !== 1 && rootNode.nodeType !== 8)) {
332
- throw new Error('applyBindings: second parameter should be a DOM element or comment node');
333
- }
334
- applyBindingsToNodeAndDescendantsInternal(
335
- getBindingContext(viewModelOrBindingContext, extendContextCallback),
336
- rootNode,
337
- );
338
- }
339
-
340
- export function applyBindingsToDescendants(
341
- viewModelOrBindingContext: unknown,
342
- rootNode: Node,
343
- ): void {
344
- ensureConfigured();
345
- if (rootNode.nodeType === 1 || rootNode.nodeType === 8) {
346
- applyBindingsToDescendantsInternal(
347
- getBindingContext(viewModelOrBindingContext),
348
- rootNode,
349
- );
350
- }
351
- }
352
-
353
- export function applyBindingsToNode(
354
- node: Node,
355
- bindings: Record<string, unknown>,
356
- viewModelOrBindingContext?: unknown,
357
- ): { shouldBindDescendants: boolean } {
358
- ensureConfigured();
359
- const context = getBindingContext(viewModelOrBindingContext);
360
- const accessors = makeBindingAccessors(bindings, context, node);
361
- return applyBindingsToNodeInternal(node, accessors, context);
362
- }
363
-
364
- export function applyBindingAccessorsToNode(
365
- node: Node,
366
- bindings: Record<string, () => unknown> | ((ctx: BindingContext, node: Node) => Record<string, () => unknown>),
367
- viewModelOrBindingContext?: unknown,
368
- ): { shouldBindDescendants: boolean } {
369
- ensureConfigured();
370
- const context = getBindingContext(viewModelOrBindingContext);
371
- return applyBindingsToNodeInternal(node, bindings, context);
372
- }
1
+ import { BindingContext, BINDING_INFO_KEY, SUBSCRIBABLE, DATA_DEPENDENCY } from './bindingContext.js';
2
+ import type { AllBindingsAccessor } from './expressionRewriting.js';
3
+ import { instance as providerInstance, getBindingHandler } from './bindingProvider.js';
4
+ import type { BindingHandler } from './bindingProvider.js';
5
+ import { Computed } from './computed.js';
6
+ import { ignore } from './dependencyDetection.js';
7
+ import { domDataGetOrSet } from './domData.js';
8
+ import { addDisposeCallback } from './domNodeDisposal.js';
9
+ import {
10
+ virtualFirstChild,
11
+ virtualNextSibling,
12
+ allowedVirtualElementBindings,
13
+ } from './virtualElements.js';
14
+ import { bindingEvent, subscribeToBindingEvent } from './bindingEvent.js';
15
+ import type { BindingInfo } from './bindingEvent.js';
16
+ import { ensureConfigured } from './configure.js';
17
+
18
+ interface SortedBinding {
19
+ key: string;
20
+ handler: BindingHandler;
21
+ }
22
+
23
+ // Elements whose descendants should not be recursively bound
24
+ const NO_RECURSE: Record<string, boolean> = {
25
+ script: true,
26
+ textarea: true,
27
+ template: true,
28
+ };
29
+
30
+ function tagNameLower(node: Node): string {
31
+ return (node as Element).tagName ? (node as Element).tagName.toLowerCase() : '';
32
+ }
33
+
34
+ // ---- Helpers ----
35
+
36
+ function evaluateValueAccessor(valueAccessor: unknown): unknown {
37
+ return typeof valueAccessor === 'function' ? (valueAccessor as () => unknown)() : valueAccessor;
38
+ }
39
+
40
+ function makeValueAccessor(value: unknown): () => unknown {
41
+ return () => value;
42
+ }
43
+
44
+ // ---- Topological Sort ----
45
+
46
+ function topologicalSortBindings(
47
+ bindings: Record<string, unknown>,
48
+ ): SortedBinding[] {
49
+ const result: SortedBinding[] = [];
50
+ const considered: Record<string, boolean> = {};
51
+ const cyclicStack: string[] = [];
52
+
53
+ function pushBinding(bindingKey: string) {
54
+ if (considered[bindingKey]) return;
55
+
56
+ const handler = getBindingHandler(bindingKey);
57
+ if (handler) {
58
+ if (handler.after) {
59
+ cyclicStack.push(bindingKey);
60
+ for (const depKey of handler.after) {
61
+ if (depKey in bindings) {
62
+ if (cyclicStack.indexOf(depKey) !== -1) {
63
+ throw new Error(
64
+ 'Cannot combine the following bindings, because they have a cyclic dependency: ' +
65
+ cyclicStack.join(', '),
66
+ );
67
+ }
68
+ pushBinding(depKey);
69
+ }
70
+ }
71
+ cyclicStack.pop();
72
+ }
73
+ result.push({ key: bindingKey, handler });
74
+ }
75
+ considered[bindingKey] = true;
76
+ }
77
+
78
+ for (const key of Object.keys(bindings)) {
79
+ pushBinding(key);
80
+ }
81
+
82
+ return result;
83
+ }
84
+
85
+ // ---- Virtual Element Validation ----
86
+
87
+ function validateVirtualElementBinding(bindingName: string): void {
88
+ if (!allowedVirtualElementBindings[bindingName]) {
89
+ throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements");
90
+ }
91
+ }
92
+
93
+ // ---- Core: Apply Bindings to a Single Node ----
94
+
95
+ function applyBindingsToNodeInternal(
96
+ node: Node,
97
+ sourceBindings: Record<string, () => unknown> | ((ctx: BindingContext, node: Node) => Record<string, () => unknown>) | null,
98
+ bindingContext: BindingContext,
99
+ ): { shouldBindDescendants: boolean; bindingContextForDescendants: BindingContext | false } {
100
+ const bindingInfo = domDataGetOrSet(node, BINDING_INFO_KEY, {}) as BindingInfo;
101
+
102
+ const alreadyBound = bindingInfo.alreadyBound;
103
+ if (!sourceBindings) {
104
+ if (alreadyBound) {
105
+ throw new Error('You cannot apply bindings multiple times to the same element.');
106
+ }
107
+ bindingInfo.alreadyBound = true;
108
+ }
109
+ if (!alreadyBound) {
110
+ bindingInfo.context = bindingContext;
111
+ }
112
+
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
+ let bindings: Record<string, any> | null = null;
115
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
+ let bindingsUpdater: Computed<any> | null = null;
117
+
118
+ if (sourceBindings && typeof sourceBindings !== 'function') {
119
+ bindings = sourceBindings;
120
+ } else {
121
+ const provider = providerInstance;
122
+ const getBindingsFn = provider.getBindingAccessors.bind(provider);
123
+
124
+ const updater = new Computed(() => {
125
+ const resolved = sourceBindings
126
+ ? (sourceBindings as (ctx: BindingContext, n: Node) => Record<string, () => unknown>)(bindingContext, node)
127
+ : getBindingsFn(node, bindingContext);
128
+
129
+ if (resolved) {
130
+ const sub = bindingContext[SUBSCRIBABLE];
131
+ if (sub) sub.get();
132
+ const dep = bindingContext[DATA_DEPENDENCY] as Computed<unknown> | undefined;
133
+ if (dep) dep.get();
134
+ }
135
+ bindings = resolved;
136
+ return resolved;
137
+ });
138
+
139
+ bindings = updater.peek() as typeof bindings;
140
+
141
+ if (!bindings || !updater.isActive()) {
142
+ if (updater.isActive()) {
143
+ addDisposeCallback(node, () => updater.dispose());
144
+ }
145
+ bindingsUpdater = null;
146
+ } else {
147
+ bindingsUpdater = updater;
148
+ addDisposeCallback(node, () => updater.dispose());
149
+ }
150
+ }
151
+
152
+ let contextToExtend = bindingContext;
153
+ let bindingHandlerThatControlsDescendants: string | undefined;
154
+
155
+ if (bindings) {
156
+ contextToExtend = subscribeToBindingEvent(node, bindings, bindingContext, evaluateValueAccessor);
157
+
158
+ const getValueAccessor: (key: string) => () => unknown = bindingsUpdater
159
+ ? (bindingKey) => () => {
160
+ const current = bindingsUpdater!.get() as Record<string, unknown> | null;
161
+ return evaluateValueAccessor(current?.[bindingKey]);
162
+ }
163
+ : (bindingKey) => bindings![bindingKey] as () => unknown;
164
+
165
+ const allBindings: AllBindingsAccessor = {
166
+ get(key: string): unknown {
167
+ return bindings![key] ? evaluateValueAccessor(getValueAccessor(key)) : undefined;
168
+ },
169
+ has(key: string): boolean {
170
+ return key in bindings!;
171
+ },
172
+ };
173
+
174
+ const orderedBindings = topologicalSortBindings(bindings);
175
+
176
+ for (const { key: bindingKey, handler } of orderedBindings) {
177
+ if (node.nodeType === 8) {
178
+ validateVirtualElementBinding(bindingKey);
179
+ }
180
+
181
+ try {
182
+ if (typeof handler.init === 'function') {
183
+ const initFn = handler.init;
184
+ ignore(() => {
185
+ const initResult = initFn(
186
+ node,
187
+ getValueAccessor(bindingKey),
188
+ allBindings,
189
+ contextToExtend.$data,
190
+ contextToExtend,
191
+ );
192
+ if (initResult && initResult.controlsDescendantBindings) {
193
+ if (bindingHandlerThatControlsDescendants !== undefined) {
194
+ throw new Error(
195
+ 'Multiple bindings (' + bindingHandlerThatControlsDescendants + ' and ' + bindingKey +
196
+ ') are trying to control descendant bindings of the same element.',
197
+ );
198
+ }
199
+ bindingHandlerThatControlsDescendants = bindingKey;
200
+ }
201
+ });
202
+ }
203
+
204
+ if (typeof handler.update === 'function') {
205
+ const updateFn = handler.update;
206
+ const updateComputed = new Computed(() => {
207
+ updateFn(
208
+ node,
209
+ getValueAccessor(bindingKey),
210
+ allBindings,
211
+ contextToExtend.$data,
212
+ contextToExtend,
213
+ );
214
+ });
215
+ if (updateComputed.isActive()) {
216
+ addDisposeCallback(node, () => updateComputed.dispose());
217
+ }
218
+ }
219
+ } catch (ex) {
220
+ const err = ex as Error;
221
+ err.message =
222
+ 'Unable to process binding "' + bindingKey + ': ' + bindings[bindingKey] +
223
+ '"\nMessage: ' + err.message;
224
+ throw err;
225
+ }
226
+ }
227
+ }
228
+
229
+ const shouldBindDescendants = bindingHandlerThatControlsDescendants === undefined;
230
+ return {
231
+ shouldBindDescendants,
232
+ bindingContextForDescendants: shouldBindDescendants && contextToExtend,
233
+ };
234
+ }
235
+
236
+ // ---- Tree Walk ----
237
+
238
+ function applyBindingsToDescendantsInternal(
239
+ bindingContext: BindingContext,
240
+ elementOrVirtualElement: Node,
241
+ ): void {
242
+ let nextInQueue = virtualFirstChild(elementOrVirtualElement);
243
+
244
+ if (nextInQueue) {
245
+ const provider = providerInstance;
246
+ const preprocessNode = provider.preprocessNode;
247
+
248
+ if (preprocessNode) {
249
+ let currentChild: Node | null = nextInQueue;
250
+ while (currentChild) {
251
+ const next = virtualNextSibling(currentChild);
252
+ preprocessNode.call(provider, currentChild);
253
+ currentChild = next;
254
+ }
255
+ nextInQueue = virtualFirstChild(elementOrVirtualElement);
256
+ }
257
+
258
+ let currentChild: Node | null = nextInQueue;
259
+ while (currentChild) {
260
+ const next = virtualNextSibling(currentChild);
261
+ applyBindingsToNodeAndDescendantsInternal(bindingContext, currentChild);
262
+ currentChild = next;
263
+ }
264
+ }
265
+
266
+ bindingEvent.notify(elementOrVirtualElement, bindingEvent.childrenComplete);
267
+ }
268
+
269
+ function applyBindingsToNodeAndDescendantsInternal(
270
+ bindingContext: BindingContext,
271
+ nodeVerified: Node,
272
+ ): void {
273
+ let bindingContextForDescendants: BindingContext | false = bindingContext;
274
+
275
+ const isElement = nodeVerified.nodeType === 1;
276
+
277
+ const shouldApplyBindings = isElement || providerInstance.nodeHasBindings(nodeVerified);
278
+ if (shouldApplyBindings) {
279
+ const result = applyBindingsToNodeInternal(nodeVerified, null, bindingContext);
280
+ bindingContextForDescendants = result.bindingContextForDescendants;
281
+ }
282
+
283
+ if (bindingContextForDescendants && !NO_RECURSE[tagNameLower(nodeVerified)]) {
284
+ applyBindingsToDescendantsInternal(bindingContextForDescendants, nodeVerified);
285
+ }
286
+ }
287
+
288
+ // ---- Utility: Wrap raw bindings as accessors ----
289
+
290
+ function makeBindingAccessors(
291
+ bindings: Record<string, unknown> | ((ctx: BindingContext, node: Node) => Record<string, unknown>),
292
+ context: BindingContext,
293
+ node: Node,
294
+ ): Record<string, () => unknown> {
295
+ if (typeof bindings === 'function') {
296
+ const boundFn = (bindings as (ctx: BindingContext, node: Node) => Record<string, unknown>).bind(null, context, node);
297
+ const initial = ignore(boundFn);
298
+ const result: Record<string, () => unknown> = {};
299
+ for (const key of Object.keys(initial)) {
300
+ result[key] = () => boundFn()[key];
301
+ }
302
+ return result;
303
+ }
304
+ const result: Record<string, () => unknown> = {};
305
+ for (const key of Object.keys(bindings)) {
306
+ result[key] = makeValueAccessor(bindings[key]);
307
+ }
308
+ return result;
309
+ }
310
+
311
+ // ---- Public Helpers ----
312
+
313
+ function getBindingContext(
314
+ viewModelOrBindingContext: unknown,
315
+ extendContextCallback?: (self: BindingContext, parentContext: BindingContext | undefined, dataItem: unknown) => void,
316
+ ): BindingContext {
317
+ if (viewModelOrBindingContext instanceof BindingContext) {
318
+ return viewModelOrBindingContext;
319
+ }
320
+ return new BindingContext(viewModelOrBindingContext, undefined, undefined, extendContextCallback);
321
+ }
322
+
323
+ // ---- Public API ----
324
+
325
+ export function applyBindings(
326
+ viewModelOrBindingContext: unknown,
327
+ rootNode: Node,
328
+ extendContextCallback?: (self: BindingContext, parentContext: BindingContext | undefined, dataItem: unknown) => void,
329
+ ): void {
330
+ ensureConfigured();
331
+ if (!rootNode || (rootNode.nodeType !== 1 && rootNode.nodeType !== 8)) {
332
+ throw new Error('applyBindings: second parameter should be a DOM element or comment node');
333
+ }
334
+ applyBindingsToNodeAndDescendantsInternal(
335
+ getBindingContext(viewModelOrBindingContext, extendContextCallback),
336
+ rootNode,
337
+ );
338
+ }
339
+
340
+ export function applyBindingsToDescendants(
341
+ viewModelOrBindingContext: unknown,
342
+ rootNode: Node,
343
+ ): void {
344
+ ensureConfigured();
345
+ if (rootNode.nodeType === 1 || rootNode.nodeType === 8) {
346
+ applyBindingsToDescendantsInternal(
347
+ getBindingContext(viewModelOrBindingContext),
348
+ rootNode,
349
+ );
350
+ }
351
+ }
352
+
353
+ export function applyBindingsToNode(
354
+ node: Node,
355
+ bindings: Record<string, unknown>,
356
+ viewModelOrBindingContext?: unknown,
357
+ ): { shouldBindDescendants: boolean } {
358
+ ensureConfigured();
359
+ const context = getBindingContext(viewModelOrBindingContext);
360
+ const accessors = makeBindingAccessors(bindings, context, node);
361
+ return applyBindingsToNodeInternal(node, accessors, context);
362
+ }
363
+
364
+ export function applyBindingAccessorsToNode(
365
+ node: Node,
366
+ bindings: Record<string, () => unknown> | ((ctx: BindingContext, node: Node) => Record<string, () => unknown>),
367
+ viewModelOrBindingContext?: unknown,
368
+ ): { shouldBindDescendants: boolean } {
369
+ ensureConfigured();
370
+ const context = getBindingContext(viewModelOrBindingContext);
371
+ return applyBindingsToNodeInternal(node, bindings, context);
372
+ }