private void Capture(ScriptScopeContext scope, PageBlockFragment block, PageStringFragment strFragment)
        {
            var  literal  = block.Argument.Span.AdvancePastWhitespace();
            bool appendTo = false;

            if (literal.StartsWith("appendTo "))
            {
                appendTo = true;
                literal  = literal.Advance("appendTo ".Length);
            }

            var minified = GetMinifiedOutputCache(strFragment.Value);

            literal = literal.ParseVarName(out var name);
            var nameString = name.Value();

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

            scope.PageResult.Args[nameString] = minified.ToString();
        }
Exemple #2
0
    /// <summary>
    /// Writes the asynchronous.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>Task.</returns>
    /// <exception cref="System.NotSupportedException">'partial' block is missing name of partial</exception>
    /// <exception cref="System.NotSupportedException">Any 'partial' argument must be an Object Dictionary</exception>
    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);
    }
Exemple #3
0
            public override async Task WriteAsync(
                ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
            {
                var collection = (IEnumerable)block.Argument.GetJsExpressionAndEvaluate(scope,
                                                                                        ifNone: () => throw new NotSupportedException("'each' block does not have a valid expression"));

                var index = 0;

                if (collection != null)
                {
                    foreach (var element in collection)
                    {
                        var scopeArgs = element.ToObjectDictionary();
                        scopeArgs["it"]          = element;
                        scopeArgs[nameof(index)] = index++;

                        var itemScope = scope.ScopeWithParams(scopeArgs);
                        await WriteBodyAsync(itemScope, block, token);
                    }
                }

                if (index == 0)
                {
                    await WriteElseAsync(scope, block.ElseBlocks, token);
                }
            }
Exemple #4
0
    //Extract usages of Span outside of async method
    /// <summary>
    /// Parses the specified scope.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <returns>Tuple.</returns>
    /// <exception cref="System.NotSupportedException">'capture' block is missing name of variable to assign captured output to</exception>
    /// <exception cref="System.NotSupportedException">'capture' block is missing name of variable to assign captured output to</exception>
    /// <exception cref="System.NotSupportedException">Any 'capture' argument must be an Object Dictionary</exception>
    private Tuple Parse(ScriptScopeContext scope, PageBlockFragment block)
    {
        if (block.Argument.IsNullOrWhiteSpace())
        {
            throw new NotSupportedException("'capture' block is missing name of variable to assign captured output to");
        }

        var  literal  = block.Argument.AdvancePastWhitespace();
        bool appendTo = false;

        if (literal.StartsWith("appendTo "))
        {
            appendTo = true;
            literal  = literal.Advance("appendTo ".Length);
        }

        literal = literal.ParseVarName(out var name);
        if (name.IsNullOrEmpty())
        {
            throw new NotSupportedException("'capture' block is missing name of variable to assign captured output to");
        }

        literal = literal.AdvancePastWhitespace();

        var argValue = literal.GetJsExpressionAndEvaluate(scope);

        var scopeArgs = argValue as Dictionary <string, object>;

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

        return(new Tuple(name.ToString(), scopeArgs, appendTo));
    }
Exemple #5
0
 protected bool Equals(PageBlockFragment other)
 {
     return(Name == other.Name &&
            Argument.Span.SequenceEqual(other.Argument.Span) &&
            Body.EquivalentTo(other.Body) &&
            ElseBlocks.EquivalentTo(other.ElseBlocks));
 }
Exemple #6
0
            public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
            {
                await scope.OutputStream.WriteAsync("<b>", cancel);

                await WriteBodyAsync(scope, fragment, cancel);

                await scope.OutputStream.WriteAsync("</b>", cancel);
            }
Exemple #7
0
            public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
            {
                await scope.OutputStream.WriteAsync("<b>", token);

                await WriteBodyAsync(scope, block, token);

                await scope.OutputStream.WriteAsync("</b>", token);
            }
Exemple #8
0
        private static void Capture(TemplateScopeContext scope, PageBlockFragment fragment, PageStringFragment strFragment)
        {
            var literal = fragment.Argument.AdvancePastWhitespace();

            literal = literal.ParseVarName(out var name);
            var nameString = name.ToString();

            scope.PageResult.Args[nameString] = MarkdownConfig.Transform(strFragment.ValueString).ToRawString();
        }
Exemple #9
0
    /// <summary>
    /// Writes the asynchronous.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="ct">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>Task.</returns>
    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);
    }
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var strFragment = (PageStringFragment)block.Body[0];

            if (!block.Argument.IsNullOrWhiteSpace())
            {
                Capture(scope, block, strFragment);
            }
            else
            {
                await scope.OutputStream.WriteAsync(MarkdownConfig.Transform(strFragment.ValueString), token);
            }
        }
