vsn 0.1.58 → 0.1.61
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 +10 -6
- package/demo/vsn.js +2 -2
- package/demo/xhr.html +36 -0
- package/dist/AST/ArithmeticAssignmentNode.d.ts +2 -1
- package/dist/AST/ArithmeticAssignmentNode.js +20 -18
- package/dist/AST/ArithmeticAssignmentNode.js.map +1 -1
- package/dist/AST/ClassNode.d.ts +20 -0
- package/dist/AST/FunctionCallNode.js +13 -8
- package/dist/AST/FunctionCallNode.js.map +1 -1
- package/dist/AST/FunctionNode.d.ts +7 -8
- package/dist/AST/FunctionNode.js +53 -9
- package/dist/AST/FunctionNode.js.map +1 -1
- package/dist/AST.d.ts +48 -46
- package/dist/AST.js +57 -46
- package/dist/AST.js.map +1 -1
- package/dist/DOM.d.ts +2 -0
- package/dist/Registry.d.ts +3 -3
- package/dist/Scope.d.ts +4 -0
- package/dist/Scope.js +5 -0
- package/dist/Scope.js.map +1 -1
- package/dist/attributes/ControllerAttribute.d.ts +9 -2
- package/dist/attributes/Exec.d.ts +1 -0
- package/dist/attributes/Exec.js +8 -1
- package/dist/attributes/Exec.js.map +1 -1
- package/dist/attributes/ModelAttribute.d.ts +2 -2
- package/dist/attributes/RootAttribute.js +3 -0
- package/dist/attributes/RootAttribute.js.map +1 -1
- package/dist/attributes/ScriptAttribute.d.ts +4 -0
- package/dist/attributes/ScriptAttribute.js +45 -0
- package/dist/attributes/ScriptAttribute.js.map +1 -0
- package/dist/attributes/StandardAttribute.js +0 -1
- package/dist/attributes/StandardAttribute.js.map +1 -1
- package/dist/attributes/_imports.d.ts +1 -0
- package/dist/attributes/_imports.js +3 -1
- package/dist/attributes/_imports.js.map +1 -1
- package/dist/vsn.js +1 -1
- package/dist/vsn.min.js +2 -0
- package/package.json +1 -1
- package/src/AST/ArithmeticAssignmentNode.ts +19 -17
- package/src/AST/ClassNode.ts +49 -0
- package/src/AST/FunctionCallNode.ts +23 -1
- package/src/AST/FunctionNode.ts +31 -11
- package/src/AST.ts +20 -0
- package/src/DOM.ts +15 -0
- package/src/Registry.ts +7 -9
- package/src/Scope.ts +15 -0
- package/src/attributes/ControllerAttribute.ts +30 -2
- package/src/attributes/Exec.ts +5 -1
- package/src/attributes/ListItem.ts +1 -1
- package/src/attributes/ModelAttribute.ts +2 -2
- package/src/attributes/RootAttribute.ts +3 -0
- package/src/attributes/ScopeAttribute.ts +3 -0
- package/src/attributes/ScriptAttribute.ts +9 -0
- package/src/attributes/SetAttribute.ts +6 -4
- package/src/attributes/StandardAttribute.ts +0 -1
- package/src/attributes/_imports.ts +1 -0
- package/src/vsn.ts +4 -4
- package/test/AST/FunctionNode.spec.ts +14 -0
- package/test/DOM.spec.ts +3 -3
- package/test/attributes/ListItem.spec.ts +3 -3
- package/test/attributes/SetAttribute.spec.ts +3 -3
- package/demo/xhr.vsn +0 -4
- package/src/attributes/ClassConstructor.ts +0 -32
package/package.json
CHANGED
|
@@ -59,7 +59,7 @@ export class ArithmeticAssignmentNode extends Node implements TreeNode {
|
|
|
59
59
|
const values = [];
|
|
60
60
|
for (let localScope of scopes) {
|
|
61
61
|
if (localScope instanceof DOMObject) {
|
|
62
|
-
await this.handleDOMObject(name, dom, localScope, tag);
|
|
62
|
+
await this.handleDOMObject(name, dom, scope, localScope, tag);
|
|
63
63
|
} else {
|
|
64
64
|
if (localScope['$wrapped'] && localScope['$scope']) {
|
|
65
65
|
localScope = localScope['$scope'];
|
|
@@ -68,16 +68,7 @@ export class ArithmeticAssignmentNode extends Node implements TreeNode {
|
|
|
68
68
|
// We get the values from the passed scope, but set the value on the local scope
|
|
69
69
|
let left: number | Array<any> | string = await this.left.evaluate(scope, dom, tag);
|
|
70
70
|
let right: number | Array<any> | string = await this.right.evaluate(scope, dom, tag);
|
|
71
|
-
|
|
72
|
-
if (left instanceof Array) {
|
|
73
|
-
left = this.handleArray(name, left, right, localScope);
|
|
74
|
-
} else if ((left as any) instanceof UnitLiteral || right instanceof UnitLiteral) {
|
|
75
|
-
left = this.handleUnit(name, left, right, localScope);
|
|
76
|
-
} else if (Number.isFinite(left)) {
|
|
77
|
-
left = this.handleNumber(name, left, right, localScope);
|
|
78
|
-
} else {
|
|
79
|
-
left = this.handleString(name, left, right, localScope);
|
|
80
|
-
}
|
|
71
|
+
left = this.handle(name, left, right, localScope);
|
|
81
72
|
|
|
82
73
|
values.push(left);
|
|
83
74
|
}
|
|
@@ -85,6 +76,20 @@ export class ArithmeticAssignmentNode extends Node implements TreeNode {
|
|
|
85
76
|
return values.length > 1 ? values : values[0];
|
|
86
77
|
}
|
|
87
78
|
|
|
79
|
+
public handle(name, left, right, localScope) {
|
|
80
|
+
if (left instanceof Array) {
|
|
81
|
+
left = this.handleArray(name, left, right, localScope);
|
|
82
|
+
} else if ((left as any) instanceof UnitLiteral || right instanceof UnitLiteral) {
|
|
83
|
+
left = this.handleUnit(name, left, right, localScope);
|
|
84
|
+
} else if (Number.isFinite(left)) {
|
|
85
|
+
left = this.handleNumber(name, left, right, localScope);
|
|
86
|
+
} else {
|
|
87
|
+
left = this.handleString(name, left, right, localScope);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return left;
|
|
91
|
+
}
|
|
92
|
+
|
|
88
93
|
public handleNumber(key, left, right, scope) {
|
|
89
94
|
if (right !== null && !Number.isFinite(right))
|
|
90
95
|
right = parseFloat(`${right}`);
|
|
@@ -167,13 +172,10 @@ export class ArithmeticAssignmentNode extends Node implements TreeNode {
|
|
|
167
172
|
return left;
|
|
168
173
|
}
|
|
169
174
|
|
|
170
|
-
public async handleDOMObject(key: string, dom: DOM, domObject: DOMObject, tag: Tag) {
|
|
175
|
+
public async handleDOMObject(key: string, dom: DOM, scope: Scope, domObject: DOMObject, tag: Tag) {
|
|
171
176
|
let left = domObject.scope.get(key);
|
|
172
|
-
let right: number | Array<any> | string = await this.right.evaluate(
|
|
173
|
-
|
|
174
|
-
return this.handleArray(key, left, right, domObject.scope);
|
|
175
|
-
|
|
176
|
-
return this.handleString(key, left, right, domObject.scope);
|
|
177
|
+
let right: number | Array<any> | string = await this.right.evaluate(scope, dom, tag);
|
|
178
|
+
return this.handle(key, left, right, domObject.scope);
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
public handleArray(key, left, right, scope) {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {Scope} from "../Scope";
|
|
2
|
+
import {DOM} from "../DOM";
|
|
3
|
+
import {Tag} from "../Tag";
|
|
4
|
+
import {BlockType, Token, TokenType, Tree, TreeNode} from "../AST";
|
|
5
|
+
import {Node} from "./Node";
|
|
6
|
+
import {BlockNode} from "./BlockNode";
|
|
7
|
+
import {Registry} from "../Registry";
|
|
8
|
+
|
|
9
|
+
export class ClassNode extends Node implements TreeNode {
|
|
10
|
+
public static readonly classes: {[name: string]: ClassNode} = {};
|
|
11
|
+
protected requiresPrep: boolean = true;
|
|
12
|
+
public readonly classScope: Scope = new Scope();
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
public readonly name: string,
|
|
16
|
+
public readonly block: BlockNode
|
|
17
|
+
) {
|
|
18
|
+
super();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async prepare(scope: Scope, dom: DOM, tag: Tag = null): Promise<void> {
|
|
22
|
+
if (ClassNode.classes[this.name]) return; // Don't re-prepare same classes
|
|
23
|
+
ClassNode.classes[this.name] = this;
|
|
24
|
+
await this.block.prepare(this.classScope, dom, tag);
|
|
25
|
+
Registry.class(this);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async prepareTag(tag: Tag, dom: DOM) {
|
|
29
|
+
tag.createScope(true);
|
|
30
|
+
await this.block.prepare(tag.scope, dom, tag);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public static parse(lastNode, token, tokens: Token[]): ClassNode {
|
|
38
|
+
tokens.shift(); // skip 'class'
|
|
39
|
+
const nameParts: string[] = [];
|
|
40
|
+
for (const t of tokens) {
|
|
41
|
+
if (t.type === TokenType.L_BRACE) break;
|
|
42
|
+
nameParts.push(t.value);
|
|
43
|
+
}
|
|
44
|
+
const name = nameParts.join('');
|
|
45
|
+
tokens.splice(0, nameParts.length);
|
|
46
|
+
const block = Tree.processTokens(Tree.getNextStatementTokens(tokens, true, true));
|
|
47
|
+
return new ClassNode(name, block);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -5,6 +5,9 @@ import {TreeNode} from "../AST";
|
|
|
5
5
|
import {Node} from "./Node";
|
|
6
6
|
import {FunctionArgumentNode} from "./FunctionArgumentNode";
|
|
7
7
|
import {ScopeMemberNode} from "./ScopeMemberNode";
|
|
8
|
+
import {FunctionNode} from "./FunctionNode";
|
|
9
|
+
import {ClassNode} from "./ClassNode";
|
|
10
|
+
import {Registry} from "../Registry";
|
|
8
11
|
|
|
9
12
|
export class FunctionCallNode<T = any> extends Node implements TreeNode {
|
|
10
13
|
constructor(
|
|
@@ -26,7 +29,26 @@ export class FunctionCallNode<T = any> extends Node implements TreeNode {
|
|
|
26
29
|
if (this.fnc instanceof ScopeMemberNode) {
|
|
27
30
|
functionScope = await this.fnc.scope.evaluate(scope, dom, tag);
|
|
28
31
|
}
|
|
32
|
+
|
|
29
33
|
const values = await this.args.evaluate(scope, dom, tag);
|
|
30
|
-
|
|
34
|
+
const func = await this.fnc.evaluate(scope, dom, tag);
|
|
35
|
+
if (!func) {
|
|
36
|
+
const functionName = await (this.fnc as any).name.evaluate(scope, dom, tag);
|
|
37
|
+
const classes: ClassNode[] = [];
|
|
38
|
+
for (const className of Array.from(tag.element.classList)) {
|
|
39
|
+
const cls = Registry.instance.classes.getSynchronous(className);
|
|
40
|
+
if (cls) classes.push(cls);
|
|
41
|
+
}
|
|
42
|
+
for (const cls of classes) {
|
|
43
|
+
if (cls.classScope.has(functionName)) {
|
|
44
|
+
const fnc = cls.classScope.get(functionName);
|
|
45
|
+
return (await fnc.evaluate(functionScope, dom, tag))(...values);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} else if (func instanceof FunctionNode) {
|
|
49
|
+
return (await func.evaluate(functionScope, dom, tag) as any)(...values);
|
|
50
|
+
} else {
|
|
51
|
+
return func.call(functionScope.wrapped || functionScope, ...values);
|
|
52
|
+
}
|
|
31
53
|
}
|
|
32
54
|
}
|
package/src/AST/FunctionNode.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {Scope} from "../Scope";
|
|
1
|
+
import {FunctionScope, Scope} from "../Scope";
|
|
2
2
|
import {DOM} from "../DOM";
|
|
3
3
|
import {Tag} from "../Tag";
|
|
4
|
-
import {Token, TreeNode} from "../AST";
|
|
4
|
+
import {Token, Tree, TreeNode} from "../AST";
|
|
5
5
|
import {Node} from "./Node";
|
|
6
|
-
import {
|
|
7
|
-
import {RootScopeMemberNode} from "./RootScopeMemberNode";
|
|
8
|
-
import {ScopeMemberNode} from "./ScopeMemberNode";
|
|
6
|
+
import {BlockNode} from "./BlockNode";
|
|
9
7
|
|
|
10
8
|
export class FunctionNode extends Node implements TreeNode {
|
|
9
|
+
protected requiresPrep: boolean = true;
|
|
10
|
+
|
|
11
11
|
constructor(
|
|
12
|
-
public readonly name:
|
|
13
|
-
public readonly
|
|
14
|
-
public readonly block:
|
|
12
|
+
public readonly name: string,
|
|
13
|
+
public readonly args: string[],
|
|
14
|
+
public readonly block: BlockNode
|
|
15
15
|
) {
|
|
16
16
|
super();
|
|
17
17
|
}
|
|
@@ -22,10 +22,30 @@ export class FunctionNode extends Node implements TreeNode {
|
|
|
22
22
|
];
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
public async prepare(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
26
|
-
|
|
25
|
+
public async prepare(scope: Scope, dom: DOM, tag: Tag = null): Promise<void> {
|
|
26
|
+
scope.set(this.name, this);
|
|
27
|
+
await super.prepare(scope, dom, tag);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
|
|
31
|
+
return async (...args) => {
|
|
32
|
+
const functionScope = new FunctionScope(scope);
|
|
33
|
+
for (const arg of this.args) {
|
|
34
|
+
functionScope.set(arg, args.shift());
|
|
35
|
+
}
|
|
36
|
+
return await this.block.evaluate(functionScope, dom, tag);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
27
39
|
|
|
28
40
|
public static parse(lastNode, token, tokens: Token[]): FunctionNode {
|
|
29
|
-
|
|
41
|
+
tokens.shift(); // skip 'func'
|
|
42
|
+
const name = tokens.shift();
|
|
43
|
+
const argTokens = Tree.getBlockTokens(tokens);
|
|
44
|
+
const funcArgs: string[] = [];
|
|
45
|
+
for (const t of argTokens) {
|
|
46
|
+
funcArgs.push(t[0].value);
|
|
47
|
+
}
|
|
48
|
+
const block = Tree.processTokens(Tree.getNextStatementTokens(tokens, true, true));
|
|
49
|
+
return new FunctionNode(name.value, funcArgs, block);
|
|
30
50
|
}
|
|
31
51
|
}
|
package/src/AST.ts
CHANGED
|
@@ -26,6 +26,8 @@ import {BooleanLiteralNode} from "./AST/BooleanLiteralNode";
|
|
|
26
26
|
import {NotNode} from "./AST/NotNode";
|
|
27
27
|
import {XHRNode} from "./AST/XHRNode";
|
|
28
28
|
import {StringFormatNode} from "./AST/StringFormatNode";
|
|
29
|
+
import {FunctionNode} from "./AST/FunctionNode";
|
|
30
|
+
import {ClassNode} from "./AST/ClassNode";
|
|
29
31
|
|
|
30
32
|
function lower(str: string): string {
|
|
31
33
|
return str ? str.toLowerCase() : null;
|
|
@@ -65,6 +67,8 @@ export enum TokenType {
|
|
|
65
67
|
IF,
|
|
66
68
|
ELSE_IF,
|
|
67
69
|
ELSE,
|
|
70
|
+
FUNC,
|
|
71
|
+
CLASS,
|
|
68
72
|
NAME,
|
|
69
73
|
L_BRACE,
|
|
70
74
|
R_BRACE,
|
|
@@ -194,6 +198,14 @@ const TOKEN_PATTERNS: TokenPattern[] = [
|
|
|
194
198
|
type: TokenType.ELSE,
|
|
195
199
|
pattern: /^else\s?(?=\{)/
|
|
196
200
|
},
|
|
201
|
+
{
|
|
202
|
+
type: TokenType.FUNC,
|
|
203
|
+
pattern: /^func\s/
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
type: TokenType.CLASS,
|
|
207
|
+
pattern: /^class\s/
|
|
208
|
+
},
|
|
197
209
|
{
|
|
198
210
|
type: TokenType.ELEMENT_ATTRIBUTE,
|
|
199
211
|
pattern: /^\.?@[-_a-zA-Z0-9]*/
|
|
@@ -482,6 +494,14 @@ export class Tree {
|
|
|
482
494
|
node = ForStatementNode.parse(node, token, tokens);
|
|
483
495
|
blockNodes.push(node);
|
|
484
496
|
node = null;
|
|
497
|
+
} else if (token.type === TokenType.FUNC) {
|
|
498
|
+
node = FunctionNode.parse(node, token, tokens);
|
|
499
|
+
blockNodes.push(node);
|
|
500
|
+
node = null;
|
|
501
|
+
} else if (token.type === TokenType.CLASS) {
|
|
502
|
+
node = ClassNode.parse(node, token, tokens);
|
|
503
|
+
blockNodes.push(node);
|
|
504
|
+
node = null;
|
|
485
505
|
} else if (StringFormatNode.match(tokens)) {
|
|
486
506
|
node = StringFormatNode.parse(node, tokens[0], tokens);
|
|
487
507
|
} else if (token.type === TokenType.STRING_LITERAL) {
|
package/src/DOM.ts
CHANGED
|
@@ -7,6 +7,8 @@ import {WrappedWindow} from "./DOM/WrappedWindow";
|
|
|
7
7
|
import {WrappedDocument} from "./DOM/WrappedDocument";
|
|
8
8
|
import {Scope} from "./Scope";
|
|
9
9
|
import {EventDispatcher} from "./EventDispatcher";
|
|
10
|
+
import {Registry} from "./Registry";
|
|
11
|
+
import {ClassNode} from "./AST/ClassNode";
|
|
10
12
|
|
|
11
13
|
export class DOM extends EventDispatcher {
|
|
12
14
|
protected static _instance: DOM;
|
|
@@ -26,6 +28,7 @@ export class DOM extends EventDispatcher {
|
|
|
26
28
|
) {
|
|
27
29
|
super();
|
|
28
30
|
this.observer = new MutationObserver(this.mutation.bind(this));
|
|
31
|
+
Registry.instance.classes.on('register', this.prepareDOMClass.bind(this));
|
|
29
32
|
this.tags = [];
|
|
30
33
|
|
|
31
34
|
this.window = new WrappedWindow(window);
|
|
@@ -56,6 +59,18 @@ export class DOM extends EventDispatcher {
|
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
public async prepareDOMClass(className: string, classObject: ClassNode) {
|
|
63
|
+
for (const element of Array.from(this.querySelectorAll(`.${className}`))) {
|
|
64
|
+
const tag: Tag = await this.getTagForElement(element as HTMLElement, true);
|
|
65
|
+
if (tag) {
|
|
66
|
+
await classObject.prepareTag(tag, this);
|
|
67
|
+
if (classObject.classScope.has('construct')) {
|
|
68
|
+
await tag.exec('construct()');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
59
74
|
public async getFromTag(tag: Tag, selector: string, create: boolean = false) {
|
|
60
75
|
const nodes = this.querySelectorElement(tag.element, selector);
|
|
61
76
|
return await this.getTagsForElements(Array.from(nodes) as Element[], create);
|
package/src/Registry.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {EventDispatcher} from "./EventDispatcher";
|
|
2
2
|
import {IDeferred, IPromise, SimplePromise} from "./SimplePromise";
|
|
3
|
+
import {ClassNode} from "./AST/ClassNode";
|
|
3
4
|
|
|
4
5
|
export function register(store: string, key: string = null, setup: () => void = null) {
|
|
5
6
|
return function(target: any, _key: string = null) {
|
|
@@ -23,6 +24,7 @@ export class RegistryStore extends EventDispatcher {
|
|
|
23
24
|
|
|
24
25
|
register(key: string, item: any) {
|
|
25
26
|
this.store[key] = item;
|
|
27
|
+
this.dispatch('register', key, item);
|
|
26
28
|
this.dispatch(`registered:${key}`, item);
|
|
27
29
|
}
|
|
28
30
|
|
|
@@ -52,7 +54,7 @@ export class RegistryStore extends EventDispatcher {
|
|
|
52
54
|
|
|
53
55
|
export class Registry extends EventDispatcher {
|
|
54
56
|
protected static _instance: Registry;
|
|
55
|
-
public readonly
|
|
57
|
+
public readonly controllers: RegistryStore;
|
|
56
58
|
public readonly classes: RegistryStore;
|
|
57
59
|
public readonly models: RegistryStore;
|
|
58
60
|
public readonly templates: RegistryStore;
|
|
@@ -63,7 +65,7 @@ export class Registry extends EventDispatcher {
|
|
|
63
65
|
|
|
64
66
|
constructor() {
|
|
65
67
|
super();
|
|
66
|
-
this.
|
|
68
|
+
this.controllers = new RegistryStore();
|
|
67
69
|
this.classes = new RegistryStore();
|
|
68
70
|
this.models = new RegistryStore();
|
|
69
71
|
this.templates = new RegistryStore();
|
|
@@ -73,16 +75,12 @@ export class Registry extends EventDispatcher {
|
|
|
73
75
|
this.attributes = new RegistryStore();
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
public static
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
public static class(key: string = null, setup = null) {
|
|
81
|
-
return register('classes', key, setup);
|
|
78
|
+
public static class(cls: ClassNode) {
|
|
79
|
+
Registry.instance.classes.register(cls.name, cls);
|
|
82
80
|
}
|
|
83
81
|
|
|
84
82
|
public static controller(key: string = null, setup = null) {
|
|
85
|
-
return register('
|
|
83
|
+
return register('controllers', key, setup);
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
public static model(key: string = null, setup = null) {
|
package/src/Scope.ts
CHANGED
|
@@ -220,4 +220,19 @@ export class Scope extends EventDispatcher {
|
|
|
220
220
|
delete toUnwrap[field];
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
+
|
|
224
|
+
public static fromObject(obj, parentScope?: Scope): Scope {
|
|
225
|
+
const scope = new Scope(parentScope);
|
|
226
|
+
scope.wrap(obj);
|
|
227
|
+
return scope;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export class FunctionScope extends Scope {
|
|
232
|
+
set(key: string, value: any) {
|
|
233
|
+
if (this.parentScope.has(key) || ['$', '@'].indexOf(key[0]) > -1)
|
|
234
|
+
this.parentScope.set(key, value);
|
|
235
|
+
else
|
|
236
|
+
super.set(key, value);
|
|
237
|
+
}
|
|
223
238
|
}
|
|
@@ -1,4 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Scope} from "../Scope";
|
|
2
|
+
import {Attribute} from "../Attribute";
|
|
3
|
+
import {Registry} from "../Registry";
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
@Registry.attribute('vsn-controller')
|
|
6
|
+
export class ControllerAttribute extends Attribute {
|
|
7
|
+
public static readonly canDefer: boolean = false;
|
|
8
|
+
public static readonly scoped: boolean = true;
|
|
9
|
+
protected attributeKey: string;
|
|
10
|
+
protected className: string;
|
|
11
|
+
protected defaultClassName: string;
|
|
12
|
+
|
|
13
|
+
public async setup() {
|
|
14
|
+
const parentScope: Scope = this.tag.parentTag.scope;
|
|
15
|
+
if (!parentScope)
|
|
16
|
+
return;
|
|
17
|
+
|
|
18
|
+
this.attributeKey = this.getAttributeBinding();
|
|
19
|
+
this.className = this.getAttributeValue(this.defaultClassName);
|
|
20
|
+
|
|
21
|
+
const cls = await Registry.instance.controllers.get(this.className);
|
|
22
|
+
this.instantiateClass(cls);
|
|
23
|
+
|
|
24
|
+
if (this.attributeKey && parentScope)
|
|
25
|
+
parentScope.set(this.attributeKey, this.tag.scope);
|
|
26
|
+
await super.setup();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected instantiateClass(cls) {
|
|
30
|
+
this.tag.wrap(cls);
|
|
31
|
+
}
|
|
4
32
|
}
|
package/src/attributes/Exec.ts
CHANGED
|
@@ -7,8 +7,12 @@ export class Exec extends Attribute {
|
|
|
7
7
|
public static readonly canDefer: boolean = false;
|
|
8
8
|
protected tree: Tree;
|
|
9
9
|
|
|
10
|
+
public get code() {
|
|
11
|
+
return this.getAttributeValue();
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
public async compile() {
|
|
11
|
-
this.tree = new Tree(this.
|
|
15
|
+
this.tree = new Tree(this.code);
|
|
12
16
|
await this.tree.prepare(this.tag.scope, this.tag.dom, this.tag);
|
|
13
17
|
await super.compile();
|
|
14
18
|
}
|
|
@@ -21,7 +21,7 @@ export class ListItem extends Attribute {
|
|
|
21
21
|
|
|
22
22
|
this.tag.scope.set(this.listItemName, this.tag.scope);
|
|
23
23
|
const modelName: string = (await this.getList()).listItemModel;
|
|
24
|
-
const cls = await Registry.instance.
|
|
24
|
+
const cls = await Registry.instance.controllers.get(modelName);
|
|
25
25
|
this.instantiateModel(cls);
|
|
26
26
|
await super.setup();
|
|
27
27
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {ControllerAttribute} from "./ControllerAttribute";
|
|
2
2
|
|
|
3
|
-
export class ModelAttribute extends
|
|
3
|
+
export class ModelAttribute extends ControllerAttribute {
|
|
4
4
|
public static readonly canDefer: boolean = false;
|
|
5
5
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {Attribute} from "../Attribute";
|
|
2
2
|
import {VisionHelper} from "../helpers/VisionHelper";
|
|
3
3
|
import {Registry} from "../Registry";
|
|
4
|
+
import {Scope} from "../Scope";
|
|
4
5
|
|
|
5
6
|
@Registry.attribute('vsn-root')
|
|
6
7
|
export class RootAttribute extends Attribute {
|
|
@@ -9,6 +10,8 @@ export class RootAttribute extends Attribute {
|
|
|
9
10
|
|
|
10
11
|
public async setup() {
|
|
11
12
|
this.tag.scope.set('$mobile', VisionHelper.isMobile());
|
|
13
|
+
if (console && !this.tag.scope.get('console'))
|
|
14
|
+
this.tag.scope.set('console', Scope.fromObject(console));
|
|
12
15
|
await super.setup();
|
|
13
16
|
}
|
|
14
17
|
}
|
|
@@ -28,6 +28,9 @@ export class ScopeAttribute extends Attribute {
|
|
|
28
28
|
this.tag.scope.set(key, value.data[key]);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
const binding = this.getAttributeBinding();
|
|
32
|
+
if (binding)
|
|
33
|
+
this.tag.scope.parentScope.set(binding, this.tag.scope);
|
|
31
34
|
await super.extract();
|
|
32
35
|
}
|
|
33
36
|
}
|
|
@@ -36,10 +36,12 @@ export class SetAttribute extends Attribute {
|
|
|
36
36
|
|
|
37
37
|
public async extract() {
|
|
38
38
|
let value = this.getAttributeValue(null);
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
for (const m of this.getAttributeModifiers()) {
|
|
40
|
+
const t = Registry.instance.types.getSynchronous(m);
|
|
41
|
+
if (t) {
|
|
42
|
+
this.boundScope.setType(this.key, m);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
43
45
|
}
|
|
44
46
|
this.boundScope.set(this.key, value);
|
|
45
47
|
}
|
|
@@ -16,7 +16,6 @@ export class StandardAttribute extends Attribute {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
public async connect() {
|
|
19
|
-
this.tag.scope.on(`change:${this.key}`, (v) => { console.log('updated', v);});
|
|
20
19
|
this.tag.scope.on(`change:${this.key}`, this.updateTo.bind(this));
|
|
21
20
|
await super.connect();
|
|
22
21
|
}
|
|
@@ -19,6 +19,7 @@ export {Referenced} from "./Referenced";
|
|
|
19
19
|
export {RootAttribute} from "./RootAttribute";
|
|
20
20
|
export {ScopeAttribute} from "./ScopeAttribute";
|
|
21
21
|
export {ScopeChange} from "./ScopeChange";
|
|
22
|
+
export {ScriptAttribute} from "./ScriptAttribute";
|
|
22
23
|
export {SetAttribute} from "./SetAttribute";
|
|
23
24
|
export {StandardAttribute} from "./StandardAttribute";
|
|
24
25
|
export {StyleAttribute} from "./StyleAttribute";
|
package/src/vsn.ts
CHANGED
|
@@ -25,9 +25,9 @@ export class Vision extends EventDispatcher {
|
|
|
25
25
|
} else {
|
|
26
26
|
console.warn('No dom, running in CLI mode.');
|
|
27
27
|
}
|
|
28
|
-
this.registry.
|
|
29
|
-
this.registry.
|
|
30
|
-
this.registry.
|
|
28
|
+
this.registry.controllers.register('Object', Object);
|
|
29
|
+
this.registry.controllers.register('WrappedArray', WrappedArray);
|
|
30
|
+
this.registry.controllers.register('Data', DynamicScopeData);
|
|
31
31
|
|
|
32
32
|
if (VisionHelper.window) {
|
|
33
33
|
window['Vision'] = Vision;
|
|
@@ -55,7 +55,7 @@ export class Vision extends EventDispatcher {
|
|
|
55
55
|
await this._dom.buildFrom(document, true);
|
|
56
56
|
const now = (new Date()).getTime();
|
|
57
57
|
const setupTime = now - startTime;
|
|
58
|
-
console.
|
|
58
|
+
console.info(`Took ${setupTime}ms to start up VisionJS. https://www.vsnjs.com/`, window ? `v${window['VSN_VERSION']}` : null);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
public static get instance() {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {Tree} from "../../src/AST";
|
|
2
|
+
import {Scope} from "../../src/Scope";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
describe('FunctionNode', () => {
|
|
6
|
+
it("properly define a simple function", async () => {
|
|
7
|
+
const tree = new Tree(`func add(a, b) { return a + b; }; add(1, 2);`);
|
|
8
|
+
const scope = new Scope();
|
|
9
|
+
await tree.prepare(scope, null, null);
|
|
10
|
+
expect(scope.get('add')).toBeDefined();
|
|
11
|
+
const v = await tree.evaluate(scope, null, null);
|
|
12
|
+
expect(v).toBe(3);
|
|
13
|
+
});
|
|
14
|
+
});
|
package/test/DOM.spec.ts
CHANGED
|
@@ -2,7 +2,7 @@ import "../src/vsn";
|
|
|
2
2
|
import {DOM} from "../src/DOM";
|
|
3
3
|
import {Registry} from "../src/Registry";
|
|
4
4
|
|
|
5
|
-
@Registry.
|
|
5
|
+
@Registry.controller('TestController')
|
|
6
6
|
class TestController {}
|
|
7
7
|
|
|
8
8
|
describe('DOM', () => {
|
|
@@ -20,9 +20,9 @@ describe('DOM', () => {
|
|
|
20
20
|
|
|
21
21
|
it("should use scopes correctly", (done) => {
|
|
22
22
|
document.body.innerHTML = `
|
|
23
|
-
<div id="parent" vsn-set:asd="123
|
|
23
|
+
<div id="parent" vsn-set:asd|integer="123">
|
|
24
24
|
<div id="testing" vsn-controller:test="TestController" vsn-set:asd="234|integer">
|
|
25
|
-
<div vsn-set:asd="345
|
|
25
|
+
<div vsn-set:asd|integer="345"></div>
|
|
26
26
|
</div>
|
|
27
27
|
</div>
|
|
28
28
|
`;
|
|
@@ -5,14 +5,14 @@ import {ListItem} from "../../src/attributes/ListItem";
|
|
|
5
5
|
import {Registry} from "../../src/Registry";
|
|
6
6
|
import {List} from "../../src/attributes/List";
|
|
7
7
|
|
|
8
|
-
@Registry.
|
|
8
|
+
@Registry.controller('ListItemController')
|
|
9
9
|
class ListItemController{
|
|
10
10
|
items: ListItemSpecTestItem[] = [];
|
|
11
11
|
|
|
12
12
|
do() {}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
@Registry.
|
|
15
|
+
@Registry.controller('ListItemSpecTestItem')
|
|
16
16
|
class ListItemSpecTestItem {
|
|
17
17
|
test: number = null;
|
|
18
18
|
|
|
@@ -96,7 +96,7 @@ describe('ListItem', () => {
|
|
|
96
96
|
|
|
97
97
|
it("vsn-list-item should work with vsn-set", (done) => {
|
|
98
98
|
document.body.innerHTML = `
|
|
99
|
-
<ul vsn-list:list id="test"><li vsn-list-item:item="ListItemSpecTestItem" id="test-item" vsn-set:item.testing="1
|
|
99
|
+
<ul vsn-list:list id="test"><li vsn-list-item:item="ListItemSpecTestItem" id="test-item" vsn-set:item.testing|integer="1"></li></ul>
|
|
100
100
|
`;
|
|
101
101
|
|
|
102
102
|
const dom = new DOM(document);
|
|
@@ -37,9 +37,9 @@ describe('Bind', () => {
|
|
|
37
37
|
|
|
38
38
|
it("vsn-set to work with a typed value", (done) => {
|
|
39
39
|
document.body.innerHTML = `
|
|
40
|
-
<span id="test-int" vsn-set:int="142.3
|
|
41
|
-
<span id="test-float" vsn-set:float="142.3
|
|
42
|
-
<span id="test-bool" vsn-set:bool="false
|
|
40
|
+
<span id="test-int" vsn-set:int|integer="142.3">testing</span>
|
|
41
|
+
<span id="test-float" vsn-set:float|float="142.3">testing</span>
|
|
42
|
+
<span id="test-bool" vsn-set:bool|boolean="false">testing</span>
|
|
43
43
|
`;
|
|
44
44
|
const dom = new DOM(document);
|
|
45
45
|
dom.once('built', async () => {
|
package/demo/xhr.vsn
DELETED