vsn 0.1.71 → 0.1.72
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 +5 -1
- package/demo/vsn.js +2 -2
- package/demo/xhr.html +27 -5
- package/dist/AST/ArithmeticAssignmentNode.js +1 -1
- package/dist/AST/ArithmeticAssignmentNode.js.map +1 -1
- package/dist/AST/ClassNode.d.ts +23 -7
- package/dist/AST/ClassNode.js +177 -48
- package/dist/AST/ClassNode.js.map +1 -1
- package/dist/AST/ElementAttributeNode.js +13 -4
- package/dist/AST/ElementAttributeNode.js.map +1 -1
- package/dist/AST/FunctionCallNode.js +2 -1
- package/dist/AST/FunctionCallNode.js.map +1 -1
- package/dist/AST/Node.d.ts +4 -1
- package/dist/AST/Node.js.map +1 -1
- package/dist/AST/ScopeMemberNode.js.map +1 -1
- package/dist/AST.js +3 -1
- package/dist/AST.js.map +1 -1
- package/dist/DOM.d.ts +2 -0
- package/dist/DOM.js +16 -3
- package/dist/DOM.js.map +1 -1
- package/dist/EventDispatcher.d.ts +1 -0
- package/dist/EventDispatcher.js +16 -0
- package/dist/EventDispatcher.js.map +1 -1
- package/dist/Registry.js +1 -1
- package/dist/Registry.js.map +1 -1
- package/dist/Tag/List.d.ts +1 -1
- package/dist/Tag/List.js +4 -14
- package/dist/Tag/List.js.map +1 -1
- package/dist/Tag.d.ts +0 -1
- package/dist/Tag.js +0 -1
- package/dist/Tag.js.map +1 -1
- package/dist/vsn.min.js +2 -2
- package/package.json +1 -1
- package/src/AST/ArithmeticAssignmentNode.ts +1 -1
- package/src/AST/ClassNode.ts +125 -33
- package/src/AST/ElementAttributeNode.ts +9 -4
- package/src/AST/FunctionCallNode.ts +2 -1
- package/src/AST/Node.ts +5 -1
- package/src/AST/ScopeMemberNode.ts +0 -1
- package/src/AST.ts +3 -1
- package/src/DOM.ts +15 -3
- package/src/EventDispatcher.ts +8 -0
- package/src/Registry.ts +1 -1
- package/src/Tag/List.ts +5 -13
- package/src/Tag.ts +0 -1
- package/test/AST/ClassNode.spec.ts +40 -11
- package/test/attributes/ListItem.spec.ts +0 -1
package/package.json
CHANGED
|
@@ -48,7 +48,7 @@ export class ArithmeticAssignmentNode extends Node implements TreeNode {
|
|
|
48
48
|
}
|
|
49
49
|
} else if ((this.left instanceof ElementAttributeNode || this.left instanceof ElementStyleNode) && this.left.elementRef) {
|
|
50
50
|
const elements = await this.left.elementRef.evaluate(scope, dom, tag);
|
|
51
|
-
if (this.left.elementRef.first) {
|
|
51
|
+
if (this.left.elementRef.first || elements instanceof DOMObject) {
|
|
52
52
|
scopes.push(elements);
|
|
53
53
|
} else {
|
|
54
54
|
scopes = elements;
|
package/src/AST/ClassNode.ts
CHANGED
|
@@ -2,45 +2,76 @@ import {Scope} from "../Scope";
|
|
|
2
2
|
import {DOM} from "../DOM";
|
|
3
3
|
import {Tag} from "../Tag";
|
|
4
4
|
import {Token, TokenType, Tree, TreeNode} from "../AST";
|
|
5
|
-
import {Node} from "./Node";
|
|
5
|
+
import {INodeMeta, Node} from "./Node";
|
|
6
6
|
import {BlockNode} from "./BlockNode";
|
|
7
7
|
import {Registry} from "../Registry";
|
|
8
8
|
import {OnNode} from "./OnNode";
|
|
9
9
|
import {FunctionNode} from "./FunctionNode";
|
|
10
10
|
|
|
11
11
|
export class ClassNode extends Node implements TreeNode {
|
|
12
|
+
public static readonly ClassesVariable = '_vsn_classes';
|
|
12
13
|
public static readonly classes: {[name: string]: ClassNode} = {};
|
|
14
|
+
public static readonly classParents: {[name: string]: string[]} = {};
|
|
15
|
+
public static readonly classChildren: {[name: string]: string[]} = {}; // List of child class selectors for a given class selector
|
|
16
|
+
public static readonly preppedTags: {[name: string]: Tag[]} = {};
|
|
17
|
+
|
|
13
18
|
protected requiresPrep: boolean = true;
|
|
14
19
|
public readonly classScope: Scope = new Scope();
|
|
15
|
-
protected
|
|
20
|
+
protected _fullSelector: string;
|
|
16
21
|
|
|
17
22
|
constructor(
|
|
18
|
-
public readonly
|
|
23
|
+
public readonly selector: string,
|
|
19
24
|
public readonly block: BlockNode
|
|
20
25
|
) {
|
|
21
26
|
super();
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
public get fullSelector(): string {
|
|
30
|
+
return this._fullSelector;
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
public updateMeta(meta?: any) {
|
|
25
34
|
meta = meta || {};
|
|
26
35
|
meta['ClassNode'] = this;
|
|
27
36
|
return meta;
|
|
28
37
|
}
|
|
29
38
|
|
|
30
|
-
public async prepare(scope: Scope, dom: DOM, tag: Tag = null, meta?:
|
|
31
|
-
meta = meta || {};
|
|
32
|
-
meta['
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
public async prepare(scope: Scope, dom: DOM, tag: Tag = null, meta?: INodeMeta): Promise<void> {
|
|
40
|
+
meta = Object.assign({}, meta) || {};
|
|
41
|
+
const initial = !!meta['initial'];
|
|
42
|
+
meta['ClassNodePrepare'] = initial;
|
|
43
|
+
|
|
44
|
+
// Only prepare once during the initial prep, all subsequent prepares are on tag class blocks
|
|
45
|
+
if (initial) {
|
|
46
|
+
if (meta['ClassNodeSelector']) {
|
|
47
|
+
ClassNode.classChildren[meta['ClassNodeSelector'] as string].push(this.selector);
|
|
48
|
+
meta['ClassNodeSelector'] = `${meta['ClassNodeSelector']} ${this.selector}`;
|
|
49
|
+
} else {
|
|
50
|
+
meta['ClassNodeSelector'] = this.selector;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this._fullSelector = meta['ClassNodeSelector'];
|
|
54
|
+
if (ClassNode.classes[this._fullSelector]) return; // Don't re-prepare same classes
|
|
55
|
+
ClassNode.classes[this._fullSelector] = this;
|
|
56
|
+
ClassNode.classChildren[this._fullSelector] = [];
|
|
57
|
+
ClassNode.preppedTags[this._fullSelector] = [];
|
|
58
|
+
|
|
59
|
+
if (ClassNode.classParents[this.selector] === undefined)
|
|
60
|
+
ClassNode.classParents[this.selector] = [];
|
|
61
|
+
|
|
62
|
+
ClassNode.classParents[this.selector].push(this._fullSelector);
|
|
63
|
+
await this.block.prepare(this.classScope, dom, tag, meta);
|
|
64
|
+
Registry.class(this);
|
|
65
|
+
|
|
66
|
+
for (const element of Array.from(dom.querySelectorAll(this._fullSelector))) {
|
|
67
|
+
await ClassNode.addElementClass(this._fullSelector, element as HTMLElement, dom, element[Tag.TaggedVariable] || null);
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
await this.block.prepare(this.classScope, dom, tag, meta);
|
|
40
71
|
}
|
|
41
72
|
}
|
|
42
73
|
|
|
43
|
-
public async
|
|
74
|
+
public async constructTag(tag: Tag, dom: DOM, hasConstructor: boolean | null = null) {
|
|
44
75
|
if (hasConstructor === null)
|
|
45
76
|
hasConstructor = this.classScope.has('construct');
|
|
46
77
|
|
|
@@ -52,10 +83,12 @@ export class ClassNode extends Node implements TreeNode {
|
|
|
52
83
|
const fnc = await fncCls.getFunction(tag.scope, dom, tag, false);
|
|
53
84
|
await fnc();
|
|
54
85
|
}
|
|
55
|
-
tag.
|
|
86
|
+
tag.dispatch(`${this.fullSelector}.construct`, tag.element.id);
|
|
87
|
+
ClassNode.preppedTags[this.fullSelector].push(tag);
|
|
88
|
+
ClassNode.addPreparedClassToElement(tag.element, this.fullSelector);
|
|
56
89
|
}
|
|
57
90
|
|
|
58
|
-
public async
|
|
91
|
+
public async deconstructTag(tag: Tag, dom: DOM, hasDeconstructor: boolean | null = null) {
|
|
59
92
|
if (hasDeconstructor === null)
|
|
60
93
|
hasDeconstructor = this.classScope.has('deconstruct');
|
|
61
94
|
|
|
@@ -70,7 +103,9 @@ export class ClassNode extends Node implements TreeNode {
|
|
|
70
103
|
tag.removeContextEventHandlers(on);
|
|
71
104
|
}
|
|
72
105
|
}
|
|
73
|
-
tag.
|
|
106
|
+
tag.dispatch(`${this.fullSelector}.deconstruct`);
|
|
107
|
+
ClassNode.preppedTags[this.fullSelector].splice(ClassNode.preppedTags[this.fullSelector].indexOf(tag), 1);
|
|
108
|
+
await ClassNode.removePreparedClassFromElement(tag.element, this.fullSelector);
|
|
74
109
|
}
|
|
75
110
|
|
|
76
111
|
public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
@@ -84,35 +119,92 @@ export class ClassNode extends Node implements TreeNode {
|
|
|
84
119
|
if (t.type === TokenType.L_BRACE) break;
|
|
85
120
|
nameParts.push(t.value);
|
|
86
121
|
}
|
|
87
|
-
const
|
|
122
|
+
const selector = nameParts.join('').trim();
|
|
88
123
|
tokens.splice(0, nameParts.length);
|
|
89
124
|
const block = Tree.processTokens(Tree.getNextStatementTokens(tokens, true, true));
|
|
90
|
-
return new ClassNode(
|
|
125
|
+
return new ClassNode(selector, block);
|
|
91
126
|
}
|
|
92
127
|
|
|
93
128
|
public static async checkForClassChanges(element: HTMLElement, dom: DOM, tag: Tag = null) {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
129
|
+
const localSelectors: string[] = [element.tagName.toLowerCase(), ...Array.from(element.classList).map(c => `.${c}`)];
|
|
130
|
+
const fullSelectors: string[] = [...ClassNode.getClassesForElement(element)];
|
|
131
|
+
if (element.id)
|
|
132
|
+
localSelectors.push(`#${element.id}`);
|
|
133
|
+
|
|
134
|
+
for (const selector in localSelectors) {
|
|
135
|
+
if (ClassNode.classParents[selector])
|
|
136
|
+
fullSelectors.push(...ClassNode.classParents[selector]);
|
|
137
|
+
}
|
|
97
138
|
|
|
98
139
|
if (!tag) {
|
|
99
140
|
tag = await dom.getTagForElement(element, true);
|
|
100
141
|
}
|
|
101
|
-
addedClasses = addedClasses.filter(c => !tag.preppedClasses.includes(c));
|
|
102
|
-
removedClasses = tag.preppedClasses.filter(c => !classes.includes(c));
|
|
103
142
|
|
|
104
|
-
for (const
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
143
|
+
for (const selector of fullSelectors) {
|
|
144
|
+
const isPrepped = ClassNode.getClassesForElement(element).includes(selector);
|
|
145
|
+
const elements = Array.from(dom.querySelectorAll(selector));
|
|
146
|
+
const inElements = elements.includes(element);
|
|
147
|
+
let changed: boolean = false;
|
|
148
|
+
|
|
149
|
+
if (inElements && !isPrepped) {
|
|
150
|
+
await ClassNode.addElementClass(selector, element, dom, tag);
|
|
151
|
+
changed = true;
|
|
152
|
+
} else if (!inElements && isPrepped) {
|
|
153
|
+
await ClassNode.removeElementClass(selector, element, dom, tag);
|
|
154
|
+
changed = true;
|
|
108
155
|
}
|
|
109
|
-
}
|
|
110
156
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
157
|
+
if (changed && ClassNode.classChildren[selector].length > 0) {
|
|
158
|
+
for (const childSelector of ClassNode.classChildren[selector]) {
|
|
159
|
+
for (const childElement of Array.from(dom.querySelectorAll(childSelector, tag)) as HTMLElement[]) {
|
|
160
|
+
await ClassNode.checkForClassChanges(childElement, dom, childElement[Tag.TaggedVariable] || null);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
115
163
|
}
|
|
164
|
+
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public static getClassesForElement(element: HTMLElement): string[] {
|
|
169
|
+
if (!element[ClassNode.ClassesVariable])
|
|
170
|
+
element[ClassNode.ClassesVariable] = [];
|
|
171
|
+
return element[ClassNode.ClassesVariable];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public static addPreparedClassToElement(element: HTMLElement, selector: string) {
|
|
175
|
+
ClassNode.getClassesForElement(element).push(selector);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public static removePreparedClassFromElement(element: HTMLElement, selector: string) {
|
|
179
|
+
const classes = ClassNode.getClassesForElement(element);
|
|
180
|
+
classes.splice(classes.indexOf(selector), 1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public static async addElementClass(selector: string, element: HTMLElement, dom: DOM, tag: Tag = null) {
|
|
184
|
+
const classes = ClassNode.getClassesForElement(element);
|
|
185
|
+
if (classes.includes(selector)) return;
|
|
186
|
+
|
|
187
|
+
if (!tag) {
|
|
188
|
+
tag = await dom.getTagForElement(element, true);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const classNode: ClassNode = Registry.instance.classes.getSynchronous(selector);
|
|
192
|
+
if (classNode) {
|
|
193
|
+
await classNode.constructTag(tag, dom);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public static async removeElementClass(selector: string, element: HTMLElement, dom: DOM, tag: Tag = null) {
|
|
198
|
+
const classes = ClassNode.getClassesForElement(element);
|
|
199
|
+
if (!classes.includes(selector)) return;
|
|
200
|
+
|
|
201
|
+
if (!tag) {
|
|
202
|
+
tag = await dom.getTagForElement(element, true);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const classNode: ClassNode = Registry.instance.classes.getSynchronous(selector);
|
|
206
|
+
if (classNode) {
|
|
207
|
+
await classNode.deconstructTag(tag, dom);
|
|
116
208
|
}
|
|
117
209
|
}
|
|
118
210
|
|
|
@@ -6,6 +6,7 @@ import {TreeNode} from "../AST";
|
|
|
6
6
|
import {Node} from "./Node";
|
|
7
7
|
import {ElementQueryNode} from "./ElementQueryNode";
|
|
8
8
|
import {LiteralNode} from "./LiteralNode";
|
|
9
|
+
import {DOMObject} from "../DOM/DOMObject";
|
|
9
10
|
|
|
10
11
|
export class ElementAttributeNode extends Node implements TreeNode {
|
|
11
12
|
protected requiresPrep: boolean = true;
|
|
@@ -53,10 +54,14 @@ export class ElementAttributeNode extends Node implements TreeNode {
|
|
|
53
54
|
async prepare(scope: Scope, dom: DOM, tag: Tag = null, meta: any = null) {
|
|
54
55
|
if (this.elementRef) {
|
|
55
56
|
await this.elementRef.prepare(scope, dom, tag, meta);
|
|
56
|
-
const tags:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const tags: any = await this.elementRef.evaluate(scope, dom, tag, true);
|
|
58
|
+
if (tags instanceof TagList) {
|
|
59
|
+
for (const t of tags)
|
|
60
|
+
await t.watchAttribute(this.attributeName);
|
|
61
|
+
} else if (tags instanceof DOMObject) {
|
|
62
|
+
await (tags as DOMObject).watchAttribute(this.attributeName);
|
|
63
|
+
}
|
|
64
|
+
} else if (tag) {
|
|
60
65
|
await tag.watchAttribute(this.attributeName);
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -8,6 +8,7 @@ import {ScopeMemberNode} from "./ScopeMemberNode";
|
|
|
8
8
|
import {FunctionNode} from "./FunctionNode";
|
|
9
9
|
import {Registry} from "../Registry";
|
|
10
10
|
import {ElementQueryNode} from "./ElementQueryNode";
|
|
11
|
+
import {ClassNode} from "./ClassNode";
|
|
11
12
|
|
|
12
13
|
export class FunctionCallNode<T = any> extends Node implements TreeNode {
|
|
13
14
|
constructor(
|
|
@@ -46,7 +47,7 @@ export class FunctionCallNode<T = any> extends Node implements TreeNode {
|
|
|
46
47
|
let calls = 0;
|
|
47
48
|
for (const _tag of tags) {
|
|
48
49
|
let tagNum = 0;
|
|
49
|
-
for (const className of _tag.
|
|
50
|
+
for (const className of _tag.element[ClassNode.ClassesVariable]) {
|
|
50
51
|
tagNum++;
|
|
51
52
|
const cls = Registry.instance.classes.getSynchronous(className);
|
|
52
53
|
if (cls) {
|
package/src/AST/Node.ts
CHANGED
|
@@ -3,6 +3,10 @@ import {DOM} from "../DOM";
|
|
|
3
3
|
import {Tag} from "../Tag";
|
|
4
4
|
import {TreeNode} from "../AST";
|
|
5
5
|
|
|
6
|
+
export interface INodeMeta {
|
|
7
|
+
[key: string]: string | number | boolean | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
export abstract class Node implements TreeNode {
|
|
7
11
|
protected requiresPrep: boolean = false;
|
|
8
12
|
protected _isPreparationRequired: boolean;
|
|
@@ -28,7 +32,7 @@ export abstract class Node implements TreeNode {
|
|
|
28
32
|
return false;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
async prepare(scope: Scope, dom: DOM, tag: Tag = null, meta:
|
|
35
|
+
async prepare(scope: Scope, dom: DOM, tag: Tag = null, meta: INodeMeta = null): Promise<void> {
|
|
32
36
|
for (const node of this.getChildNodes()) {
|
|
33
37
|
await node.prepare(scope, dom, tag, meta);
|
|
34
38
|
}
|
|
@@ -26,7 +26,6 @@ export class ScopeMemberNode extends ScopeNodeAbstract implements TreeNode {
|
|
|
26
26
|
async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
27
27
|
let scopes = [];
|
|
28
28
|
const values = [];
|
|
29
|
-
|
|
30
29
|
if (this.scope instanceof ElementQueryNode) {
|
|
31
30
|
const elements = await this.scope.evaluate(scope, dom, tag);
|
|
32
31
|
if (this.scope.first) {
|
package/src/AST.ts
CHANGED
|
@@ -420,7 +420,9 @@ export class Tree {
|
|
|
420
420
|
async prepare(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
421
421
|
if (!this._root.isPreparationRequired())
|
|
422
422
|
return;
|
|
423
|
-
return await this._root.prepare(scope, dom, tag
|
|
423
|
+
return await this._root.prepare(scope, dom, tag, {
|
|
424
|
+
initial: true
|
|
425
|
+
});
|
|
424
426
|
}
|
|
425
427
|
|
|
426
428
|
async bindToScopeChanges(scope, fnc, dom: DOM, tag: Tag = null) {
|
package/src/DOM.ts
CHANGED
|
@@ -18,6 +18,7 @@ export enum EQuerySelectDirection {
|
|
|
18
18
|
export class DOM extends EventDispatcher {
|
|
19
19
|
protected static _instance: DOM;
|
|
20
20
|
protected _root: Tag;
|
|
21
|
+
protected _ready: Promise<boolean>;
|
|
21
22
|
protected tags: Tag[];
|
|
22
23
|
protected observer: MutationObserver;
|
|
23
24
|
protected evaluateTimeout: any;
|
|
@@ -32,6 +33,12 @@ export class DOM extends EventDispatcher {
|
|
|
32
33
|
protected debug: boolean = false
|
|
33
34
|
) {
|
|
34
35
|
super();
|
|
36
|
+
this._ready = new Promise((resolve) => {
|
|
37
|
+
this.once('built', () => {
|
|
38
|
+
resolve(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
35
42
|
this.observer = new MutationObserver(this.mutation.bind(this));
|
|
36
43
|
this.tags = [];
|
|
37
44
|
|
|
@@ -49,6 +56,10 @@ export class DOM extends EventDispatcher {
|
|
|
49
56
|
return this._root;
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
public get ready(): Promise<boolean> {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
public async get(selector: string, create: boolean = false, tag: Tag = null, direction: EQuerySelectDirection = EQuerySelectDirection.DOWN): Promise<TagList> {
|
|
53
64
|
switch (selector) {
|
|
54
65
|
case 'window':
|
|
@@ -232,7 +243,7 @@ export class DOM extends EventDispatcher {
|
|
|
232
243
|
const found: Element[] = [];
|
|
233
244
|
for (const tag of this.tags)
|
|
234
245
|
{
|
|
235
|
-
if (elements.indexOf(tag.element) > -1) {
|
|
246
|
+
if (!found.includes(tag.element) && elements.indexOf(tag.element) > -1) {
|
|
236
247
|
tags.push(tag);
|
|
237
248
|
found.push(tag.element);
|
|
238
249
|
}
|
|
@@ -242,8 +253,9 @@ export class DOM extends EventDispatcher {
|
|
|
242
253
|
const notFound: Element[] = [...elements];
|
|
243
254
|
for (let i = notFound.length; i >= 0; i--) {
|
|
244
255
|
const element: Element = notFound[i];
|
|
245
|
-
if (found.indexOf(element) > -1)
|
|
246
|
-
notFound.
|
|
256
|
+
if (found.indexOf(element) > -1) {
|
|
257
|
+
notFound.splice(i, 1);
|
|
258
|
+
}
|
|
247
259
|
}
|
|
248
260
|
|
|
249
261
|
for (const element of notFound) {
|
package/src/EventDispatcher.ts
CHANGED
|
@@ -70,6 +70,14 @@ export class EventDispatcher {
|
|
|
70
70
|
return this.on(event, fct, context, true);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
promise(event: string, ...args: any[]): Promise<any> {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
this.once(event, (...args) => {
|
|
76
|
+
resolve(args);
|
|
77
|
+
}, null);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
73
81
|
off(event: string, key?: number): boolean {
|
|
74
82
|
if(!(event in this._listeners)) return false;
|
|
75
83
|
if(key) {
|
package/src/Registry.ts
CHANGED
|
@@ -90,7 +90,7 @@ export class Registry extends EventDispatcher {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
public static class(cls: ClassNode) {
|
|
93
|
-
Registry.instance.classes.register(cls.
|
|
93
|
+
Registry.instance.classes.register(cls.fullSelector, cls);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
public static controller(key: string = null, setup = null) {
|
package/src/Tag/List.ts
CHANGED
|
@@ -12,19 +12,6 @@ export class TagList extends Array<DOMObject> {
|
|
|
12
12
|
return this[0].scope
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
on(event, cbOrSelector, cb) {
|
|
16
|
-
if (typeof cbOrSelector === "function") {
|
|
17
|
-
this.forEach(e => e.element.addEventListener(event, cbOrSelector))
|
|
18
|
-
} else {
|
|
19
|
-
this.forEach(elem => {
|
|
20
|
-
elem.element.addEventListener(event, e => {
|
|
21
|
-
if (e.target.matches(cbOrSelector)) cb(e)
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
return this
|
|
26
|
-
}
|
|
27
|
-
|
|
28
15
|
get elements(): HTMLElement[] {
|
|
29
16
|
return this.map(e => e.element);
|
|
30
17
|
}
|
|
@@ -37,6 +24,11 @@ export class TagList extends Array<DOMObject> {
|
|
|
37
24
|
return this[this.length - 1];
|
|
38
25
|
}
|
|
39
26
|
|
|
27
|
+
all(event: string): Promise<number[]> {
|
|
28
|
+
const promises = this.map(e => e.promise(event));
|
|
29
|
+
return Promise.all(promises);
|
|
30
|
+
}
|
|
31
|
+
|
|
40
32
|
removeClass(className) {
|
|
41
33
|
this.forEach(e => e.element.classList.remove(className))
|
|
42
34
|
return this
|
package/src/Tag.ts
CHANGED
|
@@ -26,7 +26,6 @@ export class Tag extends DOMObject {
|
|
|
26
26
|
public readonly rawAttributes: { [key: string]: string; };
|
|
27
27
|
public readonly parsedAttributes: { [key: string]: string[]; };
|
|
28
28
|
public readonly deferredAttributes: Attribute[] = [];
|
|
29
|
-
public readonly preppedClasses: string[] = [];
|
|
30
29
|
protected _state: TagState;
|
|
31
30
|
protected attributes: Attribute[];
|
|
32
31
|
protected _nonDeferredAttributes: Attribute[] = [];
|
|
@@ -1,29 +1,58 @@
|
|
|
1
1
|
import {DOM} from "../../src/DOM";
|
|
2
|
+
import {ClassNode} from "../../src/AST/ClassNode";
|
|
3
|
+
import {Registry} from "../../src/Registry";
|
|
4
|
+
import {TagList} from "../../src/Tag/List";
|
|
2
5
|
|
|
3
6
|
|
|
4
7
|
describe('ClassNode', () => {
|
|
5
|
-
it("properly
|
|
8
|
+
it("properly combine nested classes", async () => {
|
|
6
9
|
document.body.innerHTML = `
|
|
7
10
|
<script type="text/vsn" vsn-script>
|
|
8
|
-
class simple {
|
|
11
|
+
class .simple {
|
|
12
|
+
func construct() {}
|
|
13
|
+
class input {
|
|
14
|
+
func construct() {}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
<div class="simple"><input /></div>
|
|
19
|
+
`;
|
|
20
|
+
const dom = new DOM(document);
|
|
21
|
+
await dom.ready;
|
|
22
|
+
await Registry.instance.classes.get('.simple input');
|
|
23
|
+
expect(ClassNode.classParents['input']).toBeInstanceOf(Array);
|
|
24
|
+
expect(ClassNode.classParents['input'].includes('.simple input')).toBe(true);
|
|
25
|
+
expect(ClassNode.classes['.simple input']).toBeInstanceOf(ClassNode);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("properly define a simple class", async () => {
|
|
29
|
+
document.body.innerHTML = `
|
|
30
|
+
<script type="text/vsn" vsn-script>
|
|
31
|
+
class .simple-construct {
|
|
9
32
|
func construct() {
|
|
10
|
-
a|integer = "15";
|
|
33
|
+
a|integer = "15";
|
|
34
|
+
log('####### construct', a);
|
|
11
35
|
}
|
|
12
36
|
|
|
13
37
|
func test() {
|
|
14
38
|
a += 1;
|
|
39
|
+
log('####### testing', a);
|
|
15
40
|
}
|
|
16
41
|
}
|
|
17
42
|
</script>
|
|
18
|
-
<div class="simple"></div>
|
|
19
|
-
<div class="simple"></div>
|
|
43
|
+
<div class="simple-construct" id="ele-1"></div>
|
|
44
|
+
<div class="simple-construct" id="ele-2"></div>
|
|
20
45
|
`;
|
|
21
46
|
const dom = new DOM(document);
|
|
22
|
-
dom.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
47
|
+
await dom.ready;
|
|
48
|
+
await Registry.instance.classes.get('.simple-construct');
|
|
49
|
+
const t: TagList = await dom.exec('?(.simple-construct)');
|
|
50
|
+
expect(t).toBeInstanceOf(TagList);
|
|
51
|
+
expect(t.length).toBe(2);
|
|
52
|
+
await t.all('.simple-construct.construct');
|
|
53
|
+
console.log('####### hmm?', await dom.exec('?(.simple-construct).a'));
|
|
54
|
+
expect(await dom.exec('?(.simple-construct).a')).toEqual([15, 15]);
|
|
55
|
+
await dom.exec('?(.simple-construct).test()');
|
|
56
|
+
expect(await dom.exec('?(.simple-construct).a')).toEqual([16, 16]);
|
|
28
57
|
});
|
|
29
58
|
});
|