@@ -4,25 +4,27 @@ import { IContext } from "../Interfaces";
4
4
import { ASTNode , ArrayLiteralNode , Token , TokenType , ValidFuncNames } from "./FormulaEvaluation.types" ;
5
5
6
6
export class FormulaEvaluation {
7
- constructor ( context : IContext ) {
7
+ private webUrl : string ;
8
+ constructor ( context : IContext , webUrlOverride ?: string ) {
8
9
sp . setup ( { pageContext : context . pageContext } ) ;
10
+ this . webUrl = webUrlOverride || context . pageContext . web . absoluteUrl ;
9
11
}
10
12
11
13
/** Evaluates a formula expression and returns the result, with optional context object for variables */
12
- public async evaluate ( expression : string , context : { [ key : string ] : any } = { } ) : Promise < any > {
14
+ public evaluate ( expression : string , context : { [ key : string ] : any } = { } ) : any {
13
15
const tokens : Token [ ] = this . _tokenize ( expression , context ) ;
14
16
const postfix : Token [ ] = this . _shuntingYard ( tokens ) ;
15
17
const ast : ASTNode = this . _buildAST ( postfix ) ;
16
- return await this . _evaluateAST ( ast , context ) ;
18
+ return this . evaluateASTNode ( ast , context ) ;
17
19
}
18
20
19
21
/** Tokenizes an expression into a list of tokens (primatives, operators, variables, function names, arrays etc) */
20
22
private _tokenize ( expression : string , context : { [ key : string ] : any } ) : Token [ ] {
21
23
// Each pattern captures a different token type
22
24
// and are matched in order
23
25
const patterns : [ RegExp , TokenType ] [ ] = [
24
- [ / ^ \[ \$ ? [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 ] * \] / , "VARIABLE" ] , // [$variable]
25
- [ / ^ @ [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 ] * / , "VARIABLE" ] , // @variable
26
+ [ / ^ \[ \$ ? [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 . ] * \] / , "VARIABLE" ] , // [$variable]
27
+ [ / ^ @ [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 . ] * / , "VARIABLE" ] , // @variable
26
28
[ / ^ [ 0 - 9 ] + (?: \. [ 0 - 9 ] + ) ? / , "NUMBER" ] , // Numeric literals
27
29
[ / ^ " ( [ ^ " ] * ) " / , "STRING" ] , // Match double-quoted strings
28
30
[ / ^ ' ( [ ^ ' ] * ) ' / , "STRING" ] , // Match single-quoted strings
@@ -285,14 +287,17 @@ export class FormulaEvaluation {
285
287
return stack [ 0 ] as ASTNode ;
286
288
}
287
289
288
- private async _evaluateAST ( node : ASTNode | ArrayLiteralNode | string | number , context : { [ key : string ] : any } ) : Promise < any > {
290
+ public evaluateASTNode ( node : ASTNode | ArrayLiteralNode | string | number , context : { [ key : string ] : any } ) : any {
289
291
290
292
if ( ! node ) return 0 ;
291
293
294
+ if ( typeof node === "object" && ! ( Object . prototype . hasOwnProperty . call ( node , 'type' ) && Object . prototype . hasOwnProperty . call ( node , 'value' ) ) ) {
295
+ return node ;
296
+ }
297
+
292
298
// Each element in an array literal is evaluated recursively
293
299
if ( node instanceof ArrayLiteralNode ) {
294
- const evaluatedElementsPromises = ( node as ArrayLiteralNode ) . elements . map ( element => this . _evaluateAST ( element , context ) ) ;
295
- const evaluatedElements = await Promise . all ( evaluatedElementsPromises ) ;
300
+ const evaluatedElements = ( node as ArrayLiteralNode ) . elements . map ( element => this . evaluateASTNode ( element , context ) ) ;
296
301
return evaluatedElements ;
297
302
}
298
303
@@ -322,14 +327,14 @@ export class FormulaEvaluation {
322
327
323
328
// VARIABLE nodes are looked up in the context object and returned
324
329
if ( node . type === "VARIABLE" ) {
325
- return context [ ( node . value as string ) . replace ( / ^ [ [ @ ] ? \$ ? ( [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 ] * ) \] ? / , '$1' ) ] ?? null ;
330
+ return context [ ( node . value as string ) . replace ( / ^ [ [ @ ] ? \$ ? ( [ a - z A - Z _ ] [ a - z A - Z _ 0 - 9 . ] * ) \] ? / , '$1' ) ] ?? null ;
326
331
}
327
332
328
333
// OPERATOR nodes have their OPERANDS evaluated recursively, with the operator applied to the results
329
334
if ( node . type === "OPERATOR" && [ "+" , "-" , "*" , "/" , "==" , "!=" , ">" , "<" , ">=" , "<=" , "&&" , "||" , "%" , "&" , "|" ] . includes ( node . value as string ) && node . operands ) {
330
335
331
- const leftValue = await this . _evaluateAST ( node . operands [ 0 ] , context ) ;
332
- const rightValue = await this . _evaluateAST ( node . operands [ 1 ] , context ) ;
336
+ const leftValue = this . evaluateASTNode ( node . operands [ 0 ] , context ) ;
337
+ const rightValue = this . evaluateASTNode ( node . operands [ 1 ] , context ) ;
333
338
334
339
if ( typeof leftValue === "string" || typeof rightValue === "string" ) {
335
340
// Throw an error if the operator is not valid for strings
@@ -338,7 +343,7 @@ export class FormulaEvaluation {
338
343
}
339
344
// Concatenate strings if either operand is a string
340
345
if ( node . value === "+" ) {
341
- return leftValue . toString ( ) + rightValue . toString ( ) ;
346
+ return ( leftValue || "" ) . toString ( ) + ( rightValue || "" ) . toString ( ) ;
342
347
}
343
348
}
344
349
@@ -364,8 +369,7 @@ export class FormulaEvaluation {
364
369
// Evaluation of function nodes is handled here:
365
370
366
371
if ( node . type === "FUNCTION" && node . operands ) {
367
- const funcArgsPromises = node . operands . map ( arg => this . _evaluateAST ( arg , context ) ) ;
368
- const funcArgs = await Promise . all ( funcArgsPromises ) ;
372
+ const funcArgs = node . operands . map ( arg => this . evaluateASTNode ( arg , context ) ) ;
369
373
370
374
switch ( node . value ) {
371
375
@@ -543,7 +547,7 @@ export class FormulaEvaluation {
543
547
}
544
548
case 'getThumbnailImage' : {
545
549
const imageUrl = funcArgs [ 0 ] ;
546
- const thumbnailImage = await this . _getSharePointThumbnailUrl ( imageUrl ) ;
550
+ const thumbnailImage = this . _getSharePointThumbnailUrl ( imageUrl ) ;
547
551
return thumbnailImage ;
548
552
}
549
553
@@ -570,7 +574,7 @@ export class FormulaEvaluation {
570
574
}
571
575
else {
572
576
// treat as char Array
573
- const value = await this . _evaluateAST ( array , context ) ;
577
+ const value = this . evaluateASTNode ( array , context ) ;
574
578
return value . toString ( ) . length ;
575
579
}
576
580
}
@@ -602,9 +606,8 @@ export class FormulaEvaluation {
602
606
const [ filenameNoExt , ext ] = filename . split ( '.' ) ;
603
607
return `${ url } /_t/${ filenameNoExt } _${ ext } .jpg` ;
604
608
}
605
- private async _getUserImageUrl ( userEmail : string ) : Promise < string > {
606
- const user = await sp . web . ensureUser ( userEmail ) ;
607
- return ( user . data as any ) . PictureUrl || '' ;
609
+ private _getUserImageUrl ( userEmail : string ) : string {
610
+ return `${ this . webUrl } /_layouts/15/userphoto.aspx?size=L&username=${ userEmail } `
608
611
}
609
612
}
610
613
0 commit comments