vsn 0.1.123 → 0.1.125

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 (163) hide show
  1. package/demo/demo.html +4 -1
  2. package/demo/examples/cascading-function-sheets.html +28 -0
  3. package/demo/resources/xhr-animations.html +24 -0
  4. package/demo/resources/xhr-cfs.html +1 -0
  5. package/demo/resources/xhr-lazy.html +10 -1
  6. package/demo/service.html +3 -3
  7. package/demo/silly-animations.html +12 -24
  8. package/demo/vsn.js +2 -2
  9. package/dist/AST/ArrayNode.js +70 -18
  10. package/dist/AST/ArrayNode.js.map +1 -1
  11. package/dist/AST/AssignmentNode.js +92 -35
  12. package/dist/AST/AssignmentNode.js.map +1 -1
  13. package/dist/AST/BlockNode.js +17 -1
  14. package/dist/AST/BlockNode.js.map +1 -1
  15. package/dist/AST/ClassNode.js +231 -90
  16. package/dist/AST/ClassNode.js.map +1 -1
  17. package/dist/AST/DispatchEventNode.d.ts +3 -1
  18. package/dist/AST/DispatchEventNode.js +48 -14
  19. package/dist/AST/DispatchEventNode.js.map +1 -1
  20. package/dist/AST/ElementAttributeNode.js +55 -29
  21. package/dist/AST/ElementAttributeNode.js.map +1 -1
  22. package/dist/AST/ElementStyleNode.js +48 -22
  23. package/dist/AST/ElementStyleNode.js.map +1 -1
  24. package/dist/AST/FunctionArgumentNode.js +57 -15
  25. package/dist/AST/FunctionArgumentNode.js.map +1 -1
  26. package/dist/AST/FunctionCallNode.js +120 -52
  27. package/dist/AST/FunctionCallNode.js.map +1 -1
  28. package/dist/AST/FunctionNode.js +57 -14
  29. package/dist/AST/FunctionNode.js.map +1 -1
  30. package/dist/AST/IfStatementNode.js +52 -12
  31. package/dist/AST/IfStatementNode.js.map +1 -1
  32. package/dist/AST/IndexNode.js +24 -3
  33. package/dist/AST/IndexNode.js.map +1 -1
  34. package/dist/AST/ModifierNode.js +1 -1
  35. package/dist/AST/ModifierNode.js.map +1 -1
  36. package/dist/AST/Node.d.ts +3 -2
  37. package/dist/AST/Node.js +132 -39
  38. package/dist/AST/Node.js.map +1 -1
  39. package/dist/AST/ObjectNode.js +17 -1
  40. package/dist/AST/ObjectNode.js.map +1 -1
  41. package/dist/AST/OnNode.js +23 -2
  42. package/dist/AST/OnNode.js.map +1 -1
  43. package/dist/AST/ScopeMemberNode.js +44 -16
  44. package/dist/AST/ScopeMemberNode.js.map +1 -1
  45. package/dist/AST/ScopeNodeAbstract.js +29 -8
  46. package/dist/AST/ScopeNodeAbstract.js.map +1 -1
  47. package/dist/AST/StringFormatNode.js +42 -5
  48. package/dist/AST/StringFormatNode.js.map +1 -1
  49. package/dist/AST/WASM/Function.js +20 -4
  50. package/dist/AST/WASM/Function.js.map +1 -1
  51. package/dist/AST/WASM.js +26 -10
  52. package/dist/AST/WASM.js.map +1 -1
  53. package/dist/AST/WithNode.js +81 -41
  54. package/dist/AST/WithNode.js.map +1 -1
  55. package/dist/AST.js +91 -37
  56. package/dist/AST.js.map +1 -1
  57. package/dist/Attribute.d.ts +3 -0
  58. package/dist/Attribute.js +43 -19
  59. package/dist/Attribute.js.map +1 -1
  60. package/dist/Bencmark.js +77 -20
  61. package/dist/Bencmark.js.map +1 -1
  62. package/dist/Component.js +49 -15
  63. package/dist/Component.js.map +1 -1
  64. package/dist/DOM/DOMObject.d.ts +6 -1
  65. package/dist/DOM/DOMObject.js +22 -1
  66. package/dist/DOM/DOMObject.js.map +1 -1
  67. package/dist/DOM.js +450 -193
  68. package/dist/DOM.js.map +1 -1
  69. package/dist/EventDispatcher.js +153 -39
  70. package/dist/EventDispatcher.js.map +1 -1
  71. package/dist/MessageList.js +37 -6
  72. package/dist/MessageList.js.map +1 -1
  73. package/dist/Model.js +27 -6
  74. package/dist/Model.js.map +1 -1
  75. package/dist/Modifiers.d.ts +17 -0
  76. package/dist/Modifiers.js +100 -0
  77. package/dist/Modifiers.js.map +1 -0
  78. package/dist/Scope/DynamicScopeData.js +38 -7
  79. package/dist/Scope/DynamicScopeData.js.map +1 -1
  80. package/dist/Scope/ObjectAccessor.d.ts +7 -0
  81. package/dist/Scope/ObjectAccessor.js +40 -0
  82. package/dist/Scope/ObjectAccessor.js.map +1 -0
  83. package/dist/Scope/ScopeAbstract.d.ts +9 -0
  84. package/dist/Scope/ScopeAbstract.js +28 -0
  85. package/dist/Scope/ScopeAbstract.js.map +1 -0
  86. package/dist/Scope/ScopeData.js +29 -8
  87. package/dist/Scope/ScopeData.js.map +1 -1
  88. package/dist/Scope/ScopeDataAbstract.js +103 -27
  89. package/dist/Scope/ScopeDataAbstract.js.map +1 -1
  90. package/dist/Scope/WrappedArray.js +112 -26
  91. package/dist/Scope/WrappedArray.js.map +1 -1
  92. package/dist/Scope/properties/ArrayProperty.js +17 -1
  93. package/dist/Scope/properties/ArrayProperty.js.map +1 -1
  94. package/dist/Scope/properties/Property.js +38 -7
  95. package/dist/Scope/properties/Property.js.map +1 -1
  96. package/dist/Scope.d.ts +3 -2
  97. package/dist/Scope.js +98 -14
  98. package/dist/Scope.js.map +1 -1
  99. package/dist/SimplePromise.js +71 -30
  100. package/dist/SimplePromise.js.map +1 -1
  101. package/dist/Tag/TagList.js +22 -1
  102. package/dist/Tag/TagList.js.map +1 -1
  103. package/dist/Tag.d.ts +5 -9
  104. package/dist/Tag.js +512 -195
  105. package/dist/Tag.js.map +1 -1
  106. package/dist/attributes/KeyAbstract.js +1 -1
  107. package/dist/attributes/KeyAbstract.js.map +1 -1
  108. package/dist/attributes/KeyDown.js +1 -1
  109. package/dist/attributes/KeyDown.js.map +1 -1
  110. package/dist/attributes/KeyUp.js +1 -1
  111. package/dist/attributes/KeyUp.js.map +1 -1
  112. package/dist/attributes/LazyAttribute.js +4 -2
  113. package/dist/attributes/LazyAttribute.js.map +1 -1
  114. package/dist/attributes/List.js +77 -30
  115. package/dist/attributes/List.js.map +1 -1
  116. package/dist/attributes/On.d.ts +1 -1
  117. package/dist/attributes/On.js +10 -10
  118. package/dist/attributes/On.js.map +1 -1
  119. package/dist/attributes/RootAttribute.js +29 -8
  120. package/dist/attributes/RootAttribute.js.map +1 -1
  121. package/dist/attributes/ScopeAttribute.js +30 -9
  122. package/dist/attributes/ScopeAttribute.js.map +1 -1
  123. package/dist/attributes/SetAttribute.js +32 -11
  124. package/dist/attributes/SetAttribute.js.map +1 -1
  125. package/dist/attributes/StyleAttribute.js +27 -6
  126. package/dist/attributes/StyleAttribute.js.map +1 -1
  127. package/dist/attributes/XHRAttribute.js +28 -7
  128. package/dist/attributes/XHRAttribute.js.map +1 -1
  129. package/dist/helpers/ElementHelper.js +25 -4
  130. package/dist/helpers/ElementHelper.js.map +1 -1
  131. package/dist/helpers/decorators.js +22 -1
  132. package/dist/helpers/decorators.js.map +1 -1
  133. package/dist/version.d.ts +1 -1
  134. package/dist/version.js +1 -1
  135. package/dist/vsn.js +1 -2
  136. package/dist/vsn.js.map +1 -1
  137. package/package.json +1 -1
  138. package/src/AST/ClassNode.ts +6 -3
  139. package/src/AST/DispatchEventNode.ts +19 -11
  140. package/src/AST/FunctionNode.ts +2 -0
  141. package/src/AST/ModifierNode.ts +1 -1
  142. package/src/AST/Node.ts +10 -5
  143. package/src/AST/OnNode.ts +1 -1
  144. package/src/AST/ScopeMemberNode.ts +4 -0
  145. package/src/AST/ScopeNodeAbstract.ts +3 -3
  146. package/src/AST.ts +1 -1
  147. package/src/Attribute.ts +19 -8
  148. package/src/DOM/DOMObject.ts +4 -1
  149. package/src/Modifiers.ts +54 -0
  150. package/src/Scope/ObjectAccessor.ts +21 -0
  151. package/src/Scope/ScopeAbstract.ts +11 -0
  152. package/src/Scope.ts +14 -1
  153. package/src/Tag.ts +66 -37
  154. package/src/attributes/KeyAbstract.ts +1 -1
  155. package/src/attributes/KeyDown.ts +1 -1
  156. package/src/attributes/KeyUp.ts +1 -1
  157. package/src/attributes/LazyAttribute.ts +6 -2
  158. package/src/attributes/On.ts +10 -9
  159. package/src/attributes/XHRAttribute.ts +2 -2
  160. package/src/version.ts +1 -1
  161. package/src/vsn.ts +1 -2
  162. package/test/AST/ClassNode.spec.ts +22 -0
  163. package/tsconfig.base.json +2 -1
