@reidelsaltres/pureper 0.1.174 → 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/out/foundation/worker/Router.d.ts.map +1 -1
- package/out/foundation/worker/Router.js +4 -2
- package/out/foundation/worker/Router.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/worker/Router.ts +5 -3
- 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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Use static factory methods to create instances from an HTML file or string.
|
|
6
6
|
* Designed to replace legacy Page and Component base classes.
|
|
7
7
|
*/
|
|
8
|
-
import
|
|
8
|
+
import { TemplateHolder } from "../engine/TemplateEngine.js";
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -21,30 +21,29 @@ export default class UniHtml {
|
|
|
21
21
|
*/
|
|
22
22
|
public async load(element: HTMLElement | ShadowRoot): Promise<void> {;
|
|
23
23
|
await this.preInit();
|
|
24
|
-
const preHtml: DocumentFragment = await this._init();
|
|
25
|
-
const html: DocumentFragment = await this._postInit(preHtml);
|
|
26
24
|
|
|
27
|
-
const
|
|
25
|
+
const preHtml: TemplateHolder = await this._init();
|
|
26
|
+
const html: TemplateHolder = await this._postInit(preHtml);
|
|
28
27
|
|
|
29
|
-
const
|
|
28
|
+
const localRoot = html;
|
|
30
29
|
|
|
31
30
|
// ВАЖНО: preLoad() вызывается ДО монтирования в DOM/Shadow DOM.
|
|
32
31
|
// Для компонентов (UniHtmlComponent) на этом этапе ещё нельзя полагаться на this.shadowRoot —
|
|
33
32
|
// используйте переданный localRoot для подготовки DOM, данных и навешивания обработчиков.
|
|
34
33
|
// Это предпочтительный этап инициализации для компонентов.
|
|
35
|
-
await this.preLoad(
|
|
34
|
+
await this.preLoad(html);
|
|
36
35
|
// render() отвечает за помещение содержимого из localRoot в конечную цель (renderTarget).
|
|
37
36
|
// В UniHtmlComponent.render() после вызова базового render() происходит добавление wrapper в shadowRoot.
|
|
38
|
-
await this.render(
|
|
37
|
+
await this.render(html, element);
|
|
39
38
|
// postLoad() вызывается ПОСЛЕ render(). Для компонентов к этому моменту содержимое уже добавлено
|
|
40
39
|
// внутрь shadowRoot, и можно безопасно работать с this.shadowRoot, измерениями layout и т.п.
|
|
41
|
-
await this.postLoad(
|
|
40
|
+
await this.postLoad(html);
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
private async _postInit(html:
|
|
43
|
+
private async _postInit(html: TemplateHolder): Promise<TemplateHolder> {
|
|
45
44
|
throw new Error("Method not implemented.");
|
|
46
45
|
}
|
|
47
|
-
private async _init(): Promise<
|
|
46
|
+
private async _init(): Promise<TemplateHolder> {
|
|
48
47
|
throw new Error("Method not implemented.");
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -55,29 +54,23 @@ export default class UniHtml {
|
|
|
55
54
|
* РЕКОМЕНДАЦИЯ: предпочитайте выполнять основную подготовку, поиск элементов, навешивание обработчиков
|
|
56
55
|
* на узлы из localRoot именно здесь; затем render() вставит их в целевой контейнер/теневой DOM.
|
|
57
56
|
*/
|
|
58
|
-
protected async preLoad(holder :
|
|
57
|
+
protected async preLoad(holder : TemplateHolder) { }
|
|
59
58
|
/**
|
|
60
59
|
* Hook after rendering (e.g., event binding).
|
|
61
60
|
* Для компонентов вызывается после того, как содержимое вставлено в shadowRoot (см. UniHtmlComponent.render()).
|
|
62
61
|
* Используйте этот этап только когда необходим доступ к реально смонтированному DOM (layout/measurements,
|
|
63
62
|
* интеграции, требующие присутствия в документе). В остальных случаях предпочитайте preLoad().
|
|
64
63
|
*/
|
|
65
|
-
protected async postLoad(holder:
|
|
64
|
+
protected async postLoad(holder: TemplateHolder) { }
|
|
66
65
|
/**
|
|
67
66
|
* Main rendering step. By default, simply inserts HTML into the container.
|
|
68
67
|
* Override in subclasses for custom rendering logic.
|
|
69
68
|
* @param element Target container
|
|
70
69
|
* @param html HTML content
|
|
71
70
|
*/
|
|
72
|
-
protected async render(holder:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
for (const child of children) {
|
|
76
|
-
renderTarget.appendChild(child);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Update holder to point to renderTarget (now contains the content)
|
|
80
|
-
(holder as { element: HTMLElement | DocumentFragment }).element = renderTarget;
|
|
71
|
+
protected async render(holder: TemplateHolder, renderTarget: HTMLElement | DocumentFragment): Promise<void> {
|
|
72
|
+
holder.pushTo(renderTarget);
|
|
73
|
+
|
|
81
74
|
return Promise.resolve();
|
|
82
75
|
}
|
|
83
76
|
|
|
@@ -143,7 +143,7 @@ export default class Expression {
|
|
|
143
143
|
*/
|
|
144
144
|
public execute(scope: Scope, extraVars?: Record<string, any>): any {
|
|
145
145
|
const context = scope.getVariables();
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
if (extraVars) {
|
|
148
148
|
Object.assign(context, extraVars);
|
|
149
149
|
}
|
|
@@ -164,7 +164,6 @@ export default class Expression {
|
|
|
164
164
|
*/
|
|
165
165
|
public async executeAsync(scope: Scope, extraVars?: Record<string, any>): Promise<any> {
|
|
166
166
|
const context = scope.getVariables();
|
|
167
|
-
|
|
168
167
|
if (extraVars) {
|
|
169
168
|
Object.assign(context, extraVars);
|
|
170
169
|
}
|
|
@@ -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
|
+
}
|
|
@@ -62,13 +62,15 @@ export abstract class Router {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
let pageContainer = document.getElementById('page');
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
let cloneContainer = pageContainer.cloneNode(false) as HTMLElement;
|
|
67
66
|
|
|
68
67
|
const page: UniHtml = this.createPage(found, urlH.searchParams);
|
|
69
68
|
this.currentPage = page;
|
|
70
69
|
|
|
71
|
-
page.load(
|
|
70
|
+
page.load(cloneContainer!);
|
|
71
|
+
pageContainer.replaceWith(cloneContainer);
|
|
72
|
+
pageContainer = cloneContainer;
|
|
73
|
+
|
|
72
74
|
if (pushState && typeof window !== "undefined" && window.location) {
|
|
73
75
|
window.history.pushState({}, '', urlH.href);
|
|
74
76
|
}
|