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.
Files changed (244) hide show
  1. package/demo/vsn.js +1 -1
  2. package/dist/AST/ArithmeticAssignmentNode.d.ts +23 -0
  3. package/dist/AST/ArithmeticAssignmentNode.js +324 -0
  4. package/dist/AST/ArithmeticAssignmentNode.js.map +1 -0
  5. package/dist/AST/ArithmeticNode.d.ts +15 -0
  6. package/dist/AST/ArithmeticNode.js +114 -0
  7. package/dist/AST/ArithmeticNode.js.map +1 -0
  8. package/dist/AST/ArrayNode.d.ts +14 -0
  9. package/dist/AST/ArrayNode.js +114 -0
  10. package/dist/AST/ArrayNode.js.map +1 -0
  11. package/dist/AST/BlockNode.d.ts +11 -0
  12. package/dist/AST/BlockNode.js +98 -0
  13. package/dist/AST/BlockNode.js.map +1 -0
  14. package/dist/AST/BooleanLiteralNode.d.ts +5 -0
  15. package/dist/{Model/Collection.js → AST/BooleanLiteralNode.js} +12 -18
  16. package/dist/AST/BooleanLiteralNode.js.map +1 -0
  17. package/dist/AST/ComparisonNode.d.ts +15 -0
  18. package/dist/AST/ComparisonNode.js +120 -0
  19. package/dist/AST/ComparisonNode.js.map +1 -0
  20. package/dist/AST/ConditionalNode.d.ts +13 -0
  21. package/dist/AST/ConditionalNode.js +95 -0
  22. package/dist/AST/ConditionalNode.js.map +1 -0
  23. package/dist/AST/ElementAttributeNode.d.ts +18 -0
  24. package/dist/AST/ElementAttributeNode.js +159 -0
  25. package/dist/AST/ElementAttributeNode.js.map +1 -0
  26. package/dist/AST/ElementQueryNode.d.ts +13 -0
  27. package/dist/AST/ElementQueryNode.js +116 -0
  28. package/dist/AST/ElementQueryNode.js.map +1 -0
  29. package/dist/AST/ElementStyleNode.d.ts +18 -0
  30. package/dist/AST/ElementStyleNode.js +159 -0
  31. package/dist/AST/ElementStyleNode.js.map +1 -0
  32. package/dist/AST/ForStatementNode.d.ts +17 -0
  33. package/dist/AST/ForStatementNode.js +121 -0
  34. package/dist/AST/ForStatementNode.js.map +1 -0
  35. package/dist/AST/FunctionArgumentNode.d.ts +11 -0
  36. package/dist/AST/FunctionArgumentNode.js +100 -0
  37. package/dist/AST/FunctionArgumentNode.js.map +1 -0
  38. package/dist/AST/FunctionCallNode.d.ts +13 -0
  39. package/dist/AST/FunctionCallNode.js +102 -0
  40. package/dist/AST/FunctionCallNode.js.map +1 -0
  41. package/dist/AST/IfStatementNode.d.ts +14 -0
  42. package/dist/AST/IfStatementNode.js +128 -0
  43. package/dist/AST/IfStatementNode.js.map +1 -0
  44. package/dist/AST/InNode.d.ts +15 -0
  45. package/dist/AST/InNode.js +107 -0
  46. package/dist/AST/InNode.js.map +1 -0
  47. package/dist/AST/IndexNode.d.ts +16 -0
  48. package/dist/AST/IndexNode.js +126 -0
  49. package/dist/AST/IndexNode.js.map +1 -0
  50. package/dist/AST/LiteralNode.d.ts +10 -0
  51. package/dist/AST/LiteralNode.js +74 -0
  52. package/dist/AST/LiteralNode.js.map +1 -0
  53. package/dist/AST/Node.d.ts +19 -0
  54. package/dist/AST/Node.js +117 -0
  55. package/dist/AST/Node.js.map +1 -0
  56. package/dist/AST/NotNode.d.ts +12 -0
  57. package/dist/AST/NotNode.js +103 -0
  58. package/dist/AST/NotNode.js.map +1 -0
  59. package/dist/AST/NumberLiteralNode.d.ts +5 -0
  60. package/dist/{Model/fields/EmailField.js → AST/NumberLiteralNode.js} +17 -17
  61. package/dist/AST/NumberLiteralNode.js.map +1 -0
  62. package/dist/AST/ObjectNode.d.ts +14 -0
  63. package/dist/AST/ObjectNode.js +131 -0
  64. package/dist/AST/ObjectNode.js.map +1 -0
  65. package/dist/AST/RootScopeMemberNode.d.ts +11 -0
  66. package/dist/AST/RootScopeMemberNode.js +87 -0
  67. package/dist/AST/RootScopeMemberNode.js.map +1 -0
  68. package/dist/AST/ScopeMemberNode.d.ts +12 -0
  69. package/dist/AST/ScopeMemberNode.js +134 -0
  70. package/dist/AST/ScopeMemberNode.js.map +1 -0
  71. package/dist/AST/UnitLiteralNode.d.ts +15 -0
  72. package/dist/AST/UnitLiteralNode.js +72 -0
  73. package/dist/AST/UnitLiteralNode.js.map +1 -0
  74. package/dist/AST.d.ts +7 -60
  75. package/dist/AST.js +74 -1493
  76. package/dist/AST.js.map +1 -1
  77. package/dist/Controller.d.ts +4 -2
  78. package/dist/Controller.js +10 -2
  79. package/dist/Controller.js.map +1 -1
  80. package/dist/EventDispatcher.d.ts +4 -1
  81. package/dist/EventDispatcher.js +27 -12
  82. package/dist/EventDispatcher.js.map +1 -1
  83. package/dist/MessageList.d.ts +2 -1
  84. package/dist/MessageList.js +9 -1
  85. package/dist/MessageList.js.map +1 -1
  86. package/dist/Model/Field.d.ts +8 -0
  87. package/dist/Model/{fields/StringField.js → Field.js} +19 -24
  88. package/dist/Model/Field.js.map +1 -0
  89. package/dist/Model.d.ts +5 -4
  90. package/dist/Model.js +7 -18
  91. package/dist/Model.js.map +1 -1
  92. package/dist/Registry.d.ts +3 -0
  93. package/dist/Registry.js +11 -0
  94. package/dist/Registry.js.map +1 -1
  95. package/dist/Scope/DynamicScopeData.d.ts +6 -0
  96. package/dist/{Model/DataModel.js → Scope/DynamicScopeData.js} +15 -17
  97. package/dist/Scope/DynamicScopeData.js.map +1 -0
  98. package/dist/Scope/QueryReference.d.ts +10 -0
  99. package/dist/Scope/QueryReference.js +103 -0
  100. package/dist/Scope/QueryReference.js.map +1 -0
  101. package/dist/Scope/ScopeData.d.ts +4 -0
  102. package/dist/Scope/ScopeData.js +40 -0
  103. package/dist/Scope/ScopeData.js.map +1 -0
  104. package/dist/Scope/ScopeDataAbstract.d.ts +22 -0
  105. package/dist/Scope/ScopeDataAbstract.js +138 -0
  106. package/dist/Scope/ScopeDataAbstract.js.map +1 -0
  107. package/dist/Scope/ScopeReference.d.ts +10 -0
  108. package/dist/Scope/ScopeReference.js +73 -0
  109. package/dist/Scope/ScopeReference.js.map +1 -0
  110. package/dist/Scope/ScopedVariableType.d.ts +6 -0
  111. package/dist/Scope/ScopedVariableType.js +14 -0
  112. package/dist/Scope/ScopedVariableType.js.map +1 -0
  113. package/dist/Scope/WrappedArray.d.ts +16 -0
  114. package/dist/Scope/WrappedArray.js +121 -0
  115. package/dist/Scope/WrappedArray.js.map +1 -0
  116. package/dist/Scope/properties/Property.d.ts +18 -0
  117. package/dist/Scope/properties/Property.js +93 -0
  118. package/dist/Scope/properties/Property.js.map +1 -0
  119. package/dist/Scope.d.ts +4 -45
  120. package/dist/Scope.js +31 -266
  121. package/dist/Scope.js.map +1 -1
  122. package/dist/SimplePromise.d.ts +2 -2
  123. package/dist/SimplePromise.js.map +1 -1
  124. package/dist/Tag/List.d.ts +2 -2
  125. package/dist/Tag/List.js +14 -6
  126. package/dist/Tag/List.js.map +1 -1
  127. package/dist/Tag.js +1 -1
  128. package/dist/Tag.js.map +1 -1
  129. package/dist/Types.d.ts +1 -0
  130. package/dist/Types.js +8 -2
  131. package/dist/Types.js.map +1 -1
  132. package/dist/Validators.d.ts +7 -0
  133. package/dist/Validators.js +54 -0
  134. package/dist/Validators.js.map +1 -0
  135. package/dist/attributes/Bind.js +1 -1
  136. package/dist/attributes/Bind.js.map +1 -1
  137. package/dist/attributes/ClassConstructor.d.ts +1 -0
  138. package/dist/attributes/ClassConstructor.js +1 -1
  139. package/dist/attributes/ClassConstructor.js.map +1 -1
  140. package/dist/attributes/JSONAttribute.js.map +1 -1
  141. package/dist/attributes/List.js +4 -4
  142. package/dist/attributes/List.js.map +1 -1
  143. package/dist/attributes/Radio.js.map +1 -1
  144. package/dist/attributes/SetAttribute.js.map +1 -1
  145. package/dist/attributes/StyleAttribute.js.map +1 -1
  146. package/dist/vsn.d.ts +9 -1
  147. package/dist/vsn.js +24 -9
  148. package/dist/vsn.js.map +1 -1
  149. package/package.json +1 -1
  150. package/src/AST/ArithmeticAssignmentNode.ts +243 -0
  151. package/src/AST/ArithmeticNode.ts +52 -0
  152. package/src/AST/ArrayNode.ts +39 -0
  153. package/src/AST/BlockNode.ts +25 -0
  154. package/src/AST/BooleanLiteralNode.ts +10 -0
  155. package/src/AST/ComparisonNode.ts +57 -0
  156. package/src/AST/ConditionalNode.ts +36 -0
  157. package/src/AST/ElementAttributeNode.ts +63 -0
  158. package/src/AST/ElementQueryNode.ts +27 -0
  159. package/src/AST/ElementStyleNode.ts +63 -0
  160. package/src/AST/ForStatementNode.ts +59 -0
  161. package/src/AST/FunctionArgumentNode.ts +27 -0
  162. package/src/AST/FunctionCallNode.ts +32 -0
  163. package/src/AST/IfStatementNode.ts +67 -0
  164. package/src/AST/InNode.ts +46 -0
  165. package/src/AST/IndexNode.ts +61 -0
  166. package/src/AST/LiteralNode.ts +17 -0
  167. package/src/AST/Node.ts +71 -0
  168. package/src/AST/NotNode.ts +41 -0
  169. package/src/AST/NumberLiteralNode.ts +14 -0
  170. package/src/AST/ObjectNode.ts +55 -0
  171. package/src/AST/RootScopeMemberNode.ts +25 -0
  172. package/src/AST/ScopeMemberNode.ts +58 -0
  173. package/src/AST/UnitLiteralNode.ts +51 -0
  174. package/src/AST.ts +34 -1094
  175. package/src/Controller.ts +10 -2
  176. package/src/EventDispatcher.ts +29 -12
  177. package/src/MessageList.ts +5 -1
  178. package/src/Model/Field.ts +20 -0
  179. package/src/Model.ts +8 -22
  180. package/src/Registry.ts +10 -0
  181. package/src/{Model/DataModel.ts → Scope/DynamicScopeData.ts} +8 -10
  182. package/src/Scope/QueryReference.ts +29 -0
  183. package/src/Scope/ScopeData.ts +21 -0
  184. package/src/Scope/ScopeDataAbstract.ts +127 -0
  185. package/src/Scope/ScopeReference.ts +30 -0
  186. package/src/Scope/ScopedVariableType.ts +7 -0
  187. package/src/Scope/WrappedArray.ts +88 -0
  188. package/src/Scope/properties/Property.ts +79 -0
  189. package/src/Scope.ts +28 -194
  190. package/src/SimplePromise.ts +2 -2
  191. package/src/Tag/List.ts +4 -4
  192. package/src/Tag.ts +1 -1
  193. package/src/Types.ts +7 -2
  194. package/src/Validators.ts +45 -0
  195. package/src/attributes/Bind.ts +3 -3
  196. package/src/attributes/ClassConstructor.ts +2 -1
  197. package/src/attributes/JSONAttribute.ts +2 -1
  198. package/src/attributes/List.ts +1 -1
  199. package/src/attributes/Radio.ts +2 -1
  200. package/src/attributes/ScopeChange.ts +1 -1
  201. package/src/attributes/SetAttribute.ts +2 -1
  202. package/src/attributes/StyleAttribute.ts +2 -1
  203. package/src/attributes/TypeAttribute.ts +1 -1
  204. package/src/vsn.ts +14 -4
  205. package/test/AST/ArithmeticAssignmentNode.spec.ts +47 -0
  206. package/test/AST.spec.ts +2 -2
  207. package/test/Controller.spec.ts +44 -0
  208. package/test/MessageList.spec.ts +1 -1
  209. package/test/Model/DataModel.spec.ts +0 -141
  210. package/test/Scope/DynamicScopeData.spec.ts +141 -0
  211. package/test/Scope.spec.ts +13 -1
  212. package/test/Tag/TagList.spec.ts +1 -1
  213. package/dist/Model/Collection.d.ts +0 -5
  214. package/dist/Model/Collection.js.map +0 -1
  215. package/dist/Model/DataModel.d.ts +0 -6
  216. package/dist/Model/DataModel.js.map +0 -1
  217. package/dist/Model/ModelAbstract.d.ts +0 -20
  218. package/dist/Model/ModelAbstract.js +0 -122
  219. package/dist/Model/ModelAbstract.js.map +0 -1
  220. package/dist/Model/fields/BooleanField.d.ts +0 -5
  221. package/dist/Model/fields/BooleanField.js +0 -43
  222. package/dist/Model/fields/BooleanField.js.map +0 -1
  223. package/dist/Model/fields/EmailField.d.ts +0 -5
  224. package/dist/Model/fields/EmailField.js.map +0 -1
  225. package/dist/Model/fields/Field.d.ts +0 -13
  226. package/dist/Model/fields/Field.js +0 -79
  227. package/dist/Model/fields/Field.js.map +0 -1
  228. package/dist/Model/fields/FloatField.d.ts +0 -5
  229. package/dist/Model/fields/FloatField.js +0 -47
  230. package/dist/Model/fields/FloatField.js.map +0 -1
  231. package/dist/Model/fields/PositiveNumberField.d.ts +0 -5
  232. package/dist/Model/fields/PositiveNumberField.js +0 -51
  233. package/dist/Model/fields/PositiveNumberField.js.map +0 -1
  234. package/dist/Model/fields/StringField.d.ts +0 -5
  235. package/dist/Model/fields/StringField.js.map +0 -1
  236. package/src/Model/Collection.ts +0 -13
  237. package/src/Model/ModelAbstract.ts +0 -114
  238. package/src/Model/fields/BooleanField.ts +0 -16
  239. package/src/Model/fields/EmailField.ts +0 -12
  240. package/src/Model/fields/Field.ts +0 -65
  241. package/src/Model/fields/FloatField.ts +0 -22
  242. package/src/Model/fields/PositiveNumberField.ts +0 -24
  243. package/src/Model/fields/StringField.ts +0 -16
  244. 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 {EventDispatcher} from "./EventDispatcher";
