예제 #1
0
        public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var literal = block.Argument.ParseVarName(out var name);

            if (name.IsNullOrEmpty())
            {
                throw new NotSupportedException("'partial' block is missing name of partial");
            }

            literal = literal.AdvancePastWhitespace();

            var argValue = literal.GetJsExpressionAndEvaluate(scope);
            var args     = argValue as Dictionary <string, object>;

            if (argValue != null && args == null)
            {
                throw new NotSupportedException("Any 'partial' argument must be an Object Dictionary");
            }

            var format = scope.Context.PageFormats.First().Extension;

            if (args != null && args.TryGetValue(ScriptConstants.Format, out var oFormat))
            {
                format = oFormat.ToString();
                args.Remove(ScriptConstants.Format);
            }

            var nameString = name.ToString();
            var partial    = new SharpPartialPage(scope.Context, nameString, block.Body, format, args);

            scope.PageResult.Partials[nameString] = partial;

            return(TypeConstants.EmptyTask);
        }
예제 #2
0
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken ct)
        {
            var result = await block.Argument.GetJsExpressionAndEvaluateToBoolAsync(scope,
                ifNone: () => throw new NotSupportedException("'while' block does not have a valid expression"));

            var iterations = 0;
            
            if (result)
            {
                do
                {
                    await WriteBodyAsync(scope, block, ct);
                    
                    result = await block.Argument.GetJsExpressionAndEvaluateToBoolAsync(scope,
                        ifNone: () => throw new NotSupportedException("'while' block does not have a valid expression"));

                    Context.DefaultMethods.AssertWithinMaxQuota(iterations++);
                    
                } while (result);
            }
            else
            {
                await WriteElseAsync(scope, block.ElseBlocks, ct);
            }
        }
예제 #3
0
        // {{#if ...}}
        //    ^
        public static ReadOnlyMemory <char> ParseTemplateScriptBlock(this ReadOnlyMemory <char> literal, ScriptContext context, out PageBlockFragment blockFragment)
        {
            literal = literal.ParseVarName(out var blockNameSpan);

            PageBlockFragment statement;
            var blockName  = blockNameSpan.ToString();
            var endBlock   = "{{/" + blockName + "}}";
            var endExprPos = literal.IndexOf("}}");

            if (endExprPos == -1)
            {
                throw new SyntaxErrorException($"Unterminated '{blockName}' block expression, near '{literal.DebugLiteral()}'");
            }

            var argument = literal.Slice(0, endExprPos).Trim();

            literal = literal.Advance(endExprPos + 2);

            var language = context.ParseAsLanguage.TryGetValue(blockName, out var lang)
                ? lang
                : ScriptTemplate.Language;

            if (language.Name == ScriptVerbatim.Language.Name)
            {
                var endBlockPos = literal.IndexOf(endBlock);
                if (endBlockPos == -1)
                {
                    throw new SyntaxErrorException($"Unterminated end block '{endBlock}'");
                }

                var body = literal.Slice(0, endBlockPos);
                literal = literal.Advance(endBlockPos + endBlock.Length).TrimFirstNewLine();

                blockFragment = language.ParseVerbatimBlock(blockName, argument, body);
                return(literal);
            }

            literal = literal.ParseTemplateBody(blockNameSpan, out var bodyText);
            var bodyFragments = language.Parse(context, bodyText);

            var elseBlocks = new List <PageElseBlock>();

            while (literal.StartsWith("{{else"))
            {
                literal = literal.ParseTemplateElseBlock(blockName, out var elseArgument, out var elseBody);

                var elseBlock = new PageElseBlock(elseArgument, language.Parse(context, elseBody));
                elseBlocks.Add(elseBlock);
            }

            literal = literal.Advance(2 + 1 + blockName.Length + 2);

            //remove new line after partial block end tag
            literal = literal.TrimFirstNewLine();

            blockFragment = new PageBlockFragment(blockName, argument, bodyFragments, elseBlocks);

            return(literal);
        }
