vsn 0.1.27 → 0.1.30
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/vsn.js +1 -1
- package/dist/AST/ArithmeticAssignmentNode.d.ts +23 -0
- package/dist/AST/ArithmeticAssignmentNode.js +324 -0
- package/dist/AST/ArithmeticAssignmentNode.js.map +1 -0
- package/dist/AST/ArithmeticNode.d.ts +15 -0
- package/dist/AST/ArithmeticNode.js +114 -0
- package/dist/AST/ArithmeticNode.js.map +1 -0
- package/dist/AST/ArrayNode.d.ts +14 -0
- package/dist/AST/ArrayNode.js +114 -0
- package/dist/AST/ArrayNode.js.map +1 -0
- package/dist/AST/BlockNode.d.ts +11 -0
- package/dist/AST/BlockNode.js +98 -0
- package/dist/AST/BlockNode.js.map +1 -0
- package/dist/AST/BooleanLiteralNode.d.ts +5 -0
- package/dist/{Model/Collection.js → AST/BooleanLiteralNode.js} +12 -18
- package/dist/AST/BooleanLiteralNode.js.map +1 -0
- package/dist/AST/ComparisonNode.d.ts +15 -0
- package/dist/AST/ComparisonNode.js +120 -0
- package/dist/AST/ComparisonNode.js.map +1 -0
- package/dist/AST/ConditionalNode.d.ts +13 -0
- package/dist/AST/ConditionalNode.js +95 -0
- package/dist/AST/ConditionalNode.js.map +1 -0
- package/dist/AST/ElementAttributeNode.d.ts +18 -0
- package/dist/AST/ElementAttributeNode.js +159 -0
- package/dist/AST/ElementAttributeNode.js.map +1 -0
- package/dist/AST/ElementQueryNode.d.ts +13 -0
- package/dist/AST/ElementQueryNode.js +116 -0
- package/dist/AST/ElementQueryNode.js.map +1 -0
- package/dist/AST/ElementStyleNode.d.ts +18 -0
- package/dist/AST/ElementStyleNode.js +159 -0
- package/dist/AST/ElementStyleNode.js.map +1 -0
- package/dist/AST/ForStatementNode.d.ts +17 -0
- package/dist/AST/ForStatementNode.js +121 -0
- package/dist/AST/ForStatementNode.js.map +1 -0
- package/dist/AST/FunctionArgumentNode.d.ts +11 -0
- package/dist/AST/FunctionArgumentNode.js +100 -0
- package/dist/AST/FunctionArgumentNode.js.map +1 -0
- package/dist/AST/FunctionCallNode.d.ts +13 -0
- package/dist/AST/FunctionCallNode.js +102 -0
- package/dist/AST/FunctionCallNode.js.map +1 -0
- package/dist/AST/IfStatementNode.d.ts +14 -0
- package/dist/AST/IfStatementNode.js +128 -0
- package/dist/AST/IfStatementNode.js.map +1 -0
- package/dist/AST/InNode.d.ts +15 -0
- package/dist/AST/InNode.js +107 -0
- package/dist/AST/InNode.js.map +1 -0
- package/dist/AST/IndexNode.d.ts +16 -0
- package/dist/AST/IndexNode.js +126 -0
- package/dist/AST/IndexNode.js.map +1 -0
- package/dist/AST/LiteralNode.d.ts +10 -0
- package/dist/AST/LiteralNode.js +74 -0
- package/dist/AST/LiteralNode.js.map +1 -0
- package/dist/AST/Node.d.ts +19 -0
- package/dist/AST/Node.js +117 -0
- package/dist/AST/Node.js.map +1 -0
- package/dist/AST/NotNode.d.ts +12 -0
- package/dist/AST/NotNode.js +103 -0
- package/dist/AST/NotNode.js.map +1 -0
- package/dist/AST/NumberLiteralNode.d.ts +5 -0
- package/dist/{Model/fields/EmailField.js → AST/NumberLiteralNode.js} +17 -17
- package/dist/AST/NumberLiteralNode.js.map +1 -0
- package/dist/AST/ObjectNode.d.ts +14 -0
- package/dist/AST/ObjectNode.js +131 -0
- package/dist/AST/ObjectNode.js.map +1 -0
- package/dist/AST/RootScopeMemberNode.d.ts +11 -0
- package/dist/AST/RootScopeMemberNode.js +87 -0
- package/dist/AST/RootScopeMemberNode.js.map +1 -0
- package/dist/AST/ScopeMemberNode.d.ts +12 -0
- package/dist/AST/ScopeMemberNode.js +134 -0
- package/dist/AST/ScopeMemberNode.js.map +1 -0
- package/dist/AST/UnitLiteralNode.d.ts +15 -0
- package/dist/AST/UnitLiteralNode.js +72 -0
- package/dist/AST/UnitLiteralNode.js.map +1 -0
- package/dist/AST.d.ts +7 -60
- package/dist/AST.js +74 -1493
- package/dist/AST.js.map +1 -1
- package/dist/Controller.d.ts +4 -2
- package/dist/Controller.js +10 -2
- package/dist/Controller.js.map +1 -1
- package/dist/EventDispatcher.d.ts +4 -1
- package/dist/EventDispatcher.js +27 -12
- package/dist/EventDispatcher.js.map +1 -1
- package/dist/MessageList.d.ts +2 -1
- package/dist/MessageList.js +9 -1
- package/dist/MessageList.js.map +1 -1
- package/dist/Model/Field.d.ts +8 -0
- package/dist/Model/{fields/StringField.js → Field.js} +19 -24
- package/dist/Model/Field.js.map +1 -0
- package/dist/Model.d.ts +5 -4
- package/dist/Model.js +7 -18
- package/dist/Model.js.map +1 -1
- package/dist/Registry.d.ts +3 -0
- package/dist/Registry.js +11 -0
- package/dist/Registry.js.map +1 -1
- package/dist/Scope/DynamicScopeData.d.ts +6 -0
- package/dist/{Model/DataModel.js → Scope/DynamicScopeData.js} +15 -17
- package/dist/Scope/DynamicScopeData.js.map +1 -0
- package/dist/Scope/QueryReference.d.ts +10 -0
- package/dist/Scope/QueryReference.js +103 -0
- package/dist/Scope/QueryReference.js.map +1 -0
- package/dist/Scope/ScopeData.d.ts +4 -0
- package/dist/Scope/ScopeData.js +40 -0
- package/dist/Scope/ScopeData.js.map +1 -0
- package/dist/Scope/ScopeDataAbstract.d.ts +22 -0
- package/dist/Scope/ScopeDataAbstract.js +138 -0
- package/dist/Scope/ScopeDataAbstract.js.map +1 -0
- package/dist/Scope/ScopeReference.d.ts +10 -0
- package/dist/Scope/ScopeReference.js +73 -0
- package/dist/Scope/ScopeReference.js.map +1 -0
- package/dist/Scope/ScopedVariableType.d.ts +6 -0
- package/dist/Scope/ScopedVariableType.js +14 -0
- package/dist/Scope/ScopedVariableType.js.map +1 -0
- package/dist/Scope/WrappedArray.d.ts +16 -0
- package/dist/Scope/WrappedArray.js +121 -0
- package/dist/Scope/WrappedArray.js.map +1 -0
- package/dist/Scope/properties/Property.d.ts +18 -0
- package/dist/Scope/properties/Property.js +93 -0
- package/dist/Scope/properties/Property.js.map +1 -0
- package/dist/Scope.d.ts +4 -45
- package/dist/Scope.js +31 -266
- package/dist/Scope.js.map +1 -1
- package/dist/SimplePromise.d.ts +2 -2
- package/dist/SimplePromise.js.map +1 -1
- package/dist/Tag/List.d.ts +2 -2
- package/dist/Tag/List.js +14 -6
- package/dist/Tag/List.js.map +1 -1
- package/dist/Tag.js +1 -1
- package/dist/Tag.js.map +1 -1
- package/dist/Types.d.ts +1 -0
- package/dist/Types.js +8 -2
- package/dist/Types.js.map +1 -1
- package/dist/Validators.d.ts +7 -0
- package/dist/Validators.js +54 -0
- package/dist/Validators.js.map +1 -0
- package/dist/attributes/Bind.js +1 -1
- package/dist/attributes/Bind.js.map +1 -1
- package/dist/attributes/ClassConstructor.d.ts +1 -0
- package/dist/attributes/ClassConstructor.js +1 -1
- package/dist/attributes/ClassConstructor.js.map +1 -1
- package/dist/attributes/JSONAttribute.js.map +1 -1
- package/dist/attributes/List.js +4 -4
- package/dist/attributes/List.js.map +1 -1
- package/dist/attributes/Radio.js.map +1 -1
- package/dist/attributes/SetAttribute.js.map +1 -1
- package/dist/attributes/StyleAttribute.js.map +1 -1
- package/dist/vsn.d.ts +9 -1
- package/dist/vsn.js +24 -9
- package/dist/vsn.js.map +1 -1
- package/package.json +1 -1
- package/src/AST/ArithmeticAssignmentNode.ts +243 -0
- package/src/AST/ArithmeticNode.ts +52 -0
- package/src/AST/ArrayNode.ts +39 -0
- package/src/AST/BlockNode.ts +25 -0
- package/src/AST/BooleanLiteralNode.ts +10 -0
- package/src/AST/ComparisonNode.ts +57 -0
- package/src/AST/ConditionalNode.ts +36 -0
- package/src/AST/ElementAttributeNode.ts +63 -0
- package/src/AST/ElementQueryNode.ts +27 -0
- package/src/AST/ElementStyleNode.ts +63 -0
- package/src/AST/ForStatementNode.ts +59 -0
- package/src/AST/FunctionArgumentNode.ts +27 -0
- package/src/AST/FunctionCallNode.ts +32 -0
- package/src/AST/IfStatementNode.ts +67 -0
- package/src/AST/InNode.ts +46 -0
- package/src/AST/IndexNode.ts +61 -0
- package/src/AST/LiteralNode.ts +17 -0
- package/src/AST/Node.ts +71 -0
- package/src/AST/NotNode.ts +41 -0
- package/src/AST/NumberLiteralNode.ts +14 -0
- package/src/AST/ObjectNode.ts +55 -0
- package/src/AST/RootScopeMemberNode.ts +25 -0
- package/src/AST/ScopeMemberNode.ts +58 -0
- package/src/AST/UnitLiteralNode.ts +51 -0
- package/src/AST.ts +34 -1094
- package/src/Controller.ts +10 -2
- package/src/EventDispatcher.ts +29 -12
- package/src/MessageList.ts +5 -1
- package/src/Model/Field.ts +20 -0
- package/src/Model.ts +8 -22
- package/src/Registry.ts +10 -0
- package/src/{Model/DataModel.ts → Scope/DynamicScopeData.ts} +8 -10
- package/src/Scope/QueryReference.ts +29 -0
- package/src/Scope/ScopeData.ts +21 -0
- package/src/Scope/ScopeDataAbstract.ts +127 -0
- package/src/Scope/ScopeReference.ts +30 -0
- package/src/Scope/ScopedVariableType.ts +7 -0
- package/src/Scope/WrappedArray.ts +88 -0
- package/src/Scope/properties/Property.ts +79 -0
- package/src/Scope.ts +28 -194
- package/src/SimplePromise.ts +2 -2
- package/src/Tag/List.ts +4 -4
- package/src/Tag.ts +1 -1
- package/src/Types.ts +7 -2
- package/src/Validators.ts +45 -0
- package/src/attributes/Bind.ts +3 -3
- package/src/attributes/ClassConstructor.ts +2 -1
- package/src/attributes/JSONAttribute.ts +2 -1
- package/src/attributes/List.ts +1 -1
- package/src/attributes/Radio.ts +2 -1
- package/src/attributes/ScopeChange.ts +1 -1
- package/src/attributes/SetAttribute.ts +2 -1
- package/src/attributes/StyleAttribute.ts +2 -1
- package/src/attributes/TypeAttribute.ts +1 -1
- package/src/vsn.ts +14 -4
- package/test/AST/ArithmeticAssignmentNode.spec.ts +47 -0
- package/test/AST.spec.ts +2 -2
- package/test/Controller.spec.ts +44 -0
- package/test/MessageList.spec.ts +1 -1
- package/test/Model/DataModel.spec.ts +0 -141
- package/test/Scope/DynamicScopeData.spec.ts +141 -0
- package/test/Scope.spec.ts +13 -1
- package/test/Tag/TagList.spec.ts +1 -1
- package/dist/Model/Collection.d.ts +0 -5
- package/dist/Model/Collection.js.map +0 -1
- package/dist/Model/DataModel.d.ts +0 -6
- package/dist/Model/DataModel.js.map +0 -1
- package/dist/Model/ModelAbstract.d.ts +0 -20
- package/dist/Model/ModelAbstract.js +0 -122
- package/dist/Model/ModelAbstract.js.map +0 -1
- package/dist/Model/fields/BooleanField.d.ts +0 -5
- package/dist/Model/fields/BooleanField.js +0 -43
- package/dist/Model/fields/BooleanField.js.map +0 -1
- package/dist/Model/fields/EmailField.d.ts +0 -5
- package/dist/Model/fields/EmailField.js.map +0 -1
- package/dist/Model/fields/Field.d.ts +0 -13
- package/dist/Model/fields/Field.js +0 -79
- package/dist/Model/fields/Field.js.map +0 -1
- package/dist/Model/fields/FloatField.d.ts +0 -5
- package/dist/Model/fields/FloatField.js +0 -47
- package/dist/Model/fields/FloatField.js.map +0 -1
- package/dist/Model/fields/PositiveNumberField.d.ts +0 -5
- package/dist/Model/fields/PositiveNumberField.js +0 -51
- package/dist/Model/fields/PositiveNumberField.js.map +0 -1
- package/dist/Model/fields/StringField.d.ts +0 -5
- package/dist/Model/fields/StringField.js.map +0 -1
- package/src/Model/Collection.ts +0 -13
- package/src/Model/ModelAbstract.ts +0 -114
- package/src/Model/fields/BooleanField.ts +0 -16
- package/src/Model/fields/EmailField.ts +0 -12
- package/src/Model/fields/Field.ts +0 -65
- package/src/Model/fields/FloatField.ts +0 -22
- package/src/Model/fields/PositiveNumberField.ts +0 -24
- package/src/Model/fields/StringField.ts +0 -16
- package/test/Model.spec.ts +0 -306
package/src/Controller.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {Scope} from "./Scope";
|
|
2
2
|
import {Tag} from "./Tag";
|
|
3
|
-
import {
|
|
3
|
+
import {ScopeData} from "./Scope/ScopeData";
|
|
4
4
|
|
|
5
|
-
export abstract class Controller extends
|
|
5
|
+
export abstract class Controller extends ScopeData {
|
|
6
6
|
protected _scope: Scope;
|
|
7
7
|
protected _tag: Tag;
|
|
8
8
|
protected _element: HTMLElement;
|
|
@@ -24,4 +24,12 @@ export abstract class Controller extends EventDispatcher {
|
|
|
24
24
|
this._tag = tag;
|
|
25
25
|
this._element = element;
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
public get(key: string): any {
|
|
29
|
+
return this._scope?.get(key);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public set(key: string, value: any): void {
|
|
33
|
+
this._scope?.set(key, value);
|
|
34
|
+
}
|
|
27
35
|
}
|
package/src/EventDispatcher.ts
CHANGED
|
@@ -31,6 +31,7 @@ export type EventDispatcherCallback = (...args: any[]) => any;
|
|
|
31
31
|
export class EventDispatcher {
|
|
32
32
|
private static sources: EventDispatcher[] = [];
|
|
33
33
|
private readonly _listeners: EventCallbackList;
|
|
34
|
+
private readonly _relays: EventDispatcher[] = [];
|
|
34
35
|
private _lastKey: number;
|
|
35
36
|
|
|
36
37
|
constructor() {
|
|
@@ -39,9 +40,21 @@ export class EventDispatcher {
|
|
|
39
40
|
EventDispatcher.sources.push(this);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
deconstruct() {
|
|
43
44
|
this.dispatch('deconstruct', this);
|
|
44
45
|
EventDispatcher.sources.splice(EventDispatcher.sources.indexOf(this), 1);
|
|
46
|
+
for (const k in this._listeners) {
|
|
47
|
+
delete this._listeners[k];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addRelay(relay: EventDispatcher) {
|
|
52
|
+
this._relays.push(relay);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
removeRelay(relay: EventDispatcher) {
|
|
56
|
+
if (this._relays.indexOf(relay) > -1)
|
|
57
|
+
this._relays.splice(this._relays.indexOf(relay), 1);
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
on(event: string, fct: EventDispatcherCallback, context?: any, once?: boolean): number {
|
|
@@ -98,20 +111,24 @@ export class EventDispatcher {
|
|
|
98
111
|
}
|
|
99
112
|
|
|
100
113
|
dispatch(event: string, ...args: any[]): void {
|
|
101
|
-
if(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
114
|
+
if(event in this._listeners) {
|
|
115
|
+
for (let i = 0; i < this._listeners[event].length; i++) {
|
|
116
|
+
const cb: EventCallback = this._listeners[event][i];
|
|
117
|
+
|
|
118
|
+
// We need to unbind callbacks before they're called to prevent
|
|
119
|
+
// infinite loops if the event is somehow triggered within the
|
|
120
|
+
// callback
|
|
121
|
+
if (cb.once) {
|
|
122
|
+
this.off(event, cb.key);
|
|
123
|
+
i--;
|
|
124
|
+
}
|
|
105
125
|
|
|
106
|
-
|
|
107
|
-
// infinite loops if the event is somehow triggered within the
|
|
108
|
-
// callback
|
|
109
|
-
if(cb.once) {
|
|
110
|
-
this.off(event, cb.key);
|
|
111
|
-
i--;
|
|
126
|
+
cb.call(args);
|
|
112
127
|
}
|
|
128
|
+
}
|
|
113
129
|
|
|
114
|
-
|
|
130
|
+
for (const relay of this._relays) {
|
|
131
|
+
relay.dispatch(event, ...args);
|
|
115
132
|
}
|
|
116
133
|
}
|
|
117
134
|
}
|
package/src/MessageList.ts
CHANGED
|
@@ -2,7 +2,7 @@ export interface IMessageHash {
|
|
|
2
2
|
[key: string]: string[] | null | undefined;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export class MessageList {
|
|
6
6
|
[key: string]: any; // We're trying to mimic a basic object
|
|
7
7
|
|
|
8
8
|
private _cachedList: IMessageHash | undefined;
|
|
@@ -78,4 +78,8 @@ export default class MessageList {
|
|
|
78
78
|
get length(): number {
|
|
79
79
|
return this.keys.length;
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
get isEmpty(): boolean {
|
|
83
|
+
return this.length === 0;
|
|
84
|
+
}
|
|
81
85
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {IPropertyConfig, Property} from "../Scope/properties/Property";
|
|
2
|
+
import {Registry} from "../Registry";
|
|
3
|
+
|
|
4
|
+
export interface IFieldConfig extends IPropertyConfig {
|
|
5
|
+
validators?: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class Field extends Property {
|
|
9
|
+
config: IFieldConfig;
|
|
10
|
+
|
|
11
|
+
validate() {
|
|
12
|
+
const errors = [];
|
|
13
|
+
for(const validatorName of this.config.validators || []) {
|
|
14
|
+
const validator = Registry.instance.validators.getSynchronous(validatorName);
|
|
15
|
+
errors.concat(validator(this.value));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return errors;
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/Model.ts
CHANGED
|
@@ -1,28 +1,14 @@
|
|
|
1
|
+
import {MessageList} from "./MessageList";
|
|
2
|
+
import {ScopeData} from "./Scope/ScopeData";
|
|
3
|
+
import {IScopeData} from "./Scope/ScopeDataAbstract";
|
|
1
4
|
|
|
2
|
-
import {ModelAbstract, ModelData} from "./Model/ModelAbstract";
|
|
3
|
-
import MessageList from "./MessageList";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
export class Model extends ModelAbstract {
|
|
6
|
+
export class Model extends ScopeData {
|
|
7
7
|
_errors!: MessageList;
|
|
8
8
|
_hasErrors: boolean;
|
|
9
9
|
|
|
10
|
-
constructor(data:
|
|
10
|
+
constructor(data: IScopeData | null | undefined = null) {
|
|
11
11
|
super();
|
|
12
|
-
const fields = this.__fields__.splice(0, this.__fields__.length);
|
|
13
|
-
for(const field of fields) {
|
|
14
|
-
(function(_self, field) {
|
|
15
|
-
if(!_self['__'+field+'__'])
|
|
16
|
-
return;
|
|
17
|
-
|
|
18
|
-
_self.__fields__.push(field);
|
|
19
|
-
const _field = _self['__'+field+'__'],
|
|
20
|
-
fieldType = _field[0],
|
|
21
|
-
config = _field[1] || {};
|
|
22
|
-
|
|
23
|
-
_self.createField(field, fieldType, config);
|
|
24
|
-
})(this, field);
|
|
25
|
-
}
|
|
26
12
|
|
|
27
13
|
this._hasErrors = false;
|
|
28
14
|
if (data)
|
|
@@ -36,10 +22,10 @@ export class Model extends ModelAbstract {
|
|
|
36
22
|
validate(): MessageList {
|
|
37
23
|
this._hasErrors = false;
|
|
38
24
|
this._errors = new MessageList;
|
|
39
|
-
for(const
|
|
40
|
-
const errors = this['__'+
|
|
25
|
+
for(const property of this.getProperties()) {
|
|
26
|
+
const errors = this['__'+property].validate();
|
|
41
27
|
if(errors.length > 0) {
|
|
42
|
-
this._errors.add(
|
|
28
|
+
this._errors.add(property, errors, true);
|
|
43
29
|
this._hasErrors = true;
|
|
44
30
|
}
|
|
45
31
|
}
|
package/src/Registry.ts
CHANGED
|
@@ -57,6 +57,7 @@ export class Registry extends EventDispatcher {
|
|
|
57
57
|
public readonly models: RegistryStore;
|
|
58
58
|
public readonly templates: RegistryStore;
|
|
59
59
|
public readonly types: RegistryStore;
|
|
60
|
+
public readonly validators: RegistryStore;
|
|
60
61
|
public readonly formats: RegistryStore;
|
|
61
62
|
public readonly attributes: RegistryStore;
|
|
62
63
|
|
|
@@ -68,6 +69,7 @@ export class Registry extends EventDispatcher {
|
|
|
68
69
|
this.models = new RegistryStore(w['$models'] || {});
|
|
69
70
|
this.templates = new RegistryStore(w['$templates'] || {});
|
|
70
71
|
this.types = new RegistryStore(w['$types'] || {});
|
|
72
|
+
this.validators = new RegistryStore(w['$validators'] || {});
|
|
71
73
|
this.formats = new RegistryStore(w['$formats'] || {});
|
|
72
74
|
this.attributes = new RegistryStore(w['$attributes'] || {});
|
|
73
75
|
}
|
|
@@ -76,6 +78,10 @@ export class Registry extends EventDispatcher {
|
|
|
76
78
|
return register('classes', key, setup);
|
|
77
79
|
}
|
|
78
80
|
|
|
81
|
+
public static controller(key: string = null, setup = null) {
|
|
82
|
+
return register('classes', key, setup);
|
|
83
|
+
}
|
|
84
|
+
|
|
79
85
|
public static model(key: string = null, setup = null) {
|
|
80
86
|
return register('models', key, setup);
|
|
81
87
|
}
|
|
@@ -88,6 +94,10 @@ export class Registry extends EventDispatcher {
|
|
|
88
94
|
return register('types', key, setup);
|
|
89
95
|
}
|
|
90
96
|
|
|
97
|
+
public static validator(key: string = null, setup = null) {
|
|
98
|
+
return register('validators', key, setup);
|
|
99
|
+
}
|
|
100
|
+
|
|
91
101
|
public static format(key: string = null, setup = null) {
|
|
92
102
|
return register('formats', key, setup);
|
|
93
103
|
}
|
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {IScopeData, ScopeDataAbstract} from "./ScopeDataAbstract";
|
|
2
2
|
|
|
3
|
-
export class
|
|
4
|
-
constructor(data:
|
|
3
|
+
export class DynamicScopeData extends ScopeDataAbstract {
|
|
4
|
+
constructor(data: IScopeData | string[]) {
|
|
5
5
|
super();
|
|
6
6
|
if(data instanceof Array) {
|
|
7
|
-
this.__fields__ = data;
|
|
8
7
|
for (const field of data)
|
|
9
|
-
this.
|
|
8
|
+
this.createProperty(field);
|
|
10
9
|
} else {
|
|
11
10
|
this.setData(data);
|
|
12
11
|
}
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
setData(data:
|
|
14
|
+
setData(data: IScopeData) {
|
|
16
15
|
for(const field of Object.keys(data))
|
|
17
|
-
if(this.
|
|
18
|
-
this.
|
|
19
|
-
this.createField(field);
|
|
16
|
+
if(!this.hasProperty(field)) {
|
|
17
|
+
this.createProperty(field);
|
|
20
18
|
}
|
|
21
19
|
super.setData(data);
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
on(event: string, fct: (...args: any[]) => any, context?: any, once?: boolean): number {
|
|
25
23
|
if(event.indexOf('change:') == 0)
|
|
26
|
-
this.
|
|
24
|
+
this.createProperty(event.substr(7));
|
|
27
25
|
return super.on(event, fct, context, once);
|
|
28
26
|
}
|
|
29
27
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {DOM} from "../DOM";
|
|
2
|
+
import {Scope} from "../Scope";
|
|
3
|
+
import {ScopeReference} from "./ScopeReference";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export class QueryReference extends ScopeReference {
|
|
7
|
+
constructor(
|
|
8
|
+
public readonly path: string,
|
|
9
|
+
public readonly scope: Scope
|
|
10
|
+
) {
|
|
11
|
+
super();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async getScope() {
|
|
15
|
+
let parts: string[] = this.path.split('.');
|
|
16
|
+
parts = parts.splice(0, parts.length - 1);
|
|
17
|
+
const qResult = await DOM.instance.eval(parts.join('.'));
|
|
18
|
+
return qResult.length === 1 ? qResult[0].scope : qResult.map((dobj) => dobj.scope);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async getKey() {
|
|
22
|
+
const parts: string[] = this.path.split('.');
|
|
23
|
+
return parts[parts.length - 1];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public async getValue() {
|
|
27
|
+
return await DOM.instance.eval(this.path);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {ScopeDataAbstract} from "./ScopeDataAbstract";
|
|
2
|
+
|
|
3
|
+
export class ScopeData extends ScopeDataAbstract {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
const properties = this.__properties__.splice(0, this.__properties__.length);
|
|
7
|
+
for(const property of properties) {
|
|
8
|
+
(function(_self, name) {
|
|
9
|
+
if(!_self['__'+name+'__'])
|
|
10
|
+
return;
|
|
11
|
+
|
|
12
|
+
_self.__properties__.push(name);
|
|
13
|
+
const _property = _self['__'+name+'__'],
|
|
14
|
+
propertyType = _property[0],
|
|
15
|
+
config = _property[1] || {};
|
|
16
|
+
|
|
17
|
+
_self.createProperty(name, propertyType, config);
|
|
18
|
+
})(this, property);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {IPropertyConfig, Property} from "./properties/Property";
|
|
2
|
+
import {EventDispatcher} from "../EventDispatcher";
|
|
3
|
+
|
|
4
|
+
export interface IScopeData {
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class ScopeDataAbstract extends EventDispatcher {
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
__properties__: string[];
|
|
11
|
+
protected _lastData: any;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
|
|
16
|
+
// Objects may have __properties__ from prototype
|
|
17
|
+
if(!this['__properties__'])
|
|
18
|
+
this.__properties__ = [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
createProperty(name: string, propertyType = Property, config?: IPropertyConfig): Property {
|
|
22
|
+
config = config || {};
|
|
23
|
+
const instance = new propertyType(config.default, config),
|
|
24
|
+
propDesc = Object.getOwnPropertyDescriptor(this, name);
|
|
25
|
+
this['__'+name] = instance;
|
|
26
|
+
this.__properties__.push(name);
|
|
27
|
+
|
|
28
|
+
// property getter
|
|
29
|
+
const propertyGetter = function() {
|
|
30
|
+
return instance.value;
|
|
31
|
+
};
|
|
32
|
+
const getter = propDesc ? propDesc.get : propertyGetter,
|
|
33
|
+
propertySetter = function(newVal: any) {
|
|
34
|
+
instance.value = newVal;
|
|
35
|
+
},
|
|
36
|
+
setter = propDesc ? propDesc.set : propertySetter;
|
|
37
|
+
|
|
38
|
+
// Delete the original property
|
|
39
|
+
delete this[name];
|
|
40
|
+
|
|
41
|
+
// Create new property with getter and setter
|
|
42
|
+
Object.defineProperty(this, name, {
|
|
43
|
+
get: getter,
|
|
44
|
+
set: setter,
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
instance.on('change', (...args: any[]) => {
|
|
50
|
+
this.dispatch('change', name, ...args);
|
|
51
|
+
this.dispatch('change:' + name, ...args);
|
|
52
|
+
});
|
|
53
|
+
return instance;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
hasProperty(name: string): boolean {
|
|
57
|
+
return this.__properties__.indexOf(name) !== -1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get keys(): string[] {
|
|
61
|
+
return [...this.__properties__];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setData(data: IScopeData) {
|
|
65
|
+
const properties = this.getProperties();
|
|
66
|
+
for (const key in data) {
|
|
67
|
+
if (properties.indexOf(key) > -1) {
|
|
68
|
+
this[key] = data[key];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getData(): IScopeData {
|
|
74
|
+
const data: IScopeData = {};
|
|
75
|
+
for (const key of this.getProperties()) {
|
|
76
|
+
const property = this['__'+key];
|
|
77
|
+
if(this[key] == null || !property)
|
|
78
|
+
continue;
|
|
79
|
+
|
|
80
|
+
data[key] = property.getData();
|
|
81
|
+
}
|
|
82
|
+
return data;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getProperties(): string[] {
|
|
86
|
+
return this.__properties__;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getProperty(name: string, create: boolean = false): Property {
|
|
90
|
+
let property = this['__'+name];
|
|
91
|
+
if (create && !property) {
|
|
92
|
+
property = this.createProperty(name);
|
|
93
|
+
}
|
|
94
|
+
return property;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
bindToProperties(event:string, properties:string[], callback: (...args: any[]) => any) {
|
|
98
|
+
for(const name of properties) {
|
|
99
|
+
const _property = this['__'+ name];
|
|
100
|
+
if(_property)
|
|
101
|
+
_property.on(event, callback);
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setLastData() {
|
|
107
|
+
this._lastData = this.getData();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
* Revert data to the last setData() call. Useful for forms that edit a
|
|
112
|
+
* list of items and then hit cancel rather than saving the list.
|
|
113
|
+
*/
|
|
114
|
+
revert() {
|
|
115
|
+
this.setData(this._lastData);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
isModified() {
|
|
119
|
+
const oData = this._lastData,
|
|
120
|
+
nData = this.getData();
|
|
121
|
+
for(const key of this.getProperties()) {
|
|
122
|
+
if(nData[key] != oData[key])
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {Scope} from "../Scope";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export class ScopeReference {
|
|
5
|
+
private _scope: Scope;
|
|
6
|
+
private _key: string;
|
|
7
|
+
private _value: any;
|
|
8
|
+
|
|
9
|
+
constructor(
|
|
10
|
+
scope: Scope = null,
|
|
11
|
+
key: string = null,
|
|
12
|
+
value: any = null
|
|
13
|
+
) {
|
|
14
|
+
this._scope = scope;
|
|
15
|
+
this._key = key;
|
|
16
|
+
this._value = value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public async getScope() {
|
|
20
|
+
return this._scope;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public async getKey() {
|
|
24
|
+
return this._key;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async getValue() {
|
|
28
|
+
return this._value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {EventDispatcher, EventDispatcherCallback} from "../EventDispatcher";
|
|
2
|
+
|
|
3
|
+
export class WrappedArray<T> extends Array<T> {
|
|
4
|
+
private dispatcher: EventDispatcher;
|
|
5
|
+
public readonly $wrapped: boolean = true;
|
|
6
|
+
|
|
7
|
+
constructor(...items: T[]) {
|
|
8
|
+
super(...items);
|
|
9
|
+
Object.setPrototypeOf(this, WrappedArray.prototype);
|
|
10
|
+
this.dispatcher = new EventDispatcher();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
dispatch(event: string, ...args: any[]) {
|
|
14
|
+
this.dispatcher.dispatch(event, ...args);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
on(event: string, callback: EventDispatcherCallback) {
|
|
18
|
+
this.dispatcher.on(event, callback);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
off(event: string, key?: number) {
|
|
22
|
+
this.dispatcher.off(event, key);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
once(event: string, callback: EventDispatcherCallback) {
|
|
26
|
+
this.dispatcher.once(event, callback);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
push(...items: T[]): number {
|
|
30
|
+
const num: number = super.push(...items);
|
|
31
|
+
|
|
32
|
+
this.dispatch('push', ...items);
|
|
33
|
+
this.dispatch('change', {
|
|
34
|
+
'added': items
|
|
35
|
+
});
|
|
36
|
+
for (const item of items) {
|
|
37
|
+
this.dispatch('add', item);
|
|
38
|
+
}
|
|
39
|
+
return num;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
splice(start: number, deleteCount?: number): T[] {
|
|
43
|
+
const removed: T[] = super.splice(start, deleteCount);
|
|
44
|
+
|
|
45
|
+
this.dispatch('change', {
|
|
46
|
+
'removed': removed
|
|
47
|
+
});
|
|
48
|
+
for (const item of removed) {
|
|
49
|
+
this.dispatch('remove', item);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return removed;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get(key: string) {
|
|
56
|
+
const keys: string[] = [
|
|
57
|
+
'length'
|
|
58
|
+
];
|
|
59
|
+
return keys.indexOf(key) > -1 ? this[key] : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get length(): number {
|
|
63
|
+
let c: number = 0;
|
|
64
|
+
for (const item of this) {
|
|
65
|
+
c += 1;
|
|
66
|
+
}
|
|
67
|
+
return c;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
set length(num: number) {
|
|
71
|
+
this.setLength(num);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setLength(num: number) {
|
|
75
|
+
let c: number = 0;
|
|
76
|
+
const toRemove: T[] = [];
|
|
77
|
+
for (const item of this) {
|
|
78
|
+
c += 1;
|
|
79
|
+
if (c >= num) {
|
|
80
|
+
toRemove.push(item);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const item of toRemove) {
|
|
85
|
+
this.splice(this.indexOf(item), 1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {EventDispatcher} from "../../EventDispatcher";
|
|
2
|
+
import {Registry} from "../../Registry";
|
|
3
|
+
|
|
4
|
+
export function property(propertyType = Property, config: {} | null = {}) {
|
|
5
|
+
return function(target: any, key: string) {
|
|
6
|
+
if(target.__properties__ == undefined) {
|
|
7
|
+
target.__properties__ = [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Abstract/extended classes share __properties__
|
|
11
|
+
if(target.__properties__.indexOf(key) == -1)
|
|
12
|
+
target.__properties__.push(key);
|
|
13
|
+
|
|
14
|
+
const getter = function() {
|
|
15
|
+
return [propertyType, config];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
Object.defineProperty(target, '__'+key+'__', {
|
|
19
|
+
get: getter,
|
|
20
|
+
set: v => {},
|
|
21
|
+
enumerable:false,
|
|
22
|
+
configurable: true
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface IPropertyConfig {
|
|
28
|
+
type?: string;
|
|
29
|
+
default?: any;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class Property extends EventDispatcher {
|
|
33
|
+
_type: string = 'any';
|
|
34
|
+
_value: any;
|
|
35
|
+
config: IPropertyConfig;
|
|
36
|
+
|
|
37
|
+
constructor(value?:any, config?: IPropertyConfig) {
|
|
38
|
+
super();
|
|
39
|
+
this.config = config;
|
|
40
|
+
this.type = config.type || 'any';
|
|
41
|
+
|
|
42
|
+
this.value = value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
castType(value) {
|
|
46
|
+
const caster = Registry.instance.types.getSynchronous(this.type);
|
|
47
|
+
return caster(value);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
set value(v:any) {
|
|
51
|
+
const oldValue = this._value;
|
|
52
|
+
this._value = this.castType(v);
|
|
53
|
+
if (this._value !== oldValue) {
|
|
54
|
+
this.dispatch('change', {
|
|
55
|
+
oldValue:oldValue,
|
|
56
|
+
value:v
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get value() {
|
|
62
|
+
return this._value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getData() {
|
|
66
|
+
return this.value;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
set type(type: string) {
|
|
70
|
+
if (this._type != type) {
|
|
71
|
+
this._type = type;
|
|
72
|
+
this.value = this.value; // Need to reset value to have it cast to the new type
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get type(): string {
|
|
77
|
+
return this._type;
|
|
78
|
+
}
|
|
79
|
+
}
|