package/src/Attribute.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import {Tag} from "./Tag";
2
2
  import {VisionHelper} from "./helpers/VisionHelper";
3
3
  import {EventDispatcher} from "./EventDispatcher";
4
+ import {Modifiers} from "./Modifiers";
4
5
 
5
6
  export enum AttributeState {
6
7
  Instantiated,
@@ -14,6 +15,7 @@ export enum AttributeState {
14
15
 
15
16
  export abstract class Attribute extends EventDispatcher {
16
17
  protected _state: AttributeState;
18
+ public readonly modifiers: Modifiers;
17
19
  public static readonly scoped: boolean = false;
18
20
  public static readonly canDefer: boolean = true;
19
21
 
@@ -23,6 +25,7 @@ export abstract class Attribute extends EventDispatcher {
23
25
  public readonly slot?: Tag
24
26
  ) {
25
27
  super();
28
+ this.modifiers = Modifiers.fromAttribute(attributeName)
26
29
  this.configure();
27
30
  }
28
31
 
@@ -37,25 +40,30 @@ export abstract class Attribute extends EventDispatcher {
37
40
  protected async defer() {
38
41
  this.setState(AttributeState.Deferred);
39
42
  }
43
+
40
44
  protected async configure() {
41
45
  this.setState(AttributeState.Instantiated);
42
- };
46
+ }
47
+
43
48
  public async compile() {
44
49
  this.setState(AttributeState.Compiled);
45
- };
50
+ }
51
+
46
52
  public async setup() {
47
53
  this.setState(AttributeState.Setup);
48
- };
54
+ }
49
55
 
50
56
  public async extract() {
51
57
  this.setState(AttributeState.Extracted);
52
- };
58
+ }
59
+
53
60
  public async connect() {
54
61
  this.setState(AttributeState.Connected);
55
62
  }