예제 #4
0
        public virtual PageBlockFragment ParseVerbatimBlock(string blockName, ReadOnlyMemory <char> argument, ReadOnlyMemory <char> body)
        {
            var bodyFragment = new List <PageFragment> {
                new PageStringFragment(body)
            };
            var blockFragment = new PageBlockFragment(blockName, argument, bodyFragment);

            return(blockFragment);
        }
예제 #5
0
 protected virtual async Task WriteBodyAsync(ScriptScopeContext scope, PageBlockFragment fragment, CancellationToken token)
 {
     if (fragment.Body != null)
     {
         await WriteAsync(scope, fragment.Body, GetCallTrace(fragment), token);
     }
     else if (fragment.BodyStatement?.Statements != null)
     {
         await WriteAsync(scope, fragment.BodyStatement.Statements, GetCallTrace(fragment), token);
     }
 }
예제 #6
0
        public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken ct)
        {
            var literal = block.Argument.ParseVarName(out var name);

            var strFragment = (PageStringFragment)block.Body[0];
            var csvList     = Context.DefaultMethods.parseCsv(strFragment.ValueString);

            scope.PageResult.Args[name.ToString()] = csvList;

            return(TypeConstants.EmptyTask);
        }
예제 #7
0
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var result = await block.Argument.GetJsExpressionAndEvaluateToBoolAsync(scope,
                                                                                    ifNone : () => throw new NotSupportedException("'if' block does not have a valid expression"));

            if (result)
            {
                await WriteBodyAsync(scope, block, token);
            }
            else
            {
                await WriteElseAsync(scope, block.ElseBlocks, token);
            }
        }
예제 #8
0
        public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken ct)
        {
            var literal = block.Argument.ParseVarName(out var name);

            var strFragment = (PageStringFragment)block.Body[0];
            var trimmedBody = StringBuilderCache.Allocate();

            foreach (var line in strFragment.ValueString.ReadLines())
            {
                trimmedBody.AppendLine(line.Trim());
            }
            var strList = trimmedBody.ToString().FromCsv <List <List <string> > >();

            scope.PageResult.Args[name.ToString()] = strList;

            return(TypeConstants.EmptyTask);
        }
예제 #9
0
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var result = await block.Argument.GetJsExpressionAndEvaluateAsync(scope,
                                                                              ifNone : () => throw new NotSupportedException("'with' block does not have a valid expression"));

            if (result != null)
            {
                var resultAsMap = result.ToObjectDictionary();

                var withScope = scope.ScopeWithParams(resultAsMap);

                await WriteBodyAsync(withScope, block, token);
            }
            else
            {
                await WriteElseAsync(scope, block.ElseBlocks, token);
            }
        }
예제 #10
0
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var argValue = block.Argument.GetJsExpressionAndEvaluate(scope);
            var args     = argValue as Dictionary <string, object> ?? new Dictionary <string, object>();

            var format = scope.Context.PageFormats.First().Extension;

            if (args.TryGetValue(ScriptConstants.Format, out var oFormat))
            {
                format = oFormat.ToString();
                args.Remove(ScriptConstants.Format);
            }

            var htmlDecode = false;

            if (args.TryGetValue(nameof(htmlDecode), out var oHtmlDecode) &&
                oHtmlDecode is bool b)
            {
                htmlDecode = b;
                args.Remove(nameof(htmlDecode));
            }

            var context        = scope.CreateNewContext(args);
            var unrenderedBody = new SharpPartialPage(scope.Context, "eval-page", block.Body, format, args);

            using (var ms = MemoryStreamFactory.GetStream())
            {
                var captureScope = scope.ScopeWith(outputStream: ms, scopedParams: args);
                await scope.PageResult.WritePageAsync(unrenderedBody, captureScope, token);

                var renderedBody = await ms.ReadToEndAsync();

                if (htmlDecode)
                {
                    renderedBody = renderedBody.HtmlDecode();
                }

                var pageResult = new PageResult(context.OneTimePage(renderedBody))
                {
                    Args = args,
                };
                await pageResult.WriteToAsync(scope.OutputStream, token);
            }
        }
