vsn 0.1.27 → 0.1.28

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 (232) hide show
  1. package/demo/demo.html +2 -0
  2. package/demo/vsn.js +1 -1
  3. package/dist/AST/ArithmeticAssignmentNode.d.ts +23 -0
  4. package/dist/AST/ArithmeticAssignmentNode.js +313 -0
  5. package/dist/AST/ArithmeticAssignmentNode.js.map +1 -0
  6. package/dist/AST/ArithmeticNode.d.ts +15 -0
  7. package/dist/AST/ArithmeticNode.js +114 -0
  8. package/dist/AST/ArithmeticNode.js.map +1 -0
  9. package/dist/AST/ArrayNode.d.ts +14 -0
  10. package/dist/AST/ArrayNode.js +114 -0
  11. package/dist/AST/ArrayNode.js.map +1 -0
  12. package/dist/AST/BlockNode.d.ts +11 -0
  13. package/dist/AST/BlockNode.js +98 -0
  14. package/dist/AST/BlockNode.js.map +1 -0
  15. package/dist/AST/BooleanLiteralNode.d.ts +5 -0
  16. package/dist/{Model/Collection.js → AST/BooleanLiteralNode.js} +12 -18
  17. package/dist/AST/BooleanLiteralNode.js.map +1 -0
  18. package/dist/AST/ComparisonNode.d.ts +15 -0
  19. package/dist/AST/ComparisonNode.js +120 -0
  20. package/dist/AST/ComparisonNode.js.map +1 -0
  21. package/dist/AST/ConditionalNode.d.ts +13 -0
  22. package/dist/AST/ConditionalNode.js +95 -0
  23. package/dist/AST/ConditionalNode.js.map +1 -0
  24. package/dist/AST/ElementAttributeNode.d.ts +18 -0
  25. package/dist/AST/ElementAttributeNode.js +159 -0
  26. package/dist/AST/ElementAttributeNode.js.map +1 -0
  27. package/dist/AST/ElementQueryNode.d.ts +12 -0
  28. package/dist/AST/ElementQueryNode.js +111 -0
  29. package/dist/AST/ElementQueryNode.js.map +1 -0
  30. package/dist/AST/ElementStyleNode.d.ts +18 -0
  31. package/dist/AST/ElementStyleNode.js +159 -0
  32. package/dist/AST/ElementStyleNode.js.map +1 -0
  33. package/dist/AST/ForStatementNode.d.ts +17 -0
  34. package/dist/AST/ForStatementNode.js +121 -0
  35. package/dist/AST/ForStatementNode.js.map +1 -0
  36. package/dist/AST/FunctionArgumentNode.d.ts +11 -0
  37. package/dist/AST/FunctionArgumentNode.js +100 -0
  38. package/dist/AST/FunctionArgumentNode.js.map +1 -0
  39. package/dist/AST/FunctionCallNode.d.ts +13 -0
  40. package/dist/AST/FunctionCallNode.js +102 -0
  41. package/dist/AST/FunctionCallNode.js.map +1 -0
  42. package/dist/AST/IfStatementNode.d.ts +14 -0
  43. package/dist/AST/IfStatementNode.js +128 -0
  44. package/dist/AST/IfStatementNode.js.map +1 -0
  45. package/dist/AST/InNode.d.ts +15 -0
  46. package/dist/AST/InNode.js +107 -0
  47. package/dist/AST/InNode.js.map +1 -0
  48. package/dist/AST/IndexNode.d.ts +16 -0
  49. package/dist/AST/IndexNode.js +126 -0
  50. package/dist/AST/IndexNode.js.map +1 -0
  51. package/dist/AST/LiteralNode.d.ts +10 -0
  52. package/dist/AST/LiteralNode.js +74 -0
  53. package/dist/AST/LiteralNode.js.map +1 -0
  54. package/dist/AST/Node.d.ts +19 -0
  55. package/dist/AST/Node.js +117 -0
  56. package/dist/AST/Node.js.map +1 -0
  57. package/dist/AST/NotNode.d.ts +12 -0
  58. package/dist/AST/NotNode.js +103 -0
  59. package/dist/AST/NotNode.js.map +1 -0
  60. package/dist/AST/NumberLiteralNode.d.ts +5 -0
  61. package/dist/{Model/fields/EmailField.js → AST/NumberLiteralNode.js} +17 -17
  62. package/dist/AST/NumberLiteralNode.js.map +1 -0
  63. package/dist/AST/ObjectNode.d.ts +14 -0
  64. package/dist/AST/ObjectNode.js +131 -0
  65. package/dist/AST/ObjectNode.js.map +1 -0
  66. package/dist/AST/RootScopeMemberNode.d.ts +11 -0
  67. package/dist/AST/RootScopeMemberNode.js +87 -0
  68. package/dist/AST/RootScopeMemberNode.js.map +1 -0
  69. package/dist/AST/ScopeMemberNode.d.ts +12 -0
  70. package/dist/AST/ScopeMemberNode.js +128 -0
  71. package/dist/AST/ScopeMemberNode.js.map +1 -0
  72. package/dist/AST/UnitLiteralNode.d.ts +15 -0
  73. package/dist/AST/UnitLiteralNode.js +72 -0
  74. package/dist/AST/UnitLiteralNode.js.map +1 -0
  75. package/dist/AST.d.ts +7 -60
  76. package/dist/AST.js +74 -1493
  77. package/dist/AST.js.map +1 -1
  78. package/dist/Controller.d.ts +4 -2
  79. package/dist/Controller.js +10 -2
  80. package/dist/Controller.js.map +1 -1
  81. package/dist/EventDispatcher.d.ts +4 -1
  82. package/dist/EventDispatcher.js +27 -12
  83. package/dist/EventDispatcher.js.map +1 -1
  84. package/dist/Model/Field.d.ts +8 -0
  85. package/dist/Model/{fields/StringField.js → Field.js} +19 -24
  86. package/dist/Model/Field.js.map +1 -0
  87. package/dist/Model.d.ts +4 -3
  88. package/dist/Model.js +6 -17
  89. package/dist/Model.js.map +1 -1
  90. package/dist/Registry.d.ts +3 -0
  91. package/dist/Registry.js +11 -0
  92. package/dist/Registry.js.map +1 -1
  93. package/dist/Scope/DynamicScopeData.d.ts +6 -0
  94. package/dist/{Model/DataModel.js → Scope/DynamicScopeData.js} +17 -17
  95. package/dist/Scope/DynamicScopeData.js.map +1 -0
  96. package/dist/Scope/QueryReference.d.ts +10 -0
  97. package/dist/Scope/QueryReference.js +103 -0
  98. package/dist/Scope/QueryReference.js.map +1 -0
  99. package/dist/Scope/ScopeData.d.ts +4 -0
  100. package/dist/Scope/ScopeData.js +40 -0
  101. package/dist/Scope/ScopeData.js.map +1 -0
  102. package/dist/Scope/ScopeDataAbstract.d.ts +22 -0
  103. package/dist/Scope/ScopeDataAbstract.js +137 -0
  104. package/dist/Scope/ScopeDataAbstract.js.map +1 -0
  105. package/dist/Scope/ScopeReference.d.ts +10 -0
  106. package/dist/Scope/ScopeReference.js +73 -0
  107. package/dist/Scope/ScopeReference.js.map +1 -0
  108. package/dist/Scope/ScopedVariableType.d.ts +6 -0
  109. package/dist/Scope/ScopedVariableType.js +14 -0
  110. package/dist/Scope/ScopedVariableType.js.map +1 -0
  111. package/dist/Scope/WrappedArray.d.ts +16 -0
  112. package/dist/Scope/WrappedArray.js +121 -0
  113. package/dist/Scope/WrappedArray.js.map +1 -0
  114. package/dist/Scope/properties/Property.d.ts +18 -0
  115. package/dist/Scope/properties/Property.js +93 -0
  116. package/dist/Scope/properties/Property.js.map +1 -0
  117. package/dist/Scope.d.ts +4 -45
  118. package/dist/Scope.js +31 -266
  119. package/dist/Scope.js.map +1 -1
  120. package/dist/SimplePromise.d.ts +2 -2
  121. package/dist/SimplePromise.js.map +1 -1
  122. package/dist/Tag.js +1 -1
  123. package/dist/Tag.js.map +1 -1
  124. package/dist/Types.d.ts +1 -0
  125. package/dist/Types.js +8 -1
  126. package/dist/Types.js.map +1 -1
  127. package/dist/Validators.d.ts +7 -0
  128. package/dist/Validators.js +54 -0
  129. package/dist/Validators.js.map +1 -0
  130. package/dist/attributes/Bind.js.map +1 -1
  131. package/dist/attributes/JSONAttribute.js.map +1 -1
  132. package/dist/attributes/List.js +4 -4
  133. package/dist/attributes/List.js.map +1 -1
  134. package/dist/attributes/Radio.js.map +1 -1
  135. package/dist/attributes/SetAttribute.js.map +1 -1
  136. package/dist/attributes/StyleAttribute.js.map +1 -1
  137. package/dist/vsn.d.ts +4 -1
  138. package/dist/vsn.js +14 -9
  139. package/dist/vsn.js.map +1 -1
  140. package/package.json +1 -1
  141. package/src/AST/ArithmeticAssignmentNode.ts +236 -0
  142. package/src/AST/ArithmeticNode.ts +52 -0
  143. package/src/AST/ArrayNode.ts +39 -0
  144. package/src/AST/BlockNode.ts +25 -0
  145. package/src/AST/BooleanLiteralNode.ts +10 -0
  146. package/src/AST/ComparisonNode.ts +57 -0
  147. package/src/AST/ConditionalNode.ts +36 -0
  148. package/src/AST/ElementAttributeNode.ts +63 -0
  149. package/src/AST/ElementQueryNode.ts +25 -0
  150. package/src/AST/ElementStyleNode.ts +63 -0
  151. package/src/AST/ForStatementNode.ts +59 -0
  152. package/src/AST/FunctionArgumentNode.ts +27 -0
  153. package/src/AST/FunctionCallNode.ts +32 -0
  154. package/src/AST/IfStatementNode.ts +67 -0
  155. package/src/AST/InNode.ts +46 -0
  156. package/src/AST/IndexNode.ts +61 -0
  157. package/src/AST/LiteralNode.ts +17 -0
  158. package/src/AST/Node.ts +71 -0
  159. package/src/AST/NotNode.ts +41 -0
  160. package/src/AST/NumberLiteralNode.ts +14 -0
  161. package/src/AST/ObjectNode.ts +55 -0
  162. package/src/AST/RootScopeMemberNode.ts +25 -0
  163. package/src/AST/ScopeMemberNode.ts +53 -0
  164. package/src/AST/UnitLiteralNode.ts +51 -0
  165. package/src/AST.ts +33 -1093
  166. package/src/Controller.ts +10 -2
  167. package/src/EventDispatcher.ts +29 -12
  168. package/src/Model/Field.ts +20 -0
  169. package/src/Model.ts +7 -21
  170. package/src/Registry.ts +10 -0
  171. package/src/Scope/DynamicScopeData.ts +29 -0
  172. package/src/Scope/QueryReference.ts +29 -0
  173. package/src/Scope/ScopeData.ts +21 -0
  174. package/src/Scope/ScopeDataAbstract.ts +126 -0
  175. package/src/Scope/ScopeReference.ts +30 -0
  176. package/src/Scope/ScopedVariableType.ts +7 -0
  177. package/src/Scope/WrappedArray.ts +88 -0
  178. package/src/Scope/properties/Property.ts +79 -0
  179. package/src/Scope.ts +28 -193
  180. package/src/SimplePromise.ts +2 -2
  181. package/src/Tag.ts +1 -1
  182. package/src/Types.ts +6 -1
  183. package/src/Validators.ts +45 -0
  184. package/src/attributes/Bind.ts +2 -1
  185. package/src/attributes/JSONAttribute.ts +2 -1
  186. package/src/attributes/List.ts +1 -1
  187. package/src/attributes/Radio.ts +2 -1
  188. package/src/attributes/ScopeChange.ts +1 -1
  189. package/src/attributes/SetAttribute.ts +2 -1
  190. package/src/attributes/StyleAttribute.ts +2 -1
  191. package/src/attributes/TypeAttribute.ts +1 -1
  192. package/src/vsn.ts +9 -4
  193. package/test/AST/ArithmeticAssignmentNode.spec.ts +47 -0
  194. package/test/AST.spec.ts +2 -2
  195. package/test/Controller.spec.ts +44 -0
  196. package/test/Model/DataModel.spec.ts +0 -141
  197. package/test/Scope/DynamicScopeData.spec.ts +141 -0
  198. package/test/Scope.spec.ts +13 -1
  199. package/test/Tag/TagList.spec.ts +1 -1
  200. package/dist/Model/Collection.d.ts +0 -5
  201. package/dist/Model/Collection.js.map +0 -1
  202. package/dist/Model/DataModel.d.ts +0 -6
  203. package/dist/Model/DataModel.js.map +0 -1
  204. package/dist/Model/ModelAbstract.d.ts +0 -20
  205. package/dist/Model/ModelAbstract.js +0 -122
  206. package/dist/Model/ModelAbstract.js.map +0 -1
  207. package/dist/Model/fields/BooleanField.d.ts +0 -5
  208. package/dist/Model/fields/BooleanField.js +0 -43
  209. package/dist/Model/fields/BooleanField.js.map +0 -1
  210. package/dist/Model/fields/EmailField.d.ts +0 -5
  211. package/dist/Model/fields/EmailField.js.map +0 -1
  212. package/dist/Model/fields/Field.d.ts +0 -13
  213. package/dist/Model/fields/Field.js +0 -79
  214. package/dist/Model/fields/Field.js.map +0 -1
  215. package/dist/Model/fields/FloatField.d.ts +0 -5
  216. package/dist/Model/fields/FloatField.js +0 -47
  217. package/dist/Model/fields/FloatField.js.map +0 -1
  218. package/dist/Model/fields/PositiveNumberField.d.ts +0 -5
  219. package/dist/Model/fields/PositiveNumberField.js +0 -51
  220. package/dist/Model/fields/PositiveNumberField.js.map +0 -1
  221. package/dist/Model/fields/StringField.d.ts +0 -5
  222. package/dist/Model/fields/StringField.js.map +0 -1
  223. package/src/Model/Collection.ts +0 -13
  224. package/src/Model/DataModel.ts +0 -29
  225. package/src/Model/ModelAbstract.ts +0 -114
  226. package/src/Model/fields/BooleanField.ts +0 -16
  227. package/src/Model/fields/EmailField.ts +0 -12
  228. package/src/Model/fields/Field.ts +0 -65
  229. package/src/Model/fields/FloatField.ts +0 -22
  230. package/src/Model/fields/PositiveNumberField.ts +0 -24
  231. package/src/Model/fields/StringField.ts +0 -16
  232. package/test/Model.spec.ts +0 -306
