vsn 0.1.125 → 0.1.127
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/demo/demo.html +4 -8
- package/demo/examples/component-slots.html +16 -22
- package/demo/examples/loop.html +21 -0
- package/demo/resources/xhr-test-component.html +4 -0
- package/demo/vsn.js +2 -2
- package/dist/AST/ElementQueryNode.d.ts +2 -1
- package/dist/AST/ElementQueryNode.js +5 -5
- package/dist/AST/ElementQueryNode.js.map +1 -1
- package/dist/AST/FunctionNode.d.ts +1 -1
- package/dist/AST/LoopNode.d.ts +12 -0
- package/dist/AST/LoopNode.js +121 -0
- package/dist/AST/LoopNode.js.map +1 -0
- package/dist/AST/OnNode.d.ts +1 -1
- package/dist/AST/OnNode.js.map +1 -1
- package/dist/AST.d.ts +64 -62
- package/dist/AST.js +82 -69
- package/dist/AST.js.map +1 -1
- package/dist/Attribute.d.ts +1 -0
- package/dist/Attribute.js +6 -1
- package/dist/Attribute.js.map +1 -1
- package/dist/Component.d.ts +2 -0
- package/dist/Component.js +12 -27
- package/dist/Component.js.map +1 -1
- package/dist/DOM/AbstractDOM.d.ts +48 -0
- package/dist/DOM/AbstractDOM.js +937 -0
- package/dist/DOM/AbstractDOM.js.map +1 -0
- package/dist/DOM/ShadowDOM.d.ts +5 -0
- package/dist/DOM/ShadowDOM.js +32 -0
- package/dist/DOM/ShadowDOM.js.map +1 -0
- package/dist/DOM.d.ts +3 -48
- package/dist/DOM.js +6 -913
- package/dist/DOM.js.map +1 -1
- package/dist/Tag.d.ts +2 -0
- package/dist/Tag.js +119 -20
- package/dist/Tag.js.map +1 -1
- package/dist/helpers/VisionHelper.d.ts +1 -0
- package/dist/helpers/VisionHelper.js +45 -0
- package/dist/helpers/VisionHelper.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/vsn.js +2 -7
- package/dist/vsn.js.map +1 -1
- package/package.json +1 -1
- package/src/AST/ElementQueryNode.ts +2 -1
- package/src/AST/FunctionNode.ts +1 -1
- package/src/AST/LoopNode.ts +35 -0
- package/src/AST/OnNode.ts +2 -2
- package/src/AST.ts +19 -7
- package/src/Attribute.ts +3 -1
- package/src/Component.ts +15 -13
- package/src/DOM/AbstractDOM.ts +389 -0
- package/src/DOM/ShadowDOM.ts +15 -0
- package/src/DOM.ts +4 -385
- package/src/Tag.ts +31 -0
- package/src/helpers/VisionHelper.ts +6 -0
- package/src/version.ts +1 -1
- package/src/vsn.ts +2 -5
- package/test/AST/ClassNode.spec.ts +5 -5
- package/test/AST.spec.ts +21 -21
- package/test/Controller.spec.ts +1 -1
- package/test/DOM.spec.ts +3 -3
- package/test/Tag/TagList.spec.ts +1 -1
- package/test/attributes/Bind.spec.ts +12 -12
- package/test/attributes/JSONAttribute.spec.ts +5 -5
- package/test/attributes/ListItem.spec.ts +6 -6
- package/test/attributes/ScopeAttribute.spec.ts +2 -2
- package/test/attributes/ServiceAttribute.spec.ts +1 -1
- package/test/attributes/SetAttribute.spec.ts +3 -3
- package/test/attributes/Styles.spec.ts +2 -2
package/src/AST.ts
CHANGED
|
@@ -34,6 +34,7 @@ import {DispatchEventNode} from "./AST/DispatchEventNode";
|
|
|
34
34
|
import {WithNode} from "./AST/WithNode";
|
|
35
35
|
import {AsNode} from "./AST/AsNode";
|
|
36
36
|
import {NamedStackNode} from "./AST/NamedStackNode";
|
|
37
|
+
import {LoopNode} from "./AST/LoopNode";
|
|
37
38
|
|
|
38
39
|
function lower(str: string): string {
|
|
39
40
|
return str ? str.toLowerCase() : null;
|
|
@@ -65,6 +66,7 @@ export enum TokenType {
|
|
|
65
66
|
TYPE_UINT,
|
|
66
67
|
TYPE_FLOAT,
|
|
67
68
|
TYPE_STRING,
|
|
69
|
+
TYPE_BOOL,
|
|
68
70
|
RETURN,
|
|
69
71
|
NOT,
|
|
70
72
|
OF,
|
|
@@ -77,6 +79,7 @@ export enum TokenType {
|
|
|
77
79
|
ELSE_IF,
|
|
78
80
|
ELSE,
|
|
79
81
|
FUNC,
|
|
82
|
+
LOOP,
|
|
80
83
|
ON,
|
|
81
84
|
CLASS,
|
|
82
85
|
NAME,
|
|
@@ -156,24 +159,24 @@ const TOKEN_PATTERNS: TokenPattern[] = [
|
|
|
156
159
|
},
|
|
157
160
|
{
|
|
158
161
|
type: TokenType.TYPE_INT,
|
|
159
|
-
pattern: /^int
|
|
162
|
+
pattern: /^int\s/
|
|
160
163
|
},
|
|
161
164
|
{
|
|
162
165
|
type: TokenType.TYPE_UINT,
|
|
163
|
-
pattern: /^uint
|
|
166
|
+
pattern: /^uint\s/
|
|
164
167
|
},
|
|
165
168
|
{
|
|
166
169
|
type: TokenType.TYPE_FLOAT,
|
|
167
|
-
pattern: /^float
|
|
170
|
+
pattern: /^float\s/
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
type: TokenType.TYPE_BOOL,
|
|
174
|
+
pattern: /^bool\s/
|
|
168
175
|
},
|
|
169
176
|
{
|
|
170
177
|
type: TokenType.UNIT,
|
|
171
178
|
pattern: /^\d+\.?\d?(?:cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|%)/
|
|
172
179
|
},
|
|
173
|
-
{
|
|
174
|
-
type: TokenType.TYPE_STRING,
|
|
175
|
-
pattern: /^string+/
|
|
176
|
-
},
|
|
177
180
|
{
|
|
178
181
|
type: TokenType.BOOLEAN_LITERAL,
|
|
179
182
|
pattern: /^(true|false)/
|
|
@@ -230,6 +233,10 @@ const TOKEN_PATTERNS: TokenPattern[] = [
|
|
|
230
233
|
type: TokenType.FUNC,
|
|
231
234
|
pattern: /^func\s/
|
|
232
235
|
},
|
|
236
|
+
{
|
|
237
|
+
type: TokenType.LOOP,
|
|
238
|
+
pattern: /^loop\s/
|
|
239
|
+
},
|
|
233
240
|
{
|
|
234
241
|
type: TokenType.ON,
|
|
235
242
|
pattern: /^on\s/
|
|
@@ -577,6 +584,11 @@ export class Tree {
|
|
|
577
584
|
lastBlock = node;
|
|
578
585
|
blockNodes.push(node);
|
|
579
586
|
node = null;
|
|
587
|
+
} else if (token.type === TokenType.LOOP) {
|
|
588
|
+
node = LoopNode.parse(node, token, tokens);
|
|
589
|
+
lastBlock = node;
|
|
590
|
+
blockNodes.push(node);
|
|
591
|
+
node = null;
|
|
580
592
|
} else if (token.type === TokenType.ON) {
|
|
581
593
|
node = OnNode.parse(node, token, tokens);
|
|
582
594
|
lastBlock = node;
|
package/src/Attribute.ts
CHANGED
|
@@ -61,10 +61,12 @@ export abstract class Attribute extends EventDispatcher {
|
|
|
61
61
|
this.setState(AttributeState.Connected);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
public async
|
|
64
|
+
public async finalize() {
|
|
65
65
|
this.setState(AttributeState.Built);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
public async evaluate() {}
|
|
69
|
+
|
|
68
70
|
public getAttributeValue(fallback: any = null) {
|
|
69
71
|
return this.origin.getRawAttributeValue(this.attributeName, fallback);
|
|
70
72
|
}
|
package/src/Component.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import {Registry} from "./Registry";
|
|
2
|
-
import {DOM} from "./DOM";
|
|
3
2
|
import {SlotTag} from "./Tag/SlotTag";
|
|
4
3
|
import {SlottedTag} from "./Tag/SlottedTag";
|
|
4
|
+
import {ShadowDOM} from "./DOM/ShadowDOM";
|
|
5
|
+
import {DOM} from "./DOM";
|
|
5
6
|
|
|
6
7
|
export class Component extends HTMLElement {
|
|
7
8
|
protected readonly shadow: ShadowRoot;
|
|
9
|
+
protected readonly shadowDOM: ShadowDOM;
|
|
8
10
|
|
|
9
11
|
constructor() {
|
|
10
12
|
super();
|
|
11
13
|
Object.setPrototypeOf(this, Component.prototype);
|
|
12
14
|
|
|
15
|
+
this.shadowDOM = new ShadowDOM(DOM.instance, this, false);
|
|
13
16
|
this.shadow = this.attachShadow({mode: 'open'});
|
|
17
|
+
|
|
14
18
|
const templateId = this.getAttribute('template');
|
|
15
19
|
let template: HTMLTemplateElement;
|
|
16
20
|
|
|
@@ -28,34 +32,32 @@ export class Component extends HTMLElement {
|
|
|
28
32
|
const slotPromises = [];
|
|
29
33
|
const tagsToSetup = [];
|
|
30
34
|
this.shadow.querySelectorAll('slot').forEach((slot) => {
|
|
31
|
-
const slotTagPromise =
|
|
35
|
+
const slotTagPromise = this.shadowDOM.buildTag(slot,false, SlotTag);
|
|
32
36
|
const promise = new Promise<SlotTag>((resolve, reject) => {
|
|
33
37
|
slot.addEventListener('slotchange', (e) => {
|
|
34
38
|
slotTagPromise.then(async (slotTag) => {
|
|
35
|
-
|
|
36
39
|
for (const child of slot.assignedNodes()) {
|
|
37
|
-
const t = await
|
|
40
|
+
const t = await this.shadowDOM.buildTag<SlottedTag>(child as HTMLElement, false, SlottedTag);
|
|
38
41
|
await t?.slotted(slotTag);
|
|
39
42
|
tagsToSetup.push(t);
|
|
40
43
|
}
|
|
41
44
|
resolve(slotTag);
|
|
42
45
|
});
|
|
43
46
|
});
|
|
44
|
-
})
|
|
47
|
+
});
|
|
45
48
|
slotPromises.push(promise);
|
|
46
49
|
});
|
|
47
50
|
Promise.all(slotPromises).then(async (slotTags: SlotTag[]) => {
|
|
48
|
-
|
|
49
|
-
await DOM.instance.setupTags(slotTags);
|
|
50
|
-
await DOM.instance.setupTags(tagsToSetup);
|
|
51
|
+
console.log('Building after slot setup', this);
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
async connectedCallback() {
|
|
55
|
-
const tag = await
|
|
56
|
-
tag.createScope(true);
|
|
57
|
-
|
|
58
|
-
await
|
|
59
|
-
await tag.dom.
|
|
56
|
+
//const tag = await this.shadowDOM.buildTag(this, true);
|
|
57
|
+
//tag.createScope(true);
|
|
58
|
+
console.log('Building from shadow', this.shadow);
|
|
59
|
+
await this.shadowDOM.buildFrom(this.shadow, true, true);
|
|
60
|
+
///await tag.dom.resetBranch(tag);
|
|
61
|
+
//await tag.dom.setupTags([tag]);
|
|
60
62
|
}
|
|
61
63
|
}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import {Tag} from "../Tag";
|
|
2
|
+
import {WrappedWindow} from "./WrappedWindow";
|
|
3
|
+
import {WrappedDocument} from "./WrappedDocument";
|
|
4
|
+
import {Configuration} from "../Configuration";
|
|
5
|
+
import {TagList} from "../Tag/TagList";
|
|
6
|
+
import {Tree} from "../AST";
|
|
7
|
+
import {ClassNode} from "../AST/ClassNode";
|
|
8
|
+
import {Registry} from "../Registry";
|
|
9
|
+
import {ElementHelper} from "../helpers/ElementHelper";
|
|
10
|
+
import {SlotTag} from "../Tag/SlotTag";
|
|
11
|
+
import {SlottedTag} from "../Tag/SlottedTag";
|
|
12
|
+
import {Scope} from "../Scope";
|
|
13
|
+
import {EventDispatcher} from "../EventDispatcher";
|
|
14
|
+
|
|
15
|
+
export enum EQuerySelectDirection {
|
|
16
|
+
ALL,
|
|
17
|
+
UP,
|
|
18
|
+
DOWN
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export abstract class AbstractDOM extends EventDispatcher {
|
|
22
|
+
protected _root: Tag;
|
|
23
|
+
protected _ready: Promise<boolean>;
|
|
24
|
+
protected tags: Tag[];
|
|
25
|
+
protected observer: MutationObserver;
|
|
26
|
+
protected evaluateTimeout: any;
|
|
27
|
+
protected queued: HTMLElement[] = [];
|
|
28
|
+
protected window: WrappedWindow;
|
|
29
|
+
protected document: WrappedDocument;
|
|
30
|
+
protected _built: boolean = false;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
protected rootElement: HTMLElement,
|
|
34
|
+
build: boolean = true,
|
|
35
|
+
protected debug: boolean = false
|
|
36
|
+
) {
|
|
37
|
+
super();
|
|
38
|
+
this._ready = new Promise((resolve) => {
|
|
39
|
+
this.once('built', () => {
|
|
40
|
+
resolve(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
this.observer = new MutationObserver(this.mutation.bind(this));
|
|
45
|
+
this.tags = [];
|
|
46
|
+
|
|
47
|
+
this.window = new WrappedWindow(window);
|
|
48
|
+
this.document = new WrappedDocument(window.document);
|
|
49
|
+
|
|
50
|
+
if (build) {
|
|
51
|
+
this.buildFrom(rootElement, true);
|
|
52
|
+
}
|
|
53
|
+
this.evaluate();
|
|
54
|
+
Configuration.instance.on('change', this.evaluate.bind(this));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public get built(): boolean {
|
|
58
|
+
return this._built;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public get root(): Tag {
|
|
62
|
+
return this._root;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public get ready(): Promise<boolean> {
|
|
66
|
+
return this.promise('builtRoot');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public async get(selector: string, create: boolean = false, tag: Tag = null, direction: EQuerySelectDirection = EQuerySelectDirection.DOWN): Promise<TagList> {
|
|
70
|
+
switch (selector) {
|
|
71
|
+
case 'window':
|
|
72
|
+
return new TagList(this.window);
|
|
73
|
+
case 'document':
|
|
74
|
+
return new TagList(this.document);
|
|
75
|
+
case 'body':
|
|
76
|
+
return new TagList(this.root);
|
|
77
|
+
default:
|
|
78
|
+
let nodes;
|
|
79
|
+
if (direction === EQuerySelectDirection.DOWN) {
|
|
80
|
+
nodes = this.querySelectorAll(selector, tag);
|
|
81
|
+
} else if (direction === EQuerySelectDirection.UP) {
|
|
82
|
+
nodes = [this.querySelectorClosest(selector, tag)];
|
|
83
|
+
} else {
|
|
84
|
+
nodes = this.querySelectorAll(selector);
|
|
85
|
+
}
|
|
86
|
+
return await this.getTagsForElements(Array.from(nodes) as Element[], create);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public async getFromTag(tag: Tag, selector: string, create: boolean = false) {
|
|
91
|
+
const nodes = this.querySelectorElement(tag.element, selector);
|
|
92
|
+
return await this.getTagsForElements(Array.from(nodes) as Element[], create);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public registerElementInRoot(tag: Tag): void {
|
|
96
|
+
const id: string = tag.element.getAttribute('id');
|
|
97
|
+
if (!!id)
|
|
98
|
+
this.root.scope.set(`#${id}`, tag.scope);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public querySelectorClosest(q: string, tag: Tag = null): HTMLElement {
|
|
102
|
+
return tag.element.closest(q);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public querySelectPath(path: string[], element: HTMLElement = null): HTMLElement[] {
|
|
106
|
+
const current = path.shift();
|
|
107
|
+
if (!current) return [];
|
|
108
|
+
const elements = Array.from(element ? this.querySelectorElement(element, current) : this.querySelectorAll(current));
|
|
109
|
+
if (path.length > 0) {
|
|
110
|
+
const result = [];
|
|
111
|
+
for (const _element of elements) {
|
|
112
|
+
result.push(...this.querySelectPath([...path], _element as HTMLElement));
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return elements as HTMLElement[];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public querySelectorAll(q: string, tag: Tag = null): NodeList | HTMLElement[] {
|
|
121
|
+
const element: HTMLElement | Document = tag && !q.startsWith('#') ? tag.element : this.rootElement;
|
|
122
|
+
return this.querySelectorElement(element, q);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public querySelectorElement(element: HTMLElement | Document, q: string): NodeList | HTMLElement[] {
|
|
126
|
+
const parentIndex: number = q.indexOf(':parent');
|
|
127
|
+
if (parentIndex > -1) {
|
|
128
|
+
const _q = q.substring(0, parentIndex);
|
|
129
|
+
const rest = q.substring(parentIndex + 7);
|
|
130
|
+
if (_q.length > 0) {
|
|
131
|
+
const nodeList = [];
|
|
132
|
+
for (const _element of Array.from(this.querySelectorElement(element, _q))) {
|
|
133
|
+
if (rest.length > 0) {
|
|
134
|
+
nodeList.push(...Array.from(this.querySelectorElement(AbstractDOM.getParentElement(_element as HTMLElement), rest)));
|
|
135
|
+
} else {
|
|
136
|
+
nodeList.push(AbstractDOM.getParentElement(_element as HTMLElement));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return nodeList;
|
|
140
|
+
} else if (rest.length === 0) {
|
|
141
|
+
return [AbstractDOM.getParentElement(element as HTMLElement)];
|
|
142
|
+
} else {
|
|
143
|
+
return this.querySelectorElement(AbstractDOM.getParentElement(element as HTMLElement), rest);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
let matches = element.querySelectorAll(q);
|
|
147
|
+
|
|
148
|
+
if (matches.length === 0 && (element as HTMLElement).shadowRoot) {
|
|
149
|
+
matches = (element as HTMLElement).shadowRoot.querySelectorAll(q);
|
|
150
|
+
}
|
|
151
|
+
return matches;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public querySelector(q: string): Element {
|
|
155
|
+
return this.rootElement.querySelector(q);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public async exec(code: string) {
|
|
159
|
+
const tree = new Tree(code);
|
|
160
|
+
await tree.prepare(this.root.scope, this);
|
|
161
|
+
return await tree.evaluate(this.root.scope, this);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public async evaluate() {
|
|
165
|
+
clearTimeout(this.evaluateTimeout);
|
|
166
|
+
for (const tag of this.tags) {
|
|
167
|
+
await tag.evaluate();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public async mutation(mutations: MutationRecord[]) {
|
|
172
|
+
for (const mutation of mutations) {
|
|
173
|
+
let tag: Tag = await this.getTagForElement(mutation.target as HTMLElement);
|
|
174
|
+
if (tag) {
|
|
175
|
+
tag.mutate(mutation);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check for class changes
|
|
179
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
180
|
+
await ClassNode.checkForClassChanges(mutation.target as HTMLElement, this, tag);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for (const ele of Array.from(mutation.removedNodes)) {
|
|
184
|
+
const toRemove: Tag = await this.getTagForElement(ele as HTMLElement);
|
|
185
|
+
if (toRemove) {
|
|
186
|
+
toRemove.deconstruct();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async discover(ele: HTMLElement, forComponent: boolean = false): Promise<HTMLElement[]> {
|
|
193
|
+
const discovered: HTMLElement[] = [];
|
|
194
|
+
const checkElement = (e: HTMLElement): boolean => {
|
|
195
|
+
if (!forComponent && e?.tagName?.includes('-')) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (Registry.instance.tags.has(e?.tagName?.toLowerCase())) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (ElementHelper.hasVisionAttribute(e)) {
|
|
204
|
+
if (
|
|
205
|
+
(!forComponent && e.hasAttribute('slot'))
|
|
206
|
+
) return false;
|
|
207
|
+
if (this.queued.indexOf(e) > -1) return false;
|
|
208
|
+
this.queued.push(e);
|
|
209
|
+
discovered.push(e);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
const scanChildren = (e: HTMLElement) => {
|
|
215
|
+
for (const element of Array.from(e.children) as HTMLElement[]) {
|
|
216
|
+
if (!checkElement(element)) continue;
|
|
217
|
+
if (element.tagName.toLowerCase() !== 'template')
|
|
218
|
+
scanChildren(element);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
checkElement(ele);
|
|
222
|
+
scanChildren(ele);
|
|
223
|
+
|
|
224
|
+
return discovered;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async buildTag<T = Tag>(element: HTMLElement, returnExisting: boolean = false, cls: any = Tag): Promise<T> {
|
|
228
|
+
if (element[Tag.TaggedVariable]) return returnExisting ? element[Tag.TaggedVariable] : null;
|
|
229
|
+
if (element.tagName.toLowerCase() === 'slot')
|
|
230
|
+
cls = SlotTag;
|
|
231
|
+
else if (element.hasAttribute('slot'))
|
|
232
|
+
cls = SlottedTag;
|
|
233
|
+
|
|
234
|
+
const tag: T = new cls(element, this);
|
|
235
|
+
this.tags.push(tag as any);
|
|
236
|
+
return tag;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async setupTags(tags: Tag[]) {
|
|
240
|
+
// Configure, setup & execute attributes
|
|
241
|
+
for (const tag of tags)
|
|
242
|
+
await tag.buildAttributes();
|
|
243
|
+
|
|
244
|
+
for (const tag of tags)
|
|
245
|
+
await tag.compileAttributes();
|
|
246
|
+
|
|
247
|
+
for (const tag of tags)
|
|
248
|
+
await tag.setupAttributes();
|
|
249
|
+
|
|
250
|
+
for (const tag of tags)
|
|
251
|
+
await tag.extractAttributes();
|
|
252
|
+
|
|
253
|
+
for (const tag of tags)
|
|
254
|
+
await tag.connectAttributes();
|
|
255
|
+
|
|
256
|
+
for (const tag of tags) {
|
|
257
|
+
await tag.finalize();
|
|
258
|
+
this.queued.splice(this.queued.indexOf(tag.element), 1);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const tag of tags) {
|
|
262
|
+
this.observer.observe(tag.element, {
|
|
263
|
+
attributes: true,
|
|
264
|
+
characterData: true,
|
|
265
|
+
childList: true,
|
|
266
|
+
subtree: true
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async buildFrom(ele: any, isRoot: boolean = false, forComponent: boolean = false): Promise<Tag[]> {
|
|
272
|
+
if (isRoot) {
|
|
273
|
+
this.rootElement.setAttribute('vsn-root', '');
|
|
274
|
+
this._root = await this.buildTag(this.rootElement, true);
|
|
275
|
+
this._root.createScope(true);
|
|
276
|
+
await this.setupTags([this._root]);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Setup components first
|
|
280
|
+
const templateNodes = this.querySelectorElement(ele, 'template');
|
|
281
|
+
const components: Tag[] = [];
|
|
282
|
+
for (const n of Array.from(templateNodes) as HTMLElement[]) {
|
|
283
|
+
if (!ElementHelper.hasVisionAttribute(n))
|
|
284
|
+
continue;
|
|
285
|
+
|
|
286
|
+
const tag = await this.buildTag(n);
|
|
287
|
+
if (tag)
|
|
288
|
+
components.push(tag);
|
|
289
|
+
}
|
|
290
|
+
if (components.length)
|
|
291
|
+
await this.setupTags(components);
|
|
292
|
+
|
|
293
|
+
// Create tags for each html element with a vsn-attribute
|
|
294
|
+
const newTags: Tag[] = [];
|
|
295
|
+
const toBuild: HTMLElement[] = await this.discover(ele, forComponent);
|
|
296
|
+
|
|
297
|
+
for (const element of toBuild) {
|
|
298
|
+
const tag = await this.buildTag(element);
|
|
299
|
+
if (tag)
|
|
300
|
+
newTags.push(tag);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
await this.setupTags(newTags);
|
|
304
|
+
|
|
305
|
+
if (isRoot) {
|
|
306
|
+
this._built = true;
|
|
307
|
+
this.dispatch('builtRoot')
|
|
308
|
+
}
|
|
309
|
+
this.dispatch('built', newTags);
|
|
310
|
+
return newTags;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async getTagsForElements(elements: Element[], create: boolean = false) {
|
|
314
|
+
const tags: TagList = new TagList();
|
|
315
|
+
const found: Element[] = [];
|
|
316
|
+
|
|
317
|
+
for (const element of elements) {
|
|
318
|
+
if (element && element[Tag.TaggedVariable]) {
|
|
319
|
+
tags.push(element[Tag.TaggedVariable]);
|
|
320
|
+
found.push(element);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (create) {
|
|
325
|
+
const notFound: Element[] = [...elements];
|
|
326
|
+
for (let i = notFound.length; i >= 0; i--) {
|
|
327
|
+
const element: Element = notFound[i];
|
|
328
|
+
if (found.indexOf(element) > -1) {
|
|
329
|
+
notFound.splice(i, 1);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
for (const element of notFound) {
|
|
334
|
+
tags.push(await this.getTagForElement(element, create));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return tags;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async getTagForElement(element: Element, create: boolean = false, forComponent: boolean = false) {
|
|
342
|
+
if (element && element[Tag.TaggedVariable])
|
|
343
|
+
return element[Tag.TaggedVariable];
|
|
344
|
+
|
|
345
|
+
if (element && create) {
|
|
346
|
+
if (element instanceof HTMLElement)
|
|
347
|
+
element.setAttribute('vsn-ref', '');
|
|
348
|
+
await this.buildFrom(element.parentElement || element, false, forComponent);
|
|
349
|
+
return await this.getTagForElement(element, false);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async getTagForScope(scope: Scope) {
|
|
356
|
+
for (const tag of this.tags) {
|
|
357
|
+
if (tag.uniqueScope && tag.scope === scope)
|
|
358
|
+
return tag;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
public async resetBranch(e: Tag | HTMLElement) {
|
|
365
|
+
if (e instanceof Tag)
|
|
366
|
+
e = e.element;
|
|
367
|
+
|
|
368
|
+
const tag: Tag = e[Tag.TaggedVariable];
|
|
369
|
+
if (tag) {
|
|
370
|
+
tag.findParentTag();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const children = Array.from(e.children) as HTMLElement[]
|
|
374
|
+
for (const t of children) {
|
|
375
|
+
await this.resetBranch(t);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
public static getParentElement(element: HTMLElement): HTMLElement {
|
|
380
|
+
if (element.parentElement) {
|
|
381
|
+
return element.parentElement as HTMLElement;
|
|
382
|
+
} else if (element.assignedSlot) {
|
|
383
|
+
return element.assignedSlot.parentElement as HTMLElement;
|
|
384
|
+
} else if (element['shadowParent']) {
|
|
385
|
+
return element['shadowParent'];
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {AbstractDOM} from "./AbstractDOM";
|
|
2
|
+
|
|
3
|
+
export class ShadowDOM extends AbstractDOM {
|
|
4
|
+
protected _parent: AbstractDOM;
|
|
5
|
+
|
|
6
|
+
constructor(
|
|
7
|
+
parent: AbstractDOM,
|
|
8
|
+
rootElement: HTMLElement,
|
|
9
|
+
build: boolean = true,
|
|
10
|
+
debug: boolean = false
|
|
11
|
+
) {
|
|
12
|
+
super(rootElement, build, debug);
|
|
13
|
+
this._parent = parent;
|
|
14
|
+
}
|
|
15
|
+
}
|