@gogocat/data-bind 1.11.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.
Files changed (274) hide show
  1. package/.editorconfig +14 -14
  2. package/.vscode/launch.json +12 -12
  3. package/CONFIGURATION.md +294 -0
  4. package/REACTIVE_MODE.md +553 -0
  5. package/README.md +266 -829
  6. package/babel.config.json +30 -0
  7. package/dist/js/_escape.d.ts +14 -0
  8. package/dist/js/_escape.d.ts.map +1 -0
  9. package/dist/js/applyBinding.d.ts +11 -0
  10. package/dist/js/applyBinding.d.ts.map +1 -0
  11. package/dist/js/attrBinding.d.ts +12 -0
  12. package/dist/js/attrBinding.d.ts.map +1 -0
  13. package/dist/js/binder.d.ts +67 -0
  14. package/dist/js/binder.d.ts.map +1 -0
  15. package/dist/js/changeBinding.d.ts +19 -0
  16. package/dist/js/changeBinding.d.ts.map +1 -0
  17. package/dist/js/commentWrapper.d.ts +39 -0
  18. package/dist/js/commentWrapper.d.ts.map +1 -0
  19. package/dist/js/config.d.ts +55 -0
  20. package/dist/js/config.d.ts.map +1 -0
  21. package/dist/js/createBindingOption.d.ts +32 -0
  22. package/dist/js/createBindingOption.d.ts.map +1 -0
  23. package/dist/js/createEventBinding.d.ts +10 -0
  24. package/dist/js/createEventBinding.d.ts.map +1 -0
  25. package/dist/js/cssBinding.d.ts +15 -0
  26. package/dist/js/cssBinding.d.ts.map +1 -0
  27. package/dist/js/dataBind.js +2772 -2519
  28. package/dist/js/dataBind.min.js +8 -1
  29. package/dist/js/dataBind.min.js.map +1 -1
  30. package/dist/js/domWalker.d.ts +9 -0
  31. package/dist/js/domWalker.d.ts.map +1 -0
  32. package/dist/js/forOfBinding.d.ts +12 -0
  33. package/dist/js/forOfBinding.d.ts.map +1 -0
  34. package/dist/js/hoverBinding.d.ts +13 -0
  35. package/dist/js/hoverBinding.d.ts.map +1 -0
  36. package/dist/js/ifBinding.d.ts +12 -0
  37. package/dist/js/ifBinding.d.ts.map +1 -0
  38. package/dist/js/index.d.ts +10 -0
  39. package/dist/js/index.d.ts.map +1 -0
  40. package/dist/js/modelBinding.d.ts +12 -0
  41. package/dist/js/modelBinding.d.ts.map +1 -0
  42. package/dist/js/postProcess.d.ts +3 -0
  43. package/dist/js/postProcess.d.ts.map +1 -0
  44. package/dist/js/pubSub.d.ts +11 -0
  45. package/dist/js/pubSub.d.ts.map +1 -0
  46. package/dist/js/reactiveProxy.d.ts +28 -0
  47. package/dist/js/reactiveProxy.d.ts.map +1 -0
  48. package/dist/js/renderForOfBinding.d.ts +8 -0
  49. package/dist/js/renderForOfBinding.d.ts.map +1 -0
  50. package/dist/js/renderIfBinding.d.ts +22 -0
  51. package/dist/js/renderIfBinding.d.ts.map +1 -0
  52. package/dist/js/renderIteration.d.ts +16 -0
  53. package/dist/js/renderIteration.d.ts.map +1 -0
  54. package/dist/js/renderTemplate.d.ts +14 -0
  55. package/dist/js/renderTemplate.d.ts.map +1 -0
  56. package/dist/js/renderTemplatesBinding.d.ts +19 -0
  57. package/dist/js/renderTemplatesBinding.d.ts.map +1 -0
  58. package/dist/js/showBinding.d.ts +13 -0
  59. package/dist/js/showBinding.d.ts.map +1 -0
  60. package/dist/js/switchBinding.d.ts +13 -0
  61. package/dist/js/switchBinding.d.ts.map +1 -0
  62. package/dist/js/textBinding.d.ts +13 -0
  63. package/dist/js/textBinding.d.ts.map +1 -0
  64. package/dist/js/types/_escape.d.ts +14 -0
  65. package/dist/js/types/_escape.d.ts.map +1 -0
  66. package/dist/js/types/applyBinding.d.ts +11 -0
  67. package/dist/js/types/applyBinding.d.ts.map +1 -0
  68. package/dist/js/types/attrBinding.d.ts +12 -0
  69. package/dist/js/types/attrBinding.d.ts.map +1 -0
  70. package/dist/js/types/binder.d.ts +67 -0
  71. package/dist/js/types/binder.d.ts.map +1 -0
  72. package/dist/js/types/changeBinding.d.ts +19 -0
  73. package/dist/js/types/changeBinding.d.ts.map +1 -0
  74. package/dist/js/types/commentWrapper.d.ts +39 -0
  75. package/dist/js/types/commentWrapper.d.ts.map +1 -0
  76. package/dist/js/types/config.d.ts +55 -0
  77. package/dist/js/types/config.d.ts.map +1 -0
  78. package/dist/js/types/createBindingOption.d.ts +32 -0
  79. package/dist/js/types/createBindingOption.d.ts.map +1 -0
  80. package/dist/js/types/createEventBinding.d.ts +10 -0
  81. package/dist/js/types/createEventBinding.d.ts.map +1 -0
  82. package/dist/js/types/cssBinding.d.ts +15 -0
  83. package/dist/js/types/cssBinding.d.ts.map +1 -0
  84. package/dist/js/types/domWalker.d.ts +9 -0
  85. package/dist/js/types/domWalker.d.ts.map +1 -0
  86. package/dist/js/types/forOfBinding.d.ts +12 -0
  87. package/dist/js/types/forOfBinding.d.ts.map +1 -0
  88. package/dist/js/types/hoverBinding.d.ts +13 -0
  89. package/dist/js/types/hoverBinding.d.ts.map +1 -0
  90. package/dist/js/types/ifBinding.d.ts +12 -0
  91. package/dist/js/types/ifBinding.d.ts.map +1 -0
  92. package/dist/js/types/index.d.ts +10 -0
  93. package/dist/js/types/index.d.ts.map +1 -0
  94. package/dist/js/types/modelBinding.d.ts +12 -0
  95. package/dist/js/types/modelBinding.d.ts.map +1 -0
  96. package/dist/js/types/postProcess.d.ts +3 -0
  97. package/dist/js/types/postProcess.d.ts.map +1 -0
  98. package/dist/js/types/pubSub.d.ts +11 -0
  99. package/dist/js/types/pubSub.d.ts.map +1 -0
  100. package/dist/js/types/reactiveProxy.d.ts +28 -0
  101. package/dist/js/types/reactiveProxy.d.ts.map +1 -0
  102. package/dist/js/types/renderForOfBinding.d.ts +8 -0
  103. package/dist/js/types/renderForOfBinding.d.ts.map +1 -0
  104. package/dist/js/types/renderIfBinding.d.ts +22 -0
  105. package/dist/js/types/renderIfBinding.d.ts.map +1 -0
  106. package/dist/js/types/renderIteration.d.ts +16 -0
  107. package/dist/js/types/renderIteration.d.ts.map +1 -0
  108. package/dist/js/types/renderTemplate.d.ts +14 -0
  109. package/dist/js/types/renderTemplate.d.ts.map +1 -0
  110. package/dist/js/types/renderTemplatesBinding.d.ts +19 -0
  111. package/dist/js/types/renderTemplatesBinding.d.ts.map +1 -0
  112. package/dist/js/types/showBinding.d.ts +13 -0
  113. package/dist/js/types/showBinding.d.ts.map +1 -0
  114. package/dist/js/types/switchBinding.d.ts +13 -0
  115. package/dist/js/types/switchBinding.d.ts.map +1 -0
  116. package/dist/js/types/textBinding.d.ts +13 -0
  117. package/dist/js/types/textBinding.d.ts.map +1 -0
  118. package/dist/js/types/types.d.ts +111 -0
  119. package/dist/js/types/types.d.ts.map +1 -0
  120. package/dist/js/types/util.d.ts +119 -0
  121. package/dist/js/types/util.d.ts.map +1 -0
  122. package/dist/js/types.d.ts +111 -0
  123. package/dist/js/types.d.ts.map +1 -0
  124. package/dist/js/util.d.ts +119 -0
  125. package/dist/js/util.d.ts.map +1 -0
  126. package/eslint.config.js +124 -0
  127. package/examples/DBMONSTER_COMPARISON.md +123 -0
  128. package/examples/afterRenderDemo.html +119 -0
  129. package/examples/bootstrap/css/animate.css +1579 -1579
  130. package/examples/bootstrap/css/bootstrap.min.css +6 -6
  131. package/examples/bootstrap/css/homeservices.css +378 -390
  132. package/examples/bootstrap/css/open-iconic.css +511 -511
  133. package/examples/bootstrap/fonts/open-iconic.svg +543 -543
  134. package/examples/bootstrap/js/compMessageDialog.js +20 -19
  135. package/examples/bootstrap/js/compSearchBar.js +12 -19
  136. package/examples/bootstrap/js/compSearchResults.js +50 -46
  137. package/examples/bootstrap/js/featureAdsResult.json +65 -65
  138. package/examples/bootstrap/js/searchResult.json +57 -57
  139. package/examples/bootstrap.html +343 -332
  140. package/examples/css/baseTodo.css +141 -141
  141. package/examples/css/dbMonsterStyles.css +27 -27
  142. package/examples/css/indexTodo.css +374 -374
  143. package/examples/dbmonsterForOfReactive.html +40 -0
  144. package/examples/dbmonsterReact.html +19 -0
  145. package/examples/forOfBindingSimpleDebug.html +45 -0
  146. package/examples/form.html +20 -4
  147. package/examples/globalConfig.html +131 -0
  148. package/examples/js/afterRenderDemo.js +190 -0
  149. package/examples/js/appTodo.js +46 -46
  150. package/examples/js/attrBindingDemo.js +2 -2
  151. package/examples/js/dbMonApp.js +24 -26
  152. package/examples/js/dbMonAppReact.jsx +79 -0
  153. package/examples/js/dbMonAppReactive.js +28 -0
  154. package/examples/js/fiberDemo.js +4 -4
  155. package/examples/js/filtersDemo.js +8 -8
  156. package/examples/js/forOfDemo.js +7 -9
  157. package/examples/js/forOfDemoComplex.js +44 -17
  158. package/examples/js/form.js +44 -12
  159. package/examples/js/globalConfig.js +117 -0
  160. package/examples/js/ifBindingDemo.js +16 -16
  161. package/examples/js/reactiveDemo.js +119 -0
  162. package/examples/js/switchBindingDemo.js +8 -8
  163. package/examples/react-dbmonster/dist/bundle.js +43 -0
  164. package/examples/react-dbmonster/package-lock.json +537 -0
  165. package/examples/react-dbmonster/package.json +16 -0
  166. package/examples/react-dbmonster/src/index.jsx +80 -0
  167. package/examples/reactiveDemo.html +127 -0
  168. package/examples/refreshRateTest.html +75 -75
  169. package/index.html +841 -0
  170. package/package.json +31 -34
  171. package/rollup.config.js +79 -36
  172. package/src/{_escape.js → _escape.ts} +19 -17
  173. package/src/applyBinding.ts +179 -0
  174. package/src/{attrBinding.js → attrBinding.ts} +14 -13
  175. package/src/binder.ts +289 -0
  176. package/src/changeBinding.ts +93 -0
  177. package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
  178. package/src/config.ts +107 -0
  179. package/src/createBindingOption.ts +91 -0
  180. package/src/createEventBinding.ts +88 -0
  181. package/src/{cssBinding.js → cssBinding.ts} +13 -11
  182. package/src/{domWalker.js → domWalker.ts} +44 -30
  183. package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
  184. package/src/hoverBinding.ts +84 -0
  185. package/src/{ifBinding.js → ifBinding.ts} +14 -12
  186. package/src/index.ts +53 -0
  187. package/src/{modelBinding.js → modelBinding.ts} +11 -9
  188. package/src/postProcess.ts +22 -0
  189. package/src/{pubSub.js → pubSub.ts} +24 -15
  190. package/src/reactiveProxy.ts +285 -0
  191. package/src/{renderForOfBinding.js → renderForOfBinding.ts} +55 -33
  192. package/src/{renderIfBinding.js → renderIfBinding.ts} +45 -20
  193. package/src/renderIteration.ts +53 -0
  194. package/src/renderTemplate.ts +165 -0
  195. package/src/renderTemplatesBinding.ts +73 -0
  196. package/src/{showBinding.js → showBinding.ts} +4 -3
  197. package/src/{switchBinding.js → switchBinding.ts} +18 -15
  198. package/src/{textBinding.js → textBinding.ts} +5 -4
  199. package/src/types.ts +124 -0
  200. package/src/util.ts +810 -0
  201. package/test/css/reporter.css +9 -9
  202. package/test/fixtures/dataBindBootstrap.html +2 -2
  203. package/test/fixtures/formBindings.html +9 -1
  204. package/test/globals.d.ts +19 -0
  205. package/test/helpers/testHelper.js +46 -11
  206. package/test/mocks/featureAdsResult.json +65 -65
  207. package/test/mocks/searchResult.json +57 -57
  208. package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
  209. package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
  210. package/test/specs/blurBinding.spec.ts +60 -0
  211. package/test/specs/chainableUse.spec.ts +125 -0
  212. package/test/specs/clickBinding.spec.ts +194 -0
  213. package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
  214. package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
  215. package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
  216. package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
  217. package/test/specs/formBinding.spec.ts +272 -0
  218. package/test/specs/ifBinding.spec.ts +165 -0
  219. package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
  220. package/test/specs/reactiveProxy.spec.ts +465 -0
  221. package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
  222. package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
  223. package/test/specs/templateBinding.spec.ts +273 -0
  224. package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
  225. package/test/tsconfig.json +31 -0
  226. package/test-output.txt +200 -0
  227. package/test-reactive.html +224 -0
  228. package/tsconfig.json +28 -0
  229. package/vendors/lodash.custom.js +4577 -4577
  230. package/vendors/lodash.custom.min.js +45 -45
  231. package/vitest.config.js +27 -0
  232. package/.eslintrc.js +0 -1
  233. package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
  234. package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
  235. package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
  236. package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
  237. package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
  238. package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
  239. package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
  240. package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
  241. package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
  242. package/coverage/coverage.json +0 -1
  243. package/coverage/lcov/lcov-report/base.css +0 -213
  244. package/coverage/lcov/lcov-report/index.html +0 -93
  245. package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
  246. package/coverage/lcov/lcov-report/js/index.html +0 -93
  247. package/coverage/lcov/lcov-report/prettify.css +0 -1
  248. package/coverage/lcov/lcov-report/prettify.js +0 -1
  249. package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
  250. package/coverage/lcov/lcov-report/sorter.js +0 -158
  251. package/coverage/lcov/lcov.info +0 -1991
  252. package/eslintrc.json +0 -40
  253. package/examples/bootstrap/js/bootstrap.min.js +0 -6
  254. package/examples/bootstrap/js/popper.min.js +0 -5
  255. package/examples/bootstrap/js/searchSuggestion.js +0 -58
  256. package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
  257. package/gruntfile.js +0 -92
  258. package/gulpfile.js +0 -32
  259. package/src/binder.js +0 -422
  260. package/src/changeBinding.js +0 -57
  261. package/src/config.js +0 -65
  262. package/src/createBindingOption.js +0 -66
  263. package/src/createEventBinding.js +0 -46
  264. package/src/eventSystem.js +0 -46
  265. package/src/hoverBinding.js +0 -57
  266. package/src/index.js +0 -26
  267. package/src/renderTemplate.js +0 -128
  268. package/src/util.js +0 -648
  269. package/test/specs/blurBinding.spec.js +0 -57
  270. package/test/specs/formBinding.spec.js +0 -292
  271. package/test/specs/ifBinding.spec.js +0 -169
  272. package/test/specs/templateBinding.spec.js +0 -117
  273. package/vendors/jasmine-jquery.js +0 -841
  274. package/vendors/jquery-3.2.1.min.js +0 -4