예제 #11
0
        // #if ...
        //  ^
        public static ReadOnlyMemory <char> ParseCodeScriptBlock(this ReadOnlyMemory <char> literal, ScriptContext context,
                                                                 out PageBlockFragment blockFragment)
        {
            literal = literal.ParseVarName(out var blockNameSpan);
            var endArgumentPos = literal.IndexOf('\n');
            var argument       = literal.Slice(0, endArgumentPos).Trim();

            literal = literal.Slice(endArgumentPos + 1);

            var blockName = blockNameSpan.ToString();

            var language = context.ParseAsLanguage.TryGetValue(blockName, out var lang)
                ? lang
                : ScriptCode.Language;

            if (language.Name == ScriptVerbatim.Language.Name)
            {
                literal = literal.ParseCodeBody(blockNameSpan, out var body);
                body    = body.ChopNewLine();

                blockFragment = language.ParseVerbatimBlock(blockName, argument, body);
                return(literal);
            }

            literal = literal.ParseCodeBody(blockNameSpan, out var bodyText);
            var bodyFragments = language.Parse(context, bodyText);

            var elseBlocks = new List <PageElseBlock>();

            literal = literal.AdvancePastWhitespace();
            while (literal.StartsWith("else"))
            {
                literal = literal.ParseCodeElseBlock(blockNameSpan, out var elseArgument, out var elseBody);

                var elseBlock = new PageElseBlock(elseArgument, language.Parse(context, elseBody));
                elseBlocks.Add(elseBlock);

                literal = literal.AdvancePastWhitespace();
            }

            blockFragment = new PageBlockFragment(blockName, argument, bodyFragments, elseBlocks);

            return(literal);
        }
예제 #12
0
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var tuple = Parse(scope, block);
            var name  = tuple.name;

            using var ms = MemoryStreamFactory.GetStream();
            var useScope = scope.ScopeWith(tuple.scopeArgs, ms);

            await WriteBodyAsync(useScope, block, token).ConfigAwait();

            // ReSharper disable once MethodHasAsyncOverload
            var capturedOutput = ms.ReadToEnd();

            if (tuple.appendTo && scope.PageResult.Args.TryGetValue(name, out var oVar) &&
                oVar is string existingString)
            {
                scope.PageResult.Args[name] = existingString + capturedOutput;
                return;
            }

            scope.PageResult.Args[name] = capturedOutput;
        }
예제 #13
0
        public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken ct)
        {
            var literal = block.Argument.Span.ParseVarName(out var name);

            var delimiter = " ";

            literal = literal.AdvancePastWhitespace();
            if (literal.Length > 0)
            {
                literal = literal.ParseJsToken(out var token);
                if (!(token is JsLiteral litToken))
                {
                    throw new NotSupportedException($"'keyvalues' block expected string delimiter but was {token.DebugToken()}");
                }
                delimiter = litToken.Value.ToString();
            }

            var strFragment = (PageStringFragment)block.Body[0];
            var strDict     = strFragment.ValueString.Trim().ParseAsKeyValues(delimiter);

            scope.PageResult.Args[name.ToString()] = strDict;

            return(TypeConstants.EmptyTask);
        }
