vsn 0.1.67 → 0.1.70

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