@@ -0,0 +1,91 @@
1
+ import {
2
+ bindingUpdateConditions,
3
+ } from './config';
4
+ import {extend} from './util';
5
+
6
+ export interface BindingOption {
7
+ templateBinding?: boolean;
8
+ textBinding?: boolean;
9
+ cssBinding?: boolean;
10
+ ifBinding?: boolean;
11
+ showBinding?: boolean;
12
+ modelBinding?: boolean;
13
+ attrBinding?: boolean;
14
+ forOfBinding?: boolean;
15
+ switchBinding?: boolean;
16
+ changeBinding?: boolean;
17
+ clickBinding?: boolean;
18
+ dblclickBinding?: boolean;
19
+ blurBinding?: boolean;
20
+ focusBinding?: boolean;
21
+ hoverBinding?: boolean;
22
+ inputBinding?: boolean;
23
+ submitBinding?: boolean;
24
+ forceRender?: boolean;
25
+ [key: string]: unknown;
26
+ }
27
+
28
+ /**
29
+ * createBindingOption
30
+ * @param {string} condition
31
+ * @param {object} opt
32
+ * @description
33
+ * generate binding update option object by condition
34
+ * @return {object} updateOption
35
+ */
36
+ const createBindingOption = (condition: string = '', opt: BindingOption = {}): BindingOption => {
37
+ const visualBindingOptions: BindingOption = {
38
+ templateBinding: false,
39
+ textBinding: true,
40
+ cssBinding: true,
41
+ ifBinding: true,
42
+ showBinding: true,
43
+ modelBinding: true,
44
+ attrBinding: true,
45
+ forOfBinding: true,
46
+ switchBinding: true,
47
+ };
48
+ const eventsBindingOptions: BindingOption = {
49
+ changeBinding: true,
50
+ clickBinding: true,
51
+ dblclickBinding: true,
52
+ blurBinding: true,
53
+ focusBinding: true,
54
+ hoverBinding: true,
55
+ inputBinding: true,
56
+ submitBinding: true,
57
+ };
58
+ // this is visualBindingOptions but everything false
59
+ // concrete declear for performance purpose
60
+ const serverRenderedOptions: BindingOption = {
61
+ templateBinding: false,
62
+ textBinding: false,
63
+ cssBinding: false,
64
+ ifBinding: false,
65
+ showBinding: false,
66
+ modelBinding: false,
67
+ attrBinding: false,
68
+ forOfBinding: false,
69
+ switchBinding: false,
70
+ };
71
+ let updateOption: BindingOption = {};
72
+
73
+ switch (condition) {
74
+ case bindingUpdateConditions.serverRendered:
75
+ updateOption = extend(false, {}, eventsBindingOptions, serverRenderedOptions, opt);
76
+ break;
77
+ case bindingUpdateConditions.init:
78
+ // flag templateBinding to true to render tempalte(s)
79
+ opt.templateBinding = true;
80
+ opt.forceRender = true;
81
+ updateOption = extend(false, {}, visualBindingOptions, eventsBindingOptions, opt);
82
+ break;
83
+ default:
84
+ // when called again only update visualBinding options
85
+ updateOption = extend(false, {}, visualBindingOptions, opt);
86
+ }
87
+
88
+ return updateOption;
89
+ };
90
+
91
+ export default createBindingOption;
@@ -0,0 +1,88 @@
1
+ import {
2
+ getFormData,
3
+ getViewModelValue,
4
+ resolveViewModelContext,
5
+ resolveParamList,
6
+ } from './util';
7
+ import type {BindingCache, ViewModel} from './types';
8
+
9
+ /**
10
+ * Create event handler wrapper
11
+ */
12
+ const createEventHandlerWrapper = (
13
+ type: string,
14
+ paramList: unknown[],
15
+ handlerFn: Function,
16
+ viewModelContext: ViewModel,
17
+ ): EventListener => {
18
+ return function handlerWrap(e: Event): void {
19
+ let formData: Record<string, unknown>;
20
+ let args: unknown[] = [];
21
+ if (type === 'submit') {
22
+ formData = getFormData(e.currentTarget as HTMLFormElement);
23
+ args = [e, e.currentTarget, formData, ...paramList];
24
+ } else {
25
+ args = [e, e.currentTarget, ...paramList];
26
+ }
27
+ handlerFn.apply(viewModelContext, args);
28
+ };
29
+ };
30
+
31
+ interface CreateEventBindingParams {
32
+ cache?: BindingCache;
33
+ forceRender?: boolean;
34
+ type?: string;
35
+ viewModel?: ViewModel;
36
+ }
37
+
38
+ const createEventBinding = ({
39
+ cache = {} as BindingCache,
40
+ forceRender = false,
41
+ type = '',
42
+ viewModel = {} as ViewModel,
43
+ }: CreateEventBindingParams): void => {
44
+ const handlerName = cache.dataKey;
45
+ let paramList = cache.parameters;
46
+ let viewModelContext: ViewModel;
47
+ const APP = viewModel.APP || viewModel.$root?.APP;
48
+ const rootElement = APP?.$rootElement as HTMLElement | undefined;
49
+
50
+ if (!type || !handlerName || (!forceRender && rootElement && !rootElement.contains(cache.el))) {
51
+ return;
52
+ }
53
+
54
+ const handlerFn = getViewModelValue(viewModel, handlerName);
55
+
56
+ if (typeof handlerFn === 'function') {
57
+ viewModelContext = resolveViewModelContext(viewModel, handlerName);
58
+ paramList = paramList ? resolveParamList(viewModel, paramList) : [];
59
+
60
+ // Store handler key for this event type on the DOM element itself
61
+ // This prevents duplicate handlers even if multiple cache objects exist for same element
62
+ const handlerKey = `_db_${type}Handler`;
63
+ const el = cache.el as HTMLElement & Record<string, unknown>;
64
+
65
+ // Check if handler already exists and skip if it's the same
66
+ // This prevents adding duplicate handlers when the same element is processed multiple times
67
+ if (el[handlerKey]) {
68
+ // Handler already exists, remove it before adding new one
69
+ el.removeEventListener(type, el[handlerKey] as EventListener, false);
70
+ }
71
+
72
+ // Create new handler wrapper
73
+ const handlerWrap = createEventHandlerWrapper(
74
+ type,
75
+ paramList,
76
+ handlerFn,
77
+ viewModelContext,
78
+ );
79
+
80
+ // Store the handler on the DOM element so we can remove it later
81
+ el[handlerKey] = handlerWrap;
82
+
83
+ // Add the new event listener
84
+ el.addEventListener(type, handlerWrap, false);
85
+ }
86
+ };
87
+
88
+ export default createEventBinding;
@@ -4,6 +4,7 @@ import {
4
4
  arrayRemoveMatch,
5
5
  each,
6
6
  } from './util';
