native-document 1.0.108 → 1.0.110
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/dist/native-document.components.min.js +1 -3
- package/dist/native-document.dev.js +72 -47
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/package.json +1 -1
- package/src/core/utils/validator.js +5 -6
- package/src/core/wrappers/AttributesWrapper.js +3 -1
- package/src/core/wrappers/ElementCreator.js +10 -2
- package/src/core/wrappers/NdPrototype.js +1 -3
- package/src/core/wrappers/TemplateCloner.js +248 -79
- package/types/template-cloner.ts +1 -0
|
@@ -3,150 +3,309 @@ import TemplateBinding from "./TemplateBinding";
|
|
|
3
3
|
|
|
4
4
|
const cloneBindingsDataCache = new WeakMap();
|
|
5
5
|
|
|
6
|
+
const OPERATIONS = {
|
|
7
|
+
HYDRATE_TEXT: 1,
|
|
8
|
+
ATTACH_METHOD: 2,
|
|
9
|
+
HYDRATE_ATTRIBUTES: 3,
|
|
10
|
+
HYDRATE_FULL: 4,
|
|
11
|
+
};
|
|
6
12
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
13
|
+
const hydrateFull = (node, bindDingData, data) => {
|
|
14
|
+
const cacheAttributes = bindDingData._cache;
|
|
15
|
+
|
|
16
|
+
for(let i = 0, length = bindDingData._flatAttributesLength; i < length; i++) {
|
|
17
|
+
const attr = bindDingData._flatAttributes[i];
|
|
18
|
+
cacheAttributes[attr.name] = attr.value.apply(null, data);
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
for (const className in bindDingData.classes) {
|
|
20
|
-
attributes.class[className] = bindDingData.classes[className].apply(null, data);
|
|
21
|
-
}
|
|
21
|
+
for(let i = 0, length = bindDingData._flatDynamiqueLength; i < length; i++) {
|
|
22
|
+
const dyn = bindDingData._flatDynamique[i];
|
|
23
|
+
cacheAttributes[dyn.name][dyn.key] = dyn.value.apply(null, data);
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
ElementCreator.processAttributesDirect(node, cacheAttributes);
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const hydrateDynamic = (node, bindDingData, data) => {
|
|
31
|
+
const cacheAttributes = bindDingData._cache;
|
|
32
|
+
|
|
33
|
+
for(let i = 0, length = bindDingData._flatDynamiqueLength; i < length; i++) {
|
|
34
|
+
const dyn = bindDingData._flatDynamique[i];
|
|
35
|
+
cacheAttributes[dyn.name][dyn.key] = dyn.value.apply(null, data);
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
ElementCreator.processClassAttribute(node, cacheAttributes.class);
|
|
39
|
+
ElementCreator.processStyleAttribute(node, cacheAttributes.style);
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const hydrateClassAttribute = (node, bindDingData, data) => {
|
|
44
|
+
const classAttributes = bindDingData._cache.class;
|
|
45
|
+
|
|
46
|
+
for(let i = 0, length = bindDingData._flatDynamiqueLength; i < length; i++) {
|
|
47
|
+
const dyn = bindDingData._flatDynamique[i];
|
|
48
|
+
classAttributes[dyn.key] = dyn.value.apply(null, data);
|
|
35
49
|
}
|
|
36
50
|
|
|
37
|
-
|
|
51
|
+
ElementCreator.processClassAttribute(node, classAttributes);
|
|
52
|
+
return true;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const hydrateStyleAttribute = (node, bindDingData, data) => {
|
|
56
|
+
const styleAttributes = bindDingData._cache;
|
|
57
|
+
|
|
58
|
+
for(let i = 0, length = bindDingData._flatDynamiqueLength; i < length; i++) {
|
|
59
|
+
const dyn = bindDingData._flatDynamique[i];
|
|
60
|
+
styleAttributes[dyn.key] = dyn.value.apply(null, data);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
ElementCreator.processStyleAttribute(node, styleAttributes);
|
|
64
|
+
return true;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const hydrateAttributes = (node, bindDingData, data) => {
|
|
68
|
+
const cacheAttributes = bindDingData._cache;
|
|
69
|
+
|
|
70
|
+
for(let i = 0, length = bindDingData._flatAttributesLength; i < length; i++) {
|
|
71
|
+
const attr = bindDingData._flatAttributes[i];
|
|
72
|
+
cacheAttributes[attr.name] = attr.value.apply(null, data);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
ElementCreator.processAttributesDirect(node, cacheAttributes);
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const getHydrator = (bindDingData) => {
|
|
80
|
+
if(!bindDingData._cache) {
|
|
81
|
+
return noUpdate;
|
|
82
|
+
}
|
|
83
|
+
if(bindDingData._flatAttributesLength && bindDingData._flatDynamiqueLength) {
|
|
84
|
+
return hydrateFull;
|
|
85
|
+
}
|
|
86
|
+
if(bindDingData._flatAttributesLength) {
|
|
87
|
+
return hydrateAttributes;
|
|
88
|
+
}
|
|
89
|
+
if(bindDingData._hasClassAttribute && bindDingData._hasStyleAttribute) {
|
|
90
|
+
return hydrateDynamic;
|
|
91
|
+
}
|
|
92
|
+
if(bindDingData._hasClassAttribute) {
|
|
93
|
+
return hydrateClassAttribute;
|
|
94
|
+
}
|
|
95
|
+
return hydrateStyleAttribute;
|
|
38
96
|
};
|
|
39
97
|
|
|
40
98
|
const $hydrateFn = function(hydrateFunction, targetType, element, property) {
|
|
41
99
|
if(!cloneBindingsDataCache.has(element)) {
|
|
42
|
-
|
|
43
|
-
cloneBindingsDataCache.set(element, {});
|
|
100
|
+
cloneBindingsDataCache.set(element, { attach: [] });
|
|
44
101
|
}
|
|
45
102
|
const hydrationState = cloneBindingsDataCache.get(element);
|
|
103
|
+
|
|
46
104
|
if(targetType === 'value') {
|
|
47
105
|
hydrationState.value = hydrateFunction;
|
|
48
106
|
return;
|
|
49
107
|
}
|
|
108
|
+
if(targetType === 'attach') {
|
|
109
|
+
hydrationState.attach = hydrationState.attach || [];
|
|
110
|
+
hydrationState.attach.push({ methodName: property, fn: hydrateFunction});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
50
113
|
hydrationState[targetType] = hydrationState[targetType] || {};
|
|
51
114
|
hydrationState[targetType][property] = hydrateFunction;
|
|
52
|
-
}
|
|
115
|
+
};
|
|
53
116
|
|
|
54
117
|
const bindAttachMethods = (node, bindDingData, data) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
node.nd[methodName](function(...args) {
|
|
60
|
-
bindDingData.attach[methodName].apply(this, [...args, ...data]);
|
|
118
|
+
for(let i = 0, length = bindDingData._attachLength; i < length; i++) {
|
|
119
|
+
const method = bindDingData.attach[i];
|
|
120
|
+
node.nd[method.methodName](function() {
|
|
121
|
+
method.fn.call(this, ...data, ...arguments);
|
|
61
122
|
});
|
|
62
123
|
}
|
|
63
124
|
};
|
|
64
125
|
|
|
65
126
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
127
|
+
const $applyBindingParents = [];
|
|
128
|
+
const pathProcess = (target, path, data) => {
|
|
129
|
+
if(path.operation === OPERATIONS.HYDRATE_TEXT) {
|
|
130
|
+
const value = path.value;
|
|
131
|
+
ElementCreator.bindTextNode(target, path.isString ? data[0][value] : value.apply(null, data));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if(path.operation === OPERATIONS.ATTACH_METHOD || path.operation === OPERATIONS.HYDRATE_FULL) {
|
|
135
|
+
const bindingData = path.bindingData;
|
|
136
|
+
for(let i = 0, length = bindingData._attachLength; i < length; i++) {
|
|
137
|
+
const method = bindingData.attach[i];
|
|
138
|
+
target.nd[method.methodName](function() {
|
|
139
|
+
method.fn.call(this, ...data, ...arguments);
|
|
140
|
+
});
|
|
70
141
|
}
|
|
71
|
-
|
|
72
|
-
|
|
142
|
+
}
|
|
143
|
+
if(path.operation === OPERATIONS.HYDRATE_ATTRIBUTES || path.operation === OPERATIONS.HYDRATE_FULL) {
|
|
144
|
+
path.hydrator(target, path.bindingData, data);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const applyBindingTreePath = (root, data, paths, pathSize) => {
|
|
149
|
+
const rootPath = paths[pathSize];
|
|
150
|
+
$applyBindingParents[rootPath.id] = root;
|
|
151
|
+
pathProcess(root, rootPath, data);
|
|
152
|
+
|
|
153
|
+
let target = null, path = null;
|
|
154
|
+
for(let i = 0; i < pathSize; i++) {
|
|
155
|
+
path = paths[i];
|
|
156
|
+
target = $applyBindingParents[path.parentId].childNodes[path.index];
|
|
157
|
+
$applyBindingParents[path.id] = target;
|
|
158
|
+
|
|
159
|
+
pathProcess(target, path, data);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (let i = 0; i <= pathSize; i++) {
|
|
163
|
+
$applyBindingParents[i] = null;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const buildAttributesCache = (bindDingData) => {
|
|
167
|
+
const cache = { };
|
|
168
|
+
if(bindDingData.attributes) cache.attributes = {};
|
|
169
|
+
if(bindDingData.classes) cache.class = {};
|
|
170
|
+
if(bindDingData.styles) cache.style = {};
|
|
171
|
+
bindDingData._cache = cache;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const prepareBindingMetadata = (bindDingData) => {
|
|
175
|
+
const attributes = [];
|
|
176
|
+
const classAndStyles = [];
|
|
177
|
+
|
|
178
|
+
if(bindDingData.attributes) {
|
|
179
|
+
for (const attr in bindDingData.attributes) {
|
|
180
|
+
attributes.push({
|
|
181
|
+
name: attr,
|
|
182
|
+
value: bindDingData.attributes[attr]
|
|
183
|
+
});
|
|
73
184
|
}
|
|
74
185
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
186
|
+
|
|
187
|
+
if(bindDingData.classes) {
|
|
188
|
+
for (const className in bindDingData.classes) {
|
|
189
|
+
bindDingData._hasClassAttribute = true;
|
|
190
|
+
classAndStyles.push({
|
|
191
|
+
name: 'class',
|
|
192
|
+
key: className,
|
|
193
|
+
value: bindDingData.classes[className]
|
|
194
|
+
});
|
|
80
195
|
}
|
|
81
196
|
}
|
|
197
|
+
|
|
198
|
+
if(bindDingData.styles) {
|
|
199
|
+
for (const property in bindDingData.styles) {
|
|
200
|
+
bindDingData._hasStyleAttribute = true;
|
|
201
|
+
classAndStyles.push({
|
|
202
|
+
name: 'style',
|
|
203
|
+
key: property,
|
|
204
|
+
value: bindDingData.styles[property]
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
bindDingData._flatAttributes = attributes;
|
|
210
|
+
bindDingData._flatAttributesLength = attributes.length;
|
|
211
|
+
bindDingData._flatDynamique = classAndStyles;
|
|
212
|
+
bindDingData._flatDynamiqueLength = classAndStyles.length;
|
|
213
|
+
bindDingData._attachLength = bindDingData.attach.length;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const optimizeBindingData = (bindDingData) => {
|
|
217
|
+
buildAttributesCache(bindDingData);
|
|
218
|
+
prepareBindingMetadata(bindDingData);
|
|
82
219
|
};
|
|
83
220
|
|
|
221
|
+
const noUpdate = () => {};
|
|
84
222
|
export function TemplateCloner($fn) {
|
|
85
223
|
let $node = null;
|
|
86
224
|
let $hasBindingData = false;
|
|
87
225
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
226
|
+
let $bindingTreePathSize = 0;
|
|
227
|
+
const $bindingTreePath = [
|
|
228
|
+
{
|
|
229
|
+
id: 0,
|
|
230
|
+
parentId: null
|
|
231
|
+
}
|
|
232
|
+
];
|
|
92
233
|
|
|
234
|
+
let pathCounter = 0;
|
|
93
235
|
const clone = (node, data, currentPath) => {
|
|
94
236
|
const bindDingData = cloneBindingsDataCache.get(node);
|
|
237
|
+
if(bindDingData) {
|
|
238
|
+
optimizeBindingData(bindDingData);
|
|
239
|
+
}
|
|
95
240
|
if(node.nodeType === 3) {
|
|
96
241
|
if(bindDingData && bindDingData.value) {
|
|
97
|
-
|
|
242
|
+
const value = bindDingData.value;
|
|
98
243
|
const textNode = node.cloneNode();
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
bindDingData.value(data, textNode);
|
|
244
|
+
currentPath.value = value;
|
|
245
|
+
currentPath.operation = OPERATIONS.HYDRATE_TEXT;
|
|
246
|
+
currentPath.isString = (typeof value === 'string');
|
|
247
|
+
ElementCreator.bindTextNode(textNode, (currentPath.isString ? data[0][value] : value.apply(null, data)));
|
|
104
248
|
return textNode;
|
|
105
249
|
}
|
|
106
250
|
return node.cloneNode(true);
|
|
107
251
|
}
|
|
108
252
|
const nodeCloned = node.cloneNode();
|
|
109
253
|
if(bindDingData) {
|
|
110
|
-
|
|
254
|
+
const hydrator = getHydrator(bindDingData);
|
|
255
|
+
hydrator(nodeCloned, bindDingData, data);
|
|
111
256
|
bindAttachMethods(nodeCloned, bindDingData, data);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
257
|
+
|
|
258
|
+
const hasAttributes = bindDingData.classes || bindDingData.styles || bindDingData.attributes;
|
|
259
|
+
const hasAttachMethods = bindDingData.attach.length;
|
|
260
|
+
|
|
261
|
+
currentPath.bindingData = bindDingData;
|
|
262
|
+
currentPath.hydrator = hydrator;
|
|
263
|
+
|
|
264
|
+
if(hasAttributes && hasAttachMethods) {
|
|
265
|
+
currentPath.operation = OPERATIONS.HYDRATE_FULL;
|
|
266
|
+
}
|
|
267
|
+
else if(hasAttributes) {
|
|
268
|
+
currentPath.operation = OPERATIONS.HYDRATE_ATTRIBUTES;
|
|
269
|
+
}
|
|
270
|
+
else if(hasAttachMethods) {
|
|
271
|
+
currentPath.operation = OPERATIONS.ATTACH_METHOD;
|
|
272
|
+
}
|
|
116
273
|
}
|
|
117
274
|
const childNodes = node.childNodes;
|
|
118
|
-
const
|
|
275
|
+
const parentId = currentPath.id;
|
|
276
|
+
|
|
119
277
|
for(let i = 0, length = childNodes.length; i < length; i++) {
|
|
120
278
|
const childNode = childNodes[i];
|
|
121
|
-
const path = {
|
|
279
|
+
const path = { parentId, id: ++pathCounter, index: i };
|
|
122
280
|
const childNodeCloned = clone(childNode, data, path);
|
|
123
|
-
if(path.
|
|
124
|
-
|
|
281
|
+
if(path.hasChildren || path.operation) {
|
|
282
|
+
$bindingTreePath.push(path);
|
|
283
|
+
currentPath.hasChildren = true;
|
|
125
284
|
}
|
|
126
285
|
nodeCloned.appendChild(childNodeCloned);
|
|
127
286
|
}
|
|
128
|
-
if(bindingPathChildren.length) {
|
|
129
|
-
currentPath.children = currentPath.children || [];
|
|
130
|
-
currentPath.children = bindingPathChildren;
|
|
131
|
-
}
|
|
132
287
|
return nodeCloned;
|
|
133
288
|
};
|
|
134
289
|
|
|
135
290
|
const cloneWithBindingPaths = (data) => {
|
|
136
291
|
let root = $node.cloneNode(true);
|
|
137
292
|
|
|
138
|
-
applyBindingTreePath(root,
|
|
293
|
+
applyBindingTreePath(root, data, $bindingTreePath, $bindingTreePathSize);
|
|
139
294
|
return root;
|
|
140
295
|
};
|
|
141
296
|
|
|
142
297
|
this.clone = (data) => {
|
|
143
|
-
|
|
298
|
+
const binder = createTemplateCloner(this);
|
|
299
|
+
$node = $fn(binder);
|
|
144
300
|
if(!$hasBindingData) {
|
|
145
301
|
this.clone = () => $node.cloneNode(true);
|
|
146
302
|
return $node.cloneNode(true);
|
|
147
303
|
}
|
|
148
304
|
|
|
149
|
-
const firstClone = clone($node, data, $bindingTreePath);
|
|
305
|
+
const firstClone = clone($node, data, $bindingTreePath[0]);
|
|
306
|
+
$bindingTreePath.reverse();
|
|
307
|
+
$bindingTreePathSize = $bindingTreePath.length - 1;
|
|
308
|
+
|
|
150
309
|
this.clone = cloneWithBindingPaths;
|
|
151
310
|
return firstClone;
|
|
152
311
|
};
|
|
@@ -169,12 +328,7 @@ export function TemplateCloner($fn) {
|
|
|
169
328
|
return this.value(propertyName);
|
|
170
329
|
}
|
|
171
330
|
this.value = (callbackOrProperty) => {
|
|
172
|
-
|
|
173
|
-
return createBinding(callbackOrProperty, 'value');
|
|
174
|
-
}
|
|
175
|
-
return createBinding((data, textNode) => {
|
|
176
|
-
ElementCreator.bindTextNode(textNode, callbackOrProperty(...data));
|
|
177
|
-
}, 'value');
|
|
331
|
+
return createBinding(callbackOrProperty, 'value');
|
|
178
332
|
};
|
|
179
333
|
this.text = this.value;
|
|
180
334
|
this.attr = (fn) => {
|
|
@@ -183,16 +337,31 @@ export function TemplateCloner($fn) {
|
|
|
183
337
|
this.attach = (fn) => {
|
|
184
338
|
return createBinding(fn, 'attach');
|
|
185
339
|
};
|
|
340
|
+
this.callback = this.attach;
|
|
186
341
|
|
|
187
342
|
}
|
|
188
343
|
|
|
344
|
+
function createTemplateCloner($binder) {
|
|
345
|
+
return new Proxy($binder, {
|
|
346
|
+
get(target, prop) {
|
|
347
|
+
if(prop in target) {
|
|
348
|
+
return target[prop];
|
|
349
|
+
}
|
|
350
|
+
if (typeof prop === 'symbol') return target[prop];
|
|
351
|
+
return target.value(prop);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
189
356
|
export function useCache(fn) {
|
|
190
357
|
let $cache = null;
|
|
191
358
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
359
|
+
let wrapper = function(args) {
|
|
360
|
+
$cache = new TemplateCloner(fn);
|
|
361
|
+
|
|
362
|
+
wrapper = function(args) {
|
|
363
|
+
return $cache.clone(args);
|
|
364
|
+
};
|
|
196
365
|
return $cache.clone(args);
|
|
197
366
|
};
|
|
198
367
|
|
package/types/template-cloner.ts
CHANGED
|
@@ -37,6 +37,7 @@ export declare class TemplateCloner {
|
|
|
37
37
|
property(fn: (...data: any[]) => any): BindingHydrator;
|
|
38
38
|
|
|
39
39
|
attach(fn: (element: Element, ...data: any[]) => void): BindingHydrator;
|
|
40
|
+
callback(fn: (element: Element, ...data: any[]) => void): BindingHydrator;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export declare function useCache(fn: TemplateBuilder): CachedTemplateFunction;
|