@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
package/src/util.js DELETED
@@ -1,648 +0,0 @@
1
- import * as config from './config';
2
-
3
- const hasIsArray = Array.isArray;
4
-
5
- const REGEX = {
6
- BAD_TAGS: /<(script|del)(?=[\s>])[\w\W]*?<\/\1\s*>/ig,
7
- FOR_OF: /(.*?)\s+(?:in|of)\s+(.*)/,
8
- FUNCTION_PARAM: /\((.*?)\)/,
9
- HTML_TAG: /^[\s]*<([a-z][^\/\s>]+)/i,
10
- OBJECT_LITERAL: /^\{.+\}$/,
11
- PIPE: /\|/,
12
- WHITE_SPACES: /\s+/g,
13
- LINE_BREAKS_TABS: /(\r\n|\n|\r|\t)/gm,
14
- };
15
-
16
- const IS_SUPPORT_TEMPLATE = 'content' in document.createElement('template');
17
-
18
- const WRAP_MAP = {
19
- div: ['div', '<div>', '</div>'],
20
- thead: ['table', '<table>', '</table>'],
21
- col: ['colgroup', '<table><colgroup>', '</colgroup></table>'],
22
- tr: ['tbody', '<table><tbody>', '</tbody></table>'],
23
- td: ['tr', '<table><tr>', '</tr></table>'],
24
- };
25
- WRAP_MAP.caption = WRAP_MAP.colgroup = WRAP_MAP.tbody = WRAP_MAP.tfoot = WRAP_MAP.thead;
26
- WRAP_MAP.th = WRAP_MAP.td;
27
-
28
- const isArray = (obj) => {
29
- return hasIsArray ? Array.isArray(obj) : Object.prototype.toString.call(obj) === '[object Array]';
30
- };
31
-
32
- const isJsObject = (obj) => {
33
- return obj !== null && typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object Object]';
34
- };
35
-
36
- const isPlainObject = (obj) => {
37
- if (!isJsObject(obj)) {
38
- return false;
39
- }
40
-
41
- // If has modified constructor
42
- const ctor = obj.constructor;
43
- if (typeof ctor !== 'function') return false;
44
-
45
- // If has modified prototype
46
- const prot = ctor.prototype;
47
- if (isJsObject(prot) === false) return false;
48
-
49
- // If constructor does not have an Object-specific method
50
- if (prot.hasOwnProperty('isPrototypeOf') === false) {
51
- return false;
52
- }
53
-
54
- // Most likely a plain Object
55
- return true;
56
- };
57
-
58
- // test if string contains '{...}'. string must not contains tab, line breaks
59
- const isObjectLiteralString = (str = '') => {
60
- return REGEX.OBJECT_LITERAL.test(str);
61
- };
62
-
63
- const isEmptyObject = (obj) => {
64
- if (isJsObject(obj)) {
65
- return Object.getOwnPropertyNames(obj).length === 0;
66
- }
67
- return false;
68
- };
69
-
70
- function getFirstHtmlStringTag(htmlString) {
71
- const match = htmlString.match(REGEX.HTML_TAG);
72
- if (match) {
73
- return match[1];
74
- }
75
- return null;
76
- }
77
-
78
- function removeBadTags(htmlString = '') {
79
- return htmlString.replace(REGEX.BAD_TAGS, '');
80
- }
81
-
82
- function createHtmlFragment(htmlString) {
83
- if (typeof htmlString !== 'string') {
84
- return null;
85
- }
86
- // use template element
87
- if (IS_SUPPORT_TEMPLATE) {
88
- const template = document.createElement('template');
89
- template.innerHTML = removeBadTags(htmlString);
90
- return template.content;
91
- }
92
- // use document fragment with wrap html tag for tr, td etc.
93
- const fragment = document.createDocumentFragment();
94
- const queryContainer = document.createElement('div');
95
- const firstTag = getFirstHtmlStringTag(htmlString);
96
- const wrap = WRAP_MAP[firstTag || 'div'];
97
-
98
- if (wrap[0] === 'div') {
99
- return document.createRange().createContextualFragment(htmlString);
100
- }
101
-
102
- queryContainer.insertAdjacentHTML('beforeend', `${wrap[1]}${htmlString}${wrap[2]}`);
103
-
104
- const query = queryContainer.querySelector(wrap[0]);
105
-
106
- while (query.firstChild) {
107
- fragment.appendChild(query.firstChild);
108
- }
109
-
110
- return fragment;
111
- }
112
-
113
- const generateElementCache = (bindingAttrs) => {
114
- const elementCache = {};
115
-
116
- for (const i in bindingAttrs) {
117
- if (bindingAttrs.hasOwnProperty(i)) {
118
- if (isArray(bindingAttrs)) {
119
- elementCache[bindingAttrs[i]] = [];
120
- } else {
121
- elementCache[i] = [];
122
- }
123
- }
124
- }
125
-
126
- return elementCache;
127
- };
128
-
129
-
130
- // simplified version of Lodash _.get
131
- const _get = function get(obj, path, def) {
132
- function everyFunc(step) {
133
- return !(step && (obj = obj[step]) === undefined);
134
- }
135
- const fullPath = path
136
- .replace(/\[/g, '.')
137
- .replace(/]/g, '')
138
- .split('.')
139
- .filter(Boolean);
140
-
141
- return fullPath.every(everyFunc) ? obj : def;
142
- };
143
-
144
- /**
145
- * getViewModelValue
146
- * @description walk a object by provided string path. eg 'a.b.c'
147
- * @param {object} viewModel
148
- * @param {string} prop
149
- * @return {object}
150
- */
151
- const getViewModelValue = (viewModel, prop) => {
152
- return _get(viewModel, prop);
153
- };
154
-
155
- // simplified version of Lodash _.set
156
- // https://stackoverflow.com/questions/54733539/javascript-implementation-of-lodash-set-method
157
- const _set = (obj, path, value) => {
158
- if (Object(obj) !== obj) return obj; // When obj is not an object
159
- // If not yet an array, get the keys from the string-path
160
- if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [];
161
-
162
- // Iterate all of them except the last one
163
- path.slice(0, -1).reduce((a, c, i) =>
164
- Object(a[c]) === a[c] ? // Does the key exist and is its value an object?
165
- // Yes: then follow that path
166
- a[c] :
167
- // No: create the key. Is the next key a potential array-index?
168
- a[c] = Math.abs(path[i+1])>>0 === +path[i+1] ?
169
- [] : // Yes: assign a new array object
170
- {}, // No: assign a new plain object
171
- obj)[path[path.length-1]] = value; // Finally assign the value to the last key
172
-
173
- // Return the top-level object to allow chaining
174
- return obj;
175
- };
176
-
177
- /**
178
- * setViewModelValue
179
- * @description populate viewModel object by path string
180
- * @param {object} obj
181
- * @param {string} prop
182
- * @param {string} value
183
- * @return {call} underscore set
184
- */
185
- const setViewModelValue = (obj, prop, value) => {
186
- return _set(obj, prop, value);
187
- };
188
-
189
- const getViewModelPropValue = (viewModel, bindingCache) => {
190
- let dataKey = bindingCache.dataKey;
191
- let paramList = bindingCache.parameters;
192
- const isInvertBoolean = dataKey.charAt(0) === '!';
193
-
194
- if (isInvertBoolean) {
195
- dataKey = isInvertBoolean ? dataKey.substring(1) : dataKey;
196
- }
197
-
198
- let ret = getViewModelValue(viewModel, dataKey);
199
-
200
- if (typeof ret === 'function') {
201
- const viewModelContext = resolveViewModelContext(viewModel, dataKey);
202
- const oldViewModelProValue = bindingCache.elementData ? bindingCache.elementData.viewModelPropValue : null;
203
- paramList = paramList ? resolveParamList(viewModel, paramList) : [];
204
- // let args = [oldViewModelProValue, bindingCache.el].concat(paramList);
205
- const args = paramList.concat([oldViewModelProValue, bindingCache.el]);
206
- ret = ret.apply(viewModelContext, args);
207
- }
208
-
209
- ret = isInvertBoolean ? !Boolean(ret) : ret;
210
-
211
- // call through fitlers to get final value
212
- ret = filtersViewModelPropValue({
213
- value: ret,
214
- viewModel: viewModel,
215
- bindingCache: bindingCache,
216
- });
217
-
218
- return ret;
219
- };
220
-
221
- const filtersViewModelPropValue = ({value, viewModel, bindingCache}) => {
222
- let ret = value;
223
- if (bindingCache.filters) {
224
- each(bindingCache.filters, (index, filter) => {
225
- const viewModelContext = resolveViewModelContext(viewModel, filter);
226
- const filterFn = getViewModelValue.call(viewModelContext, viewModelContext, filter);
227
- try {
228
- ret = filterFn.call(viewModelContext, ret);
229
- } catch (err) {
230
- throwErrorMessage(err, `Invalid filter: ${filter}`);
231
- }
232
- });
233
- }
234
- return ret;
235
- };
236
-
237
- const parseStringToJson = (str) => {
238
- // fix unquote or single quote keys and replace single quote to double quote
239
- const ret = str.replace(/(\s*?{\s*?|\s*?,\s*?)(['"])?([a-zA-Z0-9]+)(['"])?:/g, '$1"$3":').replace(/'/g, '"');
240
- return JSON.parse(ret);
241
- };
242
-
243
- /**
244
- * arrayRemoveMatch
245
- * @description remove match items in fromArray out of toArray
246
- * @param {array} toArray
247
- * @param {array} frommArray
248
- * @return {boolean}
249
- */
250
- const arrayRemoveMatch = (toArray, frommArray) => {
251
- return toArray.filter((value, index) => {
252
- return frommArray.indexOf(value) < 0;
253
- });
254
- };
255
-
256
- const getFormData = ($form) => {
257
- const data = {};
258
-
259
- if (!$form instanceof HTMLFormElement) {
260
- return data;
261
- }
262
-
263
- const formData = new FormData($form);
264
-
265
- formData.forEach((value, key) => {
266
- if (!Object.prototype.hasOwnProperty.call( Object, key ) ) {
267
- data[key] = value;
268
- return;
269
- }
270
- if (!Array.isArray(data[key])) {
271
- data[key] = [data[key]];
272
- }
273
- data[key].push(value);
274
- });
275
-
276
- return data;
277
- };
278
-
279
- /**
280
- * getFunctionParameterList
281
- * @description convert parameter string to arrary
282
- * eg. '("a","b","c")' > ["a","b","c"]
283
- * @param {string} str
284
- * @return {array} paramlist
285
- */
286
- const getFunctionParameterList = (str) => {
287
- if (!str || str.length > config.maxDatakeyLength) {
288
- return;
289
- }
290
- let paramlist = str.match(REGEX.FUNCTION_PARAM);
291
-
292
- if (paramlist && paramlist[1]) {
293
- paramlist = paramlist[1].split(',');
294
- paramlist.forEach(function(v, i) {
295
- paramlist[i] = v.trim();
296
- });
297
- }
298
- return paramlist;
299
- };
300
-
301
- const extractFilterList = (cacheData) => {
302
- if (!cacheData || !cacheData.dataKey || cacheData.dataKey.length > config.maxDatakeyLength) {
303
- return cacheData;
304
- }
305
- const filterList = cacheData.dataKey.split(REGEX.PIPE);
306
- let isOnceIndex;
307
- cacheData.dataKey = filterList[0].trim();
308
- if (filterList.length > 1) {
309
- filterList.shift(0);
310
- filterList.forEach(function(v, i) {
311
- filterList[i] = v.trim();
312
- if (filterList[i] === config.constants.filters.ONCE) {
313
- cacheData.isOnce = true;
314
- isOnceIndex = i;
315
- }
316
- });
317
- // don't store filter 'once' - because it is internal logic not a property from viewModel
318
- if (isOnceIndex >= 0) {
319
- filterList.splice(isOnceIndex, 1);
320
- }
321
- cacheData.filters = filterList;
322
- }
323
- return cacheData;
324
- };
325
-
326
- const invertObj = (sourceObj) => {
327
- return Object.keys(sourceObj).reduce(function(obj, key) {
328
- obj[sourceObj[key]] = key;
329
- return obj;
330
- }, {});
331
- };
332
-
333
- const createDeferredObj = () => {
334
- const dfObj = {};
335
-
336
- dfObj.promise = new Promise((resolve, reject) => {
337
- dfObj.resolve = resolve;
338
- dfObj.reject = reject;
339
- });
340
-
341
- return dfObj;
342
- };
343
-
344
- /**
345
- * debounce
346
- * @description decorate a function to be debounce using requestAnimationFrame
347
- * @param {function} fn
348
- * @param {context} ctx
349
- * @return {function}
350
- */
351
- const debounceRaf = (fn, ctx = null) => {
352
- return (function(fn, ctx) {
353
- let dfObj = createDeferredObj();
354
- let rafId = 0;
355
-
356
- // return decorated fn
357
- return function() {
358
- /* eslint-disable prefer-rest-params */
359
- const args = Array.from ? Array.from(arguments) : Array.prototype.slice.call(arguments);
360
-
361
- window.cancelAnimationFrame(rafId);
362
- rafId = window.requestAnimationFrame(() => {
363
- try {
364
- // fn is Binder.render function
365
- fn.apply(ctx, args);
366
- // dfObj.resolve is function provided in .then promise chain
367
- // ctx is the current component
368
- dfObj.resolve(ctx);
369
- } catch (err) {
370
- console.error('error in rendering: ', err);
371
- dfObj.reject(err);
372
- }
373
-
374
- // reset dfObj - otherwise then callbacks will not be in execution order
375
- // example:
376
- // myApp.render().then(function(){console.log('ok1')});
377
- // myApp.render().then(function(){console.log('ok2')});
378
- // myApp.render().then(function(){console.log('ok3')});
379
- // >> ok1, ok2, ok3
380
- dfObj = createDeferredObj();
381
-
382
- window.cancelAnimationFrame(rafId);
383
- });
384
-
385
- return dfObj.promise;
386
- };
387
- })(fn, ctx);
388
- };
389
-
390
- /**
391
- * getNodeAttrObj
392
- * @description convert Node attributes object to a json object
393
- * @param {object} node
394
- * @param {array} skipList
395
- * @return {object}
396
- */
397
- const getNodeAttrObj = (node, skipList) => {
398
- let attributesLength = 0;
399
- let skipArray;
400
-
401
- if (!node || node.nodeType !== 1 || !node.hasAttributes()) {
402
- return;
403
- }
404
- if (skipList) {
405
- skipArray = [];
406
- skipArray = typeof skipList === 'string' ? skipArray.push(skipList) : skipList;
407
- }
408
- const attrObj = {};
409
- attributesLength = node.attributes.length;
410
-
411
- if (attributesLength) {
412
- for (let i = 0; i < attributesLength; i += 1) {
413
- const attribute = node.attributes.item(i);
414
- attrObj[attribute.nodeName] = attribute.nodeValue;
415
- }
416
- }
417
-
418
- if (isArray(skipArray)) {
419
- skipArray.forEach((item) => {
420
- if (attrObj[item]) {
421
- delete attrObj[item];
422
- }
423
- });
424
- }
425
- return attrObj;
426
- };
427
-
428
- /**
429
- * extend
430
- * @param {boolean} isDeepMerge
431
- * @param {object} target
432
- * @param {object} sources
433
- * @return {object} merged object
434
- */
435
- const extend = (isDeepMerge = false, target, ...sources) => {
436
- if (!sources.length) {
437
- return target;
438
- }
439
- const source = sources.shift();
440
- if (source === undefined) {
441
- return target;
442
- }
443
-
444
- if (!isDeepMerge) {
445
- return Object.assign(target, ...sources);
446
- }
447
-
448
- if (isMergebleObject(target) && isMergebleObject(source)) {
449
- Object.keys(source).forEach((key) => {
450
- if (isMergebleObject(source[key])) {
451
- if (!target[key]) {
452
- target[key] = {};
453
- }
454
- extend(target[key], source[key]);
455
- } else {
456
- target[key] = source[key];
457
- }
458
- });
459
- }
460
-
461
- return extend(true, target, ...sources);
462
- };
463
-
464
- const each = (obj, fn) => {
465
- if (typeof obj !== 'object' || typeof fn !== 'function') {
466
- return;
467
- }
468
- let keys = [];
469
- let keysLength = 0;
470
- const isArrayObj = isArray(obj);
471
- let key;
472
- let value;
473
- let i = 0;
474
-
475
- if (isArrayObj) {
476
- keysLength = obj.length;
477
- } else if (isJsObject(obj)) {
478
- keys = Object.keys(obj);
479
- keysLength = keys.length;
480
- } else {
481
- throw new TypeError('Object is not an array or object');
482
- }
483
-
484
- for (i = 0; i < keysLength; i += 1) {
485
- if (isArrayObj) {
486
- key = i;
487
- value = obj[i];
488
- } else {
489
- key = keys[i];
490
- value = obj[key];
491
- }
492
- fn(key, value);
493
- }
494
- };
495
-
496
- const isMergebleObject = (item) => {
497
- return isJsObject(item) && !isArray(item);
498
- };
499
-
500
- /**
501
- * cloneDomNode
502
- * @param {object} element
503
- * @return {object} cloned element
504
- * @description helper function to clone node
505
- */
506
- const cloneDomNode = (element) => {
507
- return element.cloneNode(true);
508
- };
509
-
510
- /**
511
- * insertAfter
512
- * @param {object} parentNode
513
- * @param {object} newNode
514
- * @param {object} referenceNode
515
- * @return {object} node
516
- * @description helper function to insert new node before the reference node
517
- */
518
- const insertAfter = (parentNode, newNode, referenceNode) => {
519
- const refNextElement = referenceNode && referenceNode.nextSibling ? referenceNode.nextSibling : null;
520
- return parentNode.insertBefore(newNode, refNextElement);
521
- };
522
-
523
- const resolveViewModelContext = (viewModel, datakey) => {
524
- let ret = viewModel;
525
- if (typeof datakey !== 'string') {
526
- return ret;
527
- }
528
- const bindingDataContext = datakey.split('.');
529
- if (bindingDataContext.length > 1) {
530
- if (bindingDataContext[0] === config.bindingDataReference.rootDataKey) {
531
- ret = viewModel[config.bindingDataReference.rootDataKey] || viewModel;
532
- } else if (bindingDataContext[0] === config.bindingDataReference.currentData) {
533
- ret = viewModel[config.bindingDataReference.currentData] || viewModel;
534
- }
535
- }
536
- return ret;
537
- };
538
-
539
- const resolveParamList = (viewModel, paramList) => {
540
- if (!viewModel || !isArray(paramList)) {
541
- return;
542
- }
543
- return paramList.map((param) => {
544
- param = param.trim();
545
-
546
- if (param === config.bindingDataReference.currentIndex) {
547
- // convert '$index' to value
548
- param = viewModel[config.bindingDataReference.currentIndex];
549
- } else if (param === config.bindingDataReference.currentData) {
550
- // convert '$data' to value or current viewModel
551
- param = viewModel[config.bindingDataReference.currentData] || viewModel;
552
- } else if (param === config.bindingDataReference.rootDataKey) {
553
- // convert '$root' to root viewModel
554
- param = viewModel[config.bindingDataReference.rootDataKey] || viewModel;
555
- }
556
- return param;
557
- });
558
- };
559
-
560
- const removeElement = (el) => {
561
- if (el && el.parentNode) {
562
- el.parentNode.removeChild(el);
563
- }
564
- };
565
-
566
- const emptyElement = (node) => {
567
- if (node && node.firstChild) {
568
- while (node.firstChild) {
569
- node.removeChild(node.firstChild);
570
- }
571
- }
572
- return node;
573
- };
574
-
575
- const throwErrorMessage = (err = null, errorMessage = '') => {
576
- const message = err && err.message ? err.message : errorMessage;
577
- if (typeof console.error === 'function') {
578
- return console.error(message);
579
- }
580
- return console.log(message);
581
- };
582
-
583
- /**
584
- * parseBindingObjectString
585
- * @description parse bining object string to object with value always stringify
586
- * @param {string} str - eg '{ id: $data.id, name: $data.name }'
587
- * @return {object} - eg { id: '$data.id', name: '$data.name'}
588
- */
589
- const parseBindingObjectString = (str = '') => {
590
- let objectLiteralString = str.trim();
591
- const ret = {};
592
-
593
- if (!REGEX.OBJECT_LITERAL.test(str)) {
594
- return null;
595
- }
596
-
597
- // clearn up line breaks and remove first { character
598
- objectLiteralString = objectLiteralString
599
- .replace(REGEX.LINE_BREAKS_TABS, '')
600
- .substring(1);
601
-
602
- // remove last } character
603
- objectLiteralString = objectLiteralString.substring(0, objectLiteralString.length - 1);
604
-
605
- objectLiteralString.split(',').forEach((item) => {
606
- const keyVal = item.trim();
607
- // ignore if last empty item - eg split last comma in object literal
608
- if (keyVal) {
609
- const prop = keyVal.split(':');
610
- const key = prop[0].trim();
611
- ret[key] = `${prop[1]}`.trim();
612
- }
613
- });
614
-
615
- return ret;
616
- };
617
-
618
- export {
619
- REGEX,
620
- arrayRemoveMatch,
621
- cloneDomNode,
622
- createHtmlFragment,
623
- debounceRaf,
624
- each,
625
- emptyElement,
626
- extend,
627
- extractFilterList,
628
- generateElementCache,
629
- getFormData,
630
- getFunctionParameterList,
631
- getNodeAttrObj,
632
- getViewModelPropValue,
633
- getViewModelValue,
634
- insertAfter,
635
- invertObj,
636
- isArray,
637
- isEmptyObject,
638
- isJsObject,
639
- isPlainObject,
640
- isObjectLiteralString,
641
- parseStringToJson,
642
- parseBindingObjectString,
643
- removeElement,
644
- resolveParamList,
645
- resolveViewModelContext,
646
- setViewModelValue,
647
- throwErrorMessage,
648
- };
@@ -1,57 +0,0 @@
1
- describe('Given [data-bind-comp="blur-component"] initised', () => {
2
- const namespace = {};
3
- const testBlurValue = 'onBlur called';
4
-
5
- jasmine.getFixtures().fixturesPath = 'test';
6
-
7
- beforeEach(() => {
8
- loadFixtures('./fixtures/blurBinding.html');
9
-
10
- namespace.viewModel = {
11
- heading: 'blur component test',
12
- myData: 'blur component',
13
- onFocusFn: function(e, $element) {},
14
- onBlurFn: function(e, $element) {
15
- this.myData = testBlurValue;
16
- this.updateView();
17
- },
18
- updateView: function(opt) {
19
- this.APP.render(opt);
20
- },
21
- };
22
-
23
- namespace.blurComponent = dataBind.init(document.querySelector('[data-bind-comp="blur-component"]'), namespace.viewModel);
24
- namespace.blurComponent.render();
25
- });
26
-
27
- afterEach(() => {
28
- // clean up app
29
- // clean up all app/components
30
- for (const prop in namespace) {
31
- if (namespace.hasOwnProperty(prop)) {
32
- delete namespace[prop];
33
- }
34
- }
35
- });
36
-
37
- it('should render heading defined in viewModel', (done) => {
38
- setTimeout(() => {
39
- expect(document.getElementById('heading').textContent).toBe(namespace.viewModel.heading);
40
- expect(document.getElementById('blurInput').value).toBe(namespace.viewModel.myData);
41
- done();
42
- }, 200);
43
- });
44
-
45
- it('should update "blurInput" value after onBlur', (done) => {
46
- const $blurInput = document.getElementById('blurInput');
47
- setTimeout(() => {
48
- $blurInput.focus();
49
- $blurInput.blur();
50
-
51
- setTimeout(() => {
52
- expect($blurInput.value).toBe(testBlurValue);
53
- done();
54
- }, 200);
55
- }, 200);
56
- });
57
- });