7
+ import type {BindingCache, ViewModel, BindingAttrs, PlainObject} from './types';
7
8
 
8
9
  /**
9
10
  * cssBinding
@@ -16,11 +17,11 @@ import {
16
17
  * @param {object} bindingAttrs
17
18
  * @param {boolean} forceRender
18
19
  */
19
- const cssBinding = (cache, viewModel, bindingAttrs, forceRender) => {
20
+ const cssBinding = (cache: BindingCache, viewModel: ViewModel, bindingAttrs: BindingAttrs, forceRender: boolean): void => {
20
21
  const dataKey = cache.dataKey;
21
- const APP = viewModel.APP || viewModel.$root.APP;
22
+ const APP = viewModel.APP || viewModel.$root?.APP;
22
23
 
23
- if (!dataKey || (!forceRender && !APP.$rootElement.contains(cache.el))) {
24
+ if (!dataKey || (!forceRender && !(APP?.$rootElement as HTMLElement)?.contains(cache.el))) {
24
25
  return;
25
26
  }
26
27
 
@@ -30,10 +31,10 @@ const cssBinding = (cache, viewModel, bindingAttrs, forceRender) => {
30
31
  const oldCssList = cache.elementData.viewModelPropValue;
31
32
  let newCssList = '';
32
33
  const vmCssListObj = getViewModelPropValue(viewModel, cache);
33
- let vmCssListArray = [];
34
+ let vmCssListArray: string[] = [];
34
35
  let isViewDataObject = false;
35
36
  let isViewDataString = false;
36
- let cssList = [];
37
+ let cssList: string[] = [];
37
38
 
38
39
  if (typeof vmCssListObj === 'string') {
39
40
  isViewDataString = true;
@@ -47,7 +48,7 @@ const cssBinding = (cache, viewModel, bindingAttrs, forceRender) => {
47
48
  if (isViewDataObject) {
48
49
  newCssList = JSON.stringify(vmCssListObj);
49
50
  } else {
50
- newCssList = vmCssListObj.replace(/\s\s+/g, ' ').trim();
51
+ newCssList = (vmCssListObj as string).replace(/\s\s+/g, ' ').trim();
51
52
  vmCssListArray = newCssList.split(' ');
52
53
  }
53
54
  // reject if nothing changed
@@ -64,7 +65,7 @@ const cssBinding = (cache, viewModel, bindingAttrs, forceRender) => {
64
65
  }
65
66
 
66
67
  if (isViewDataObject) {
67
- each(vmCssListObj, function(k, v) {
68
+ each(vmCssListObj as PlainObject, (k: string, v: unknown) => {
68
69
  const i = cssList.indexOf(k);
69
70
  if (v === true) {
70
71
  cssList.push(k);
@@ -74,20 +75,21 @@ const cssBinding = (cache, viewModel, bindingAttrs, forceRender) => {
74
75
  });
75
76
  } else if (isViewDataString) {
76
77
  // remove oldCssList items from cssList
77
- cssList = arrayRemoveMatch(cssList, oldCssList);
78
+ const oldCssArray = typeof oldCssList === 'string' && oldCssList ? oldCssList.split(' ') : [];
79
+ cssList = arrayRemoveMatch(cssList, oldCssArray) as string[];
78
80
  cssList = cssList.concat(vmCssListArray);
79
81
  }
80
82
 
81
83
  // unique cssList array
82
- cssList = cssList.filter((v, i, a) => {
84
+ cssList = cssList.filter((v: string, i: number, a: string[]) => {
83
85
  return a.indexOf(v) === i;
84
86
  });
85
87
 
86
- cssList = cssList.join(' ');
88
+ const cssListString = cssList.join(' ');
87
89
  // update element data
88
90
  cache.elementData.viewModelPropValue = newCssList;
89
91
  // replace all css classes
90
- cache.el.setAttribute('class', cssList);
92
+ cache.el.setAttribute('class', cssListString);
91
93
  };
92
94
 
93
95
  export default cssBinding;
@@ -1,7 +1,8 @@
1
1
  import {invertObj, extractFilterList, getFunctionParameterList, REGEX} from './util';
2
2
  import {constants} from './config';
3
+ import type {PlainObject, BindingAttrs, ElementCache, BindingCache} from './types';
3
4
 
4
- let bindingAttrsMap;
5
+ let bindingAttrsMap: PlainObject | undefined;
5
6
 
6
7
  /**
7
8
  * walkDOM
@@ -10,47 +11,52 @@ let bindingAttrsMap;
10
11
  * @param {object} node
11
12
  * @param {function} func
12
13
  */
13
- const walkDOM = (node, func) => {
14
+ const walkDOM = (node: HTMLElement, func: (node: HTMLElement) => boolean): void => {
14
15
  let parseChildNode = true;
15
- node = node.firstElementChild;
16
- while (node) {
17
- parseChildNode = func(node);
16
+ let currentNode = node.firstElementChild as HTMLElement | null;
17
+ while (currentNode) {
18
+ parseChildNode = func(currentNode);
18
19
  if (parseChildNode) {
19
- walkDOM(node, func);
20
+ walkDOM(currentNode, func);
20
21
  }
21
- node = node.nextElementSibling;
22
+ currentNode = currentNode.nextElementSibling as HTMLElement | null;
22
23
  }
23
24
  };
24
25
 
25
- const getAttributesObject = (node) => {
26
- const ret = {};
27
- Array.prototype.slice.call(node.attributes).forEach((item) => {
26
+ const getAttributesObject = (node: HTMLElement): PlainObject => {
27
+ const ret: PlainObject = {};
28
+ Array.prototype.slice.call(node.attributes).forEach((item: Attr) => {
28
29
  ret[item.name] = item.value;
29
30
  });
30
31
  return ret;
31
32
  };
32
33
 
33
- const checkSkipChildParseBindings = (attrObj = {}, bindingAttrs) => {
34
- return [bindingAttrs.forOf, bindingAttrs.if, bindingAttrs.case, bindingAttrs.default].filter((type) => {
34
+ const checkSkipChildParseBindings = (attrObj: PlainObject = {}, bindingAttrs: BindingAttrs): string[] => {
35
+ return [bindingAttrs.forOf, bindingAttrs.if, bindingAttrs.case, bindingAttrs.default].filter((type: string) => {
35
36
  return typeof attrObj[type] !== 'undefined';
36
37
  });
37
38
  };
38
39
 
39
- const rootSkipCheck = (node) => {
40
+ const rootSkipCheck = (node: HTMLElement): boolean => {
40
41
  return node.tagName === 'SVG';
41
42
  };
42
43
 
43
- const defaultSkipCheck = (node, bindingAttrs) => {
44
+ const defaultSkipCheck = (node: HTMLElement, bindingAttrs: BindingAttrs): boolean => {
44
45
  return node.tagName === 'SVG' || node.hasAttribute(bindingAttrs.comp);
45
46
  };
46
47
 
47
- const populateBindingCache = ({node, attrObj, bindingCache, type}) => {
48
- let attrValue;
49
- let cacheData;
48
+ const populateBindingCache = ({node, attrObj, bindingCache, type}: {
49
+ node: HTMLElement;
50
+ attrObj: PlainObject;
51
+ bindingCache: ElementCache;
52
+ type: string;
53
+ }): ElementCache => {
54
+ let attrValue: string;
55
+ let cacheData: Partial<BindingCache>;
50
56
 
51
57
  if (bindingAttrsMap && bindingAttrsMap[type] && typeof attrObj[type] !== 'undefined') {
52
58
  bindingCache[type] = bindingCache[type] || [];
53
- attrValue = attrObj[type] || '';
59
+ attrValue = (attrObj[type] as string) || '';
54
60
 
55
61
  if (attrValue) {
56
62
  attrValue = attrValue.replace(REGEX.LINE_BREAKS_TABS, '').replace(REGEX.WHITE_SPACES, ' ').trim();
@@ -67,28 +73,36 @@ const populateBindingCache = ({node, attrObj, bindingCache, type}) => {
67
73
  // populate cacheData.parameters
68
74
  // for store function call parameters eg. '$index', '$root'
69
75
  // useful with DOM for-loop template as reference to binding data
70
- const paramList = getFunctionParameterList(cacheData.dataKey);
76
+ const paramList = getFunctionParameterList(cacheData.dataKey || '');
71
77
  if (paramList) {
72
78
  cacheData.parameters = paramList;
73
- cacheData.dataKey = cacheData.dataKey.replace(REGEX.FUNCTION_PARAM, '').trim();
79
+ cacheData.dataKey = (cacheData.dataKey || '').replace(REGEX.FUNCTION_PARAM, '').trim();
74
80
  }
75
81
  // store parent array reference to cacheData
76
82
  cacheData[constants.PARENT_REF] = bindingCache[type];
77
- bindingCache[type].push(cacheData);
83
+ bindingCache[type].push(cacheData as BindingCache);
78
84
  }
79
85
  return bindingCache;
80
86
  };
81
87
 
82
- const createBindingCache = ({rootNode = null, bindingAttrs = {}, skipCheck, isRenderedTemplate = false}) => {
83
- let bindingCache = {};
88
+ const createBindingCache = ({rootNode = null, bindingAttrs = {} as BindingAttrs, skipCheck, isRenderedTemplate = false}: {
89
+ rootNode?: HTMLElement | null;
90
+ bindingAttrs?: BindingAttrs;
91
+ skipCheck?: (node: HTMLElement) => boolean;
92
+ isRenderedTemplate?: boolean;
93
+ }): ElementCache => {
94
+ let bindingCache: ElementCache = {};
84
95
 
85
- if (!rootNode instanceof window.Node) {
96
+ if (!(rootNode instanceof window.Node)) {
86
97
  throw new TypeError('walkDOM: Expected a DOM node');
87
98
  }
88
99
 
89
100
  bindingAttrsMap = bindingAttrsMap || invertObj(bindingAttrs);
90
101
 
91
- const parseNode = (node, skipNodeCheckFn = defaultSkipCheck) => {
102
+ const parseNode = (
103
+ node: HTMLElement,
104
+ skipNodeCheckFn: (node: HTMLElement, bindingAttrs: BindingAttrs) => boolean = defaultSkipCheck,
105
+ ): boolean => {
92
106
  let isSkipForOfChild = false;
93
107
 
94
108
  if (node.nodeType !== 1 || !node.hasAttributes()) {
@@ -102,7 +116,7 @@ const createBindingCache = ({rootNode = null, bindingAttrs = {}, skipCheck, isRe
102
116
  // skip same element that has forOf binding the forOf is alredy parsed
103
117
  const attrObj = getAttributesObject(node);
104
118
  const hasSkipChildParseBindings = checkSkipChildParseBindings(attrObj, bindingAttrs);
105
- let iterateList = [];
119
+ let iterateList: string[] = [];
106
120
 
107
121
  if (hasSkipChildParseBindings.length) {
108
122
  isSkipForOfChild = true;
@@ -114,13 +128,13 @@ const createBindingCache = ({rootNode = null, bindingAttrs = {}, skipCheck, isRe
114
128
  iterateList = Object.keys(attrObj);
115
129
  }
116
130
 
117
- iterateList.forEach((key) => {
131
+ iterateList.forEach((key: string) => {
118
132
  // skip for switch case and default bining
119
133
  if (key !== bindingAttrs.case && key !== bindingAttrs.default) {
120
134
  bindingCache = populateBindingCache({
121
- node: node,
122
- attrObj: attrObj,
123
- bindingCache: bindingCache,
135
+ node,
136
+ attrObj,
137
+ bindingCache,
124
138
  type: key,
125
139
  });
126
140
  }
@@ -1,6 +1,7 @@
1
1
  import {maxDatakeyLength} from './config';
2
2
  import {REGEX} from './util';
3
3
  import renderForOfBinding from './renderForOfBinding';
4
+ import type {BindingCache, ViewModel, BindingAttrs} from './types';
4
5
 
5
6
  /**
6
7
  * forOfBinding
@@ -10,7 +11,7 @@ import renderForOfBinding from './renderForOfBinding';
10
11
  * @param {object} viewModel
11
12
  * @param {object} bindingAttrs
12
13
  */
13
- const forOfBinding = (cache, viewModel, bindingAttrs) => {
14
+ const forOfBinding = (cache: BindingCache, viewModel: ViewModel, bindingAttrs: BindingAttrs, _forceRender?: boolean): void => {
14
15
  const dataKey = cache.dataKey;
15
16
 
16
17
  if (!dataKey || dataKey.length > maxDatakeyLength) {
@@ -42,8 +43,8 @@ const forOfBinding = (cache, viewModel, bindingAttrs) => {
42
43
 
43
44
  renderForOfBinding({
44
45
  bindingData: cache,
45
- viewModel: viewModel,
46
- bindingAttrs: bindingAttrs,
46
+ viewModel,
47
+ bindingAttrs,
47
48
  });
48
49
  };
49
50
 
@@ -0,0 +1,84 @@
1
+
2
+ import {bindingDataReference} from './config';
3
+ import {
4
+ getViewModelValue,
5
+ resolveViewModelContext,
6
+ resolveParamList,
7
+ } from './util';
8
+ import type {BindingCache, ViewModel, BindingAttrs, PlainObject} from './types';
9
+
10
+ /**
11
+ * Create mouse enter handler
12
+ */
13
+ const createMouseEnterHandler = (
14
+ cache: BindingCache,
15
+ handlers: PlainObject,
16
+ inHandlerName: string,
17
+ viewModelContext: ViewModel,
18
+ paramList: unknown[],
19
+ ): (e: MouseEvent) => void => {
20
+ return function onMouseEnterHandler(e: MouseEvent) {
21
+ const args = [e, cache.el, ...paramList];
22
+ (handlers[inHandlerName] as Function).apply(viewModelContext, args);
23
+ };
24
+ };
25
+
26
+ /**
27
+ * Create mouse leave handler
28
+ */
29
+ const createMouseLeaveHandler = (
30
+ cache: BindingCache,
31
+ handlers: PlainObject,
32
+ outHandlerName: string,
33
+ viewModelContext: ViewModel,
34
+ paramList: unknown[],
35
+ ): (e: MouseEvent) => void => {
36
+ return function onMouseLeaveHandler(e: MouseEvent) {
37
+ const args = [e, cache.el, ...paramList];
38
+ (handlers[outHandlerName] as Function).apply(viewModelContext, args);
39
+ };
40
+ };
41
+
42
+ /**
43
+ * hoverBinding
44
+ * DOM decleartive on hover event binding
45
+ * event handler bind to viewModel method according to the DOM attribute
46
+ * @param {object} cache
47
+ * @param {object} viewModel
48
+ * @param {object} bindingAttrs
49
+ * @param {boolean} forceRender
50
+ */
51
+ const hoverBinding = (cache: BindingCache, viewModel: ViewModel, bindingAttrs: BindingAttrs, forceRender: boolean): void => {
52
+ const handlerName = cache.dataKey;
53
+ let paramList = cache.parameters;
54
+ const inHandlerName = bindingDataReference.mouseEnterHandlerName;
55
+ const outHandlerName = bindingDataReference.mouseLeaveHandlerName;
56
+ let viewModelContext: ViewModel;
57
+ const APP = viewModel.APP || viewModel.$root?.APP;
58
+
59
+ cache.elementData = cache.elementData || {};
60
+
61
+ // TODO: check what is APP.$rootElement.contains(cache.el)
62
+ const rootElement = APP?.$rootElement as HTMLElement | undefined;
63
+ if (!handlerName || (!forceRender && rootElement && !rootElement.contains(cache.el))) {
64
+ return;
65
+ }
66
+
67
+ const handlers = getViewModelValue(viewModel, handlerName) as PlainObject;
68
+
69
+ if (handlers && typeof handlers[inHandlerName] === 'function' && typeof handlers[outHandlerName] === 'function') {
70
+ viewModelContext = resolveViewModelContext(viewModel, handlerName);
71
+ paramList = paramList ? resolveParamList(viewModel, paramList) : [];
72
+
73
+ const onMouseEnterHandler = createMouseEnterHandler(cache, handlers as PlainObject, inHandlerName, viewModelContext, paramList);
74
+ const onMouseLeaveHandler = createMouseLeaveHandler(cache, handlers as PlainObject, outHandlerName, viewModelContext, paramList);
75
+
76
+ cache.el.removeEventListener('mouseenter', onMouseEnterHandler, false);
77
+ cache.el.removeEventListener('mouseleave', onMouseLeaveHandler, false);
78
+
79
+ cache.el.addEventListener('mouseenter', onMouseEnterHandler, false);
80
+ cache.el.addEventListener('mouseleave', onMouseLeaveHandler, false);
81
+ }
82
+ };
83
+
84
+ export default hoverBinding;
@@ -2,6 +2,7 @@ import {bindingAttrs as configBindingAttrs, constants} from './config';
2
2
  import {getViewModelPropValue, removeElement} from './util';
3
3
  import {createClonedElementCache, wrapCommentAround} from './commentWrapper';
4
4
  import {renderIfBinding, removeIfBinding} from './renderIfBinding';
5
+ import type {BindingCache, ViewModel, BindingAttrs} from './types';
5
6
 
6
7
  /**
7
8
  * if-Binding
@@ -11,7 +12,7 @@ import {renderIfBinding, removeIfBinding} from './renderIfBinding';
11
12
  * @param {object} viewModel
12
13
  * @param {object} bindingAttrs
13
14
  */
14
- const ifBinding = (cache, viewModel, bindingAttrs) => {
15
+ const ifBinding = (cache: BindingCache, viewModel: ViewModel, bindingAttrs: BindingAttrs, _forceRender?: boolean): void => {
15
16
  const dataKey = cache.dataKey;
16
17
 
17
18
  // isOnce only return if there is no child bindings
@@ -38,8 +39,8 @@ const ifBinding = (cache, viewModel, bindingAttrs) => {
38
39
  removeElement(cache.el);
39
40
  // delete cache.fragment;
40
41
  removeBindingInQueue({
41
- viewModel: viewModel,
42
- cache: cache,
42
+ viewModel,
43
+ cache,
43
44
  });
44
45
  return;
45
46
  }
@@ -63,8 +64,8 @@ const ifBinding = (cache, viewModel, bindingAttrs) => {
63
64
  // render element
64
65
  renderIfBinding({
65
66
  bindingData: cache,
66
- viewModel: viewModel,
67
- bindingAttrs: bindingAttrs,
67
+ viewModel,
68
+ bindingAttrs,
68
69
  });
69
70
 
70
71
  // if render once
@@ -72,20 +73,21 @@ const ifBinding = (cache, viewModel, bindingAttrs) => {
72
73
  if (cache.isOnce && !cache.hasIterationBindingCache) {
73
74
  // delete cache.fragment;
74
75
  removeBindingInQueue({
75
- viewModel: viewModel,
76
- cache: cache,
76
+ viewModel,
77
+ cache,
77
78
  });
78
79
  }
79
80
  }
80
81
  };
81
82
 
82
- const removeBindingInQueue = ({viewModel, cache}) => {
83
+ const removeBindingInQueue = ({viewModel, cache}: {viewModel: ViewModel; cache: BindingCache}): boolean => {
83
84
  let ret = false;
84
- if (viewModel.APP.postProcessQueue) {
85
+ if (viewModel.APP?.postProcessQueue) {
86
+ const parentRef = cache[constants.PARENT_REF] as BindingCache[];
85
87
  viewModel.APP.postProcessQueue.push(
86
- ((cache, index) => () => {
87
- cache[constants.PARENT_REF].splice(index, 1);
88
- })(cache, cache[constants.PARENT_REF].indexOf(cache)),
88
+ ((cache: BindingCache, index: number) => () => {
89
+ parentRef.splice(index, 1);
90
+ })(cache, parentRef.indexOf(cache)),
89
91
  );
90
92
  ret = true;
91
93
  }
package/src/index.ts ADDED
@@ -0,0 +1,53 @@
1
+ import * as config from './config';
2
+ import {extend} from './util';
3
+ import Binder from './binder';
4
+ import type {PlainObject, ViewModel, BindingAttrs, BinderOptions} from './types';
5
+
6
+ const isSupportPromise = typeof window['Promise'] === 'function';
7
+
8
+ let bindingAttrs = config.bindingAttrs;
9
+
10
+ // Global default options for all Binder instances
11
+ const defaultOptions: BinderOptions = {
12
+ reactive: true, // Enable reactive mode by default
13
+ };
14
+
15
+ interface DataBindAPI {
16
+ use: (settings: PlainObject) => DataBindAPI;
17
+ init: ($rootElement: HTMLElement, viewModel: ViewModel | null, options?: BinderOptions) => Binder | void;
18
+ version: string;
19
+ }
20
+
21
+ const use = (settings: PlainObject = {}): DataBindAPI => {
22
+ if (settings.bindingAttrs) {
23
+ bindingAttrs = extend(false, {}, settings.bindingAttrs as PlainObject) as unknown as typeof config.bindingAttrs;
24
+ }
25
+ // Allow setting global reactive option
26
+ if (typeof settings.reactive === 'boolean') {
27
+ defaultOptions.reactive = settings.reactive;
28
+ }
29
+ // Allow setting global trackChanges option
30
+ if (typeof settings.trackChanges === 'boolean') {
31
+ defaultOptions.trackChanges = settings.trackChanges;
32
+ }
33
+ // Return API for chaining
34
+ return api;
35
+ };
36
+
37
+ const init = ($rootElement: HTMLElement, viewModel: ViewModel | null = null, options?: BinderOptions): Binder | void => {
38
+ if (!isSupportPromise) {
39
+ return console.warn('Browser not support Promise');
40
+ }
41
+ // Merge global defaults with instance-specific options
42
+ // Instance options take precedence over global defaults
43
+ const mergedOptions = {...defaultOptions, ...options};
44
+ return new Binder($rootElement, viewModel, bindingAttrs as unknown as BindingAttrs, mergedOptions);
45
+ };
46
+
47
+ const api: DataBindAPI = {
48
+ use,
49
+ init,
50
+ version: '@version@',
51
+ };
52
+
53
+ export default api;