package/src/AST.ts CHANGED
@@ -1,8 +1,29 @@
1
- import {Scope, WrappedArray} from "./Scope";
1
+ import {Scope} from "./Scope";
2
2
  import {DOM} from "./DOM";
3
- import {DOMObject} from "./DOM/DOMObject";
4
- import {TagList} from "./Tag/List";
5
3
  import {Tag} from "./Tag";
4
+ import {RootScopeMemberNode} from "./AST/RootScopeMemberNode";
5
+ import {ScopeMemberNode} from "./AST/ScopeMemberNode";
6
+ import {ElementAttributeNode} from "./AST/ElementAttributeNode";
7
+ import {Node} from "./AST/Node";
8
+ import {BlockNode} from "./AST/BlockNode";
9
+ import {LiteralNode} from "./AST/LiteralNode";
10
+ import {IfStatementNode} from "./AST/IfStatementNode";
11
+ import {ForStatementNode} from "./AST/ForStatementNode";
12
+ import {NumberLiteralNode} from "./AST/NumberLiteralNode";
13
+ import {ElementQueryNode} from "./AST/ElementQueryNode";
14
+ import {IndexNode} from "./AST/IndexNode";
15
+ import {ArrayNode} from "./AST/ArrayNode";
16
+ import {ObjectNode} from "./AST/ObjectNode";
17
+ import {ElementStyleNode} from "./AST/ElementStyleNode";
18
+ import {FunctionCallNode} from "./AST/FunctionCallNode";
19
+ import {FunctionArgumentNode} from "./AST/FunctionArgumentNode";
20
+ import {InNode} from "./AST/InNode";
21
+ import {ComparisonNode} from "./AST/ComparisonNode";
22
+ import {ArithmeticNode} from "./AST/ArithmeticNode";
23
+ import {ArithmeticAssignmentNode} from "./AST/ArithmeticAssignmentNode";
24
+ import {UnitLiteralNode} from "./AST/UnitLiteralNode";
25
+ import {BooleanLiteralNode} from "./AST/BooleanLiteralNode";
26
+ import {NotNode} from "./AST/NotNode";
6
27
 