3
+ import {ScopeData} from "./Scope/ScopeData";
4
4
 
5
- export abstract class Controller extends EventDispatcher {
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
  }
@@ -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
- deconstructor() {
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(!(event in this._listeners)) return;
102
-
103
- for(let i = 0; i < this._listeners[event].length; i++) {
104
- const cb: EventCallback = this._listeners[event][i];
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
- // We need to unbind callbacks before they're called to prevent
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
- cb.call(args);
130
+ for (const relay of this._relays) {
131
+ relay.dispatch(event, ...args);
115
132
  }
116
133
  }
117
134
  }
@@ -2,7 +2,7 @@ export interface IMessageHash {
2
2
  [key: string]: string[] | null | undefined;
3
3
  }
4
4
 
5
- export default class MessageList {
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: ModelData | null | undefined = null) {
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 field of this.getFields()) {
40
- const errors = this['__'+field].validate();
25
+ for(const property of this.getProperties()) {
26
+ const errors = this['__'+property].validate();
41
27
  if(errors.length > 0) {
42
- this._errors.add(field, errors, true);
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 {ModelAbstract, ModelData} from "./ModelAbstract";
1
+ import {IScopeData, ScopeDataAbstract} from "./ScopeDataAbstract";
2
2
 
3
- export class DataModel extends ModelAbstract {
4
- constructor(data: ModelData | string[]) {
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.createField(field);
8
+ this.createProperty(field);
10
9
  } else {
11
10
  this.setData(data);
12
11
  }
13
12
  }
14
13
 
15
- setData(data: ModelData) {
14
+ setData(data: IScopeData) {
16
15
  for(const field of Object.keys(data))
17
- if(this.__fields__.indexOf(field) == -1) {
18
- this.__fields__.push(field);
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.createField(event.substr(7));
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,7 @@
1
+
2
+ export class ScopeVariableType {
3
+ public static readonly Integer: string = 'integer';
4
+ public static readonly Float: string = 'float';
5
+ public static readonly Boolean: string = 'boolean';
6
+ public static readonly String: string = 'string';
7
+ }
@@ -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
+ }