예제 #14
0
        EachArg ParseArgument(ScriptScopeContext scope, PageBlockFragment fragment)
        {
            var literal = fragment.Argument.Span.ParseJsExpression(out var token);

            if (token == null)
            {
                throw new NotSupportedException("'each' block requires the collection to iterate");
            }

            var binding = "it";

            literal = literal.AdvancePastWhitespace();

            JsToken source, where, orderBy, orderByDescending, skip, take;

            where = orderBy = orderByDescending = skip = take = null;

            var hasExplicitBinding = literal.StartsWith("in ");

            if (hasExplicitBinding)
            {
                if (!(token is JsIdentifier identifier))
                {
                    throw new NotSupportedException($"'each' block expected identifier but was {token.DebugToken()}");
                }

                binding = identifier.Name;

                literal = literal.Advance(3);
                literal = literal.ParseJsExpression(out source);
                if (source == null)
                {
                    throw new NotSupportedException("'each' block requires the collection to iterate");
                }
            }
            else
            {
                source = token;
            }

            if (literal.StartsWith("where "))
            {
                literal = literal.Advance("where ".Length);
                literal = literal.ParseJsExpression(out where);
            }

            literal = literal.AdvancePastWhitespace();
            if (literal.StartsWith("orderby "))
            {
                literal = literal.Advance("orderby ".Length);
                literal = literal.ParseJsExpression(out orderBy);

                literal = literal.AdvancePastWhitespace();
                if (literal.StartsWith("descending"))
                {
                    literal           = literal.Advance("descending".Length);
                    orderByDescending = orderBy;
                    orderBy           = null;
                }
            }

            literal = literal.AdvancePastWhitespace();
            if (literal.StartsWith("skip "))
            {
                literal = literal.Advance("skip ".Length);
                literal = literal.ParseJsExpression(out skip);
            }

            literal = literal.AdvancePastWhitespace();
            if (literal.StartsWith("take "))
            {
                literal = literal.Advance("take ".Length);
                literal = literal.ParseJsExpression(out take);
            }

            return(new EachArg(binding, hasExplicitBinding, source, where, orderBy, orderByDescending, skip, take));
        }
예제 #15
0
        // {{#if ...}}
        //    ^
        public static ReadOnlyMemory <char> ParseTemplateScriptBlock(this ReadOnlyMemory <char> literal, ScriptContext context, out PageBlockFragment blockFragment)
        {
            literal = literal.ParseVarName(out var blockNameSpan);

            PageBlockFragment statement;
            var blockName  = blockNameSpan.ToString();
            var endBlock   = "{{/" + blockName + "}}";
            var endExprPos = literal.IndexOf("}}");

            if (endExprPos == -1)
            {
                throw new SyntaxErrorException($"Unterminated '{blockName}' block expression, near '{literal.DebugLiteral()}'");
            }

            var blockExpr = literal.Slice(0, endExprPos).Trim();

            literal = literal.Advance(endExprPos + 2);

            if (context.ParseAsVerbatimBlock.Contains(blockName))
            {
                var endBlockPos = literal.IndexOf(endBlock);
                if (endBlockPos == -1)
                {
                    throw new SyntaxErrorException($"Unterminated end block '{endBlock}'");
                }

                var blockBody = literal.Slice(0, endBlockPos);
                literal = literal.Advance(endBlockPos + endBlock.Length).TrimFirstNewLine();

                var body = new List <PageFragment> {
                    new PageStringFragment(blockBody)
                };
                blockFragment = new PageBlockFragment(blockName, blockExpr, body);

                return(literal);
            }
            else
            {
                var parseAsCode = context.ParseAsCodeBlock.Contains(blockName);

                literal = literal.ParseTemplateBody(blockNameSpan, out var bodyText);
                List <PageFragment> bodyFragments  = null;
                JsBlockStatement    bodyStatements = null;

                if (!parseAsCode)
                {
                    bodyFragments = context.ParseTemplate(bodyText);
                }
                else
                {
                    bodyStatements = context.ParseCode(bodyText);
                }

                var elseBlocks = new List <PageElseBlock>();
                while (literal.StartsWith("{{else"))
                {
                    literal = literal.ParseTemplateElseBlock(blockName, out var elseArgument, out var elseBody);

                    var elseBlock = !parseAsCode
                        ? new PageElseBlock(elseArgument, context.ParseTemplate(elseBody))
                        : new PageElseBlock(elseArgument, context.ParseCode(elseBody));
                    elseBlocks.Add(elseBlock);
                }

                literal = literal.Advance(2 + 1 + blockName.Length + 2);

                //remove new line after partial block end tag
                literal = literal.TrimFirstNewLine();

                blockFragment = !parseAsCode
                    ? new PageBlockFragment(blockName, blockExpr, bodyFragments, elseBlocks)
                    : new PageBlockFragment(blockName, blockExpr, bodyStatements, elseBlocks);

                return(literal);
            }
        }