7
28
  function lower(str: string): string {
8
29
  return str ? str.toLowerCase() : null;
@@ -275,7 +296,7 @@ const TOKEN_PATTERNS: TokenPattern[] = [
275
296
  type: TokenType.ADD,
276
297
  pattern: /^\+/
277
298
  },
278
- {
299
+ {
279
300
  type: TokenType.SUBTRACT,
280
301
  pattern: /^-/
281
302
  },
@@ -299,1096 +320,10 @@ const TOKEN_PATTERNS: TokenPattern[] = [
299
320
 
300
321
  export interface TreeNode<T = any> {
301
322
  evaluate(scope: Scope, dom: DOM, tag?: Tag);
302
- prepare(scope: Scope, dom: DOM, tag?: Tag);
303
- }
304
-
305
- export abstract class Node implements TreeNode {
306
- protected requiresPrep: boolean = false;
307
- protected _isPreparationRequired: boolean;
308
- protected childNodes: Node[];
309
- protected nodeCache: {[key: string]: Node[]} = {};
310
- abstract evaluate(scope: Scope, dom: DOM, tag?: Tag);
311
-
312
- isPreparationRequired(): boolean {
313
- if (this.requiresPrep)
314
- return true;
315
-
316
- if (this._isPreparationRequired !== undefined)
317
- return this._isPreparationRequired;
318
-
319
- for (const node of this.getChildNodes()) {
320
- if (node.isPreparationRequired()) {
321
- this._isPreparationRequired = true;
322
- return true;
323
- }
324
- }
325
-
326
- return false;
327
- }
328
-
329
- async prepare(scope: Scope, dom: DOM, tag: Tag = null) {
330
- for (const node of this.getChildNodes()) {
331
- await node.prepare(scope, dom, tag);
332
- }
333
- }
334
-
335
- protected _getChildNodes(): Node[] {
336
- return [];
337
- }
338
-
339
- getChildNodes(): Node[] {
340
- if (this.childNodes === undefined) {
341
- this.childNodes = this._getChildNodes();
342
- }
343
- return this.childNodes;
344
- }
345
-
346
- findChildrenByType<T = Node>(t: any): T[] {
347
- return this.findChildrenByTypes([t]);
348
- }
349
-
350
- findChildrenByTypes<T = Node>(types: any[], cacheKey: string = null): T[] {
351
- if (cacheKey !== null && this.nodeCache[cacheKey])
352
- return this.nodeCache[cacheKey] as any;
353
-
354
- const nodes: T[] = [];
355
- for (const child of this.getChildNodes()) {
356
- for (const t of types) {
357
- if (child instanceof t)
358
- nodes.push(child as any as T);
359
- const childNodes: T[] = child.findChildrenByType<T>(t);
360
- nodes.push(...childNodes);
361
- }
362
- }
363
-
364
- if (cacheKey !== null)
365
- this.nodeCache[cacheKey] = nodes as any;
366
-
367
- return nodes;
368
- }
369
- }
370
-
371
-
372
- export class BlockNode extends Node implements TreeNode {
373
- constructor(
374
- public readonly statements: Node[]
375
- ) {
376
- super();
377
- }
378
-
379
- protected _getChildNodes(): Node[] {
380
- return [...(this.statements as Node[])];
381
- }
382
-
383
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
384
- let returnValue: any = null;
385
- for (let i = 0; i < this.statements.length; i++) {
386
- returnValue = await this.statements[i].evaluate(scope, dom, tag);
387
- }
388
- return returnValue;
389
- }
390
- }
391
-
392
- class ComparisonNode extends Node implements TreeNode {
393
- constructor(
394
- public readonly left: Node,
395
- public readonly right: Node,
396
- public readonly type: TokenType
397
- ) {
398
- super();
399
- }
400
-
401
- protected _getChildNodes(): Node[] {
402
- return [
403
- this.left as Node,
404
- this.right as Node
405
- ];
406
- }
407
-
408
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
409
- const left: any = await this.left.evaluate(scope, dom, tag);
410
- const right: any = await this.right.evaluate(scope, dom, tag);
411
- switch (this.type) {
412
- case TokenType.EQUALS:
413
- return left === right;
414
- case TokenType.NOT_EQUALS:
415
- return left !== right;
416
- case TokenType.GREATER_THAN:
417
- return left > right;
418
- case TokenType.LESS_THAN:
419
- return left < right;
420
- case TokenType.GREATER_THAN_EQUAL:
421
- return left >= right;
422
- case TokenType.LESS_THAN_EQUAL:
423
- return left <= right;
424
- }
425
- }
426
-
427
- public static match(tokens: Token[]): boolean {
428
- return [
429
- TokenType.EQUALS,
430
- TokenType.NOT_EQUALS,
431
- TokenType.GREATER_THAN,
432
- TokenType.LESS_THAN,
433
- TokenType.GREATER_THAN_EQUAL,
434
- TokenType.LESS_THAN_EQUAL
435
- ].indexOf(tokens[0].type) > -1
436
- }
437
-
438
- public static parse(lastNode, token, tokens: Token[]) {
439
- tokens.splice(0, 1); // Remove comparison operator
440
- return new ComparisonNode(lastNode, Tree.processTokens(Tree.getNextStatementTokens(tokens)), token.type)
441
- }
442
- }
443
-
444
- class ConditionalNode extends Node implements TreeNode {
445
- constructor(
446
- public readonly condition: Node,
447
- public readonly block: BlockNode
448
- ) {
449
- super();
450
- }
451
-
452
- protected _getChildNodes(): Node[] {
453
- return [
454
- this.condition as Node,
455
- this.block as Node
456
- ];
457
- }
458
-
459
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
460
- const condition = await this.condition.evaluate(scope, dom, tag);
461
- let evaluation = false;
462
-
463
- if (condition instanceof WrappedArray) {
464
- evaluation = condition.length > 0;
465
- } else {
466
- evaluation = !!condition;
467
- }
468
-
469
- return evaluation;
470
- }
471
- }
472
-
473
- class IfStatementNode extends Node implements TreeNode {
474
- constructor(
475
- protected nodes: ConditionalNode[]
476
- ) {
477
- super();
478
- }
479
-
480
- protected _getChildNodes(): Node[] {
481
- return [
482
- ...(this.nodes as Node[])
483
- ]
484
- }
485
-
486
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
487
- for (const condition of this.nodes) {
488
- const uno: boolean = await condition.evaluate(scope, dom, tag);
489
- if (uno) {
490
- return await condition.block.evaluate(scope, dom, tag);
491
- }
492
- }
493
- }
494
-
495
- public static parseConditional(tokens: Token[]): ConditionalNode {
496
- if ([
497
- TokenType.IF,
498
- TokenType.ELSE_IF
499
- ].indexOf(tokens[0].type) === -1) {
500
- throw SyntaxError('Invalid Syntax');
501
- }
502
-
503
- tokens.splice(0, 1); // consume if and else if
504
- return new ConditionalNode(
505
- Tree.processTokens(Tree.getBlockTokens(tokens, null)[0]),
506
- Tree.processTokens(Tree.getBlockTokens(tokens, null)[0])
507
- );
508
- }
509
-
510
- public static parse(lastNode, token, tokens: Token[]): IfStatementNode {
511
- if (tokens[1].type !== TokenType.L_PAREN) {
512
- throw SyntaxError('If statement needs to be followed by a condition encased in parenthesis.');
513
- }
514
- const nodes: ConditionalNode[] = [];
515
- nodes.push(IfStatementNode.parseConditional(tokens));
516
-
517
- while(tokens.length > 0 && TokenType.ELSE_IF === tokens[0].type) {
518
- nodes.push(IfStatementNode.parseConditional(tokens));
519
- }
520
-
521
- if (tokens.length > 0 && TokenType.ELSE === tokens[0].type) {
522
- tokens.splice(0, 1); // Consume else
523
- nodes.push(new ConditionalNode(
524
- new LiteralNode(true),
525
- Tree.processTokens(Tree.getBlockTokens(tokens, null)[0])
526
- ))
527
- }
528
-
529
- return new IfStatementNode(nodes);
530
- }
531
- }
532
-
533
- class ForStatementNode extends Node implements TreeNode {
534
- constructor(
535
- public readonly variable: LiteralNode<string>,
536
- public readonly list: RootScopeMemberNode | ScopeMemberNode,
537
- public readonly block: RootScopeMemberNode | ScopeMemberNode,
538
- ) {
539
- super();
540
- }
541
-
542
- protected _getChildNodes(): Node[] {
543
- return [
544
- this.variable,
545
- this.list,
546
- this.block
547
- ];
548
- }
549
-
550
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
551
- const variable: string = await this.variable.evaluate(scope, dom, tag);
552
- const list: any[] = await this.list.evaluate(scope, dom, tag);
553
- for (let i = 0;i < list.length; i++) {
554
- scope.set(variable, list[i]);
555
- await this.block.evaluate(scope, dom, tag);
556
- }
557
-
558
- return null;
559
- }
560
-
561
- public static parse(lastNode, token, tokens: Token[]): ForStatementNode {
562
- if (tokens[1].type !== TokenType.L_PAREN) {
563
- throw SyntaxError('Syntax error: Missing (');
564
- }
565
- if (tokens[3].type !== TokenType.OF) {
566
- throw SyntaxError('Syntax error: Missing of');
567
- }
568
-
569
- tokens.splice(0, 1); // consume for
570
- const loopDef: Token[] = Tree.getNextStatementTokens(tokens);
571
- const variableName: Token = loopDef.splice(0, 1)[0];
572
- loopDef.splice(0, 1); // consume of
573
- const list: TreeNode = Tree.processTokens(loopDef);
574
- const block: TreeNode = Tree.processTokens(Tree.getBlockTokens(tokens, null)[0]);
575
-
576
- return new ForStatementNode(
577
- new LiteralNode<string>(variableName.value),
578
- list as any,
579
- block as any
580
- );
581
- }
582
- }
583
-
584
- class NotNode extends Node implements TreeNode {
585
- constructor(
586
- public readonly toFlip: Node
587
- ) {
588
- super();
589
- }
590
-
591
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
592
- const flipping = await this.toFlip.evaluate(scope, dom, tag);
593
- return !flipping;
594
- }
595
-
596
- protected _getChildNodes(): Node[] {
597
- return [
598
- this.toFlip
599
- ];
600
- }
601
-
602
- public static parse(lastNode, token, tokens: Token[]) {
603
- tokens.splice(0, 1); // Remove not operator
604
- let containedTokens;
605
- if (tokens[0].type === TokenType.L_PAREN) {
606
- containedTokens = Tree.getNextStatementTokens(tokens);
607
- } else {
608
- containedTokens = Tree.consumeTypes(tokens, [
609
- TokenType.BOOLEAN_LITERAL,
610
- TokenType.NUMBER_LITERAL,
611
- TokenType.STRING_LITERAL,
612
- TokenType.NAME,
613
- TokenType.PERIOD
614
- ]);
615
- }
616
- return new NotNode(Tree.processTokens(containedTokens));
617
- }
618
- }
619
-
620
- class InNode extends Node implements TreeNode {
621
- constructor(
622
- public readonly left: TreeNode,
623
- public readonly right: TreeNode,
624
- public readonly flip: boolean = false
625
- ) {
626
- super();
627
- }
628
-
629
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
630
- const toCheck = await this.left.evaluate(scope, dom, tag);
631
- const array = await this.right.evaluate(scope, dom, tag);
632
-
633
- let inArray = array.indexOf(toCheck) > -1;
634
- if (this.flip)
635
- inArray = !inArray;
636
- return inArray;
637
- }
638
-
639
- protected _getChildNodes(): Node[] {
640
- return [
641
- this.left as Node,
642
- this.right as Node
643
- ];
644
- }
645
-
646
- public static match(tokens: Token[]): boolean {
647
- return tokens[0].type === TokenType.IN || (tokens[0].type === TokenType.NOT && tokens[1].type === TokenType.IN);
648
- }
649
-
650
- public static parse(lastNode, token, tokens: Token[]) {
651
- const flip: boolean = tokens[0].type === TokenType.NOT;
652
- if (flip)
653
- tokens.splice(0, 1); // consume not
654
- tokens.splice(0, 1); // consume in
655
-
656
- const containedTokens = Tree.getNextStatementTokens(tokens, false, false, true);
657
- return new InNode(lastNode, Tree.processTokens(containedTokens), flip);
658
- }
659
- }
660
-
661
- class LiteralNode<T = any> extends Node implements TreeNode {
662
- constructor(
663
- public readonly value: T
664
- ) {
665
- super();
666
- }
667
-
668
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
669
- return this.value;
670
- }
671
- }
672
-
673
- class BooleanLiteralNode extends LiteralNode<number> {
674
- constructor(
675
- public readonly value: any
676
- ) {
677
- super(value);
678
- this.value = value === 'true';
679
- }
680
- }
681
-
682
- class NumberLiteralNode extends LiteralNode<number> {
683
- constructor(
684
- public readonly value: any
685
- ) {
686
- super(value);
687
- if (this.value.indexOf('.') > -1) {
688
- this.value = parseFloat(this.value)
689
- } else {
690
- this.value = parseInt(this.value);
691
- }
692
- }
693
- }
694
-
695
- class UnitLiteral {
696
- protected _amount: number;
697
- protected _unit: string;
698
-
699
- constructor(
700
- protected _value: any
701
- ) {
702
- this.value = this._value;
703
- }
704
-
705
- get amount(): number {
706
- return this._amount;
707
- }
708
-
709
- get unit(): string {
710
- return this._unit;
711
- }
712
-
713
- get value(): string {
714
- return `${this._amount}${this._unit}`;
715
- }
716
-
717
- set value(value: string) {
718
- if (value.indexOf('.') > -1) {
719
- this._amount = parseFloat(value)
720
- } else {
721
- this._amount = parseInt(value);
722
- }
723
-
724
- if (isNaN(this._amount))
725
- this._amount = 0;
726
-
727
- const unit = /[^\d.]+$/.exec(value);
728
- this._unit = unit && unit[0] || '';
729
- }
730
-
731
- public toString() {
732
- return this.value;
733
- }
734
- }
735
-
736
- class UnitLiteralNode extends LiteralNode<UnitLiteral> {
737
- constructor(
738
- _value: any
739
- ) {
740
- super(new UnitLiteral(_value));
741
- }
742
- }
743
-
744
-
745
- class FunctionCallNode<T = any> extends Node implements TreeNode {
746
- constructor(
747
- public readonly fnc: TreeNode<(...args: any[]) => any>,
748
- public readonly args: FunctionArgumentNode<any[]>
749
- ) {
750
- super();
751
- }
752
-
753
- protected _getChildNodes(): Node[] {
754
- return [
755
- this.fnc as Node,
756
- this.args as Node
757
- ]
758
- }
759
-
760
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
761
- let functionScope: Scope = scope;
762
- if (this.fnc instanceof ScopeMemberNode) {
763
- functionScope = await this.fnc.scope.evaluate(scope, dom, tag);
764
- }
765
- const values = await this.args.evaluate(scope, dom, tag);
766
- return (await this.fnc.evaluate(scope, dom, tag)).call(functionScope.wrapped || functionScope, ...values);
767
- }
768
- }
769
-
770
-
771
- class FunctionArgumentNode<T = any> extends Node implements TreeNode {
772
- constructor(
773
- protected args: Node[]
774
- ) {
775
- super();
776
- }
777
-
778
- protected _getChildNodes(): Node[] {
779
- return [
780
- ...(this.args as Node[])
781
- ]
782
- }
783
-
784
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
785
- const values: any[] = [];
786
- for (const arg of this.args) {
787
- values.push(await arg.evaluate(scope, dom, tag));
788
- }
789
- return values;
790
- }
791
- }
792
-
793
-
794
- class ScopeMemberNode extends Node implements TreeNode {
795
- constructor(
796
- public readonly scope: TreeNode<Scope>,
797
- public readonly name: TreeNode<string>
798
- ) {
799
- super();
800
- }
801
-
802
- protected _getChildNodes(): Node[] {
803
- return [
804
- this.scope as Node,
805
- this.name as Node
806
- ]
807
- }
808
-
809
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
810
- let scopes = [];
811
- const values = [];
812
-
813
- if (this.scope instanceof ElementQueryNode) {
814
- scopes = await this.scope.evaluate(scope, dom, tag);
815
- } else {
816
- const evalScope = await this.scope.evaluate(scope, dom, tag)
817
- if (evalScope instanceof TagList) {
818
- scopes = evalScope;
819
- } else {
820
- scopes.push(evalScope);
821
- }
822
- }
823
-
824
- for (let parent of scopes) {
825
- if (parent instanceof DOMObject)
826
- parent = parent.scope;
827
-
828
- if (!parent) {
829
- throw Error(`Cannot access "${await this.name.evaluate(scope, dom, tag)}" of undefined.`);
830
- }
831
- const name = await this.name.evaluate(scope, dom, tag);
832
- const value: any = parent.get(name, false);
833
- values.push(value instanceof Scope && value.wrapped || value);
834
- }
835
- return values.length === 1 ? values[0] : values;
836
- }
837
- }
838
-
839
-
840
- class RootScopeMemberNode<T = any> extends Node implements TreeNode {
841
- constructor(
842
- public readonly name: TreeNode<string>
843
- ) {
844
- super();
845
- }
846
-
847
- protected _getChildNodes(): Node[] {
848
- return [
849
- this.name as Node
850
- ]
851
- }
852
-
853
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
854
- const name = await this.name.evaluate(scope, dom, tag);
855
- const value = scope.get(name);
856
- return value instanceof Scope && value.wrapped || value;
857
- }
858
- }
859
-
860
- class ArithmeticNode extends Node implements TreeNode {
861
- constructor(
862
- public readonly left: TreeNode,
863
- public readonly right: TreeNode,
864
- public readonly type: TokenType
865
- ) {
866
- super();
867
- }
868
-
869
- protected _getChildNodes(): Node[] {
870
- return [
871
- this.left as Node,
872
- this.right as Node
873
- ]
874
- }
875
-
876
- public async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
877
- const left: any = await this.left.evaluate(scope, dom, tag);
878
- const right: any = await this.right.evaluate(scope, dom, tag);
879
-
880
- switch (this.type) {
881
- case TokenType.ADD:
882
- return left + right;
883
- case TokenType.SUBTRACT:
884
- return left - right;
885
- case TokenType.MULTIPLY:
886
- return left * right;
887
- case TokenType.DIVIDE:
888
- return left / right;
889
- }
890
- }
891
-
892
- public static match(tokens: Token[]): boolean {
893
- return [
894
- TokenType.ADD,
895
- TokenType.SUBTRACT,
896
- TokenType.MULTIPLY,
897
- TokenType.DIVIDE
898
- ].indexOf(tokens[0].type) > -1
899
- }
900
-
901
- public static parse(lastNode, token, tokens: Token[]) {
902
- tokens.splice(0, 1); // Remove arithmetic operator
903
- return new ArithmeticNode(lastNode, Tree.processTokens(Tree.getNextStatementTokens(tokens)), token.type)
904
- }
905
- }
906
-
907
- class ArithmeticAssignmentNode extends Node implements TreeNode {
908
- constructor(
909
- public readonly left: RootScopeMemberNode,
910
- public readonly right: TreeNode,
911
- public readonly type: TokenType
912
- ) {
913
- super();
914
- }
915
-
916
- protected _getChildNodes(): Node[] {
917
- return [
918
- this.left as Node,
919
- this.right as Node
920
- ]
921
- }
922
-
923
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
924
- let scopes = [];
925
- const name: string = await this.left.name.evaluate(scope, dom, tag);
926
-
927
- if (this.left instanceof ScopeMemberNode) {
928
- const inner = await this.left.scope.evaluate(scope, dom, tag);
929
- if (this.left.scope instanceof ElementQueryNode) {
930
- scopes.push(...inner);
931
- } else {
932
- scopes.push(inner);
933
- }
934
- } else if ((this.left instanceof ElementAttributeNode || this.left instanceof ElementStyleNode) && this.left.elementRef) {
935
- scopes = await this.left.elementRef.evaluate(scope, dom, tag);
936
- } else
937
- scopes.push(scope);
938
-
939
- const values = [];
940
- for (let localScope of scopes) {
941
- if (localScope instanceof DOMObject) {
942
- await this.handleDOMObject(name, dom, localScope, tag);
943
- } else {
944
- if (localScope['$wrapped'] && localScope['$scope'])
945
- localScope = localScope['$scope'];
946
-
947
- let left: number | Array<any> | string = await this.left.evaluate(localScope, dom, tag);
948
- let right: number | Array<any> | string = await this.right.evaluate(localScope, dom, tag);
949
-
950
- if (left instanceof Array) {
951
- left = this.handleArray(name, left, right, localScope);
952
- } else if ((left as any) instanceof UnitLiteral || right instanceof UnitLiteral) {
953
- left = this.handleUnit(name, left, right, localScope);
954
- } else if (Number.isFinite(left)) {
955
- left = this.handleNumber(name, left, right, localScope);
956
- } else {
957
- left = this.handleString(name, left, right, localScope);
958
- }
959
-
960
- values.push(left);
961
- }
962
- }
963
- return values.length > 1 ? values : values[0];
964
- }
965
-
966
- public handleNumber(key, left, right, scope) {
967
- if (right !== null && !Number.isFinite(right))
968
- right = parseFloat(`${right}`);
969
-
970
- left = left as number;
971
- right = right as number;
972
-
973
- switch (this.type) {
974
- case TokenType.ASSIGN:
975
- left = right;
976
- break;
977
- case TokenType.ADD_ASSIGN:
978
- left += right;
979
- break;
980
- case TokenType.SUBTRACT_ASSIGN:
981
- left -= right;
982
- break;
983
- case TokenType.MULTIPLY_ASSIGN:
984
- left *= right;
985
- break;
986
- case TokenType.DIVIDE_ASSIGN:
987
- left /= right;
988
- break;
989
- }
990
- scope.set(key, left);
991
- return left;
992
- }
993
-
994
- public handleString(key, left, right, scope) {
995
- switch (this.type) {
996
- case TokenType.ASSIGN:
997
- left = right;
998
- break;
999
- case TokenType.ADD_ASSIGN:
1000
- left = `${left}${right}`;
1001
- break;
1002
- case TokenType.SUBTRACT_ASSIGN:
1003
- left.replace(right, '');
1004
- break;
1005
- case TokenType.MULTIPLY_ASSIGN:
1006
- left *= right;
1007
- break;
1008
- case TokenType.DIVIDE_ASSIGN:
1009
- left /= right;
1010
- break;
1011
- }
1012
-
1013
- scope.set(key, left);
1014
- return left;
1015
- }
1016
-
1017
- public handleUnit(key, left, right, scope) {
1018
- if (!(left instanceof UnitLiteral)) {
1019
- left = new UnitLiteral(left);
1020
- }
1021
-
1022
- if (!(right instanceof UnitLiteral)) {
1023
- right = new UnitLiteral(right);
1024
- }
1025
- const unit = left.unit || right.unit || 'px';
1026
-
1027
- switch (this.type) {
1028
- case TokenType.ASSIGN:
1029
- left = right;
1030
- break;
1031
- case TokenType.ADD_ASSIGN:
1032
- left = new UnitLiteral(`${left.amount+right.amount}${unit}`);
1033
- break;
1034
- case TokenType.SUBTRACT_ASSIGN:
1035
- left = new UnitLiteral(`${left.amount-right.amount}${unit}`);
1036
- break;
1037
- case TokenType.MULTIPLY_ASSIGN:
1038
- left = new UnitLiteral(`${left.amount*right.amount}${unit}`);
1039
- break;
1040
- case TokenType.DIVIDE_ASSIGN:
1041
- left = new UnitLiteral(`${left.amount/right.amount}${unit}`);
1042
- break;
1043
- }
1044
-
1045
- scope.set(key, left);
1046
- return left;
1047
- }
1048
323
 