Exemple #11
0
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            var strFragment = (PageStringFragment)fragment.Body[0];

            if (!fragment.Argument.IsNullOrWhiteSpace())
            {
                Capture(scope, fragment, strFragment);
            }
            else
            {
                await scope.OutputStream.WriteAsync(MarkdownConfig.Transform(strFragment.ValueString), cancel);
            }
        }
Exemple #12
0
    /// <summary>
    /// Write as an asynchronous operation.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>A Task representing the asynchronous operation.</returns>
    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")).ConfigAwait();

        if (result)
        {
            await WriteBodyAsync(scope, block, token).ConfigAwait();
        }
        else
        {
            await WriteElseAsync(scope, block.ElseBlocks, token).ConfigAwait();
        }
    }
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var strFragment = (PageStringFragment)block.Body[0];

            if (!block.Argument.IsNullOrWhiteSpace())
            {
                Capture(scope, block, strFragment);
            }
            else
            {
                var minified = GetMinifiedOutputCache(strFragment.Value);
                await scope.OutputStream.WriteAsync(minified.Span, token);
            }
        }
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            var strFragment = (PageStringFragment)fragment.Body[0];

            if (!fragment.Argument.IsNullOrWhiteSpace())
            {
                var literal = fragment.Argument.AdvancePastWhitespace();

                literal = literal.ParseVarName(out var name);
                var nameString = name.Value;
                scope.PageResult.Args[nameString] = MarkdownConfig.Transform(strFragment.Value.Value).ToRawString();
            }
            else
            {
                await scope.OutputStream.WriteAsync(MarkdownConfig.Transform(strFragment.Value.Value), cancel);
            }
        }
        public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            if (block.Argument.IsEmpty)
                throw new NotSupportedException($"Name required in {Name} script block");
            
            var argumentStr = block.Argument.ToString();
            var args = argumentStr.SplitOnFirst(' ');
            var name = args[0].Trim();

            using (var ms = MemoryStreamFactory.GetStream())
            {
                var useScope = scope.ScopeWithStream(ms);
                await WriteBodyAsync(useScope, block, token);

                var capturedSvg = ms.ReadToEnd();                
                Svg.AddImage(capturedSvg, name, args.Length == 2 ? args[1].Trim() : null);
            }
        }
Exemple #16
0
    /// <summary>
    /// Write as an asynchronous operation.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>A Task representing the asynchronous operation.</returns>
    public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var argValue = await block.Argument.GetJsExpressionAndEvaluateAsync(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).ConfigAwait();

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

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

        var pageResult = new PageResult(context.OneTimePage(renderedBody))
        {
            Args = args,
        };
        await pageResult.WriteToAsync(scope.OutputStream, token).ConfigAwait();
    }
Exemple #17
0
    /// <summary>
    /// Write as an asynchronous operation.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>A Task representing the asynchronous operation.</returns>
    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;
    }
Exemple #18
0
    /// <summary>
    /// Writes the asynchronous.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="ct">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>Task.</returns>
    /// <exception cref="System.NotSupportedException">#keyvalues expected string delimiter but was {token.DebugToken()}</exception>
    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 not JsLiteral litToken)
            {
                throw new NotSupportedException($"#keyvalues expected string delimiter but was {token.DebugToken()}");
            }
            delimiter = litToken.Value.ToString();
        }

        var strFragment = (PageStringFragment)block.Body[0];
        var strDict     = Context.DefaultMethods.parseKeyValues(strFragment.ValueString, delimiter);

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

        return(TypeConstants.EmptyTask);
    }