예제 #16
0
        public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            // block.Argument key is unique to exact memory fragment, not string equality
            var invokerCtx = (Tuple <string, StaticMethodInvoker>)scope.Context.CacheMemory.GetOrAdd(block.Argument, key => {
                var literal = block.Argument.Span.ParseVarName(out var name);
                var strName = name.ToString();
                literal     = literal.AdvancePastWhitespace();

                literal  = literal.AdvancePastWhitespace();
                var args = TypeConstants.EmptyStringArray;
                if (!literal.IsEmpty)
                {
                    literal = literal.ParseArgumentsList(out var argIdentifiers);
                    args    = new string[argIdentifiers.Count];
                    for (var i = 0; i < argIdentifiers.Count; i++)
                    {
                        args[i] = argIdentifiers[i].Name;
                    }
                }

                StaticMethodInvoker invoker = null;

                // Allow recursion by initializing lazy Delegate
                MethodInvoker LazyInvoker = (instance, paramValues) => {
                    if (invoker == null)
                    {
                        throw new NotSupportedException($"Uninitialized function '{strName}'");
                    }

                    return(invoker(instance, paramValues));
                };

                invoker = (paramValues) => {
                    scope.PageResult.StackDepth++;
                    try
                    {
                        var page       = new SharpPage(Context, block.Body);
                        var pageResult = new PageResult(page)
                        {
                            Args =
                            {
                                [strName] = LazyInvoker
                            },
                            StackDepth = scope.PageResult.StackDepth
                        };

                        var len = Math.Min(paramValues.Length, args.Length);
                        for (int i = 0; i < len; i++)
                        {
                            var paramValue           = paramValues[i];
                            pageResult.Args[args[i]] = paramValue;
                        }

                        if (pageResult.EvaluateResult(out var returnValue))
                        {
                            return(returnValue);
                        }

                        return(IgnoreResult.Value);
                    }
                    finally
                    {
                        scope.PageResult.StackDepth--;
                    }
                };
예제 #17
0
 public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token) =>
 TypeConstants.EmptyTask;
예제 #18
0
        public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var invokerCtx = (Tuple <string, MethodInvoker>)scope.Context.CacheMemory.GetOrAdd(block.Argument, key => {
                var literal = block.Argument.Span.ParseVarName(out var name);
                var strName = name.ToString();
                literal     = literal.AdvancePastWhitespace();

                literal  = literal.AdvancePastWhitespace();
                var args = TypeConstants.EmptyStringList;
                if (!literal.IsEmpty)
                {
                    literal = literal.ParseArgumentsList(out var argIdentifiers);
                    args    = argIdentifiers.Map(x => x.Name);
                }

                var strFragment = (PageStringFragment)block.Body[0];

                var script       = ScriptPreprocessors.TransformStatementBody(strFragment.ValueString);
                var parsedScript = scope.Context.OneTimePage(script);

                MethodInvoker invoker = null;

                // Allow recursion by initializing lazy Delegate
                object LazyInvoker(object instance, object[] paramValues)
                {
                    if (invoker == null)
                    {
                        throw new NotSupportedException($"Uninitialized function '{strName}'");
                    }

                    return(invoker(instance, paramValues));
                }

                invoker = (instance, paramValues) => {
                    scope.PageResult.StackDepth++;
                    try
                    {
                        var pageResult = new PageResult(parsedScript)
                        {
                            Args =
                            {
                                [strName] = (MethodInvoker)LazyInvoker
                            },
                            StackDepth = scope.PageResult.StackDepth
                        };

                        var len = Math.Min(paramValues.Length, args.Count);
                        for (int i = 0; i < len; i++)
                        {
                            var paramValue           = paramValues[i];
                            pageResult.Args[args[i]] = paramValue;
                        }

                        var discard = ScriptContextUtils.GetPageResultOutput(pageResult);
                        if (pageResult.ReturnValue == null)
                        {
                            throw new NotSupportedException(ScriptContextUtils.ErrorNoReturn);
                        }

                        return(pageResult.ReturnValue.Result);
                    }
                    finally
                    {
                        scope.PageResult.StackDepth--;
                    }
                };

                return(Tuple.Create(strName, invoker));
            });

            scope.PageResult.Args[invokerCtx.Item1] = invokerCtx.Item2;

            return(TypeConstants.EmptyTask);
        }