63
+
56
64
  public async evaluate() {
57
65
  this.setState(AttributeState.Built);
58
- };
66
+ }
59
67
 
60
68
  public getAttributeValue(fallback: any = null) {
61
69
  return this.origin.getRawAttributeValue(this.attributeName, fallback);
@@ -66,12 +74,15 @@ export abstract class Attribute extends EventDispatcher {
66
74
  }
67
75
 
68
76
  public getAttributeModifiers(fallback: any = []): string[] {
69
- const modifiers = this.origin.getAttributeModifiers(this.attributeName);
70
- return modifiers.length && modifiers || fallback;
77
+ return this.modifiers.length && this.modifiers.names || fallback;
78
+ }
79
+
80
+ public getAttributeModifierArguments(modifier: string, fallback: string[] = []): string[] {
81
+ return this.modifiers.has(modifier) ? this.modifiers.get(modifier).arguments : fallback;
71
82
  }
72
83
 
73
84
  public hasModifier(mod: string): boolean {
74
- return this.getAttributeModifiers().indexOf(mod) > -1;
85
+ return this.modifiers.has(mod);
75
86
  }
76
87
 
77
88
  public mutate(mutation: MutationRecord): void {}
@@ -1,10 +1,13 @@
1
1
  import {Scope} from "../Scope";
2
2
  import {EventDispatcher} from "../EventDispatcher";
3
+ import {Modifiers} from "../Modifiers";
3
4
 
4
5
  export interface IEventHandler {
5
6
  event: string;
6
7
  handler: (...args: any[]) => any;
7
- context?: any;
8
+ context: any;
9
+ state: { [key: string]: any };
10
+ modifiers: Modifiers;
8
11
  }
9
12
 
10
13
  export abstract class DOMObject extends EventDispatcher {
@@ -0,0 +1,54 @@
1
+
2
+ export class Modifier {
3
+ public readonly name: string;
4
+ public readonly arguments: string[];
5
+
6
+ constructor(modifier: string) {
7
+ const [name, ...args] = modifier.split(',');
8
+ this.name = name;
9
+ this.arguments = args;
10
+ }
11
+
12
+ public getArguments(fallback: string[] = []): string[] {
13
+ return this.arguments.length ? this.arguments : fallback;
14
+ }
15
+ }
16
+
17
+ export class Modifiers {
18
+ protected modifiers: Map<string, Modifier> = new Map<string, Modifier>();
19
+
20
+ constructor(modifiers: string[] = []) {
21
+ for (const modifier of modifiers) {
22
+ this.add(modifier);
23
+ }
24
+ }
25
+
26
+ public add(modifier: string) {
27
+ const m = new Modifier(modifier);
28
+ this.modifiers.set(m.name, m);
29
+ }
30
+
31
+ public has(name: string): boolean {
32
+ return this.modifiers.has(name);
33
+ }
34
+
35
+ public get(name: string): Modifier {
36
+ return this.modifiers.get(name);
37
+ }
38
+
39
+ public get iter(): Modifier[] {
40
+ return Array.from(this.modifiers.values());
41
+ }
42
+
43
+ public get names(): string[] {
44
+ return this.iter.map(m => m.name);
45
+ }
46
+
47
+ public get length(): number {
48
+ return this.modifiers.size;
49
+ }
50
+
51
+ public static fromAttribute(attribute: string): Modifiers {
52
+ return new Modifiers(attribute.split('|').splice(1));
53
+ }
54
+ }
@@ -0,0 +1,21 @@
1
+ import {ScopeAbstract} from "./ScopeAbstract";
2
+
3
+ export class ObjectAccessor extends ScopeAbstract {
4
+ public readonly data: any;
5
+
6
+ constructor(data: any) {
7
+ super();
8
+ this.data = data;
9
+ }
10
+
11
+ public get(field: string, fallback = null): any {
12
+ const value = this.data[field];
13
+ if (typeof value === 'object')
14
+ return new ObjectAccessor(value);
15
+ return value === undefined ? fallback : value;
16
+ }
17
+
18
+ public set(field: string, value: any): void {
19
+ this.data[field] = value;
20
+ }
21
+ }
@@ -0,0 +1,11 @@
1
+ import {EventDispatcher} from "../EventDispatcher";
2
+
3
+ export interface IScope {
4
+ get(field: string, fallback?: any): any;
5
+ set(field: string, value: any): void;
6
+ }
7
+
8
+ export abstract class ScopeAbstract extends EventDispatcher implements IScope {
9
+ public abstract get(field: string, fallback?: any): any;
10
+ public abstract set(field: string, value: any): void;
11
+ }
package/src/Scope.ts CHANGED
@@ -5,9 +5,10 @@ import {WrappedArray} from "./Scope/WrappedArray";
5
5
  import {ScopeData} from "./Scope/ScopeData";
6
6
  import {DynamicScopeData} from "./Scope/DynamicScopeData";
7
7
  import {DOM} from "./DOM";
8
+ import {ScopeAbstract} from "./Scope/ScopeAbstract";
8
9
 
9
10
 
10
- export class Scope extends EventDispatcher {
11
+ export class Scope extends ScopeAbstract {
11
12
  public wrapped: any;
12
13
  protected _data: ScopeData;
13
14
  protected children: Scope[];
@@ -29,6 +30,18 @@ export class Scope extends EventDispatcher {
29
30
  return this._data;
30
31
  }
31
32
 
33
+ public get objectify(): any {
34
+ const obj = {};
35
+ for (const key of this.keys) {
36
+ const value = this.get(key);
37
+ if (value instanceof Scope)
38
+ obj[key] = value.objectify;
39
+ else
40
+ obj[key] = value;
41
+ }
42
+ return obj;
43
+ }
44
+
32
45
  public get parentScope(): Scope {
33
46
  if (this._parentScope)
34
47
  return this._parentScope;
package/src/Tag.ts CHANGED
@@ -9,6 +9,7 @@ import {Registry} from "./Registry";
9
9
  import {DOMObject} from "./DOM/DOMObject";
10
10
  import {Tree} from "./AST";
11
11
  import {StyleAttribute} from "./attributes/StyleAttribute";
12
+ import {Modifiers} from "./Modifiers";
12
13
 
13
14
  export enum TagState {
14
15
  Instantiated,
@@ -27,8 +28,7 @@ export class Tag extends DOMObject {
27
28
  public readonly deferredAttributes: Attribute[] = [];
28
29
  protected _state: TagState;
29
30
  protected _meta: { [key: string]: any; };
30
- protected attributes: Attribute[];
31
- protected attributeMap: { [key: string]: Attribute; };
31
+ protected attributes: Map<string, Attribute> = new Map<string, Attribute>();
32
32
  protected _nonDeferredAttributes: Attribute[] = [];
33
33
  protected _parentTag: Tag;
34
34
  protected _children: Tag[] = [];
@@ -78,8 +78,6 @@ export class Tag extends DOMObject {
78
78
  element[Tag.TaggedVariable] = this;
79
79
  this.rawAttributes = {};
80
80
  this.parsedAttributes = {};
81
- this.attributes = [];
82
- this.attributeMap = {};
83
81
  this.onEventHandlers = {};
84
82
  this.analyzeElementAttributes();
85
83
  this._state = TagState.Instantiated;
@@ -97,14 +95,23 @@ export class Tag extends DOMObject {
97
95
  }
98
96
 
99
97
  public getAttributesWithState(state: AttributeState): Attribute[] {
100
- return this.attributes.filter(attr => attr.state === state);
98
+ const attrs: Attribute[] = [];
99
+ for (const attr of this.attributes.values()) {
100
+ if (attr.state === state)
101
+ attrs.push(attr);
102
+ }
103
+ return attrs;
101
104
  }
102
105
 
103
106
  public get nonDeferredAttributes(): Attribute[] {
104
107
  if (this._nonDeferredAttributes.length > 0)
105
108
  return this._nonDeferredAttributes;
106
109
 
107
- const attrs: Attribute[] = this.attributes.filter(attr => attr.state !== AttributeState.Deferred);
110
+ const attrs: Attribute[] = [];
111
+ for (const attr of this.attributes.values()) {
112
+ if (attr.state !== AttributeState.Deferred)
113
+ attrs.push(attr);
114
+ }
108
115
  this._nonDeferredAttributes = attrs;
109
116
  return attrs;
110
117
  }
@@ -146,7 +153,7 @@ export class Tag extends DOMObject {
146
153
  }
147
154
 
148
155
  mutate(mutation: MutationRecord): void {
149
- this.attributes.map(attr => attr.mutate(mutation));
156
+ this.attributes.forEach(attr => attr.mutate(mutation));
150
157
  this.dispatch('mutate', mutation);
151
158
  }
152
159
 
@@ -185,10 +192,6 @@ export class Tag extends DOMObject {
185
192
  return null;
186
193
  }
187
194
 
188
- getAttributeModifiers(attr: string): string[] {
189
- return attr.split('|').splice(1);
190
- }
191
-
192
195
  get isInput(): boolean {
193
196
  return this.inputTags.indexOf(this.element.tagName.toLowerCase()) > -1;
194
197
  }
@@ -390,7 +393,7 @@ export class Tag extends DOMObject {
390
393
  public async getAttribute<T = Attribute>(key: string): Promise<T> {
391
394
  const cls: any = await Registry.instance.attributes.get(key);
392
395
  if (!cls) return;
393
- for (const attr of this.attributes)
396
+ for (const attr of this.attributes.values())
394
397
  if (attr instanceof cls)
395
398
  return attr as any as T;
396
399
  }
@@ -481,8 +484,7 @@ export class Tag extends DOMObject {
481
484
  const slot: Tag = this.isSlot ? this : null;
482
485
  for (const tag of tags) {
483
486
  for (let attr in this.rawAttributes) {
484
- if (tag.attributeMap[attr])
485
- continue;
487
+ if (tag.attributes.has(attr)) continue;
486
488
 
487
489
  if (this.hasModifier(attr, 'mobile') && !isMobile)
488
490
  continue;
@@ -498,8 +500,7 @@ export class Tag extends DOMObject {
498
500
 
499
501
  const attrObj = attrClass.create(tag, attr, attrClass, slot);
500
502
 
501
- tag.attributes.push(attrObj);
502
- tag.attributeMap[attr] = attrObj;
503
+ tag.attributes.set(attr, attrObj);
503
504
  if (defer && attrClass.canDefer) {
504
505
  await attrObj.defer();
505
506
  tag.deferredAttributes.push(attrObj);
@@ -557,7 +558,7 @@ export class Tag extends DOMObject {
557
558
  const tags: Tag[] = await this.getTagsToBuild() as Tag[];
558
559
  for (const tag of tags) {
559
560
  if (tag.isInput) {
560
- tag.addEventHandler('input', [], tag.inputMutation, tag);
561
+ tag.addEventHandler('input', null, tag.inputMutation, tag);
561
562
  }
562
563
 
563
564
  for (const attr of tag.getAttributesWithState(AttributeState.Extracted)) {
@@ -612,12 +613,42 @@ export class Tag extends DOMObject {
612
613
 
613
614
  this.scope.set('$event', e);
614
615
  this.scope.set('$value', this.value);
616
+ let preventedDefault = false;
615
617
  for (const handler of this.onEventHandlers[eventType]) {
616
- handler.handler.call(handler.context, e);
618
+ if (!preventedDefault && handler.modifiers.has('preventdefault') && e.cancelable) {
619
+ e.preventDefault();
620
+ preventedDefault = true;
621
+ }
622
+
623
+ if (handler.modifiers.has('once'))
624
+ this.removeEventHandler(handler.event, handler.handler, handler.context);
625
+
626
+ if (handler.modifiers.has('throttle')) {
627
+ const modifierArguments = handler.modifiers.get('throttle').getArguments(['1000']);
628
+ const throttleTime = parseInt(modifierArguments[0]);
629
+ const now = Math.floor(Date.now());
630
+ if (!handler.state.lastCalled || handler.state.lastCalled + throttleTime < now) {
631
+ handler.state.lastCalled = now;
632
+ } else {
633
+ continue;
634
+ }
635
+ }
636
+
637
+ if (handler.modifiers.has('debounce')) {
638
+ clearTimeout(handler.state.debounceTimeout);
639
+ const modifierArguments = handler.modifiers.get('debounce').getArguments(['1000']);
640
+ const debounceTime = parseInt(modifierArguments[0]);
641
+ handler.state.debounceTimeout = setTimeout(async () => {
642
+ await handler.handler.call(handler.context, e);
643
+ }, debounceTime);
644
+ } else {
645
+ handler.handler.call(handler.context, e);
646
+ }
617
647
  }
618
648
  }
619
649
 
620
650
  public hasModifier(attribute: string, modifier: string): boolean {
651
+ this.attributes
621
652
  return attribute.indexOf(`|${modifier}`) > -1;
622
653
  }
623
654
 
@@ -625,28 +656,32 @@ export class Tag extends DOMObject {
625
656
  return attribute.replace(`|${modifier}`, '');
626
657
  }
627
658
 
628
- public addEventHandler(eventType: string, modifiers: string[], handler, context: any = null) {
659
+ public addEventHandler(eventType: string, modifiers: Modifiers, handler, context: any = null) {
629
660
  let passiveValue: boolean = null;
630
- if (modifiers.indexOf('active') > -1) {
661
+ modifiers = modifiers || new Modifiers();
662
+
663
+ if (modifiers.has('active')) {
631
664
  passiveValue = false;
632
- } else if (modifiers.indexOf('passive') > -1) {
665
+ } else if (modifiers.has('passive')) {
633
666
  passiveValue = true;
634
667
  }
635
668
 
636
669
  if (!this.onEventHandlers[eventType]) {
637
670
  this.onEventHandlers[eventType] = [];
638
671
  const element: HTMLElement | Window = On.WindowEvents.indexOf(eventType) > -1 && window ? window : this.element;
639
- const opts: any = {};
672
+ const eventListenerOpts: any = {};
640
673
  if (eventType.indexOf('touch') > -1 || passiveValue !== null)
641
- opts['passive'] = passiveValue === null && true || passiveValue;
674
+ eventListenerOpts['passive'] = passiveValue === null && true || passiveValue;
642
675
 
643
- element.addEventListener(eventType, this.handleEvent.bind(this, eventType), opts);
676
+ element.addEventListener(eventType, this.handleEvent.bind(this, eventType), eventListenerOpts);
644
677
  }
645
678
 
646
679
  this.onEventHandlers[eventType].push({
647
680
  handler: handler,
648
681
  event: eventType,
649
682
  context: context,
683
+ modifiers: modifiers,
684
+ state: {}
650
685
  });
651
686
  }
652
687
 
@@ -689,32 +724,26 @@ export class Tag extends DOMObject {
689
724
  }
690
725
 
691
726
  async watchAttribute(attributeName: string) {
692
- for (const attribute of this.attributes) {
693
- if (attribute instanceof StandardAttribute && attribute.attributeName == attributeName) {
694
- return attribute;
695
- }
727
+ if (this.attributes.has(attributeName) && this.attributes.get(attributeName) instanceof StandardAttribute) {
728
+ return this.attributes.get(attributeName);
696
729
  }
697
730
 
698
731
  this.createScope(true);
699
-
700
732
  const standardAttribute = new StandardAttribute(this, attributeName);
701
- this.attributes.push(standardAttribute);
733
+ this.attributes.set(attributeName, standardAttribute);
702
734
  await this.setupAttribute(standardAttribute);
703
735
 
704
736
  return standardAttribute;
705
737
  }
706
738
 
707
739
  async watchStyle(styleName: string) {
708
- for (const attribute of this.attributes) {
709
- if (attribute instanceof StyleAttribute) {
710
- return attribute;
711
- }
712
- }
740
+ if (this.attributes.has('style'))
741
+ return this.attributes.get('style');
713
742
 
714
743
  this.createScope(true);
715
744
 
716
745
  const styleAttribute = new StyleAttribute(this, 'style');
717
- this.attributes.push(styleAttribute);
746
+ this.attributes.set('style', styleAttribute);
718
747
  await this.setupAttribute(styleAttribute);
719
748
 
720
749
  return styleAttribute;
@@ -735,7 +764,7 @@ export class Tag extends DOMObject {
735
764
 
736
765
  deconstruct() {
737
766
  this.attributes.forEach(attr => attr.deconstruct());
738
- this.attributes.length = 0;
767
+ this.attributes.clear();
739
768
  this._children.forEach(child => child.deconstruct());
740
769
  this._children.length = 0;
741
770
  super.deconstruct();
@@ -9,7 +9,7 @@ export abstract class KeyAbstract extends On {
9
9
  }
10
10
 
11
11
  public async connect() {
12
- this.tag.addEventHandler('keydown', this.getAttributeModifiers(), this.handleEvent, this);
12
+ this.tag.addEventHandler('keydown', this.modifiers, this.handleEvent, this);
13
13
  await super.connect();
14
14
  }
15
15
 
@@ -4,7 +4,7 @@ import {Registry} from "../Registry";
4
4
  @Registry.attribute('vsn-key-down')
5
5
  export class KeyDown extends KeyAbstract {
6
6
  public async connect() {
7
- this.tag.addEventHandler('keydown', this.getAttributeModifiers(), this.handleEvent, this);
7
+ this.tag.addEventHandler('keydown', this.modifiers, this.handleEvent, this);
8
8
  await super.connect();
9
9
  }
10
10
  }
@@ -4,7 +4,7 @@ import {Registry} from "../Registry";
4
4
  @Registry.attribute('vsn-key-up')
5
5
  export class KeyUp extends KeyAbstract {
6
6
  public async connect() {
7
- this.tag.addEventHandler('keyup', this.getAttributeModifiers(), this.handleEvent, this);
7
+ this.tag.addEventHandler('keyup', this.modifiers, this.handleEvent, this);
8
8
  await super.connect();
9
9
  }
10
10
  }
@@ -1,5 +1,6 @@
1
1
  import {Registry} from "../Registry";
2
2
  import {On} from "./On";
3
+ import {Modifiers} from "../Modifiers";
3
4
 
4
5
  @Registry.attribute('vsn-lazy')
5
6
  export class LazyAttribute extends On {
@@ -12,14 +13,17 @@ export class LazyAttribute extends On {
12
13
  }
13
14
 
14
15
  public async connect() {
15
- this.tag.addEventHandler('scroll', ['passive'], this.handleEvent, this);
16
+ if (!this.modifiers.has('active'))
17
+ this.modifiers.add('passive');
18
+
19
+ this.tag.addEventHandler('scroll', this.modifiers, this.handleEvent, this);
16
20
  await this.handleEvent();
17
21
  }
18
22
 
19
23
  async handleEvent(e?: Event) {
20
24
  if (!this.loaded && window.scrollY + window.outerHeight >= this.eleTop) {
21
25
  this.loaded = true;
22
- await this.handler.evaluate(this.tag.scope, this.tag.dom, this.tag);
26
+ await super.handleEvent(e);
23
27
  this.tag.removeEventHandler('scroll', this.handleEvent, this);
24
28
  }
25
29
  }
@@ -18,21 +18,22 @@ export abstract class On extends Attribute {
18
18
  'unload',
19
19
  ];
20
20
 
21
- public async compile() {
22
- const code: string = this.getAttributeValue();
23
- this.handler = new Tree(code);
24
- await this.handler.prepare(this.tag.scope, this.tag.dom, this.tag);
25
- await super.compile();
21
+ protected async getTree(): Promise<Tree> {
22
+ if (!this.handler) {
23
+ const code: string = this.getAttributeValue();
24
+ this.handler = new Tree(code);
25
+ await this.handler.prepare(this.tag.scope, this.tag.dom, this.tag);
26
+ }
27
+ return this.handler;
26
28
  }
27
29
 
28
30
  async handleEvent(e) {
29
- if (this.hasModifier('preventdefault') && e.cancelable)
30
- e.preventDefault();
31
- await this.handler.evaluate(this.tag.scope, this.tag.dom, this.tag);
31
+ const tree = await this.getTree();
32
+ await tree.evaluate(this.tag.scope, this.tag.dom, this.tag);
32
33
  }
33
34
 
34
35
  public async connect() {
35
- this.tag.addEventHandler(this.getAttributeBinding(), this.getAttributeModifiers(), this.handleEvent, this);
36
+ this.tag.addEventHandler(this.getAttributeBinding(), this.modifiers, this.handleEvent, this);
36
37
  await super.connect();
37
38
  }
38
39
  }
@@ -22,9 +22,9 @@ export class XHRAttribute extends Attribute {
22
22
 
23
23
  public async connect() {
24
24
  if (this.isForm) {
25
- this.tag.addEventHandler('submit', this.getAttributeModifiers(), this.handleEvent, this);
25
+ this.tag.addEventHandler('submit', this.modifiers, this.handleEvent, this);
26
26
  } else if (this.isAnchor) {
27
- this.tag.addEventHandler('click', this.getAttributeModifiers(), this.handleEvent, this);
27
+ this.tag.addEventHandler('click', this.modifiers, this.handleEvent, this);
28
28
  }
29
29
  await super.connect();
30
30
  }
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.1.123';
1
+ export const VERSION = '0.1.125';
2
2
 
package/src/vsn.ts CHANGED
@@ -4,7 +4,6 @@ import {Registry} from "./Registry";
4
4
  import {Configuration} from "./Configuration";
5
5
  import {VisionHelper} from "./helpers/VisionHelper";
6
6
  import {Tree} from "./AST";
7
- import {Query} from "./Query";
8
7
  import {EventDispatcher} from "./EventDispatcher";
9
8
  import {DynamicScopeData} from "./Scope/DynamicScopeData";
10
9
  import {Controller} from "./Controller";
@@ -46,7 +45,7 @@ export class Vision extends EventDispatcher {
46
45
  window['Registry'] = Registry;
47
46
  window['vision'] = window['vsn'] = this;
48
47
  window['Tree'] = Tree;
49
- window['$'] = Query;
48
+ window['$'] = this.exec.bind(this);
50
49
  VisionHelper.window.dispatchEvent(new Event('vsn'));
51
50
  }
52
51
  }
@@ -2,6 +2,7 @@ import {DOM} from "../../src/DOM";
2
2
  import {ClassNode} from "../../src/AST/ClassNode";
3
3
  import {Registry} from "../../src/Registry";
4
4
  import {TagList} from "../../src/Tag/TagList";
5
+ import {Tag} from "../../src/vsn";
5
6
 
6
7
 
7
8
  describe('ClassNode', () => {
@@ -16,6 +17,7 @@ describe('ClassNode', () => {
16
17
  }
17
18
  </script>
18
19
  <div class="simple"><input /></div>
20
+ <input id="not-nested" />
19
21
  `;
20
22
  const dom = new DOM(document);
21
23
  await dom.ready;
@@ -25,6 +27,26 @@ describe('ClassNode', () => {
25
27
  expect(ClassNode.classes['.simple input']).toBeInstanceOf(ClassNode);
26
28
  });
27
29
 
30
+ it("properly build classes on newly added elements in the dom", async () => {
31
+ document.body.innerHTML = `
32
+ <script type="text/vsn" vsn-script>
33
+ class .added-html {
34
+ func construct() {}
35
+ }
36
+ </script>
37
+ <div id="container"></div>
38
+ `;
39
+ const dom = new DOM(document);
40
+ await dom.ready;
41
+ await Registry.instance.classes.get('.added-html');
42
+ const newHtml: string = `<div class="added-html" id="added"></div>`;
43
+ dom.root.scope.set('newHtml', newHtml);
44
+ await dom.exec('#container.@html = newHtml');
45
+ const addedTag = await dom.exec('#added');
46
+ expect(addedTag).toBeInstanceOf(Tag);
47
+ expect(ClassNode.preppedTags['.added-html'].indexOf(addedTag)).toBeGreaterThan(-1);
48
+ });
49
+
28
50
  it("properly define a simple class", async () => {
29
51
  document.body.innerHTML = `
30
52
  <script type="text/vsn" vsn-script>
@@ -8,7 +8,8 @@
8
8
  "rootDir": "./src",
9
9
  "outDir": "./dist",
10
10
  "strict": false,
11
- "experimentalDecorators": true
11
+ "experimentalDecorators": true,
12
+ "downlevelIteration": true
12
13
  },
13
14
  "exclude": [
14
15
  "node_modules"