1049
- public async handleDOMObject(key: string, dom: DOM, domObject: DOMObject, tag: Tag) {
1050
- let left = domObject.scope.get(key);
1051
- let right: number | Array<any> | string = await this.right.evaluate(domObject.scope, dom, tag);
1052
- if (left instanceof Array)
1053
- return this.handleArray(key, left, right, domObject.scope);
1054
-
1055
- return this.handleString(key, left, right, domObject.scope);
1056
- }
1057
-
1058
- public handleArray(key, left, right, scope) {
1059
- if (!(right instanceof Array))
1060
- right = [right];
1061
- switch (this.type) {
1062
- case TokenType.ASSIGN:
1063
- left.splice(0, left.length);
1064
- left.push(...right);
1065
- break;
1066
- case TokenType.ADD_ASSIGN:
1067
- left.push(...right);
1068
- break;
1069
- case TokenType.SUBTRACT_ASSIGN:
1070
- for (let i = left.length - 1; i >= 0; i--) {
1071
- if (right.indexOf(left[i]) > -1) {
1072
- left.splice(i, 1);
1073
- i++;
1074
- }
1075
- }
1076
- break;
1077
- case TokenType.TILDE:
1078
- for (const toggle of right) {
1079
- const index = left.indexOf(toggle);
1080
- if (index > -1) {
1081
- left.splice(index, 1);
1082
- } else {
1083
- left.push(toggle);
1084
- }
1085
- }
1086
- break;
1087
- }
1088
-
1089
- /*
1090
- We have to trigger a change manually here. Setting the variable on the scope with an array won't trigger
1091
- it since we are modifying values inside of the array instance.
1092
- */
1093
- scope.dispatch(`change:${key}`);
1094
-
1095
- return left;
1096
- }
1097
-
1098
- public static match(tokens: Token[]): boolean {
1099
- return [
1100
- TokenType.ASSIGN,
1101
- TokenType.ADD_ASSIGN,
1102
- TokenType.SUBTRACT_ASSIGN,
1103
- TokenType.MULTIPLY_ASSIGN,
1104
- TokenType.DIVIDE_ASSIGN,
1105
- TokenType.TILDE,
1106
- ].indexOf(tokens[0].type) > -1;
1107
- }
1108
-
1109
- public static parse(lastNode: any, token, tokens: Token[]): ArithmeticAssignmentNode {
1110
- if (!(lastNode instanceof RootScopeMemberNode) && !(lastNode instanceof ScopeMemberNode) && !(lastNode instanceof ElementAttributeNode) && !(lastNode instanceof ElementStyleNode)) {
1111
- throw SyntaxError(`Invalid assignment syntax near ${Tree.toCode(tokens.splice(0, 10))}`);
1112
- }
1113
- tokens.splice(0, 1); // consume =
1114
- const assignmentTokens: Token[] = Tree.getNextStatementTokens(tokens, false, false, true);
1115
-
1116
- return new ArithmeticAssignmentNode(
1117
- lastNode as RootScopeMemberNode,
1118
- Tree.processTokens(assignmentTokens),
1119
- token.type
1120
- );
1121
- }
1122
- }
1123
-
1124
- class IndexNode extends Node implements TreeNode {
1125
- constructor(
1126
- public readonly object: Node,
1127
- public readonly index: Node,
1128
- public readonly indexTwo: Node = null
1129
- ) {
1130
- super();
1131
- }
1132
-
1133
- protected _getChildNodes(): Node[] {
1134
- const children = [
1135
- this.object,
1136
- this.index
1137
- ];
1138
- if (this.indexTwo)
1139
- children.push(this.indexTwo);
1140
-
1141
- return children;
1142
- }
1143
-
1144
- public negativeIndex(obj: any[], index: number | string): number | string {
1145
- if (Number.isFinite(index) && index < 0)
1146
- return obj.length + (index as number);
1147
- return index;
1148
- }
1149
-
1150
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
1151
- const obj = await this.object.evaluate(scope, dom, tag);
1152
- const index: string | number = this.negativeIndex(obj, await this.index.evaluate(scope, dom, tag));
1153
-
1154
- if (Number.isFinite(index) && this.indexTwo) {
1155
- const indexTwo: number = this.negativeIndex(obj, await this.indexTwo.evaluate(scope, dom, tag)) as number;
1156
- const values = [];
1157
- for (let i: number = index as number; i <= indexTwo; i++) {
1158
- values.push(obj[i]);
1159
- }
1160
- return values;
1161
- } else {
1162
- return (obj)[index];
1163
- }
1164
- }
1165
-
1166
- public static match(tokens: Token[]): boolean {
1167
- return tokens[0].type === TokenType.L_BRACKET;
1168
- }
1169
-
1170
- public static parse(lastNode, token, tokens: Token[]): IndexNode {
1171
- const valueTokens: Token[][] = Tree.getBlockTokens(tokens, TokenType.COLON);
1172
- const values: Node[] = [];
1173
- for (const arg of valueTokens) {
1174
- values.push(Tree.processTokens(arg));
1175
- }
1176
- return new IndexNode(lastNode, values[0], values.length > 1 && values[1]);
1177
- }
1178
- }
1179
-
1180
- class ArrayNode extends Node implements TreeNode {
1181
- constructor(
1182
- public readonly values: Node[]
1183
- ) {
1184
- super();
1185
- }
1186
-
1187
- protected _getChildNodes(): Node[] {
1188
- return new Array(...this.values);
1189
- }
1190
-
1191
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
1192
- const arr: WrappedArray<any> = new WrappedArray();
1193
- for (const val of this.values) {
1194
- arr.push(await val.evaluate(scope, dom, tag));
1195
- }
1196
- return arr;
1197
- }
1198
-
1199
- public static match(tokens: Token[]): boolean {
1200
- return tokens[0].type === TokenType.L_BRACKET;
1201
- }
1202
-
1203
- public static parse(lastNode, token, tokens: Token[]): ArrayNode {
1204
- const valueTokens: Token[][] = Tree.getBlockTokens(tokens);
1205
- const values: Node[] = [];
1206
- for (const arg of valueTokens) {
1207
- values.push(Tree.processTokens(arg));
1208
- }
1209
- return new ArrayNode(values);
1210
- }
1211
- }
1212
-
1213
- class ObjectNode extends Node implements TreeNode {
1214
- constructor(
1215
- public readonly keys: Node[],
1216
- public readonly values: Node[]
1217
- ) {
1218
- super();
1219
- }
1220
-
1221
- protected _getChildNodes(): Node[] {
1222
- return new Array(...this.values);
1223
- }
1224
-
1225
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
1226
- const obj: Scope = new Scope();
1227
- for (let i = 0; i < this.values.length; i++) {
1228
- const key = this.keys[i];
1229
- const val = this.values[i];
1230
- obj.set(await key.evaluate(scope, dom, tag), await val.evaluate(scope, dom, tag));
1231
- }
1232
- return obj;
1233
- }
1234
-
1235
- public static match(tokens: Token[]): boolean {
1236
- return tokens[0].type === TokenType.L_BRACE;
1237
- }
1238
-
1239
- public static parse(lastNode, token, tokens: Token[]): ObjectNode {
1240
- const valueTokens: Token[] = Tree.getNextStatementTokens(tokens);
1241
- const keys: Node[] = [];
1242
- const values: Node[] = [];
1243
-
1244
- while (valueTokens.length > 0) {
1245
- const key: Token[] = Tree.getTokensUntil(valueTokens, TokenType.COLON, false);
1246
- if (valueTokens[0].type !== TokenType.COLON)
1247
- throw Error('Invalid object literal syntax. Expecting :');
1248
- valueTokens.splice(0, 1); // Consume :
1249
- const val: Token[] = Tree.getTokensUntil(valueTokens, TokenType.COMMA, true, false, true, {
1250
- type: BlockType.STATEMENT,
1251
- open: null,
1252
- close: null,
1253
- openCharacter: null,
1254
- closeCharacter: null
1255
- });
1256
- keys.push(Tree.processTokens(key));
1257
- values.push(Tree.processTokens(val));
1258
- }
1259
- return new ObjectNode(keys, values);
1260
- }
1261
- }
1262
-
1263
- class ElementQueryNode extends Node implements TreeNode {
1264
- protected requiresPrep: boolean = true;
1265
-
1266
- constructor(
1267
- public readonly query: string
1268
- ) {
1269
- super();
1270
- }
1271
-
1272
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
1273
- tag = tag || await dom.getTagForScope(scope);
1274
- return await dom.get(this.query, true, tag);
1275
- }
1276
-
1277
- async prepare(scope: Scope, dom: DOM, tag: Tag = null) {
1278
- tag = tag || await dom.getTagForScope(scope);
1279
- await dom.get(this.query, true, tag);
1280
- }
1281
- }
1282
-
1283
- class ElementAttributeNode extends Node implements TreeNode {
1284
- protected requiresPrep: boolean = true;
1285
-
1286
- constructor(
1287
- public readonly elementRef: ElementQueryNode | null,
1288
- public readonly attr: string
1289
- ) {
1290
- super();
1291
- }
1292
-
1293
- public get name(): LiteralNode<string> {
1294
- return new LiteralNode<string>(`@${this.attributeName}`);
1295
- }
1296
-
1297
- protected _getChildNodes(): Node[] {
1298
- let nodes = [];
1299
- if (this.elementRef)
1300
- nodes.push(this.elementRef)
1301
- return nodes;
1302
- }
1303
-
1304
- get attributeName(): string {
1305
- if (this.attr.startsWith('.'))
1306
- return this.attr.substring(2);
1307
- return this.attr.substring(1);
1308
- }
1309
-
1310
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
1311
- let tags: TagList;
1312
- if (this.elementRef) {
1313
- tags = await this.elementRef.evaluate(scope, dom, tag);
1314
- } else if (tag) {
1315
- tags = new TagList(tag)
1316
- } else {
1317
- return;
1318
- }
1319
-
1320
- if (tags.length === 1)
1321
- return tags[0].scope.get(`@${this.attributeName}`);
1322
-
1323
- return tags.map((tag) => tag.scope.get(`@${this.attributeName}`));
1324
- }
1325
-
1326
- async prepare(scope: Scope, dom: DOM, tag: Tag = null) {
1327
- if (this.elementRef) {
1328
- await this.elementRef.prepare(scope, dom, tag);
1329
- const tags: TagList = await this.elementRef.evaluate(scope, dom, tag);
1330
- for (const t of tags)
1331
- await t.watchAttribute(this.attributeName);
1332
- } else if(tag) {
1333
- await tag.watchAttribute(this.attributeName);
1334
- }
1335
- }
324
+ prepare(scope: Scope, dom: DOM, tag?: Tag);
1336
325
  }