예제 #19
0
 protected virtual async Task WriteBodyAsync(ScriptScopeContext scope, PageBlockFragment fragment, CancellationToken token)
 {
     await WriteAsync(scope, fragment.Body, GetCallTrace(fragment), token);
 }
예제 #20
0
 public abstract Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token);
예제 #21
0
 protected virtual string GetCallTrace(PageBlockFragment fragment) => "Block: " + Name +
 (fragment.Argument.IsNullOrEmpty() ? "" : " (" + fragment.Argument + ")");
예제 #22
0
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var         htmlAttrs          = block.Argument.GetJsExpressionAndEvaluate(scope) as Dictionary <string, object>;
            var         hasEach            = false;
            IEnumerable each               = null;
            var         binding            = "it";
            var         hasExplicitBinding = false;

            JsToken where = null;

            if (htmlAttrs != null)
            {
                if (htmlAttrs.TryGetValue("if", out var oIf))
                {
                    if (Script.DefaultScripts.isFalsy(oIf))
                    {
                        return;
                    }
                    htmlAttrs.Remove("if");
                }

                if (htmlAttrs.TryGetValue(nameof(where), out var oWhere))
                {
                    if (!(oWhere is string whereExpr))
                    {
                        throw new NotSupportedException($"'where' should be a string expression but instead found '{oWhere.GetType().Name}'");
                    }

                    where = whereExpr.GetCachedJsExpression(scope);
                    htmlAttrs.Remove(nameof(where));
                }

                if (htmlAttrs.TryGetValue(nameof(each), out var oEach))
                {
                    hasEach = true;
                    htmlAttrs.Remove(nameof(each));
                }
                each = oEach as IEnumerable;

                if (htmlAttrs.TryGetValue("it", out var oIt) && oIt is string it)
                {
                    binding            = it;
                    hasExplicitBinding = true;
                    htmlAttrs.Remove("it");
                }

                if (htmlAttrs.TryGetValue("class", out var oClass))
                {
                    var cls = scope.Context.HtmlMethods.htmlClassList(oClass);
                    if (string.IsNullOrEmpty(cls))
                    {
                        htmlAttrs.Remove("class");
                    }
                    else
                    {
                        htmlAttrs["class"] = cls;
                    }
                }
            }

            var attrString = scope.Context.HtmlMethods.htmlAttrsList(htmlAttrs);

            if (HtmlScripts.VoidElements.Contains(Tag)) //e.g. img, input, br, etc
            {
                await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", token);
            }
            else
            {
                if (hasEach)
                {
                    var hasElements = each != null && each.GetEnumerator().MoveNext();
                    if (hasElements)
                    {
                        await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", token);

                        var index      = 0;
                        var whereIndex = 0;
                        foreach (var element in each)
                        {
                            // Add all properties into scope if called without explicit in argument
                            var scopeArgs = !hasExplicitBinding && CanExportScopeArgs(element)
                                ? element.ToObjectDictionary()
                                : new Dictionary <string, object>();

                            scopeArgs[binding]       = element;
                            scopeArgs[nameof(index)] = AssertWithinMaxQuota(whereIndex++);
                            var itemScope = scope.ScopeWithParams(scopeArgs);

                            if (where != null)
                            {
                                var result = where.EvaluateToBool(itemScope);
                                if (!result)
                                {
                                    continue;
                                }
                            }

                            itemScope.ScopedParams[nameof(index)] = AssertWithinMaxQuota(index++);

                            await WriteBodyAsync(itemScope, block, token);
                        }

                        await scope.OutputStream.WriteAsync($"</{Tag}>{Suffix}", token);
                    }
                    else
                    {
                        await WriteElseAsync(scope, block.ElseBlocks, token);
                    }
                }
                else
                {
                    await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", token);
                    await WriteBodyAsync(scope, block, token);

                    await scope.OutputStream.WriteAsync($"</{Tag}>{Suffix}", token);
                }
            }
        }
