protected override MarkdownNode VisitEmoji(EmojiNode emoji) { var emojiImageUrl = Emoji.GetImageUrl(emoji.Id, emoji.Name, emoji.IsAnimated); var jumboClass = _isJumbo ? "emoji--large" : ""; _buffer .Append($"<img class=\"emoji {jumboClass}\" alt=\"{emoji.Name}\" title=\"{emoji.Name}\" src=\"{emojiImageUrl}\">"); return(base.VisitEmoji(emoji)); }
private static string FormatMarkdownNode(RenderContext context, Node node, bool isJumbo) { // Text node if (node is TextNode textNode) { // Return HTML-encoded text return(HtmlEncode(textNode.Text)); } // Formatted node if (node is FormattedNode formattedNode) { // Recursively get inner html var innerHtml = FormatMarkdownNodes(context, formattedNode.Children, false); // Bold if (formattedNode.Formatting == TextFormatting.Bold) { return($"<strong>{innerHtml}</strong>"); } // Italic if (formattedNode.Formatting == TextFormatting.Italic) { return($"<em>{innerHtml}</em>"); } // Underline if (formattedNode.Formatting == TextFormatting.Underline) { return($"<u>{innerHtml}</u>"); } // Strikethrough if (formattedNode.Formatting == TextFormatting.Strikethrough) { return($"<s>{innerHtml}</s>"); } // Spoiler if (formattedNode.Formatting == TextFormatting.Spoiler) { return($"<span class=\"spoiler spoiler--hidden\" onclick=\"showSpoiler(event, this)\"><span class=\"spoiler-text\">{innerHtml}</span></span>"); } // Quote if (formattedNode.Formatting == TextFormatting.Quote) { return($"<div class=\"quote\">{innerHtml}</div>"); } } // Inline code block node if (node is InlineCodeBlockNode inlineCodeBlockNode) { return($"<span class=\"pre pre--inline\">{HtmlEncode(inlineCodeBlockNode.Code)}</span>"); } // Multi-line code block node if (node is MultiLineCodeBlockNode multilineCodeBlockNode) { // Set CSS class for syntax highlighting var highlightCssClass = !string.IsNullOrWhiteSpace(multilineCodeBlockNode.Language) ? $"language-{multilineCodeBlockNode.Language}" : "nohighlight"; return($"<div class=\"pre pre--multiline {highlightCssClass}\">{HtmlEncode(multilineCodeBlockNode.Code)}</div>"); } // Mention node if (node is MentionNode mentionNode) { // Meta mention node if (mentionNode.Type == MentionType.Meta) { return($"<span class=\"mention\">@{HtmlEncode(mentionNode.Id)}</span>"); } // User mention node if (mentionNode.Type == MentionType.User) { var user = context.MentionableUsers.FirstOrDefault(u => u.Id == mentionNode.Id) ?? User.CreateUnknownUser(mentionNode.Id); var nick = Guild.GetUserNick(context.Guild, user); return($"<span class=\"mention\" title=\"{HtmlEncode(user.FullName)}\">@{HtmlEncode(nick)}</span>"); } // Channel mention node if (mentionNode.Type == MentionType.Channel) { var channel = context.MentionableChannels.FirstOrDefault(c => c.Id == mentionNode.Id) ?? Channel.CreateDeletedChannel(mentionNode.Id); return($"<span class=\"mention\">#{HtmlEncode(channel.Name)}</span>"); } // Role mention node if (mentionNode.Type == MentionType.Role) { var role = context.MentionableRoles.FirstOrDefault(r => r.Id == mentionNode.Id) ?? Role.CreateDeletedRole(mentionNode.Id); var style = ""; if (role.Color != Color.Black) { style = $"style=\"color: {role.ColorAsHex}; background-color: rgba({role.ColorAsRgb}, 0.1); font-weight: 400;\""; } return($"<span class=\"mention\" {style}>@{HtmlEncode(role.Name)}</span>"); } } // Emoji node if (node is EmojiNode emojiNode) { // Get emoji image URL var emojiImageUrl = Emoji.GetImageUrl(emojiNode.Id, emojiNode.Name, emojiNode.IsAnimated); // Make emoji large if it's jumbo var jumboableCssClass = isJumbo ? "emoji--large" : null; return ($"<img class=\"emoji {jumboableCssClass}\" alt=\"{emojiNode.Name}\" title=\"{emojiNode.Name}\" src=\"{emojiImageUrl}\" />"); } // Link node if (node is LinkNode linkNode) { // Extract message ID if the link points to a Discord message var linkedMessageId = Regex.Match(linkNode.Url, "^https?://discordapp.com/channels/.*?/(\\d+)/?$").Groups[1].Value; return(string.IsNullOrWhiteSpace(linkedMessageId) ? $"<a href=\"{Uri.EscapeUriString(linkNode.Url)}\">{HtmlEncode(linkNode.Title)}</a>" : $"<a href=\"{Uri.EscapeUriString(linkNode.Url)}\" onclick=\"scrollToMessage(event, '{linkedMessageId}')\">{HtmlEncode(linkNode.Title)}</a>"); } // Throw on unexpected nodes throw new InvalidOperationException($"Unexpected node [{node.GetType()}]."); }
private string FormatMarkdown(Node node, bool isJumbo) { // Text node if (node is TextNode textNode) { // Return HTML-encoded text return(HtmlEncode(textNode.Text)); } // Formatted node if (node is FormattedNode formattedNode) { // Recursively get inner html var innerHtml = FormatMarkdown(formattedNode.Children, false); // Bold if (formattedNode.Formatting == TextFormatting.Bold) { return($"<strong>{innerHtml}</strong>"); } // Italic if (formattedNode.Formatting == TextFormatting.Italic) { return($"<em>{innerHtml}</em>"); } // Underline if (formattedNode.Formatting == TextFormatting.Underline) { return($"<u>{innerHtml}</u>"); } // Strikethrough if (formattedNode.Formatting == TextFormatting.Strikethrough) { return($"<s>{innerHtml}</s>"); } // Spoiler if (formattedNode.Formatting == TextFormatting.Spoiler) { return($"<span class=\"spoiler\">{innerHtml}</span>"); } // Quote if (formattedNode.Formatting == TextFormatting.Quote) { return($"<div class=\"quote\">{innerHtml}</div>"); } } // Inline code block node if (node is InlineCodeBlockNode inlineCodeBlockNode) { return($"<span class=\"pre pre--inline\">{HtmlEncode(inlineCodeBlockNode.Code)}</span>"); } // Multi-line code block node if (node is MultiLineCodeBlockNode multilineCodeBlockNode) { // Set CSS class for syntax highlighting var highlightCssClass = !multilineCodeBlockNode.Language.IsNullOrWhiteSpace() ? $"language-{multilineCodeBlockNode.Language}" : "nohighlight"; return($"<div class=\"pre pre--multiline {highlightCssClass}\">{HtmlEncode(multilineCodeBlockNode.Code)}</div>"); } // Mention node if (node is MentionNode mentionNode) { // Meta mention node if (mentionNode.Type == MentionType.Meta) { return($"<span class=\"mention\">@{HtmlEncode(mentionNode.Id)}</span>"); } // User mention node if (mentionNode.Type == MentionType.User) { var user = _chatLog.Mentionables.GetUser(mentionNode.Id); return($"<span class=\"mention\" title=\"{HtmlEncode(user.FullName)}\">@{HtmlEncode(user.Name)}</span>"); } // Channel mention node if (mentionNode.Type == MentionType.Channel) { var channel = _chatLog.Mentionables.GetChannel(mentionNode.Id); return($"<span class=\"mention\">#{HtmlEncode(channel.Name)}</span>"); } // Role mention node if (mentionNode.Type == MentionType.Role) { var role = _chatLog.Mentionables.GetRole(mentionNode.Id); return($"<span class=\"mention\">@{HtmlEncode(role.Name)}</span>"); } } // Emoji node if (node is EmojiNode emojiNode) { // Get emoji image URL var emojiImageUrl = Emoji.GetImageUrl(emojiNode.Id, emojiNode.Name, emojiNode.IsAnimated); // Make emoji large if it's jumbo var jumboableCssClass = isJumbo ? "emoji--large" : null; return($"<img class=\"emoji {jumboableCssClass}\" alt=\"{emojiNode.Name}\" title=\"{emojiNode.Name}\" src=\"{emojiImageUrl}\" />"); } // Link node if (node is LinkNode linkNode) { // Extract message ID if the link points to a Discord message var linkedMessageId = Regex.Match(linkNode.Url, "^https?://discordapp.com/channels/.*?/(\\d+)/?$").Groups[1].Value; return(linkedMessageId.IsNullOrWhiteSpace() ? $"<a href=\"{Uri.EscapeUriString(linkNode.Url)}\">{HtmlEncode(linkNode.Title)}</a>" : $"<a href=\"{Uri.EscapeUriString(linkNode.Url)}\" onclick=\"scrollToMessage(event, '{linkedMessageId}')\">{HtmlEncode(linkNode.Title)}</a>"); } // Throw on unexpected nodes throw new InvalidOperationException($"Unexpected node: [{node.GetType()}]."); }
private string FormatMarkdown(Node node, bool isJumbo) { // Text node if (node is TextNode textNode) { // Return HTML-encoded text return(HtmlEncode(textNode.Text)); } // Formatted node if (node is FormattedNode formattedNode) { // Recursively get inner html var innerHtml = FormatMarkdown(formattedNode.Children, false); // Bold if (formattedNode.Formatting == TextFormatting.Bold) { return($"<strong>{innerHtml}</strong>"); } // Italic if (formattedNode.Formatting == TextFormatting.Italic) { return($"<em>{innerHtml}</em>"); } // Underline if (formattedNode.Formatting == TextFormatting.Underline) { return($"<u>{innerHtml}</u>"); } // Strikethrough if (formattedNode.Formatting == TextFormatting.Strikethrough) { return($"<s>{innerHtml}</s>"); } // Spoiler if (formattedNode.Formatting == TextFormatting.Spoiler) { return($"<span class=\"spoiler\">{innerHtml}</span>"); } } // Inline code block node if (node is InlineCodeBlockNode inlineCodeBlockNode) { return($"<span class=\"pre pre--inline\">{HtmlEncode(inlineCodeBlockNode.Code)}</span>"); } // Multi-line code block node if (node is MultilineCodeBlockNode multilineCodeBlockNode) { // Set language class for syntax highlighting var languageCssClass = !multilineCodeBlockNode.Language.IsNullOrWhiteSpace() ? "language-" + multilineCodeBlockNode.Language : null; return($"<div class=\"pre pre--multiline {languageCssClass}\">{HtmlEncode(multilineCodeBlockNode.Code)}</div>"); } // Mention node if (node is MentionNode mentionNode) { // Meta mention node if (mentionNode.Type == MentionType.Meta) { return($"<span class=\"mention\">@{HtmlEncode(mentionNode.Id)}</span>"); } // User mention node if (mentionNode.Type == MentionType.User) { var user = _chatLog.Mentionables.GetUser(mentionNode.Id); return($"<span class=\"mention\" title=\"{HtmlEncode(user.FullName)}\">@{HtmlEncode(user.Name)}</span>"); } // Channel mention node if (mentionNode.Type == MentionType.Channel) { var channel = _chatLog.Mentionables.GetChannel(mentionNode.Id); return($"<span class=\"mention\">#{HtmlEncode(channel.Name)}</span>"); } // Role mention node if (mentionNode.Type == MentionType.Role) { var role = _chatLog.Mentionables.GetRole(mentionNode.Id); return($"<span class=\"mention\">@{HtmlEncode(role.Name)}</span>"); } } // Emoji node if (node is EmojiNode emojiNode) { // Get emoji image URL var emojiImageUrl = Emoji.GetImageUrl(emojiNode.Id, emojiNode.Name, emojiNode.IsAnimated); // Make emoji large if it's jumbo var jumboableCssClass = isJumbo ? "emoji--large" : null; return($"<img class=\"emoji {jumboableCssClass}\" alt=\"{emojiNode.Name}\" title=\"{emojiNode.Name}\" src=\"{emojiImageUrl}\" />"); } // Link node if (node is LinkNode linkNode) { return($"<a href=\"{Uri.EscapeUriString(linkNode.Url)}\">{HtmlEncode(linkNode.Title)}</a>"); } // All other nodes - simply return source return(node.Source); }