@reidelsaltres/pureper 0.1.175 → 0.1.176
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/out/foundation/Triplet.d.ts.map +1 -1
- package/out/foundation/Triplet.js +3 -5
- package/out/foundation/Triplet.js.map +1 -1
- package/out/foundation/component_api/Component.d.ts +2 -2
- package/out/foundation/component_api/Component.d.ts.map +1 -1
- package/out/foundation/component_api/Component.js.map +1 -1
- package/out/foundation/component_api/UniHtml.d.ts +4 -4
- package/out/foundation/component_api/UniHtml.d.ts.map +1 -1
- package/out/foundation/component_api/UniHtml.js +4 -11
- package/out/foundation/component_api/UniHtml.js.map +1 -1
- package/out/foundation/engine/Expression.d.ts.map +1 -1
- package/out/foundation/engine/Expression.js.map +1 -1
- package/out/foundation/engine/TemplateEngine.d.ts +45 -3
- package/out/foundation/engine/TemplateEngine.d.ts.map +1 -1
- package/out/foundation/engine/TemplateEngine.js +385 -49
- package/out/foundation/engine/TemplateEngine.js.map +1 -1
- package/package.json +1 -1
- package/src/foundation/Triplet.ts +5 -6
- package/src/foundation/component_api/Component.ts +2 -1
- package/src/foundation/component_api/UniHtml.ts +14 -21
- package/src/foundation/engine/Expression.ts +1 -2
- package/src/foundation/engine/TemplateEngine.ts +414 -53
- package/src/foundation/engine/BalancedParser.ts +0 -353
- package/src/foundation/engine/EscapeHandler.ts +0 -54
- package/src/foundation/engine/Rule.ts +0 -138
- package/src/foundation/engine/TemplateEngine.old.ts +0 -318
- package/src/foundation/engine/TemplateInstance.md +0 -110
- package/src/foundation/engine/TemplateInstance.old.ts +0 -673
- package/src/foundation/engine/TemplateInstance.ts +0 -843
- package/src/foundation/engine/exceptions/TemplateExceptions.ts +0 -27
- package/src/foundation/engine/rules/attribute/EventRule.ts +0 -171
- package/src/foundation/engine/rules/attribute/InjectionRule.ts +0 -140
- package/src/foundation/engine/rules/attribute/RefRule.ts +0 -126
- package/src/foundation/engine/rules/syntax/ExpressionRule.ts +0 -102
- package/src/foundation/engine/rules/syntax/ForRule.ts +0 -267
- package/src/foundation/engine/rules/syntax/IfRule.ts +0 -261
- package/src/foundation/hmle/Context.ts +0 -90
|
@@ -1,68 +1,429 @@
|
|
|
1
|
+
import Observable from "../api/Observer.js";
|
|
1
2
|
import Expression from "./Expression.js";
|
|
2
3
|
import Scope from "./Scope.js";
|
|
3
4
|
|
|
4
5
|
export default class TemplateEngine {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
private readonly ref_component: TemplateComponent = new class implements TemplateComponent {
|
|
7
|
+
public globalScope?: Scope;
|
|
8
|
+
public constructor(private engine: TemplateEngine) { }
|
|
9
|
+
public acceptNode(node: Element): boolean {
|
|
10
|
+
return node.hasAttribute("ref");
|
|
11
|
+
}
|
|
12
|
+
public walkthrough(walker: Walker<Scope>, node: Node, data?: Scope): boolean {
|
|
13
|
+
const element = node as Element;
|
|
14
|
+
const bool = this.acceptNode(element);
|
|
15
|
+
if (bool) {
|
|
16
|
+
const refAtt = element.getAttribute("ref")!;
|
|
17
|
+
const refName: string = new Expression(refAtt).eval(data!);
|
|
18
|
+
|
|
19
|
+
this.doWork({ element, refName, data });
|
|
16
20
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
public doWork(context?: { element: Element, refName: string, data?: Scope }): void {
|
|
24
|
+
context!.data!.set(context!.refName, context!.element);
|
|
25
|
+
this.globalScope!.set(context!.refName, context!.element);
|
|
26
|
+
|
|
27
|
+
this.engine.bindings.set(context!.element, () => {
|
|
28
|
+
context!.data!.delete(context!.refName);
|
|
29
|
+
this.globalScope!.delete(context!.refName);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}(this);
|
|
33
|
+
private readonly on_component: TemplateComponent = new class implements TemplateComponent {
|
|
34
|
+
public constructor(private engine: TemplateEngine) { }
|
|
35
|
+
public acceptNode(node: Element): boolean {
|
|
36
|
+
return Array.from(node.attributes).some(attr => /^on\[[^\]]+\]$/i.test(attr.name));
|
|
37
|
+
}
|
|
38
|
+
public walkthrough(walker: Walker<Scope>, node: Node, data?: Scope): boolean {
|
|
39
|
+
const element = node as Element;
|
|
40
|
+
const bool = this.acceptNode(element);
|
|
41
|
+
if (bool) {
|
|
42
|
+
const onAttribute = Array.from(element.attributes)
|
|
43
|
+
.find(attr => /^on\[[^\]]+\]$/i.test(attr.name));
|
|
44
|
+
const eventName = onAttribute!.name.substring(3, onAttribute!.name.length - 1);
|
|
45
|
+
const handler = new Expression(onAttribute.value);
|
|
46
|
+
const listener = (event: Event) => {
|
|
47
|
+
handler.eval(data!, { event });
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
element.addEventListener(eventName, listener);
|
|
51
|
+
this.engine.bindings.set(element, () => {
|
|
52
|
+
element.removeEventListener(eventName, listener);
|
|
53
|
+
});
|
|
21
54
|
}
|
|
55
|
+
return false;
|
|
22
56
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
public doWork(context?: { element: Element, refName: string, data?: Scope }): void {
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
}(this);
|
|
61
|
+
private readonly injection_component: TemplateComponent = new class implements TemplateComponent {
|
|
62
|
+
public constructor(private engine: TemplateEngine) { }
|
|
63
|
+
public acceptNode(node: Element): boolean {
|
|
64
|
+
return node.tagName === "INJECTION";
|
|
65
|
+
}
|
|
66
|
+
public walkthrough(walker: Walker<Scope>, node: Node, data?: Scope): boolean {
|
|
67
|
+
const element = node as Element;
|
|
68
|
+
const bool = this.acceptNode(element);
|
|
69
|
+
|
|
70
|
+
if (bool) {
|
|
71
|
+
function insideWork() {
|
|
72
|
+
const targetAtt = element.getAttribute("target")!;
|
|
73
|
+
const targetName: string = new Expression(targetAtt).eval(data!);
|
|
74
|
+
const at: string = element.getAttribute("at")!;
|
|
75
|
+
|
|
76
|
+
element.remove();
|
|
77
|
+
|
|
78
|
+
const targetElement = data!.get(targetName) as Element;
|
|
79
|
+
if (!targetElement) {
|
|
80
|
+
this.engine.processLogs.push(`Injection target '${targetName}' not found.`);
|
|
81
|
+
return true;
|
|
82
|
+
};
|
|
83
|
+
this.doWork({ element, target: targetElement, at: at as "head" | "tail" });
|
|
84
|
+
}
|
|
85
|
+
insideWork.call(this);
|
|
86
|
+
this.engine.onChange(() => insideWork.call(this));
|
|
87
|
+
}
|
|
88
|
+
return bool;
|
|
89
|
+
}
|
|
90
|
+
public doWork(context?: { element: Element, target: Element, at: "head" | "tail" }): void {
|
|
91
|
+
const { element, target, at } = context!;
|
|
92
|
+
if (at === "head") {
|
|
93
|
+
target.prepend(...element.childNodes);
|
|
94
|
+
} else {
|
|
95
|
+
target.append(...element.childNodes);
|
|
39
96
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
97
|
+
}
|
|
98
|
+
}(this);
|
|
99
|
+
private readonly exp_component: TemplateComponent = new class implements TemplateComponent {
|
|
100
|
+
public constructor(private engine: TemplateEngine) { }
|
|
101
|
+
public acceptNode(node: Element): boolean {
|
|
102
|
+
return node.tagName === "EXP";
|
|
103
|
+
}
|
|
104
|
+
public walkthrough(walker: Walker<Scope>, node: Node, data?: Scope): boolean {
|
|
105
|
+
const element = node as Element;
|
|
106
|
+
const bool = this.acceptNode(element);
|
|
107
|
+
if (bool) {
|
|
108
|
+
const vvv = element.getAttribute("of")!;
|
|
109
|
+
const of: string | any = new Expression(vvv).eval(data!);
|
|
110
|
+
|
|
111
|
+
const value = of instanceof Observable ? of.getObject() : of;
|
|
112
|
+
if (of instanceof Observable) {
|
|
113
|
+
of.subscribe((newValue: any[]) => {
|
|
114
|
+
element.textContent = "";
|
|
115
|
+
this.engine.change();
|
|
116
|
+
this.doWork({ element, value: newValue });
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
this.doWork({ element, value });
|
|
43
120
|
|
|
121
|
+
/*const textNode = document.createTextNode(of);
|
|
122
|
+
element.parentNode!.replaceChild(textNode, element);*/
|
|
44
123
|
}
|
|
124
|
+
return bool;
|
|
125
|
+
}
|
|
126
|
+
public doWork(context?: { element: Element, value: any }): void {
|
|
127
|
+
context.element.textContent = context.value;
|
|
128
|
+
}
|
|
129
|
+
}(this);
|
|
130
|
+
private readonly for_component: TemplateComponent = new class implements TemplateComponent {
|
|
131
|
+
public constructor(private engine: TemplateEngine) { }
|
|
132
|
+
public acceptNode(element: Element): boolean {
|
|
133
|
+
return element.tagName === "FOR";
|
|
45
134
|
}
|
|
46
|
-
|
|
135
|
+
public walkthrough(walker: Walker<Scope>, node: Node, data?: Scope): boolean {
|
|
136
|
+
const element = node as Element;
|
|
137
|
+
const bool = this.acceptNode(element);
|
|
138
|
+
if (bool) {
|
|
139
|
+
const shadow = TemplateEngine.transContentToShadow(element);
|
|
140
|
+
|
|
141
|
+
const index: string = element.getAttribute("index")!;
|
|
142
|
+
const value: string = element.getAttribute("value")!;
|
|
143
|
+
const vvv = element.getAttribute("of")!;
|
|
144
|
+
const of: string | any = new Expression(vvv).eval(data!);
|
|
145
|
+
|
|
146
|
+
const iterable = of instanceof Observable ? of.getObject() : of;
|
|
147
|
+
if (of instanceof Observable) {
|
|
148
|
+
of.subscribe((newValue: any[]) => {
|
|
149
|
+
this.engine.change();
|
|
150
|
+
this.doWork({ element, iterable: newValue, index, value, walker, shadow, data });
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
this.doWork({ element, iterable: iterable, index, value, walker, shadow, data });
|
|
154
|
+
}
|
|
155
|
+
return bool;
|
|
156
|
+
}
|
|
157
|
+
public doWork(context?: {
|
|
158
|
+
element: Element, iterable: any, index?: string,
|
|
159
|
+
value: string, walker: Walker<Scope>,
|
|
160
|
+
shadow: HTMLTemplateElement, data: Scope
|
|
161
|
+
}): void {
|
|
162
|
+
const conseg = [];
|
|
163
|
+
|
|
164
|
+
const element = context!.element;
|
|
165
|
+
const iterable = context!.iterable;
|
|
166
|
+
const index = context!.index;
|
|
167
|
+
const value = context!.value;
|
|
168
|
+
const walker = context!.walker;
|
|
169
|
+
const shadow = context!.shadow;
|
|
170
|
+
const data = context!.data;
|
|
171
|
+
|
|
172
|
+
element.childNodes.forEach(n => this.engine.fullCleanup(n));
|
|
173
|
+
|
|
174
|
+
const lenght = typeof iterable === "number" ? iterable : iterable.length;
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < lenght; i++) {
|
|
177
|
+
const z = i;
|
|
178
|
+
const nestedScope = data.createChild();
|
|
179
|
+
|
|
180
|
+
const setToScope = typeof iterable === "number" ? z : (iterable as any)[z];
|
|
181
|
+
|
|
182
|
+
if (index) nestedScope.set(index, z);
|
|
183
|
+
nestedScope.set(value, setToScope);
|
|
184
|
+
|
|
185
|
+
const block = document.createElement("for-temporary-block");
|
|
186
|
+
block.appendChild(shadow.content.cloneNode(true));
|
|
187
|
+
|
|
188
|
+
walker.walk(block, nestedScope);
|
|
189
|
+
|
|
190
|
+
conseg.push(block);
|
|
191
|
+
}
|
|
192
|
+
conseg.forEach(c => {
|
|
193
|
+
element.append(...c.childNodes);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
}(this);
|
|
198
|
+
private readonly if_component: TemplateComponent = new class implements TemplateComponent {
|
|
199
|
+
public constructor(private engine: TemplateEngine) { }
|
|
200
|
+
public acceptNode(node: Element): boolean {
|
|
201
|
+
return node.tagName === "IF"
|
|
202
|
+
|| node.tagName === "ELSEIF"
|
|
203
|
+
|| node.tagName === "ELSE";
|
|
204
|
+
}
|
|
205
|
+
public walkthrough(walker: Walker<Scope>, node: Node, data?: Scope): boolean {
|
|
206
|
+
const element = node as Element;
|
|
207
|
+
const bool = element.tagName === "IF";
|
|
208
|
+
if (bool) {
|
|
209
|
+
const allParts: { element: Element, shadow: HTMLTemplateElement }[] = [];
|
|
210
|
+
allParts.push({ element, shadow: TemplateEngine.transContentToShadow(element) });
|
|
211
|
+
|
|
212
|
+
let point: Node = element;
|
|
213
|
+
while (true) {
|
|
214
|
+
if (!point.nextSibling) break;
|
|
215
|
+
point = point.nextSibling;
|
|
216
|
+
if (!point || point.nodeType !== Node.ELEMENT_NODE) continue;
|
|
217
|
+
if (!point || !this.acceptNode(point as Element)) break;
|
|
218
|
+
allParts.push({ element: point as Element, shadow: TemplateEngine.transContentToShadow(point as Element) });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for (const part of allParts) {
|
|
222
|
+
const conditionAtt = part.element.getAttribute('condition');
|
|
223
|
+
if (!conditionAtt) continue;
|
|
224
|
+
const condition = new Expression(conditionAtt).eval(data!);
|
|
225
|
+
|
|
226
|
+
if (condition instanceof Observable) {
|
|
227
|
+
condition.subscribe((newValue: boolean) => {
|
|
228
|
+
this.engine.change();
|
|
229
|
+
this.doWork({ walker, data, allParts: allParts });
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.doWork({ walker, data, allParts: allParts });
|
|
235
|
+
|
|
236
|
+
}
|
|
237
|
+
return bool;
|
|
238
|
+
}
|
|
239
|
+
public doWork(context?: {
|
|
240
|
+
walker: Walker<Scope>,
|
|
241
|
+
data: Scope,
|
|
242
|
+
allParts: { element: Element, shadow: HTMLTemplateElement, condition?: string }[]
|
|
243
|
+
}): void {
|
|
244
|
+
const { walker, data, allParts } = context!;
|
|
245
|
+
|
|
246
|
+
allParts.forEach(part => {
|
|
247
|
+
this.engine.fullCleanup(part.element);
|
|
248
|
+
/*part.element.childNodes.forEach(n =>
|
|
249
|
+
this.engine.fullCleanup(n));*/
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
for (const part of allParts) {
|
|
253
|
+
const conditionAtt = part.element.getAttribute('condition');
|
|
254
|
+
if (!conditionAtt && part.element.tagName === "ELSE") {
|
|
255
|
+
const block = document.createElement("if-temporary-block");
|
|
256
|
+
block.appendChild(part.shadow.content.cloneNode(true));
|
|
257
|
+
|
|
258
|
+
walker.walk(block, data);
|
|
259
|
+
part.element.append(...block.childNodes);
|
|
260
|
+
|
|
261
|
+
break;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const condition = new Expression(conditionAtt).eval(data!);
|
|
265
|
+
const conditionValue = condition instanceof Observable ? condition.getObject() : condition;
|
|
266
|
+
|
|
267
|
+
if (conditionValue) {
|
|
268
|
+
const block = document.createElement("if-temporary-block");
|
|
269
|
+
block.appendChild(part.shadow.content.cloneNode(true));
|
|
270
|
+
|
|
271
|
+
walker.walk(block, data);
|
|
272
|
+
part.element.append(...block.childNodes);
|
|
273
|
+
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}(this);
|
|
279
|
+
private readonly components: TemplateComponent[] = [
|
|
280
|
+
this.ref_component, this.on_component, this.injection_component,
|
|
281
|
+
this.exp_component, this.for_component, this.if_component
|
|
282
|
+
];
|
|
283
|
+
public readonly bindings: Map<Element, () => void> = new Map();
|
|
284
|
+
private readonly onChangeCallbacks: (() => void)[] = [];
|
|
285
|
+
public processLogs: string[] = [];
|
|
286
|
+
public static fullProcess(root: Node, scope: Scope): string[] {
|
|
287
|
+
const engine = new TemplateEngine();
|
|
288
|
+
engine.fullProcess(root, scope);
|
|
289
|
+
return engine.processLogs;
|
|
290
|
+
}
|
|
291
|
+
public static createHolder(markup: string, scope: Scope): TemplateHolder {
|
|
292
|
+
const engine = new TemplateEngine();
|
|
293
|
+
const fragment = document.createRange().createContextualFragment(markup);
|
|
294
|
+
engine.fullProcess(fragment, scope);
|
|
295
|
+
return new TemplateHolder(engine, fragment);
|
|
296
|
+
}
|
|
297
|
+
private change() {
|
|
298
|
+
this.processLogs.push('--- Change triggered ---');
|
|
299
|
+
this.onChangeCallbacks.forEach(cb => cb());
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
public onChange(funct: () => void) {
|
|
303
|
+
this.onChangeCallbacks.push(funct);
|
|
47
304
|
}
|
|
48
|
-
public
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const expr = new Expression(attr.value);
|
|
62
|
-
element.addEventListener(eventName, (e) => {});
|
|
63
|
-
});5
|
|
305
|
+
public fullProcess(root: Node, scope: Scope) {
|
|
306
|
+
this.processLogs = [];
|
|
307
|
+
(this.ref_component as any).globalScope = scope;
|
|
308
|
+
|
|
309
|
+
const walker = new Walker<Scope>(root, {
|
|
310
|
+
nodeFilter: NodeFilter.SHOW_ELEMENT,
|
|
311
|
+
walkerFunction: (walker: Walker<Scope>, node: Node, data?: Scope) => {
|
|
312
|
+
for (const component of this.components)
|
|
313
|
+
if (component.walkthrough?.call(component, walker, node, data)) return;
|
|
314
|
+
|
|
315
|
+
node.childNodes.forEach(child => {
|
|
316
|
+
walker.walk(child, data);
|
|
317
|
+
});
|
|
64
318
|
}
|
|
319
|
+
});
|
|
320
|
+
let i = 0;
|
|
321
|
+
|
|
322
|
+
walker.onEnterNode((node: Node, data?: Scope) => {
|
|
323
|
+
this.processLogs.push((" ").repeat(i) + "layer " + i + " Entering: " + node.nodeName + " - Vars: " + Object.keys(data.getVariables()).join(", "));
|
|
324
|
+
i++;
|
|
325
|
+
});
|
|
326
|
+
walker.onLeaveNode((node: Node, data?: Scope) => {
|
|
327
|
+
i--;
|
|
328
|
+
this.processLogs.push((" ").repeat(i) + "layer " + i + " Leaving: " + node.nodeName + " - Vars: " + Object.keys(data.getVariables()).join(", "));
|
|
329
|
+
});
|
|
330
|
+
walker.walk(null, scope);
|
|
331
|
+
}
|
|
332
|
+
public fullCleanup(root: Node): void {
|
|
333
|
+
const walker = new Walker<Scope>(root, {
|
|
334
|
+
nodeFilter: NodeFilter.SHOW_ELEMENT
|
|
335
|
+
});
|
|
336
|
+
let i = 0;
|
|
337
|
+
walker.onEnterNode((node: Node, data?: Scope) => {
|
|
338
|
+
const element = node as Element;
|
|
339
|
+
const binding = this.bindings.get(element);
|
|
340
|
+
if (binding) binding();
|
|
341
|
+
|
|
342
|
+
this.processLogs.push((" ").repeat(i) + "layer " + i + " Cleaning up Starting: " + node.nodeName);
|
|
343
|
+
i++;
|
|
344
|
+
});
|
|
345
|
+
walker.onLeaveNode((node: Node, data?: Scope) => {
|
|
346
|
+
const element = node as Element;
|
|
347
|
+
element.innerHTML = "";
|
|
348
|
+
|
|
349
|
+
i--;
|
|
350
|
+
this.processLogs.push((" ").repeat(i) + "layer " + i + " Cleaning up Ending: " + node.nodeName);
|
|
351
|
+
});
|
|
352
|
+
walker.walk(root);
|
|
353
|
+
}
|
|
354
|
+
private static transContentToShadow(root: Element): HTMLTemplateElement {
|
|
355
|
+
const template = document.createElement("template");
|
|
356
|
+
while (root.firstChild) {
|
|
357
|
+
template.content.appendChild(root.firstChild);
|
|
65
358
|
}
|
|
66
|
-
return
|
|
359
|
+
return template;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
export class TemplateHolder {
|
|
363
|
+
public readonly engine: TemplateEngine;
|
|
364
|
+
public documentFragment: HTMLElement | DocumentFragment;
|
|
365
|
+
|
|
366
|
+
public constructor(engine: TemplateEngine, documentFragment: DocumentFragment) {
|
|
367
|
+
this.engine = engine;
|
|
368
|
+
this.documentFragment = documentFragment;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
public pushTo(element: HTMLElement | DocumentFragment): void {
|
|
372
|
+
element.appendChild(this.documentFragment);
|
|
373
|
+
this.documentFragment = element;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
export class Walker<D> {
|
|
377
|
+
private onEnterNodeCallbacks: ((node: Node, data?: D) => void)[] = [];
|
|
378
|
+
private onLeaveNodeCallbacks: ((node: Node, data?: D) => void)[] = [];
|
|
379
|
+
|
|
380
|
+
private root?: Node;
|
|
381
|
+
public filter?: (node: Node) => number;
|
|
382
|
+
private walkerFunction?: (walker: Walker<D>, node: Node, data?: D) => void;
|
|
383
|
+
private nodeFilter: number;
|
|
384
|
+
|
|
385
|
+
public constructor(root: Node, settings?: {
|
|
386
|
+
nodeFilter?: number,
|
|
387
|
+
filter?: (node: Node) => number,
|
|
388
|
+
walkerFunction?: (walker: Walker<D>, node: Node, data?: D) => void
|
|
389
|
+
}) {
|
|
390
|
+
|
|
391
|
+
this.root = root;
|
|
392
|
+
this.filter = settings?.filter;
|
|
393
|
+
this.nodeFilter = settings?.nodeFilter || NodeFilter.SHOW_ALL;
|
|
394
|
+
this.walkerFunction = settings?.walkerFunction;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
public walk(node?: Node, data?: D, walkerFunction?: (walker: Walker<D>, node: Node, data?: D) => void): void {
|
|
398
|
+
if (!node) node = this.root!;
|
|
399
|
+
if (!walkerFunction) walkerFunction = this.walkerFunction;
|
|
400
|
+
if (!this.matchesMask(node, this.nodeFilter)) return;
|
|
401
|
+
|
|
402
|
+
this.onEnterNodeCallbacks.forEach(cb => cb(node, data));
|
|
403
|
+
|
|
404
|
+
if (walkerFunction) {
|
|
405
|
+
walkerFunction(this, node, data);
|
|
406
|
+
} else {
|
|
407
|
+
node.childNodes.forEach(child => {
|
|
408
|
+
if (this.filter && this.filter(child) !== NodeFilter.FILTER_ACCEPT) return;
|
|
409
|
+
this.walk(child, data);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
this.onLeaveNodeCallbacks.forEach(cb => cb(node, data));
|
|
413
|
+
}
|
|
414
|
+
private matchesMask(node: Node, mask: number): boolean {
|
|
415
|
+
return (mask & (1 << (node.nodeType - 1))) !== 0;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
public onEnterNode(callback: (node: Node, data?: D) => void) {
|
|
419
|
+
this.onEnterNodeCallbacks.push(callback);
|
|
420
|
+
}
|
|
421
|
+
public onLeaveNode(callback: (node: Node, data?: D) => void) {
|
|
422
|
+
this.onLeaveNodeCallbacks.push(callback);
|
|
67
423
|
}
|
|
68
|
-
}
|
|
424
|
+
}
|
|
425
|
+
export interface TemplateComponent {
|
|
426
|
+
acceptNode(element: Element): boolean;
|
|
427
|
+
walkthrough?(walker: Walker<Scope>, node: Node, data?: Scope): boolean;
|
|
428
|
+
doWork?(context?: any): void;
|
|
429
|
+
}
|