예제 #23
0
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            if (block.Argument.IsNullOrEmpty())
            {
                throw new NotSupportedException("'each' block requires the collection to iterate");
            }

            var cache = (EachArg)scope.Context.Cache.GetOrAdd(block.ArgumentString, _ => ParseArgument(scope, block));

            var collection = cache.Source.Evaluate(scope, out var syncResult, out var asyncResult)
                ? (IEnumerable)syncResult
                : (IEnumerable)(await asyncResult.ConfigAwait());

            var index = 0;

            if (collection != null)
            {
                if (cache.Where != null || cache.OrderBy != null || cache.OrderByDescending != null ||
                    cache.Skip != null || cache.Take != null)
                {
                    var filteredResults = new List <Dictionary <string, object> >();
                    foreach (var element in collection)
                    {
                        // Add all properties into scope if called without explicit in argument
                        var scopeArgs = !cache.HasExplicitBinding && CanExportScopeArgs(element)
                            ? element.ToObjectDictionary()
                            : new Dictionary <string, object>();

                        scopeArgs[cache.Binding] = element;
                        scopeArgs[nameof(index)] = AssertWithinMaxQuota(index++);
                        var itemScope = scope.ScopeWithParams(scopeArgs);

                        if (cache.Where != null)
                        {
                            var result = await cache.Where.EvaluateToBoolAsync(itemScope).ConfigAwait();

                            if (!result)
                            {
                                continue;
                            }
                        }

                        filteredResults.Add(scopeArgs);
                    }

                    IEnumerable <Dictionary <string, object> > selectedResults = filteredResults;

                    var comparer = (IComparer <object>)Comparer <object> .Default;
                    if (cache.OrderBy != null)
                    {
                        var i = 0;
                        selectedResults = selectedResults.OrderBy(scopeArgs =>
                        {
                            scopeArgs[nameof(index)] = i++;
                            return(cache.OrderBy.Evaluate(scope.ScopeWithParams(scopeArgs)));
                        }, comparer);
                    }
                    else if (cache.OrderByDescending != null)
                    {
                        var i = 0;
                        selectedResults = selectedResults.OrderByDescending(scopeArgs =>
                        {
                            scopeArgs[nameof(index)] = i++;
                            return(cache.OrderByDescending.Evaluate(scope.ScopeWithParams(scopeArgs)));
                        }, comparer);
                    }

                    if (cache.Skip != null)
                    {
                        var skip = cache.Skip.Evaluate(scope).ConvertTo <int>();
                        selectedResults = selectedResults.Skip(skip);
                    }

                    if (cache.Take != null)
                    {
                        var take = cache.Take.Evaluate(scope).ConvertTo <int>();
                        selectedResults = selectedResults.Take(take);
                    }

                    index = 0;
                    foreach (var scopeArgs in selectedResults)
                    {
                        var itemScope = scope.ScopeWithParams(scopeArgs);
                        itemScope.ScopedParams[nameof(index)] = index++;
                        await WriteBodyAsync(itemScope, block, token).ConfigAwait();
                    }
                }
                else
                {
                    foreach (var element in collection)
                    {
                        // Add all properties into scope if called without explicit in argument
                        var scopeArgs = !cache.HasExplicitBinding && CanExportScopeArgs(element)
                            ? element.ToObjectDictionary()
                            : new Dictionary <string, object>();

                        scopeArgs[cache.Binding] = element;
                        scopeArgs[nameof(index)] = AssertWithinMaxQuota(index++);
                        var itemScope = scope.ScopeWithParams(scopeArgs);

                        await WriteBodyAsync(itemScope, block, token).ConfigAwait();
                    }
                }
            }

            if (index == 0)
            {
                await WriteElseAsync(scope, block.ElseBlocks, token).ConfigAwait();
            }
        }