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.
- package/demo/demo.html +2 -0
- package/demo/vsn.js +1 -1
- package/dist/AST/ArithmeticAssignmentNode.d.ts +23 -0
- package/dist/AST/ArithmeticAssignmentNode.js +313 -0
- package/dist/AST/ArithmeticAssignmentNode.js.map +1 -0
- package/dist/AST/ArithmeticNode.d.ts +15 -0
- package/dist/AST/ArithmeticNode.js +114 -0
- package/dist/AST/ArithmeticNode.js.map +1 -0
- package/dist/AST/ArrayNode.d.ts +14 -0
- package/dist/AST/ArrayNode.js +114 -0
- package/dist/AST/ArrayNode.js.map +1 -0
- package/dist/AST/BlockNode.d.ts +11 -0
- package/dist/AST/BlockNode.js +98 -0
- package/dist/AST/BlockNode.js.map +1 -0
- package/dist/AST/BooleanLiteralNode.d.ts +5 -0
- package/dist/{Model/Collection.js → AST/BooleanLiteralNode.js} +12 -18
- package/dist/AST/BooleanLiteralNode.js.map +1 -0
- package/dist/AST/ComparisonNode.d.ts +15 -0
- package/dist/AST/ComparisonNode.js +120 -0
- package/dist/AST/ComparisonNode.js.map +1 -0
- package/dist/AST/ConditionalNode.d.ts +13 -0
- package/dist/AST/ConditionalNode.js +95 -0
- package/dist/AST/ConditionalNode.js.map +1 -0
- package/dist/AST/ElementAttributeNode.d.ts +18 -0
- package/dist/AST/ElementAttributeNode.js +159 -0
- package/dist/AST/ElementAttributeNode.js.map +1 -0
- package/dist/AST/ElementQueryNode.d.ts +12 -0
- package/dist/AST/ElementQueryNode.js +111 -0
- package/dist/AST/ElementQueryNode.js.map +1 -0
- package/dist/AST/ElementStyleNode.d.ts +18 -0
- package/dist/AST/ElementStyleNode.js +159 -0
- package/dist/AST/ElementStyleNode.js.map +1 -0
- package/dist/AST/ForStatementNode.d.ts +17 -0
- package/dist/AST/ForStatementNode.js +121 -0
- package/dist/AST/ForStatementNode.js.map +1 -0
- package/dist/AST/FunctionArgumentNode.d.ts +11 -0
- package/dist/AST/FunctionArgumentNode.js +100 -0
- package/dist/AST/FunctionArgumentNode.js.map +1 -0
- package/dist/AST/FunctionCallNode.d.ts +13 -0
- package/dist/AST/FunctionCallNode.js +102 -0
- package/dist/AST/FunctionCallNode.js.map +1 -0
- package/dist/AST/IfStatementNode.d.ts +14 -0
- package/dist/AST/IfStatementNode.js +128 -0
- package/dist/AST/IfStatementNode.js.map +1 -0
- package/dist/AST/InNode.d.ts +15 -0
- package/dist/AST/InNode.js +107 -0
- package/dist/AST/InNode.js.map +1 -0
- package/dist/AST/IndexNode.d.ts +16 -0
- package/dist/AST/IndexNode.js +126 -0
- package/dist/AST/IndexNode.js.map +1 -0
- package/dist/AST/LiteralNode.d.ts +10 -0
- package/dist/AST/LiteralNode.js +74 -0
- package/dist/AST/LiteralNode.js.map +1 -0
- package/dist/AST/Node.d.ts +19 -0
- package/dist/AST/Node.js +117 -0
- package/dist/AST/Node.js.map +1 -0
- package/dist/AST/NotNode.d.ts +12 -0
- package/dist/AST/NotNode.js +103 -0
- package/dist/AST/NotNode.js.map +1 -0
- package/dist/AST/NumberLiteralNode.d.ts +5 -0
- package/dist/{Model/fields/EmailField.js → AST/NumberLiteralNode.js} +17 -17
- package/dist/AST/NumberLiteralNode.js.map +1 -0
- package/dist/AST/ObjectNode.d.ts +14 -0
- package/dist/AST/ObjectNode.js +131 -0
- package/dist/AST/ObjectNode.js.map +1 -0
- package/dist/AST/RootScopeMemberNode.d.ts +11 -0
- package/dist/AST/RootScopeMemberNode.js +87 -0
- package/dist/AST/RootScopeMemberNode.js.map +1 -0
- package/dist/AST/ScopeMemberNode.d.ts +12 -0
- package/dist/AST/ScopeMemberNode.js +128 -0
- package/dist/AST/ScopeMemberNode.js.map +1 -0
- package/dist/AST/UnitLiteralNode.d.ts +15 -0
- package/dist/AST/UnitLiteralNode.js +72 -0
- package/dist/AST/UnitLiteralNode.js.map +1 -0
- package/dist/AST.d.ts +7 -60
- package/dist/AST.js +74 -1493
- package/dist/AST.js.map +1 -1
- package/dist/Controller.d.ts +4 -2
- package/dist/Controller.js +10 -2
- package/dist/Controller.js.map +1 -1
- package/dist/EventDispatcher.d.ts +4 -1
- package/dist/EventDispatcher.js +27 -12
- package/dist/EventDispatcher.js.map +1 -1
- package/dist/Model/Field.d.ts +8 -0
- package/dist/Model/{fields/StringField.js → Field.js} +19 -24
- package/dist/Model/Field.js.map +1 -0
- package/dist/Model.d.ts +4 -3
- package/dist/Model.js +6 -17
- package/dist/Model.js.map +1 -1
- package/dist/Registry.d.ts +3 -0
- package/dist/Registry.js +11 -0
- package/dist/Registry.js.map +1 -1
- package/dist/Scope/DynamicScopeData.d.ts +6 -0
- package/dist/{Model/DataModel.js → Scope/DynamicScopeData.js} +17 -17
- package/dist/Scope/DynamicScopeData.js.map +1 -0
- package/dist/Scope/QueryReference.d.ts +10 -0
- package/dist/Scope/QueryReference.js +103 -0
- package/dist/Scope/QueryReference.js.map +1 -0
- package/dist/Scope/ScopeData.d.ts +4 -0
- package/dist/Scope/ScopeData.js +40 -0
- package/dist/Scope/ScopeData.js.map +1 -0
- package/dist/Scope/ScopeDataAbstract.d.ts +22 -0
- package/dist/Scope/ScopeDataAbstract.js +137 -0
- package/dist/Scope/ScopeDataAbstract.js.map +1 -0
- package/dist/Scope/ScopeReference.d.ts +10 -0
- package/dist/Scope/ScopeReference.js +73 -0
- package/dist/Scope/ScopeReference.js.map +1 -0
- package/dist/Scope/ScopedVariableType.d.ts +6 -0
- package/dist/Scope/ScopedVariableType.js +14 -0
- package/dist/Scope/ScopedVariableType.js.map +1 -0
- package/dist/Scope/WrappedArray.d.ts +16 -0
- package/dist/Scope/WrappedArray.js +121 -0
- package/dist/Scope/WrappedArray.js.map +1 -0
- package/dist/Scope/properties/Property.d.ts +18 -0
- package/dist/Scope/properties/Property.js +93 -0
- package/dist/Scope/properties/Property.js.map +1 -0
- package/dist/Scope.d.ts +4 -45
- package/dist/Scope.js +31 -266
- package/dist/Scope.js.map +1 -1
- package/dist/SimplePromise.d.ts +2 -2
- package/dist/SimplePromise.js.map +1 -1
- package/dist/Tag.js +1 -1
- package/dist/Tag.js.map +1 -1
- package/dist/Types.d.ts +1 -0
- package/dist/Types.js +8 -1
- package/dist/Types.js.map +1 -1
- package/dist/Validators.d.ts +7 -0
- package/dist/Validators.js +54 -0
- package/dist/Validators.js.map +1 -0
- package/dist/attributes/Bind.js.map +1 -1
- package/dist/attributes/JSONAttribute.js.map +1 -1
- package/dist/attributes/List.js +4 -4
- package/dist/attributes/List.js.map +1 -1
- package/dist/attributes/Radio.js.map +1 -1
- package/dist/attributes/SetAttribute.js.map +1 -1
- package/dist/attributes/StyleAttribute.js.map +1 -1
- package/dist/vsn.d.ts +4 -1
- package/dist/vsn.js +14 -9
- package/dist/vsn.js.map +1 -1
- package/package.json +1 -1
- package/src/AST/ArithmeticAssignmentNode.ts +236 -0
- package/src/AST/ArithmeticNode.ts +52 -0
- package/src/AST/ArrayNode.ts +39 -0
- package/src/AST/BlockNode.ts +25 -0
- package/src/AST/BooleanLiteralNode.ts +10 -0
- package/src/AST/ComparisonNode.ts +57 -0
- package/src/AST/ConditionalNode.ts +36 -0
- package/src/AST/ElementAttributeNode.ts +63 -0
- package/src/AST/ElementQueryNode.ts +25 -0
- package/src/AST/ElementStyleNode.ts +63 -0
- package/src/AST/ForStatementNode.ts +59 -0
- package/src/AST/FunctionArgumentNode.ts +27 -0
- package/src/AST/FunctionCallNode.ts +32 -0
- package/src/AST/IfStatementNode.ts +67 -0
- package/src/AST/InNode.ts +46 -0
- package/src/AST/IndexNode.ts +61 -0
- package/src/AST/LiteralNode.ts +17 -0
- package/src/AST/Node.ts +71 -0
- package/src/AST/NotNode.ts +41 -0
- package/src/AST/NumberLiteralNode.ts +14 -0
- package/src/AST/ObjectNode.ts +55 -0
- package/src/AST/RootScopeMemberNode.ts +25 -0
- package/src/AST/ScopeMemberNode.ts +53 -0
- package/src/AST/UnitLiteralNode.ts +51 -0
- package/src/AST.ts +33 -1093
- package/src/Controller.ts +10 -2
- package/src/EventDispatcher.ts +29 -12
- package/src/Model/Field.ts +20 -0
- package/src/Model.ts +7 -21
- package/src/Registry.ts +10 -0
- package/src/Scope/DynamicScopeData.ts +29 -0
- package/src/Scope/QueryReference.ts +29 -0
- package/src/Scope/ScopeData.ts +21 -0
- package/src/Scope/ScopeDataAbstract.ts +126 -0
- package/src/Scope/ScopeReference.ts +30 -0
- package/src/Scope/ScopedVariableType.ts +7 -0
- package/src/Scope/WrappedArray.ts +88 -0
- package/src/Scope/properties/Property.ts +79 -0
- package/src/Scope.ts +28 -193
- package/src/SimplePromise.ts +2 -2
- package/src/Tag.ts +1 -1
- package/src/Types.ts +6 -1
- package/src/Validators.ts +45 -0
- package/src/attributes/Bind.ts +2 -1
- package/src/attributes/JSONAttribute.ts +2 -1
- package/src/attributes/List.ts +1 -1
- package/src/attributes/Radio.ts +2 -1
- package/src/attributes/ScopeChange.ts +1 -1
- package/src/attributes/SetAttribute.ts +2 -1
- package/src/attributes/StyleAttribute.ts +2 -1
- package/src/attributes/TypeAttribute.ts +1 -1
- package/src/vsn.ts +9 -4
- package/test/AST/ArithmeticAssignmentNode.spec.ts +47 -0
- package/test/AST.spec.ts +2 -2
- package/test/Controller.spec.ts +44 -0
- package/test/Model/DataModel.spec.ts +0 -141
- package/test/Scope/DynamicScopeData.spec.ts +141 -0
- package/test/Scope.spec.ts +13 -1
- package/test/Tag/TagList.spec.ts +1 -1
- package/dist/Model/Collection.d.ts +0 -5
- package/dist/Model/Collection.js.map +0 -1
- package/dist/Model/DataModel.d.ts +0 -6
- package/dist/Model/DataModel.js.map +0 -1
- package/dist/Model/ModelAbstract.d.ts +0 -20
- package/dist/Model/ModelAbstract.js +0 -122
- package/dist/Model/ModelAbstract.js.map +0 -1
- package/dist/Model/fields/BooleanField.d.ts +0 -5
- package/dist/Model/fields/BooleanField.js +0 -43
- package/dist/Model/fields/BooleanField.js.map +0 -1
- package/dist/Model/fields/EmailField.d.ts +0 -5
- package/dist/Model/fields/EmailField.js.map +0 -1
- package/dist/Model/fields/Field.d.ts +0 -13
- package/dist/Model/fields/Field.js +0 -79
- package/dist/Model/fields/Field.js.map +0 -1
- package/dist/Model/fields/FloatField.d.ts +0 -5
- package/dist/Model/fields/FloatField.js +0 -47
- package/dist/Model/fields/FloatField.js.map +0 -1
- package/dist/Model/fields/PositiveNumberField.d.ts +0 -5
- package/dist/Model/fields/PositiveNumberField.js +0 -51
- package/dist/Model/fields/PositiveNumberField.js.map +0 -1
- package/dist/Model/fields/StringField.d.ts +0 -5
- package/dist/Model/fields/StringField.js.map +0 -1
- package/src/Model/Collection.ts +0 -13
- package/src/Model/DataModel.ts +0 -29
- package/src/Model/ModelAbstract.ts +0 -114
- package/src/Model/fields/BooleanField.ts +0 -16
- package/src/Model/fields/EmailField.ts +0 -12
- package/src/Model/fields/Field.ts +0 -65
- package/src/Model/fields/FloatField.ts +0 -22
- package/src/Model/fields/PositiveNumberField.ts +0 -24
- package/src/Model/fields/StringField.ts +0 -16
- package/test/Model.spec.ts +0 -306
package/src/AST.ts
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
|
-
import {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
|
-
|
|
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
|
}
|