vsn 0.1.67 → 0.1.70
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 +15 -0
- package/demo/vsn.js +2 -2
- package/demo/xhr.html +6 -61
- package/dist/AST/ClassNode.d.ts +1 -0
- package/dist/AST/ClassNode.js +37 -25
- package/dist/AST/ClassNode.js.map +1 -1
- package/dist/AST/FunctionCallNode.js +46 -24
- package/dist/AST/FunctionCallNode.js.map +1 -1
- package/dist/AST/FunctionNode.d.ts +4 -2
- package/dist/AST/FunctionNode.js +25 -2
- package/dist/AST/FunctionNode.js.map +1 -1
- package/dist/AST/ModifierNode.d.ts +14 -0
- package/dist/AST/ModifierNode.js +88 -0
- package/dist/AST/ModifierNode.js.map +1 -0
- package/dist/AST/Node.d.ts +2 -0
- package/dist/AST/Node.js +24 -0
- package/dist/AST/Node.js.map +1 -1
- package/dist/AST/OnNode.js +26 -8
- package/dist/AST/OnNode.js.map +1 -1
- package/dist/AST/RootScopeMemberNode.d.ts +2 -1
- package/dist/AST/RootScopeMemberNode.js +5 -2
- package/dist/AST/RootScopeMemberNode.js.map +1 -1
- package/dist/AST/ScopeMemberNode.d.ts +2 -1
- package/dist/AST/ScopeMemberNode.js +9 -6
- package/dist/AST/ScopeMemberNode.js.map +1 -1
- package/dist/AST/ScopeNodeAbstract.d.ts +8 -0
- package/dist/AST/ScopeNodeAbstract.js +82 -0
- package/dist/AST/ScopeNodeAbstract.js.map +1 -0
- package/dist/AST.d.ts +2 -1
- package/dist/AST.js +9 -0
- package/dist/AST.js.map +1 -1
- package/dist/Scope.d.ts +4 -0
- package/dist/Scope.js +32 -3
- package/dist/Scope.js.map +1 -1
- package/dist/Tag.d.ts +1 -1
- package/dist/Tag.js +5 -5
- package/dist/Tag.js.map +1 -1
- package/dist/helpers/ElementHelper.js +6 -5
- package/dist/helpers/ElementHelper.js.map +1 -1
- package/dist/vsn.min.js +2 -2
- package/package.json +1 -1
- package/src/AST/ClassNode.ts +14 -13
- package/src/AST/FunctionCallNode.ts +29 -10
- package/src/AST/FunctionNode.ts +18 -2
- package/src/AST/ModifierNode.ts +30 -0
- package/src/AST/Node.ts +7 -0
- package/src/AST/OnNode.ts +7 -2
- package/src/AST/RootScopeMemberNode.ts +3 -1
- package/src/AST/ScopeMemberNode.ts +3 -1
- package/src/AST/ScopeNodeAbstract.ts +20 -0
- package/src/AST.ts +8 -0
- package/src/Scope.ts +32 -3
- package/src/Tag.ts +4 -5
- package/src/helpers/ElementHelper.ts +6 -5
- package/test/AST/ClassNode.spec.ts +29 -0
package/package.json
CHANGED
package/src/AST/ClassNode.ts
CHANGED
|
@@ -6,11 +6,13 @@ import {Node} from "./Node";
|
|
|
6
6
|
import {BlockNode} from "./BlockNode";
|
|
7
7
|
import {Registry} from "../Registry";
|
|
8
8
|
import {OnNode} from "./OnNode";
|
|
9
|
+
import {FunctionNode} from "./FunctionNode";
|
|
9
10
|
|
|
10
11
|
export class ClassNode extends Node implements TreeNode {
|
|
11
12
|
public static readonly classes: {[name: string]: ClassNode} = {};
|
|
12
13
|
protected requiresPrep: boolean = true;
|
|
13
14
|
public readonly classScope: Scope = new Scope();
|
|
15
|
+
protected _ready: boolean = false;
|
|
14
16
|
|
|
15
17
|
constructor(
|
|
16
18
|
public readonly name: string,
|
|
@@ -32,6 +34,10 @@ export class ClassNode extends Node implements TreeNode {
|
|
|
32
34
|
ClassNode.classes[this.name] = this;
|
|
33
35
|
await this.block.prepare(this.classScope, dom, tag, meta);
|
|
34
36
|
Registry.class(this);
|
|
37
|
+
|
|
38
|
+
for (const element of Array.from(dom.querySelectorAll(`.${this.name}`))) {
|
|
39
|
+
await ClassNode.checkForClassChanges(element as HTMLElement, dom, element[Tag.TaggedVariable] || null);
|
|
40
|
+
}
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
public async prepareTag(tag: Tag, dom: DOM, hasConstructor: boolean | null = null) {
|
|
@@ -42,17 +48,10 @@ export class ClassNode extends Node implements TreeNode {
|
|
|
42
48
|
const meta = this.updateMeta();
|
|
43
49
|
await this.block.prepare(tag.scope, dom, tag, meta);
|
|
44
50
|
if (hasConstructor) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
/*
|
|
49
|
-
for (const key of this.classScope.keys) {
|
|
50
|
-
if (this.classScope.get(key) instanceof OnNode) {
|
|
51
|
-
const on = this.classScope.get(key) as OnNode;
|
|
52
|
-
tag.addEventHandler(on.name, [], await on.getFunction(tag.scope, dom, tag), on);
|
|
53
|
-
}
|
|
51
|
+
const fncCls: FunctionNode = this.classScope.get('construct') as FunctionNode;
|
|
52
|
+
const fnc = await fncCls.getFunction(tag.scope, dom, tag, false);
|
|
53
|
+
await fnc();
|
|
54
54
|
}
|
|
55
|
-
*/
|
|
56
55
|
tag.preppedClasses.push(this.name);
|
|
57
56
|
}
|
|
58
57
|
|
|
@@ -61,8 +60,9 @@ export class ClassNode extends Node implements TreeNode {
|
|
|
61
60
|
hasDeconstructor = this.classScope.has('deconstruct');
|
|
62
61
|
|
|
63
62
|
if (hasDeconstructor) {
|
|
64
|
-
const
|
|
65
|
-
|
|
63
|
+
const fncCls: FunctionNode = this.classScope.get('deconstruct') as FunctionNode;
|
|
64
|
+
const fnc = await fncCls.getFunction(tag.scope, dom, tag, false);
|
|
65
|
+
await fnc();
|
|
66
66
|
}
|
|
67
67
|
for (const key of this.classScope.keys) {
|
|
68
68
|
if (this.classScope.get(key) instanceof OnNode) {
|
|
@@ -93,7 +93,8 @@ export class ClassNode extends Node implements TreeNode {
|
|
|
93
93
|
public static async checkForClassChanges(element: HTMLElement, dom: DOM, tag: Tag = null) {
|
|
94
94
|
const classes: string[] = Array.from(element.classList);
|
|
95
95
|
let addedClasses: string[] = classes.filter(c => Registry.instance.classes.has(c));
|
|
96
|
-
let removedClasses: string[]
|
|
96
|
+
let removedClasses: string[];
|
|
97
|
+
|
|
97
98
|
if (!tag) {
|
|
98
99
|
tag = await dom.getTagForElement(element, true);
|
|
99
100
|
}
|
|
@@ -7,6 +7,7 @@ import {FunctionArgumentNode} from "./FunctionArgumentNode";
|
|
|
7
7
|
import {ScopeMemberNode} from "./ScopeMemberNode";
|
|
8
8
|
import {FunctionNode} from "./FunctionNode";
|
|
9
9
|
import {Registry} from "../Registry";
|
|
10
|
+
import {ElementQueryNode} from "./ElementQueryNode";
|
|
10
11
|
|
|
11
12
|
export class FunctionCallNode<T = any> extends Node implements TreeNode {
|
|
12
13
|
constructor(
|
|
@@ -24,27 +25,43 @@ export class FunctionCallNode<T = any> extends Node implements TreeNode {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
28
|
+
// @todo: Need to rewrite/refactor this. It's a bit of a mess with element queries.
|
|
29
|
+
let tags: Tag[];
|
|
27
30
|
let functionScope: Scope = scope;
|
|
28
31
|
if (this.fnc instanceof ScopeMemberNode) {
|
|
29
32
|
functionScope = await this.fnc.scope.evaluate(scope, dom, tag);
|
|
33
|
+
if (this.fnc.scope instanceof ElementQueryNode) {
|
|
34
|
+
tags = await this.fnc.scope.evaluate(scope, dom, tag) as Tag[];
|
|
35
|
+
} else {
|
|
36
|
+
tags = [tag];
|
|
37
|
+
}
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
const values = await this.args.evaluate(scope, dom, tag);
|
|
33
|
-
|
|
34
|
-
if (!func) {
|
|
41
|
+
let func = await this.fnc.evaluate(scope, dom, tag);
|
|
42
|
+
if (!func || func instanceof Array) {
|
|
35
43
|
const functionName = await (this.fnc as any).name.evaluate(scope, dom, tag);
|
|
36
44
|
const returnValues = [];
|
|
45
|
+
const toCleanup = [];
|
|
37
46
|
let calls = 0;
|
|
38
|
-
for (const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
for (const _tag of tags) {
|
|
48
|
+
let tagNum = 0;
|
|
49
|
+
for (const className of _tag.preppedClasses) {
|
|
50
|
+
tagNum++;
|
|
51
|
+
const cls = Registry.instance.classes.getSynchronous(className);
|
|
52
|
+
if (cls) {
|
|
53
|
+
if (cls.classScope.has(functionName)) {
|
|
54
|
+
const fnc = cls.classScope.get(functionName);
|
|
55
|
+
toCleanup.push(fnc);
|
|
56
|
+
returnValues.push(await (await fnc.evaluate(_tag.scope, dom, _tag))(...values));
|
|
57
|
+
calls++;
|
|
58
|
+
}
|
|
45
59
|
}
|
|
46
60
|
}
|
|
47
61
|
}
|
|
62
|
+
for (const fnc of toCleanup) {
|
|
63
|
+
await fnc.collectGarbage();
|
|
64
|
+
}
|
|
48
65
|
if (calls === 1) {
|
|
49
66
|
return returnValues[0];
|
|
50
67
|
} else if (calls === 0) {
|
|
@@ -53,7 +70,9 @@ export class FunctionCallNode<T = any> extends Node implements TreeNode {
|
|
|
53
70
|
return returnValues;
|
|
54
71
|
}
|
|
55
72
|
} else if (func instanceof FunctionNode) {
|
|
56
|
-
|
|
73
|
+
const r = await (await func.evaluate(functionScope, dom, tag) as any)(...values);
|
|
74
|
+
await func.collectGarbage();
|
|
75
|
+
return r;
|
|
57
76
|
} else {
|
|
58
77
|
return func.call(functionScope.wrapped || functionScope, ...values);
|
|
59
78
|
}
|
package/src/AST/FunctionNode.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {BlockNode} from "./BlockNode";
|
|
|
7
7
|
|
|
8
8
|
export class FunctionNode extends Node implements TreeNode {
|
|
9
9
|
protected requiresPrep: boolean = true;
|
|
10
|
+
protected garbage: FunctionScope[] = [];
|
|
10
11
|
|
|
11
12
|
constructor(
|
|
12
13
|
public readonly name: string,
|
|
@@ -32,9 +33,24 @@ export class FunctionNode extends Node implements TreeNode {
|
|
|
32
33
|
return await this.getFunction(scope, dom, tag);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
public async
|
|
36
|
+
public async collectGarbage() {
|
|
37
|
+
for (const f of this.garbage) {
|
|
38
|
+
f.collectGarbage();
|
|
39
|
+
}
|
|
40
|
+
this.garbage = [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async getFunction(scope: Scope, dom: DOM, tag: Tag = null, createFunctionScope: boolean = true) {
|
|
44
|
+
console.log(`FunctionNode.getFunction(${this.name})`);
|
|
45
|
+
const self = this;
|
|
36
46
|
return async (...args) => {
|
|
37
|
-
|
|
47
|
+
let functionScope;
|
|
48
|
+
if (createFunctionScope && !(scope instanceof FunctionScope)) {
|
|
49
|
+
functionScope = new FunctionScope(scope);
|
|
50
|
+
self.garbage.push(functionScope);
|
|
51
|
+
} else {
|
|
52
|
+
functionScope = scope;
|
|
53
|
+
}
|
|
38
54
|
for (const arg of this.args) {
|
|
39
55
|
functionScope.set(arg, args.shift());
|
|
40
56
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {Scope} from "../Scope";
|
|
2
|
+
import {DOM} from "../DOM";
|
|
3
|
+
import {Tag} from "../Tag";
|
|
4
|
+
import {Token, TreeNode} from "../AST";
|
|
5
|
+
import {Node} from "./Node";
|
|
6
|
+
import {BlockNode} from "./BlockNode";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export class ModifierNode extends Node implements TreeNode {
|
|
10
|
+
constructor(
|
|
11
|
+
public readonly name: string,
|
|
12
|
+
public readonly block: BlockNode
|
|
13
|
+
) {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public async prepare(scope: Scope, dom: DOM, tag: Tag = null, meta?: any): Promise<void> {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public static parse(lastNode, token, tokens: Token[]): Node {
|
|
26
|
+
const modifier = tokens.shift().value.substr(1);
|
|
27
|
+
lastNode.modifiers.push(modifier);
|
|
28
|
+
return lastNode;
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/AST/Node.ts
CHANGED
|
@@ -8,6 +8,7 @@ export abstract class Node implements TreeNode {
|
|
|
8
8
|
protected _isPreparationRequired: boolean;
|
|
9
9
|
protected childNodes: Node[];
|
|
10
10
|
protected nodeCache: {[key: string]: Node[]} = {};
|
|
11
|
+
public readonly modifiers: string[] = [];
|
|
11
12
|
abstract evaluate(scope: Scope, dom: DOM, tag?: Tag);
|
|
12
13
|
|
|
13
14
|
isPreparationRequired(): boolean {
|
|
@@ -33,6 +34,12 @@ export abstract class Node implements TreeNode {
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
async cleanup(scope: Scope, dom: DOM, tag: Tag) {
|
|
38
|
+
for (const node of this.getChildNodes()) {
|
|
39
|
+
await node.cleanup(scope, dom, tag);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
protected _getChildNodes(): Node[] {
|
|
37
44
|
return [];
|
|
38
45
|
}
|
package/src/AST/OnNode.ts
CHANGED
|
@@ -7,8 +7,13 @@ import {Tag} from "../Tag";
|
|
|
7
7
|
export class OnNode extends FunctionNode implements TreeNode {
|
|
8
8
|
public async prepare(scope: Scope, dom: DOM, tag: Tag = null, meta): Promise<void> {
|
|
9
9
|
const classPrep = meta?.ClassNodePrepare; // Don't add event handler if we're in class prep
|
|
10
|
-
if (tag && !classPrep)
|
|
11
|
-
|
|
10
|
+
if (tag && !classPrep) {
|
|
11
|
+
const func = await this.getFunction(scope, dom, tag);
|
|
12
|
+
tag.addEventHandler(this.name, [], async (...args) => {
|
|
13
|
+
await func(...args);
|
|
14
|
+
await this.collectGarbage();
|
|
15
|
+
}, this);
|
|
16
|
+
}
|
|
12
17
|
await super.prepare(scope, dom, tag, meta);
|
|
13
18
|
}
|
|
14
19
|
|
|
@@ -3,8 +3,9 @@ import {DOM} from "../DOM";
|
|
|
3
3
|
import {Tag} from "../Tag";
|
|
4
4
|
import {TreeNode} from "../AST";
|
|
5
5
|
import {Node} from "./Node";
|
|
6
|
+
import {ScopeNodeAbstract} from "./ScopeNodeAbstract";
|
|
6
7
|
|
|
7
|
-
export class RootScopeMemberNode<T = any> extends
|
|
8
|
+
export class RootScopeMemberNode<T = any> extends ScopeNodeAbstract implements TreeNode {
|
|
8
9
|
constructor(
|
|
9
10
|
public readonly name: TreeNode<string>
|
|
10
11
|
) {
|
|
@@ -19,6 +20,7 @@ export class RootScopeMemberNode<T = any> extends Node implements TreeNode {
|
|
|
19
20
|
|
|
20
21
|
async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
21
22
|
const name = await this.name.evaluate(scope, dom, tag);
|
|
23
|
+
await this.applyModifiers(name, scope, dom, tag);
|
|
22
24
|
const value = scope.get(name);
|
|
23
25
|
return value instanceof Scope && value.wrapped || value;
|
|
24
26
|
}
|
|
@@ -6,8 +6,9 @@ import {DOMObject} from "../DOM/DOMObject";
|
|
|
6
6
|
import {TreeNode} from "../AST";
|
|
7
7
|
import {Node} from "./Node";
|
|
8
8
|
import {ElementQueryNode} from "./ElementQueryNode";
|
|
9
|
+
import {ScopeNodeAbstract} from "./ScopeNodeAbstract";
|
|
9
10
|
|
|
10
|
-
export class ScopeMemberNode extends
|
|
11
|
+
export class ScopeMemberNode extends ScopeNodeAbstract implements TreeNode {
|
|
11
12
|
constructor(
|
|
12
13
|
public readonly scope: TreeNode<Scope>,
|
|
13
14
|
public readonly name: TreeNode<string>
|
|
@@ -50,6 +51,7 @@ export class ScopeMemberNode extends Node implements TreeNode {
|
|
|
50
51
|
throw Error(`Cannot access "${await this.name.evaluate(scope, dom, tag)}" of undefined.`);
|
|
51
52
|
}
|
|
52
53
|
const name = await this.name.evaluate(scope, dom, tag);
|
|
54
|
+
await this.applyModifiers(name, parent, dom, tag);
|
|
53
55
|
const value: any = parent.get(name, false);
|
|
54
56
|
values.push(value instanceof Scope && value.wrapped || value);
|
|
55
57
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {Node} from "./Node";
|
|
2
|
+
import {TreeNode} from "../AST";
|
|
3
|
+
import {Scope} from "../Scope";
|
|
4
|
+
import {DOM} from "../DOM";
|
|
5
|
+
import {Tag} from "../Tag";
|
|
6
|
+
import {Registry} from "../Registry";
|
|
7
|
+
|
|
8
|
+
export abstract class ScopeNodeAbstract extends Node implements TreeNode {
|
|
9
|
+
async applyModifiers(name: string, scope: Scope, dom: DOM, tag: Tag) {
|
|
10
|
+
let type: string;
|
|
11
|
+
for (const modifier of this.modifiers) {
|
|
12
|
+
if (Registry.instance.types.has(modifier)) {
|
|
13
|
+
type = modifier;
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (type)
|
|
18
|
+
scope.setType(name, type);
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/AST.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {StringFormatNode} from "./AST/StringFormatNode";
|
|
|
29
29
|
import {FunctionNode} from "./AST/FunctionNode";
|
|
30
30
|
import {ClassNode} from "./AST/ClassNode";
|
|
31
31
|
import {OnNode} from "./AST/OnNode";
|
|
32
|
+
import {ModifierNode} from "./AST/ModifierNode";
|
|
32
33
|
|
|
33
34
|
function lower(str: string): string {
|
|
34
35
|
return str ? str.toLowerCase() : null;
|
|
@@ -117,6 +118,7 @@ export enum TokenType {
|
|
|
117
118
|
XHR_POST,
|
|
118
119
|
XHR_PUT,
|
|
119
120
|
XHR_DELETE,
|
|
121
|
+
MODIFIER,
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
const TOKEN_PATTERNS: TokenPattern[] = [
|
|
@@ -363,6 +365,10 @@ const TOKEN_PATTERNS: TokenPattern[] = [
|
|
|
363
365
|
{
|
|
364
366
|
type: TokenType.EXCLAMATION_POINT,
|
|
365
367
|
pattern: /^!/
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
type: TokenType.MODIFIER,
|
|
371
|
+
pattern: /^\|\S+/
|
|
366
372
|
}
|
|
367
373
|
];
|
|
368
374
|
|
|
@@ -585,6 +591,8 @@ export class Tree {
|
|
|
585
591
|
tokens.shift()
|
|
586
592
|
} else if (tokens[0].type === TokenType.EXCLAMATION_POINT) {
|
|
587
593
|
node = NotNode.parse(node, tokens[0], tokens);
|
|
594
|
+
} else if (tokens[0].type === TokenType.MODIFIER) {
|
|
595
|
+
node = ModifierNode.parse(node, tokens[0], tokens);
|
|
588
596
|
} else {
|
|
589
597
|
let code: string = Tree.toCode(tokens, 10);
|
|
590
598
|
throw Error(`Syntax Error. Near ${code}`);
|
package/src/Scope.ts
CHANGED
|
@@ -32,14 +32,25 @@ export class Scope extends EventDispatcher {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
public set parentScope(scope: Scope) {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if (scope) {
|
|
36
|
+
this._parentScope = scope;
|
|
37
|
+
scope.addChild(this);
|
|
38
|
+
} else if (this._parentScope){
|
|
39
|
+
this._parentScope.removeChild(this);
|
|
40
|
+
this._parentScope = null;
|
|
41
|
+
}
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
public addChild(scope: Scope) {
|
|
40
45
|
this.children.push(scope);
|
|
41
46
|
}
|
|
42
47
|
|
|
48
|
+
public removeChild(scope: Scope) {
|
|
49
|
+
const index = this.children.indexOf(scope);
|
|
50
|
+
if (index > -1)
|
|
51
|
+
this.children.splice(index, 1);
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
getReference(path: string, createIfNotFound: boolean = true): ScopeReference {
|
|
44
55
|
const scopePath: string[] = path.split('.');
|
|
45
56
|
let key: string = scopePath[0];
|
|
@@ -127,6 +138,19 @@ export class Scope extends EventDispatcher {
|
|
|
127
138
|
this.parentScope = null;
|
|
128
139
|
}
|
|
129
140
|
|
|
141
|
+
collectGarbage(force: boolean = false) {
|
|
142
|
+
for (const child of this.children) {
|
|
143
|
+
child.collectGarbage(force);
|
|
144
|
+
}
|
|
145
|
+
if (force)
|
|
146
|
+
this.cleanup();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
deconstruct() {
|
|
150
|
+
super.deconstruct();
|
|
151
|
+
this.collectGarbage(true);
|
|
152
|
+
}
|
|
153
|
+
|
|
130
154
|
public wrap(toWrap: any, triggerUpdates: boolean = false, updateFromWrapped: boolean = true) {
|
|
131
155
|
if (toWrap instanceof ScopeData) {
|
|
132
156
|
if (this._data instanceof EventDispatcher) {
|
|
@@ -235,10 +259,15 @@ export class FunctionScope extends Scope {
|
|
|
235
259
|
}
|
|
236
260
|
|
|
237
261
|
set(key: string, value: any) {
|
|
238
|
-
if (this.parentScope.has(key) || ['$', '@'].indexOf(key[0]) > -1) {
|
|
262
|
+
if (this.parentScope && (this.parentScope.has(key) || ['$', '@'].indexOf(key[0]) > -1)) {
|
|
239
263
|
this.parentScope.set(key, value);
|
|
240
264
|
} else {
|
|
241
265
|
super.set(key, value);
|
|
242
266
|
}
|
|
243
267
|
}
|
|
268
|
+
|
|
269
|
+
collectGarbage(force: boolean = true) {
|
|
270
|
+
super.collectGarbage(true);
|
|
271
|
+
this.cleanup();
|
|
272
|
+
}
|
|
244
273
|
}
|
package/src/Tag.ts
CHANGED
|
@@ -21,9 +21,8 @@ export enum TagState {
|
|
|
21
21
|
Built,
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export const TaggedVariable:string = '_vsn_tag';
|
|
25
|
-
|
|
26
24
|
export class Tag extends DOMObject {
|
|
25
|
+
public static readonly TaggedVariable: string = '_vsn_tag';
|
|
27
26
|
public readonly rawAttributes: { [key: string]: string; };
|
|
28
27
|
public readonly parsedAttributes: { [key: string]: string[]; };
|
|
29
28
|
public readonly deferredAttributes: Attribute[] = [];
|
|
@@ -58,7 +57,7 @@ export class Tag extends DOMObject {
|
|
|
58
57
|
...props
|
|
59
58
|
) {
|
|
60
59
|
super(element, props);
|
|
61
|
-
element[TaggedVariable] = this;
|
|
60
|
+
element[Tag.TaggedVariable] = this;
|
|
62
61
|
this.rawAttributes = {};
|
|
63
62
|
this.parsedAttributes = {};
|
|
64
63
|
this.attributes = [];
|
|
@@ -240,9 +239,9 @@ export class Tag extends DOMObject {
|
|
|
240
239
|
let parentElement: HTMLElement = this.element.parentElement as HTMLElement;
|
|
241
240
|
let foundParent = false;
|
|
242
241
|
while (parentElement) {
|
|
243
|
-
if (parentElement[TaggedVariable]) {
|
|
242
|
+
if (parentElement[Tag.TaggedVariable]) {
|
|
244
243
|
foundParent = true;
|
|
245
|
-
this.parentTag = parentElement[TaggedVariable];
|
|
244
|
+
this.parentTag = parentElement[Tag.TaggedVariable];
|
|
246
245
|
break;
|
|
247
246
|
}
|
|
248
247
|
|
|
@@ -2,11 +2,6 @@ import {ClassNode} from "../AST/ClassNode";
|
|
|
2
2
|
|
|
3
3
|
export class ElementHelper {
|
|
4
4
|
public static hasVisionAttribute(element: HTMLElement | Element, testAttr: string = 'vsn-'): boolean {
|
|
5
|
-
for (const cls of Array.from(element.classList)) {
|
|
6
|
-
if (ClassNode.isClass(cls))
|
|
7
|
-
return true;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
5
|
if (!element.attributes || element.attributes.length <= 0) return false;
|
|
11
6
|
for (let i: number = 0; i < element.attributes.length; i++) {
|
|
12
7
|
const attr: Attr = element.attributes[i];
|
|
@@ -16,6 +11,12 @@ export class ElementHelper {
|
|
|
16
11
|
}
|
|
17
12
|
}
|
|
18
13
|
|
|
14
|
+
for (const cls of Array.from(element.classList)) {
|
|
15
|
+
if (ClassNode.isClass(cls)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
19
20
|
return false;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {DOM} from "../../src/DOM";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
describe('ClassNode', () => {
|
|
5
|
+
it("properly define a simple class", (done) => {
|
|
6
|
+
document.body.innerHTML = `
|
|
7
|
+
<script type="text/vsn" vsn-script>
|
|
8
|
+
class simple {
|
|
9
|
+
func construct() {
|
|
10
|
+
a|integer = "15";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func test() {
|
|
14
|
+
a += 1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
<div class="simple"></div>
|
|
19
|
+
<div class="simple"></div>
|
|
20
|
+
`;
|
|
21
|
+
const dom = new DOM(document);
|
|
22
|
+
dom.once('built', async () => {
|
|
23
|
+
expect(await dom.exec('?(.simple).a')).toEqual([15, 15]);
|
|
24
|
+
await dom.exec('?(.simple).test()');
|
|
25
|
+
expect(await dom.exec('?(.simple).a')).toEqual([16, 16]);
|
|
26
|
+
done();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|