string RenderPartial(Token token, MustacheContext ctx, string indent) { string name = token.Name; if (Partials == null) { return(string.Empty); } var partial = string.Empty; if (!Partials.TryGetValue(name, out partial)) { return(string.Empty); } var key = "# partial #" + name + "#" + indent; if (!Cache.ContainsKey(key)) { if (string.IsNullOrEmpty(indent)) { Cache[key] = new MustacheParser().Parse(partial, Delimiter.Default()); } else { var replaced = Regex.Replace(partial, @"^(.+)$", indent + "$1", RegexOptions.Multiline); Cache[key] = new MustacheParser().Parse(replaced, Delimiter.Default()); } } return(RenderTokens(ctx, Cache[key])); }
string RenderName(Token token, MustacheContext ctx, bool escape) { var value = ctx.Lookup(token.Name); if (value == null && StrictMode) { throw new MustacheException(string.Format("lookup failed name:'{0}' around:\n...{1}...\n", token.Name, token.SurroundingTemplate)); } if (value == null) { return(string.Empty); } if (value.IsLambda()) { var template = value.InvokeNameLambda() as string; var tokens = new MustacheParser().Parse(template, Delimiter.Default()); value = RenderTokens(ctx, tokens); // Recheck for null if (value == null) { return(string.Empty); } } var s = value.ToString(); if (s == "{ }") { return(string.Empty); } if (escape) { return(System.Web.HttpUtility.HtmlEncode(s)); } return(s); }
/// <summary> /// Parses and applies given template and returns rendered string. /// </summary> /// <param name="template">Mustache template string.</param> /// <param name="data">Data object.</param> /// <param name="partials">Partial templates.</param> /// <returns>Rendered string.</returns> public string Render(string template, object data, Dictionary <string, string> partials = null) { if (template == null) { throw new ArgumentNullException("template"); } if (partials != null) { Partials = partials; } if (string.IsNullOrEmpty(template)) { return(string.Empty); } if (!Cache.ContainsKey(template)) { Cache[template] = new MustacheParser().Parse(template, Delimiter.Default()); } return(RenderTokens(new MustacheContext(data, null), Cache[template])); }
/// <summary> /// Parses given template and returns the corresponding tokens. /// </summary> /// <param name="template">The mustache template string</param> /// <param name="delimiter">The delimiter.</param> /// <returns>Corresponding tokens.</returns> public List <Token> Parse(string template, Delimiter delimiter) { var scanner = new MustacheScanner(template); var tokens = new List <Token>(); var start = scanner.Pos; var lastBol = 0; Action <int> pushText = (int end) => { if (start < end && end <= template.Length) { tokens.Add(new Token { Template = template, Type = TokenType.Text, StartIndex = start, TextLength = end - start, IsBol = start == lastBol, CurrentDelimiter = delimiter, }); } }; for (var ok = true; ok;) { if (scanner.Pos == 0 || scanner.Peek(-1) == '\n') { lastBol = scanner.Pos; } if (scanner.Peek() == '\n') { pushText(scanner.Pos + 1); start = scanner.Pos + 1; ok = scanner.Seek(1); } else if (scanner.StartsWith(delimiter.Open)) { if (start < scanner.Pos) { pushText(scanner.Pos); start = scanner.Pos; } ok = scanner.Seek(delimiter.Open.Length); // discard begin tag var tokenChar = scanner.Peek(); var tokenType = TokenInfo(tokenChar); var value = string.Empty; if (tokenType != TokenType.Variable) { scanner.Seek(1); // discard token type character } if (tokenType == TokenType.DelimiterChange) { value = scanner.ReadUntilJustBefore("=").Trim(); scanner.Seek(1); // discard '=' scanner.SeekUntilJustBefore(delimiter.Close); } else if (tokenType == TokenType.UnescapedVariable && tokenChar == '{') { value = scanner.ReadUntilJustBefore("}" + delimiter.Close).Trim(); scanner.Seek(1); // discard '}' } else { value = scanner.ReadUntilJustBefore(delimiter.Close).Trim(); } if (!scanner.StartsWith(delimiter.Close)) { throw new MustacheException("Unclosed tag at " + scanner.Pos); } if (tokenType != TokenType.Comment && tokenType != TokenType.DelimiterChange) { if (string.IsNullOrEmpty(value)) { throw new MustacheException("Empty token name at " + scanner.Pos); } foreach (var c in value) { if (!(char.IsLetterOrDigit(c) || c == '.' || c == '_')) { throw new MustacheException("Invalid token name '" + value + "' at " + scanner.Pos); } } if (ProhibitedWords.Contains(value)) { throw new MustacheException("Invalid token name '" + value + "' at " + scanner.Pos); } } var token = new Token { Template = template, Type = tokenType, Name = value, StartIndex = start, IsBol = start == lastBol, CurrentDelimiter = delimiter, }; start = scanner.Pos + delimiter.Close.Length; ok = scanner.Seek(delimiter.Close.Length); // discard end tag token.SectionStartIndex = start; tokens.Add(token); if (tokenType == TokenType.DelimiterChange) { if (string.IsNullOrEmpty(value)) { throw new MustacheException("Empty delimiter at " + scanner.Pos); } var sp = value.Split(null as string[], StringSplitOptions.RemoveEmptyEntries); if (sp.Length != 2) { throw new MustacheException("Invalid delimiter format at " + scanner.Pos); } delimiter = new Delimiter { Open = sp[0], Close = sp[1] }; } } else { ok = scanner.Seek(1); } } pushText(scanner.Pos + 1); SquashTokens(ref tokens); return(NestTokens(tokens)); }