Exemple #19
0
    /// <summary>
    /// Write as an asynchronous operation.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="ct">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>A Task representing the asynchronous operation.</returns>
    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")).ConfigAwait();

        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);
        }
    }
        public static List <PageFragment> ParseTemplatePage(ReadOnlyMemory <char> text)
        {
            var to = new List <PageFragment>();

            if (text.IsNullOrWhiteSpace())
            {
                return(to);
            }

            int pos;
            var lastPos = 0;

            while ((pos = text.IndexOf("{{", lastPos)) != -1)
            {
                var block = text.Slice(lastPos, pos - lastPos);
                if (!block.IsNullOrEmpty())
                {
                    to.Add(new PageStringFragment(block));
                }

                var varStartPos = pos + 2;

                var firstChar = text.Span[varStartPos];
                if (firstChar == '*') //comment
                {
                    lastPos = text.IndexOf("*}}", varStartPos) + 3;
                }
                else if (firstChar == '#') //block statement
                {
                    var literal = text.Slice(varStartPos + 1);
                    literal = literal.ParseVarName(out var blockNameSpan);

                    var blockName  = blockNameSpan.ToString();
                    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 (!ScriptConfig.DontEvaluateBlocksNamed.Contains(blockName))
                    {
                        literal = literal.ParseStatementBody(blockNameSpan, out var body);
                        var elseStatements = new List <PageElseBlock>();

                        while (literal.StartsWith("{{else"))
                        {
                            literal = literal.ParseElseStatement(blockName, out var elseStatement);
                            elseStatements.Add(elseStatement);
                        }

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

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

                        var length       = text.Length - pos - literal.Length;
                        var originalText = text.Slice(pos, length);
                        lastPos = pos + length;

                        var statement = new PageBlockFragment(originalText, blockName, blockExpr, body, elseStatements);
                        to.Add(statement);
                    }
                    else
                    {
                        var endBlock    = "{{/" + blockName + "}}";
                        var endBlockPos = literal.IndexOf(endBlock);
                        if (endBlockPos == -1)
                        {
                            throw new SyntaxErrorException($"Unterminated end block '{endBlock}'");
                        }

                        var endBlockBody = literal.Slice(0, endBlockPos);
                        literal = literal.Advance(endBlockPos + endBlock.Length).TrimFirstNewLine();
                        var body = new List <PageFragment> {
                            new PageStringFragment(endBlockBody)
                        };

                        var length       = text.Length - pos - literal.Length;
                        var originalText = text.Slice(pos, length);
                        lastPos = pos + length;

                        var statement = new PageBlockFragment(originalText, blockName, blockExpr, body);
                        to.Add(statement);
                    }
                }
                else
                {
                    var literal = text.Slice(varStartPos).Span;
                    literal = literal.ParseJsExpression(out var expr, filterExpression: true);

                    var filters = new List <JsCallExpression>();

                    if (!literal.StartsWith("}}"))
                    {
                        literal = literal.AdvancePastWhitespace();
                        if (literal.FirstCharEquals(FilterSep))
                        {
                            literal = literal.Advance(1);

                            while (true)
                            {
                                literal = literal.ParseJsCallExpression(out var filter, filterExpression: true);

                                filters.Add(filter);

                                literal = literal.AdvancePastWhitespace();

                                if (literal.IsNullOrEmpty())
                                {
                                    throw new SyntaxErrorException("Unterminated filter expression");
                                }

                                if (literal.StartsWith("}}"))
                                {
                                    literal = literal.Advance(2);
                                    break;
                                }

                                if (!literal.FirstCharEquals(FilterSep))
                                {
                                    throw new SyntaxErrorException(
                                              $"Expected filter separator '|' but was {literal.DebugFirstChar()}");
                                }

                                literal = literal.Advance(1);
                            }
                        }
                        else
                        {
                            if (!literal.IsNullOrEmpty())
                            {
                                literal = literal.Advance(1);
                            }
                        }
                    }
                    else
                    {
                        literal = literal.Advance(2);
                    }

                    var length       = text.Length - pos - literal.Length;
                    var originalText = text.Slice(pos, length);
                    lastPos = pos + length;

                    var varFragment = new PageVariableFragment(originalText, expr, filters);
                    to.Add(varFragment);

                    var newLineLen = literal.StartsWith("\n")
                        ? 1
                        : literal.StartsWith("\r\n")
                            ? 2
                            : 0;

                    if (newLineLen > 0)
                    {
                        var lastExpr   = varFragment.FilterExpressions?.LastOrDefault();
                        var filterName = lastExpr?.Name ??
                                         varFragment?.InitialExpression?.Name ?? varFragment.Binding;
                        if (filterName != null && ScriptConfig.RemoveNewLineAfterFiltersNamed.Contains(filterName))
                        {
                            lastPos += newLineLen;
                        }
                    }
                }
            }

            if (lastPos != text.Length)
            {
                var lastBlock = lastPos == 0 ? text : text.Slice(lastPos);
                to.Add(new PageStringFragment(lastBlock));
            }

            return(to);
        }
