@gogocat/data-bind 1.12.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +14 -14
- package/.vscode/launch.json +12 -12
- package/CONFIGURATION.md +294 -0
- package/REACTIVE_MODE.md +553 -0
- package/README.md +266 -829
- package/babel.config.json +30 -0
- package/dist/js/_escape.d.ts +14 -0
- package/dist/js/_escape.d.ts.map +1 -0
- package/dist/js/applyBinding.d.ts +11 -0
- package/dist/js/applyBinding.d.ts.map +1 -0
- package/dist/js/attrBinding.d.ts +12 -0
- package/dist/js/attrBinding.d.ts.map +1 -0
- package/dist/js/binder.d.ts +67 -0
- package/dist/js/binder.d.ts.map +1 -0
- package/dist/js/changeBinding.d.ts +19 -0
- package/dist/js/changeBinding.d.ts.map +1 -0
- package/dist/js/commentWrapper.d.ts +39 -0
- package/dist/js/commentWrapper.d.ts.map +1 -0
- package/dist/js/config.d.ts +55 -0
- package/dist/js/config.d.ts.map +1 -0
- package/dist/js/createBindingOption.d.ts +32 -0
- package/dist/js/createBindingOption.d.ts.map +1 -0
- package/dist/js/createEventBinding.d.ts +10 -0
- package/dist/js/createEventBinding.d.ts.map +1 -0
- package/dist/js/cssBinding.d.ts +15 -0
- package/dist/js/cssBinding.d.ts.map +1 -0
- package/dist/js/dataBind.js +2756 -2530
- package/dist/js/dataBind.min.js +8 -1
- package/dist/js/dataBind.min.js.map +1 -1
- package/dist/js/domWalker.d.ts +9 -0
- package/dist/js/domWalker.d.ts.map +1 -0
- package/dist/js/forOfBinding.d.ts +12 -0
- package/dist/js/forOfBinding.d.ts.map +1 -0
- package/dist/js/hoverBinding.d.ts +13 -0
- package/dist/js/hoverBinding.d.ts.map +1 -0
- package/dist/js/ifBinding.d.ts +12 -0
- package/dist/js/ifBinding.d.ts.map +1 -0
- package/dist/js/index.d.ts +10 -0
- package/dist/js/index.d.ts.map +1 -0
- package/dist/js/modelBinding.d.ts +12 -0
- package/dist/js/modelBinding.d.ts.map +1 -0
- package/dist/js/postProcess.d.ts +3 -0
- package/dist/js/postProcess.d.ts.map +1 -0
- package/dist/js/pubSub.d.ts +11 -0
- package/dist/js/pubSub.d.ts.map +1 -0
- package/dist/js/reactiveProxy.d.ts +28 -0
- package/dist/js/reactiveProxy.d.ts.map +1 -0
- package/dist/js/renderForOfBinding.d.ts +8 -0
- package/dist/js/renderForOfBinding.d.ts.map +1 -0
- package/dist/js/renderIfBinding.d.ts +22 -0
- package/dist/js/renderIfBinding.d.ts.map +1 -0
- package/dist/js/renderIteration.d.ts +16 -0
- package/dist/js/renderIteration.d.ts.map +1 -0
- package/dist/js/renderTemplate.d.ts +14 -0
- package/dist/js/renderTemplate.d.ts.map +1 -0
- package/dist/js/renderTemplatesBinding.d.ts +19 -0
- package/dist/js/renderTemplatesBinding.d.ts.map +1 -0
- package/dist/js/showBinding.d.ts +13 -0
- package/dist/js/showBinding.d.ts.map +1 -0
- package/dist/js/switchBinding.d.ts +13 -0
- package/dist/js/switchBinding.d.ts.map +1 -0
- package/dist/js/textBinding.d.ts +13 -0
- package/dist/js/textBinding.d.ts.map +1 -0
- package/dist/js/types/_escape.d.ts +14 -0
- package/dist/js/types/_escape.d.ts.map +1 -0
- package/dist/js/types/applyBinding.d.ts +11 -0
- package/dist/js/types/applyBinding.d.ts.map +1 -0
- package/dist/js/types/attrBinding.d.ts +12 -0
- package/dist/js/types/attrBinding.d.ts.map +1 -0
- package/dist/js/types/binder.d.ts +67 -0
- package/dist/js/types/binder.d.ts.map +1 -0
- package/dist/js/types/changeBinding.d.ts +19 -0
- package/dist/js/types/changeBinding.d.ts.map +1 -0
- package/dist/js/types/commentWrapper.d.ts +39 -0
- package/dist/js/types/commentWrapper.d.ts.map +1 -0
- package/dist/js/types/config.d.ts +55 -0
- package/dist/js/types/config.d.ts.map +1 -0
- package/dist/js/types/createBindingOption.d.ts +32 -0
- package/dist/js/types/createBindingOption.d.ts.map +1 -0
- package/dist/js/types/createEventBinding.d.ts +10 -0
- package/dist/js/types/createEventBinding.d.ts.map +1 -0
- package/dist/js/types/cssBinding.d.ts +15 -0
- package/dist/js/types/cssBinding.d.ts.map +1 -0
- package/dist/js/types/domWalker.d.ts +9 -0
- package/dist/js/types/domWalker.d.ts.map +1 -0
- package/dist/js/types/forOfBinding.d.ts +12 -0
- package/dist/js/types/forOfBinding.d.ts.map +1 -0
- package/dist/js/types/hoverBinding.d.ts +13 -0
- package/dist/js/types/hoverBinding.d.ts.map +1 -0
- package/dist/js/types/ifBinding.d.ts +12 -0
- package/dist/js/types/ifBinding.d.ts.map +1 -0
- package/dist/js/types/index.d.ts +10 -0
- package/dist/js/types/index.d.ts.map +1 -0
- package/dist/js/types/modelBinding.d.ts +12 -0
- package/dist/js/types/modelBinding.d.ts.map +1 -0
- package/dist/js/types/postProcess.d.ts +3 -0
- package/dist/js/types/postProcess.d.ts.map +1 -0
- package/dist/js/types/pubSub.d.ts +11 -0
- package/dist/js/types/pubSub.d.ts.map +1 -0
- package/dist/js/types/reactiveProxy.d.ts +28 -0
- package/dist/js/types/reactiveProxy.d.ts.map +1 -0
- package/dist/js/types/renderForOfBinding.d.ts +8 -0
- package/dist/js/types/renderForOfBinding.d.ts.map +1 -0
- package/dist/js/types/renderIfBinding.d.ts +22 -0
- package/dist/js/types/renderIfBinding.d.ts.map +1 -0
- package/dist/js/types/renderIteration.d.ts +16 -0
- package/dist/js/types/renderIteration.d.ts.map +1 -0
- package/dist/js/types/renderTemplate.d.ts +14 -0
- package/dist/js/types/renderTemplate.d.ts.map +1 -0
- package/dist/js/types/renderTemplatesBinding.d.ts +19 -0
- package/dist/js/types/renderTemplatesBinding.d.ts.map +1 -0
- package/dist/js/types/showBinding.d.ts +13 -0
- package/dist/js/types/showBinding.d.ts.map +1 -0
- package/dist/js/types/switchBinding.d.ts +13 -0
- package/dist/js/types/switchBinding.d.ts.map +1 -0
- package/dist/js/types/textBinding.d.ts +13 -0
- package/dist/js/types/textBinding.d.ts.map +1 -0
- package/dist/js/types/types.d.ts +111 -0
- package/dist/js/types/types.d.ts.map +1 -0
- package/dist/js/types/util.d.ts +119 -0
- package/dist/js/types/util.d.ts.map +1 -0
- package/dist/js/types.d.ts +111 -0
- package/dist/js/types.d.ts.map +1 -0
- package/dist/js/util.d.ts +119 -0
- package/dist/js/util.d.ts.map +1 -0
- package/eslint.config.js +124 -0
- package/examples/DBMONSTER_COMPARISON.md +123 -0
- package/examples/afterRenderDemo.html +119 -0
- package/examples/bootstrap/css/animate.css +1579 -1579
- package/examples/bootstrap/css/bootstrap.min.css +6 -6
- package/examples/bootstrap/css/homeservices.css +378 -390
- package/examples/bootstrap/css/open-iconic.css +511 -511
- package/examples/bootstrap/fonts/open-iconic.svg +543 -543
- package/examples/bootstrap/js/compMessageDialog.js +20 -19
- package/examples/bootstrap/js/compSearchBar.js +12 -19
- package/examples/bootstrap/js/compSearchResults.js +50 -46
- package/examples/bootstrap/js/featureAdsResult.json +65 -65
- package/examples/bootstrap/js/searchResult.json +57 -57
- package/examples/bootstrap.html +343 -332
- package/examples/css/baseTodo.css +141 -141
- package/examples/css/dbMonsterStyles.css +27 -27
- package/examples/css/indexTodo.css +374 -374
- package/examples/dbmonsterForOfReactive.html +40 -0
- package/examples/dbmonsterReact.html +19 -0
- package/examples/forOfBindingSimpleDebug.html +45 -0
- package/examples/globalConfig.html +131 -0
- package/examples/js/afterRenderDemo.js +190 -0
- package/examples/js/appTodo.js +46 -46
- package/examples/js/attrBindingDemo.js +2 -2
- package/examples/js/dbMonApp.js +24 -26
- package/examples/js/dbMonAppReact.jsx +79 -0
- package/examples/js/dbMonAppReactive.js +28 -0
- package/examples/js/fiberDemo.js +4 -4
- package/examples/js/filtersDemo.js +8 -8
- package/examples/js/forOfDemo.js +7 -9
- package/examples/js/forOfDemoComplex.js +44 -17
- package/examples/js/form.js +14 -14
- package/examples/js/globalConfig.js +117 -0
- package/examples/js/ifBindingDemo.js +16 -16
- package/examples/js/reactiveDemo.js +119 -0
- package/examples/js/switchBindingDemo.js +8 -8
- package/examples/react-dbmonster/dist/bundle.js +43 -0
- package/examples/react-dbmonster/package-lock.json +537 -0
- package/examples/react-dbmonster/package.json +16 -0
- package/examples/react-dbmonster/src/index.jsx +80 -0
- package/examples/reactiveDemo.html +127 -0
- package/examples/refreshRateTest.html +75 -75
- package/index.html +841 -0
- package/package.json +31 -34
- package/rollup.config.js +79 -36
- package/src/{_escape.js → _escape.ts} +19 -17
- package/src/{applyBinding.js → applyBinding.ts} +27 -18
- package/src/{attrBinding.js → attrBinding.ts} +14 -13
- package/src/{binder.js → binder.ts} +289 -181
- package/src/changeBinding.ts +93 -0
- package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
- package/src/config.ts +107 -0
- package/src/{createBindingOption.js → createBindingOption.ts} +39 -15
- package/src/createEventBinding.ts +88 -0
- package/src/{cssBinding.js → cssBinding.ts} +13 -11
- package/src/{domWalker.js → domWalker.ts} +44 -30
- package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
- package/src/hoverBinding.ts +84 -0
- package/src/{ifBinding.js → ifBinding.ts} +14 -12
- package/src/index.ts +53 -0
- package/src/{modelBinding.js → modelBinding.ts} +11 -9
- package/src/{postProcess.js → postProcess.ts} +6 -4
- package/src/{pubSub.js → pubSub.ts} +24 -21
- package/src/reactiveProxy.ts +285 -0
- package/src/{renderForOfBinding.js → renderForOfBinding.ts} +54 -32
- package/src/{renderIfBinding.js → renderIfBinding.ts} +41 -19
- package/src/{renderIteration.js → renderIteration.ts} +24 -8
- package/src/renderTemplate.ts +165 -0
- package/src/renderTemplatesBinding.ts +73 -0
- package/src/{showBinding.js → showBinding.ts} +4 -3
- package/src/{switchBinding.js → switchBinding.ts} +18 -15
- package/src/{textBinding.js → textBinding.ts} +5 -4
- package/src/types.ts +124 -0
- package/src/util.ts +810 -0
- package/test/css/reporter.css +9 -9
- package/test/globals.d.ts +19 -0
- package/test/helpers/testHelper.js +46 -11
- package/test/mocks/featureAdsResult.json +65 -65
- package/test/mocks/searchResult.json +57 -57
- package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
- package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
- package/test/specs/blurBinding.spec.ts +60 -0
- package/test/specs/chainableUse.spec.ts +125 -0
- package/test/specs/clickBinding.spec.ts +194 -0
- package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
- package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
- package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
- package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
- package/test/specs/formBinding.spec.ts +272 -0
- package/test/specs/ifBinding.spec.ts +165 -0
- package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
- package/test/specs/reactiveProxy.spec.ts +465 -0
- package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
- package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
- package/test/specs/templateBinding.spec.ts +273 -0
- package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
- package/test/tsconfig.json +31 -0
- package/test-output.txt +200 -0
- package/test-reactive.html +224 -0
- package/tsconfig.json +28 -0
- package/vendors/lodash.custom.js +4577 -4577
- package/vendors/lodash.custom.min.js +45 -45
- package/vitest.config.js +27 -0
- package/.eslintrc.js +0 -1
- package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
- package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
- package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
- package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
- package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
- package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
- package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
- package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
- package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
- package/coverage/coverage.json +0 -1
- package/coverage/lcov/lcov-report/base.css +0 -213
- package/coverage/lcov/lcov-report/index.html +0 -93
- package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
- package/coverage/lcov/lcov-report/js/index.html +0 -93
- package/coverage/lcov/lcov-report/prettify.css +0 -1
- package/coverage/lcov/lcov-report/prettify.js +0 -1
- package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov/lcov-report/sorter.js +0 -158
- package/coverage/lcov/lcov.info +0 -1991
- package/eslintrc.json +0 -40
- package/examples/bootstrap/js/bootstrap.min.js +0 -6
- package/examples/bootstrap/js/popper.min.js +0 -5
- package/examples/bootstrap/js/searchSuggestion.js +0 -58
- package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
- package/gruntfile.js +0 -92
- package/gulpfile.js +0 -32
- package/src/applyBindingExport.js +0 -5
- package/src/changeBinding.js +0 -63
- package/src/config.js +0 -66
- package/src/createEventBinding.js +0 -46
- package/src/eventSystem.js +0 -46
- package/src/hoverBinding.js +0 -57
- package/src/index.js +0 -26
- package/src/renderTemplate.js +0 -128
- package/src/renderTemplatesBinding.js +0 -44
- package/src/util.js +0 -648
- package/test/specs/blurBinding.spec.js +0 -57
- package/test/specs/formBinding.spec.js +0 -316
- package/test/specs/ifBinding.spec.js +0 -169
- package/test/specs/templateBinding.spec.js +0 -117
- package/vendors/jasmine-jquery.js +0 -841
- package/vendors/jquery-3.2.1.min.js +0 -4
|
@@ -1,181 +1,289 @@
|
|
|
1
|
-
import * as config from './config';
|
|
2
|
-
import {debounceRaf} from './util';
|
|
3
|
-
import createBindingCache from './domWalker';
|
|
4
|
-
import createBindingOption from './createBindingOption';
|
|
5
|
-
import applyBinding from './applyBinding';
|
|
6
|
-
import renderTemplatesBinding from './renderTemplatesBinding';
|
|
7
|
-
import postProcess from './postProcess';
|
|
8
|
-
import * as pubSub from './pubSub';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
//
|
|
45
|
-
this
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
1
|
+
import * as config from './config';
|
|
2
|
+
import {debounceRaf} from './util';
|
|
3
|
+
import createBindingCache from './domWalker';
|
|
4
|
+
import createBindingOption from './createBindingOption';
|
|
5
|
+
import applyBinding from './applyBinding';
|
|
6
|
+
import renderTemplatesBinding from './renderTemplatesBinding';
|
|
7
|
+
import postProcess from './postProcess';
|
|
8
|
+
import * as pubSub from './pubSub';
|
|
9
|
+
import {createReactiveProxy, isProxySupported} from './reactiveProxy';
|
|
10
|
+
import type {ViewModel, ElementCache, UpdateOption, BindingAttrs, BinderOptions} from './types';
|
|
11
|
+
|
|
12
|
+
let compIdIndex = 0;
|
|
13
|
+
|
|
14
|
+
class Binder {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
public initRendered: boolean;
|
|
17
|
+
public compId: number;
|
|
18
|
+
public $rootElement: HTMLElement;
|
|
19
|
+
public viewModel: ViewModel;
|
|
20
|
+
public bindingAttrs: BindingAttrs;
|
|
21
|
+
public isServerRendered: boolean;
|
|
22
|
+
public elementCache: ElementCache;
|
|
23
|
+
public postProcessQueue: Array<() => void>;
|
|
24
|
+
public render: (opt?: UpdateOption) => void;
|
|
25
|
+
public isReactive: boolean;
|
|
26
|
+
public originalViewModel: ViewModel;
|
|
27
|
+
private afterRenderCallbacks: Array<() => void>;
|
|
28
|
+
|
|
29
|
+
constructor($rootElement: HTMLElement, viewModel: ViewModel, bindingAttrs: BindingAttrs, options: BinderOptions = {}) {
|
|
30
|
+
if (!$rootElement || $rootElement.nodeType !== 1 || viewModel === null || typeof viewModel !== 'object') {
|
|
31
|
+
throw new TypeError('$rootElement or viewModel is invalid');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.initRendered = false;
|
|
35
|
+
|
|
36
|
+
this.compId = compIdIndex += 1;
|
|
37
|
+
|
|
38
|
+
this.$rootElement = $rootElement;
|
|
39
|
+
|
|
40
|
+
this.bindingAttrs = bindingAttrs;
|
|
41
|
+
|
|
42
|
+
this.isServerRendered = this.$rootElement.getAttribute(config.serverRenderedAttr) !== null;
|
|
43
|
+
|
|
44
|
+
// Initialize afterRender callbacks array
|
|
45
|
+
this.afterRenderCallbacks = [];
|
|
46
|
+
|
|
47
|
+
// Initialize render method with debounced version
|
|
48
|
+
this.render = debounceRaf(this._render.bind(this), this) as (opt?: UpdateOption) => void;
|
|
49
|
+
|
|
50
|
+
// Store original viewModel reference
|
|
51
|
+
this.originalViewModel = viewModel;
|
|
52
|
+
|
|
53
|
+
// Reactive mode is controlled by options (defaults merged in index.ts)
|
|
54
|
+
// options.reactive is guaranteed to be defined due to merge in index.ts
|
|
55
|
+
this.isReactive = !!options.reactive;
|
|
56
|
+
|
|
57
|
+
// If reactive mode is enabled, wrap viewModel in proxy
|
|
58
|
+
if (this.isReactive) {
|
|
59
|
+
if (!isProxySupported()) {
|
|
60
|
+
console.warn('Reactive mode requires Proxy support. Falling back to manual mode.');
|
|
61
|
+
this.isReactive = false;
|
|
62
|
+
this.viewModel = viewModel;
|
|
63
|
+
} else {
|
|
64
|
+
this.viewModel = createReactiveProxy(viewModel, {
|
|
65
|
+
onChange: () => this.render(),
|
|
66
|
+
deep: true,
|
|
67
|
+
trackChanges: options.trackChanges,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
this.viewModel = viewModel;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// inject instance into viewModel
|
|
75
|
+
this.viewModel.APP = this;
|
|
76
|
+
|
|
77
|
+
// add $root pointer to viewModel so binding can be refer as $root.something
|
|
78
|
+
this.viewModel.$root = this.viewModel;
|
|
79
|
+
|
|
80
|
+
// 1st step
|
|
81
|
+
// parsView walk the DOM and create binding cache that holds each element's binding details
|
|
82
|
+
// this binding cache is like AST for render and update
|
|
83
|
+
this.parseView();
|
|
84
|
+
|
|
85
|
+
// for jquery user set viewModel referece to $rootElement for easy debug
|
|
86
|
+
// otherwise use Expando to attach viewModel to $rootElement
|
|
87
|
+
this.$rootElement[config.bindingDataReference.rootDataKey] = this.viewModel;
|
|
88
|
+
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* parseView
|
|
94
|
+
* @description
|
|
95
|
+
* @return {this}
|
|
96
|
+
* traver from $rootElement to find each data-bind-* element
|
|
97
|
+
* then apply data binding
|
|
98
|
+
*/
|
|
99
|
+
public parseView(): this {
|
|
100
|
+
this.elementCache = createBindingCache({
|
|
101
|
+
rootNode: this.$rootElement,
|
|
102
|
+
bindingAttrs: this.bindingAttrs,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// updateElementCache if server rendered on init
|
|
106
|
+
if (this.isServerRendered && !this.initRendered) {
|
|
107
|
+
this.updateElementCache({
|
|
108
|
+
templateCache: true,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* updateElementCache
|
|
116
|
+
* @param {object} opt
|
|
117
|
+
* @description call createBindingCache to parse view and generate bindingCache
|
|
118
|
+
*/
|
|
119
|
+
public updateElementCache(
|
|
120
|
+
opt: {
|
|
121
|
+
allCache?: boolean;
|
|
122
|
+
templateCache?: boolean;
|
|
123
|
+
elementCache?: ElementCache;
|
|
124
|
+
isRenderedTemplates?: boolean;
|
|
125
|
+
} = {},
|
|
126
|
+
): void {
|
|
127
|
+
const elementCache = opt.elementCache || this.elementCache;
|
|
128
|
+
|
|
129
|
+
if (opt.allCache) {
|
|
130
|
+
// walk dom from root element to regenerate elementCache
|
|
131
|
+
this.elementCache = createBindingCache({
|
|
132
|
+
rootNode: this.$rootElement,
|
|
133
|
+
bindingAttrs: this.bindingAttrs,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
// walk from first rendered template node to create/update child bindingCache
|
|
137
|
+
if (opt.allCache || opt.templateCache) {
|
|
138
|
+
if (elementCache[this.bindingAttrs.tmp] && elementCache[this.bindingAttrs.tmp].length) {
|
|
139
|
+
// Use for loop to handle templates added during rendering
|
|
140
|
+
for (let i = 0; i < elementCache[this.bindingAttrs.tmp].length; i++) {
|
|
141
|
+
const cache = elementCache[this.bindingAttrs.tmp][i];
|
|
142
|
+
// set skipCheck as skipForOfParseFn whenever an node has
|
|
143
|
+
// both template and forOf bindings
|
|
144
|
+
// then the template bindingCache should be an empty object
|
|
145
|
+
let skipForOfParseFn: (() => boolean) | null = null;
|
|
146
|
+
if (cache.el.hasAttribute(this.bindingAttrs.forOf)) {
|
|
147
|
+
skipForOfParseFn = (): boolean => {
|
|
148
|
+
return true;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
cache.bindingCache = createBindingCache({
|
|
152
|
+
rootNode: cache.el,
|
|
153
|
+
bindingAttrs: this.bindingAttrs,
|
|
154
|
+
skipCheck: skipForOfParseFn,
|
|
155
|
+
isRenderedTemplate: opt.isRenderedTemplates,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private _render(opt: UpdateOption = {}): void {
|
|
163
|
+
let updateOption: UpdateOption = {};
|
|
164
|
+
|
|
165
|
+
if (!this.initRendered) {
|
|
166
|
+
// only update eventsBinding if server rendered
|
|
167
|
+
if (this.isServerRendered) {
|
|
168
|
+
this.$rootElement.removeAttribute(config.serverRenderedAttr);
|
|
169
|
+
updateOption = createBindingOption(config.bindingUpdateConditions.serverRendered, opt);
|
|
170
|
+
} else {
|
|
171
|
+
updateOption = createBindingOption(config.bindingUpdateConditions.init, opt);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
// when called again only update visualBinding options
|
|
175
|
+
updateOption = createBindingOption('', opt);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// create postProcessQueue before start rendering
|
|
179
|
+
this.postProcessQueue = [];
|
|
180
|
+
|
|
181
|
+
const renderBindingOption = {
|
|
182
|
+
ctx: this,
|
|
183
|
+
elementCache: this.elementCache,
|
|
184
|
+
updateOption,
|
|
185
|
+
bindingAttrs: this.bindingAttrs,
|
|
186
|
+
viewModel: this.viewModel,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// always render template binding first
|
|
190
|
+
// render and apply binding to template(s)
|
|
191
|
+
// this is an share function therefore passing 'this' context
|
|
192
|
+
renderTemplatesBinding(renderBindingOption);
|
|
193
|
+
|
|
194
|
+
// apply bindings to rest of the DOM
|
|
195
|
+
applyBinding(renderBindingOption);
|
|
196
|
+
|
|
197
|
+
// trigger postProcess
|
|
198
|
+
postProcess(this.postProcessQueue);
|
|
199
|
+
// clear postProcessQueue
|
|
200
|
+
this.postProcessQueue.length = 0;
|
|
201
|
+
delete this.postProcessQueue;
|
|
202
|
+
|
|
203
|
+
this.initRendered = true;
|
|
204
|
+
|
|
205
|
+
// Call afterRender callbacks after rendering is fully complete
|
|
206
|
+
this._callAfterRenderCallbacks();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Call all registered afterRender callbacks
|
|
211
|
+
* Called automatically after each render completes
|
|
212
|
+
*/
|
|
213
|
+
private _callAfterRenderCallbacks(): void {
|
|
214
|
+
if (this.afterRenderCallbacks.length > 0) {
|
|
215
|
+
// Clone array to avoid issues if callbacks modify the array
|
|
216
|
+
const callbacks = this.afterRenderCallbacks.slice();
|
|
217
|
+
for (let i = 0, len = callbacks.length; i < len; i += 1) {
|
|
218
|
+
try {
|
|
219
|
+
callbacks[i]();
|
|
220
|
+
} catch (err) {
|
|
221
|
+
console.error('Error in afterRender callback:', err);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Register a callback to be called after each render completes
|
|
229
|
+
* Useful for reactive mode where renders happen automatically
|
|
230
|
+
* @param callback Function to call after render completes
|
|
231
|
+
* @returns this for chaining
|
|
232
|
+
*/
|
|
233
|
+
public afterRender(callback: () => void): this {
|
|
234
|
+
if (typeof callback !== 'function') {
|
|
235
|
+
throw new TypeError('afterRender callback must be a function');
|
|
236
|
+
}
|
|
237
|
+
this.afterRenderCallbacks.push(callback);
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Remove a specific afterRender callback
|
|
243
|
+
* @param callback The callback function to remove
|
|
244
|
+
* @returns this for chaining
|
|
245
|
+
*/
|
|
246
|
+
public removeAfterRender(callback: () => void): this {
|
|
247
|
+
const index = this.afterRenderCallbacks.indexOf(callback);
|
|
248
|
+
if (index !== -1) {
|
|
249
|
+
this.afterRenderCallbacks.splice(index, 1);
|
|
250
|
+
}
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Clear all afterRender callbacks
|
|
256
|
+
* @returns this for chaining
|
|
257
|
+
*/
|
|
258
|
+
public clearAfterRender(): this {
|
|
259
|
+
this.afterRenderCallbacks.length = 0;
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
public subscribe(eventName: string = '', fn: (...args: unknown[]) => void): this {
|
|
264
|
+
pubSub.subscribeEvent(this, eventName, fn);
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public subscribeOnce(eventName: string = '', fn: (...args: unknown[]) => void): this {
|
|
269
|
+
pubSub.subscribeEventOnce(this, eventName, fn);
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
public unsubscribe(eventName: string = ''): this {
|
|
274
|
+
pubSub.unsubscribeEvent(this.compId, eventName);
|
|
275
|
+
return this;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
public unsubscribeAll(): this {
|
|
279
|
+
pubSub.unsubscribeAllEvent(this.compId);
|
|
280
|
+
return this;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
public publish(eventName: string = '', ...args: unknown[]): this {
|
|
284
|
+
pubSub.publishEvent(eventName, ...args);
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export default Binder;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
|
|
2
|
+
import {
|
|
3
|
+
getViewModelValue,
|
|
4
|
+
setViewModelValue,
|
|
5
|
+
resolveViewModelContext,
|
|
6
|
+
resolveParamList,
|
|
7
|
+
} from './util';
|
|
8
|
+
import _escape from './_escape';
|
|
9
|
+
import type {BindingCache, ViewModel, BindingAttrs} from './types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create change handler
|
|
13
|
+
*/
|
|
14
|
+
const createChangeHandler = (
|
|
15
|
+
viewModel: ViewModel,
|
|
16
|
+
modelDataKey: string | null,
|
|
17
|
+
paramList: unknown[],
|
|
18
|
+
handlerFn: Function,
|
|
19
|
+
viewModelContext: ViewModel,
|
|
20
|
+
): EventListener => {
|
|
21
|
+
let oldValue: unknown = '';
|
|
22
|
+
let newValue: unknown = '';
|
|
23
|
+
|
|
24
|
+
return function changeHandler(this: HTMLInputElement, e: Event) {
|
|
25
|
+
const $this = this;
|
|
26
|
+
const isCheckbox = $this.type === 'checkbox';
|
|
27
|
+
newValue = isCheckbox ? $this.checked : _escape($this.value);
|
|
28
|
+
// set data to viewModel
|
|
29
|
+
if (modelDataKey) {
|
|
30
|
+
oldValue = getViewModelValue(viewModel, modelDataKey);
|
|
31
|
+
setViewModelValue(viewModel, modelDataKey, newValue);
|
|
32
|
+
}
|
|
33
|
+
const args = [e, e.currentTarget, newValue, oldValue, ...paramList];
|
|
34
|
+
handlerFn.apply(viewModelContext, args);
|
|
35
|
+
oldValue = newValue;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
interface ChangeBindingParams {
|
|
40
|
+
cache: BindingCache;
|
|
41
|
+
viewModel: ViewModel;
|
|
42
|
+
bindingAttrs: BindingAttrs;
|
|
43
|
+
forceRender: boolean;
|
|
44
|
+
type?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* changeBinding
|
|
49
|
+
* @description input element on change event binding. DOM -> viewModel update
|
|
50
|
+
* @param {object} cache
|
|
51
|
+
* @param {object} viewModel
|
|
52
|
+
* @param {object} bindingAttrs
|
|
53
|
+
* @param {boolean} forceRender
|
|
54
|
+
*/
|
|
55
|
+
const changeBinding = ({
|
|
56
|
+
cache,
|
|
57
|
+
viewModel,
|
|
58
|
+
bindingAttrs,
|
|
59
|
+
forceRender,
|
|
60
|
+
type = 'change',
|
|
61
|
+
}: ChangeBindingParams): void => {
|
|
62
|
+
const handlerName = cache.dataKey;
|
|
63
|
+
let paramList = cache.parameters;
|
|
64
|
+
const modelDataKey = cache.el.getAttribute(bindingAttrs.model);
|
|
65
|
+
let viewModelContext: ViewModel;
|
|
66
|
+
const APP = viewModel.APP || viewModel.$root?.APP;
|
|
67
|
+
const rootElement = APP?.$rootElement as HTMLElement | undefined;
|
|
68
|
+
|
|
69
|
+
if (!handlerName || (!forceRender && rootElement && !rootElement.contains(cache.el))) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const handlerFn = getViewModelValue(viewModel, handlerName);
|
|
74
|
+
|
|
75
|
+
if (typeof handlerFn === 'function') {
|
|
76
|
+
viewModelContext = resolveViewModelContext(viewModel, handlerName);
|
|
77
|
+
paramList = paramList ? resolveParamList(viewModel, paramList) : [];
|
|
78
|
+
|
|
79
|
+
const changeHandler = createChangeHandler(
|
|
80
|
+
viewModel,
|
|
81
|
+
modelDataKey,
|
|
82
|
+
paramList,
|
|
83
|
+
handlerFn,
|
|
84
|
+
viewModelContext,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// assign on change event
|
|
88
|
+
cache.el.removeEventListener(type, changeHandler, false);
|
|
89
|
+
cache.el.addEventListener(type, changeHandler, false);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default changeBinding;
|