1337
326
 
1338
- class ElementStyleNode extends Node implements TreeNode {
1339
- protected requiresPrep: boolean = true;
1340
-
1341
- constructor(
1342
- public readonly elementRef: ElementQueryNode | null,
1343
- public readonly attr: string
1344
- ) {
1345
- super();
1346
- }
1347
-
1348
- public get name(): LiteralNode<string> {
1349
- return new LiteralNode<string>(`$${this.attributeName}`);
1350
- }
1351
-
1352
- protected _getChildNodes(): Node[] {
1353
- let nodes = [];
1354
- if (this.elementRef)
1355
- nodes.push(this.elementRef)
1356
- return nodes;
1357
- }
1358
-
1359
- get attributeName(): string {
1360
- if (this.attr.startsWith('.'))
1361
- return this.attr.substring(2);
1362
- return this.attr.substring(1);
1363
- }
1364
-
1365
- async evaluate(scope: Scope, dom: DOM, tag: Tag = null) {
1366
- let tags: TagList;
1367
- if (this.elementRef) {
1368
- tags = await this.elementRef.evaluate(scope, dom, tag);
1369
- } else if (tag) {
1370
- tags = new TagList(tag)
1371
- } else {
1372
- return;
1373
- }
1374
-
1375
- if (tags.length === 1)
1376
- return tags[0].scope.get(`$${this.attributeName}`);
1377
-
1378
- return tags.map((tag) => tag.scope.get(`$${this.attributeName}`));
1379
- }
1380
-
1381
- async prepare(scope: Scope, dom: DOM, tag: Tag = null) {
1382
- if (this.elementRef) {
1383
- await this.elementRef.prepare(scope, dom, tag);
1384
- const tags: TagList = await this.elementRef.evaluate(scope, dom, tag);
1385
- for (const t of tags)
1386
- await t.watchStyle(this.attributeName);
1387
- } else if(tag) {
1388
- await tag.watchStyle(this.attributeName);
1389
- }
1390
- }
1391
- }
1392
327
 
1393
328
  export interface IBlockInfo {
1394
329
  type: BlockType,
@@ -1405,7 +340,7 @@ export const AttributableNodes = [
1405
340
  ];
1406
341
 
1407
342
  export class Tree {
1408
- protected static cache: {[key: string]: Node} = {};
343
+ protected static cache: { [key: string]: Node } = {};
1409
344
  protected rootNode: Node;
1410
345
 
1411
346
  constructor(
@@ -1627,7 +562,7 @@ export class Tree {
1627
562
  let openCharacter: string;
1628
563
  let closeCharacter: string;
1629
564
 
1630
- switch(blockType) {
565
+ switch (blockType) {
1631
566
  case BlockType.PAREN:
1632
567
  open = TokenType.L_PAREN;
1633
568
  close = TokenType.R_PAREN;
@@ -1777,4 +712,9 @@ export class Tree {
1777
712
  tokens.splice(0, matching.length);
1778
713
  return matching;
1779
714
  }
715
+
716
+ static async apply(code: string, scope: Scope, dom: DOM, tag: Tag) {
717
+ const t = new Tree(code);
718
+ return await t.evaluate(scope, dom, tag);
719
+ }
1780
720
  }