Exemple #21
0
    /// <summary>
    /// Write as an asynchronous operation.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>A Task representing the asynchronous operation.</returns>
    /// <exception cref="System.NotSupportedException">'where' should be a string expression but instead found '{oWhere.GetType().Name}'</exception>
    public override async Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var htmlAttrs = await block.Argument.GetJsExpressionAndEvaluateAsync(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 (DefaultScripts.isFalsy(oIf))
                {
                    return;
                }
                htmlAttrs.Remove("if");
            }

            if (htmlAttrs.TryGetValue(nameof(where), out var oWhere))
            {
                if (oWhere is not 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).ConfigAwait();
        }
        else
        {
            if (hasEach)
            {
                var hasElements = each != null && each.GetEnumerator().MoveNext();
                if (hasElements)
                {
                    await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", token).ConfigAwait();

                    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 = await where.EvaluateToBoolAsync(itemScope);
                            if (!result)
                            {
                                continue;
                            }
                        }

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

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

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

                await scope.OutputStream.WriteAsync($"</{Tag}>{Suffix}", token).ConfigAwait();
            }
        }
    }
Exemple #22
0
 /// <summary>
 /// Writes the asynchronous.
 /// </summary>
 /// <param name="scope">The scope.</param>
 /// <param name="block">The block.</param>
 /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
 /// <returns>Task.</returns>
 public abstract Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token);
Exemple #23
0
 /// <summary>
 /// Gets the call trace.
 /// </summary>
 /// <param name="fragment">The fragment.</param>
 /// <returns>System.String.</returns>
 protected virtual string GetCallTrace(PageBlockFragment fragment) => "Block: " + Name +
 (fragment.Argument.IsNullOrEmpty() ? "" : " (" + fragment.Argument + ")");
Exemple #24
0
    /// <summary>
    /// Writes the asynchronous.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>Task.</returns>
    public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        // block.Argument key is unique to exact memory fragment, not string equality
        // Parse into AST once for all Page Results
        var invokerCtx = (Tuple <string, StaticMethodInvoker>)scope.Context.CacheMemory.GetOrAdd(block.Argument, _ =>
        {
            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.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
            object LazyInvoker(object instance, object[] 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] = (MethodInvoker)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--;
                }
            };
Exemple #25
0
    /// <summary>
    /// Parses the argument.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="fragment">The fragment.</param>
    /// <returns>EachArg.</returns>
    /// <exception cref="System.NotSupportedException">'each' block requires the collection to iterate</exception>
    /// <exception cref="System.NotSupportedException">'each' block expected identifier but was {token.DebugToken()}</exception>
    /// <exception cref="System.NotSupportedException">'each' block requires the collection to iterate</exception>
    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, orderBy, orderByDescending, skip, take;

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

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

        if (hasExplicitBinding)
        {
            if (token is not 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));
    }
Exemple #26
0
    /// <summary>
    /// Write as an asynchronous operation.
    /// </summary>
    /// <param name="scope">The scope.</param>
    /// <param name="block">The block.</param>
    /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>A Task representing the asynchronous operation.</returns>
    /// <exception cref="System.NotSupportedException">'each' block requires the collection to iterate</exception>
    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();
        }
    }
Exemple #27
0
 public JsPageBlockFragmentStatement(PageBlockFragment block) => Block = block;
Exemple #28
0
 /// <summary>
 /// Write body as an asynchronous operation.
 /// </summary>
 /// <param name="scope">The scope.</param>
 /// <param name="fragment">The fragment.</param>
 /// <param name="token">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
 /// <returns>A Task representing the asynchronous operation.</returns>
 protected virtual async Task WriteBodyAsync(ScriptScopeContext scope, PageBlockFragment fragment, CancellationToken token)
 {
     await WriteAsync(scope, fragment.Body, GetCallTrace(fragment), token).ConfigAwait();
 }