@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.
Files changed (37) hide show
  1. package/out/foundation/Triplet.d.ts.map +1 -1
  2. package/out/foundation/Triplet.js +3 -5
  3. package/out/foundation/Triplet.js.map +1 -1
  4. package/out/foundation/component_api/Component.d.ts +2 -2
  5. package/out/foundation/component_api/Component.d.ts.map +1 -1
  6. package/out/foundation/component_api/Component.js.map +1 -1
  7. package/out/foundation/component_api/UniHtml.d.ts +4 -4
  8. package/out/foundation/component_api/UniHtml.d.ts.map +1 -1
  9. package/out/foundation/component_api/UniHtml.js +4 -11
  10. package/out/foundation/component_api/UniHtml.js.map +1 -1
  11. package/out/foundation/engine/Expression.d.ts.map +1 -1
  12. package/out/foundation/engine/Expression.js.map +1 -1
  13. package/out/foundation/engine/TemplateEngine.d.ts +45 -3
  14. package/out/foundation/engine/TemplateEngine.d.ts.map +1 -1
  15. package/out/foundation/engine/TemplateEngine.js +385 -49
  16. package/out/foundation/engine/TemplateEngine.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/foundation/Triplet.ts +5 -6
  19. package/src/foundation/component_api/Component.ts +2 -1
  20. package/src/foundation/component_api/UniHtml.ts +14 -21
  21. package/src/foundation/engine/Expression.ts +1 -2
  22. package/src/foundation/engine/TemplateEngine.ts +414 -53
  23. package/src/foundation/engine/BalancedParser.ts +0 -353
  24. package/src/foundation/engine/EscapeHandler.ts +0 -54
  25. package/src/foundation/engine/Rule.ts +0 -138
  26. package/src/foundation/engine/TemplateEngine.old.ts +0 -318
  27. package/src/foundation/engine/TemplateInstance.md +0 -110
  28. package/src/foundation/engine/TemplateInstance.old.ts +0 -673
  29. package/src/foundation/engine/TemplateInstance.ts +0 -843
  30. package/src/foundation/engine/exceptions/TemplateExceptions.ts +0 -27
  31. package/src/foundation/engine/rules/attribute/EventRule.ts +0 -171
  32. package/src/foundation/engine/rules/attribute/InjectionRule.ts +0 -140
  33. package/src/foundation/engine/rules/attribute/RefRule.ts +0 -126
  34. package/src/foundation/engine/rules/syntax/ExpressionRule.ts +0 -102
  35. package/src/foundation/engine/rules/syntax/ForRule.ts +0 -267
  36. package/src/foundation/engine/rules/syntax/IfRule.ts +0 -261
  37. 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
- public static process(root: Node, onlyRoot: boolean = false): Node[] {
6
- let elements: Node[] = [];
7
- const walker = document.createTreeWalker(
8
- root,
9
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
10
- );
11
- if (onlyRoot) {
12
- walker.nextNode();
13
- while (walker.nextSibling()) {
14
- const node = walker.currentNode;
15
- elements.push(node);
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
- } else {
18
- while (walker.nextNode()) {
19
- const node = walker.currentNode;
20
- elements.push(node);
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
- return elements;
24
- }
25
- public static processFors(root: Node, scope: Scope): Scope {
26
- const walker = document.createTreeWalker(
27
- root,
28
- NodeFilter.SHOW_TEXT,
29
- );
30
-
31
-
32
- let leftCircleBracketCount = 0;
33
- let rightCircleBracketCount = 0;
34
- while (walker.nextNode()) {
35
- const node = walker.currentNode;
36
- if (node.textContent?.includes("@for(")) {
37
- leftCircleBracketCount = 1;
38
- rightCircleBracketCount = 0;
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
- if (node.textContent?.includes("(")) leftCircleBracketCount++;
41
- if (node.textContent?.includes(")")) rightCircleBracketCount++;
42
- if (leftCircleBracketCount > 0 && leftCircleBracketCount === rightCircleBracketCount) {
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
- return scope;
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 static processAttributes(root: Node, scope: Scope): Scope {
49
- const walker = document.createTreeWalker(
50
- root,
51
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
52
- );
53
-
54
- while (walker.nextNode()) {
55
- const node = walker.currentNode;
56
- if (node.nodeType === Node.ELEMENT_NODE) {
57
- const element = node as Element;
58
- if (element.hasAttribute("ref")) scope.set(element.getAttribute("ref")!, element);
59
- [...element.attributes].filter(a => /^on/.test(a.name)).forEach(attr => {
60
- const eventName = attr.name.substring(2);
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 scope;
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
+ }