|
|
|
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
using System.Threading.Tasks;
|
|
|
using MarkdownDeep;
|
|
|
using Microsoft.AspNet.Mvc.Rendering;
|
|
|
using Microsoft.AspNet.Razor.TagHelpers;
|
|
|
|
|
|
namespace Yavsc.Helpers
|
|
|
{
|
|
|
[HtmlTargetElement("div", Attributes = MarkdownContentAttributeName)]
|
|
|
[HtmlTargetElement("p", Attributes = "ismarkdown")]
|
|
|
[HtmlTargetElement("markdown")]
|
|
|
[OutputElementHint("p")]
|
|
|
public class MarkdownTagHelper : TagHelper
|
|
|
{
|
|
|
private const string MarkdownContentAttributeName = "markdown";
|
|
|
private const string MarkdownMarkAttributeName = "ismarkdown";
|
|
|
|
|
|
[HtmlAttributeName("site")]
|
|
|
public SiteSettings Site { get; set; }
|
|
|
|
|
|
[HtmlAttributeName("base")]
|
|
|
public string Base { get; set; }
|
|
|
|
|
|
[HtmlAttributeName(MarkdownContentAttributeName)]
|
|
|
public string MarkdownContent { get; set; }
|
|
|
|
|
|
static Regex rxExtractLanguage = new Regex("^({{(.+)}}[\r\n])", RegexOptions.Compiled);
|
|
|
private static string FormatCodePrettyPrint(MarkdownDeep.Markdown m, string code)
|
|
|
{
|
|
|
// Try to extract the language from the first line
|
|
|
var match = rxExtractLanguage.Match(code);
|
|
|
string language = null;
|
|
|
|
|
|
if (match.Success)
|
|
|
{
|
|
|
// Save the language
|
|
|
var g = (Group)match.Groups[2];
|
|
|
language = g.ToString();
|
|
|
|
|
|
// Remove the first line
|
|
|
code = code.Substring(match.Groups[1].Length);
|
|
|
}
|
|
|
|
|
|
// If not specified, look for a link definition called "default_syntax" and
|
|
|
// grab the language from its title
|
|
|
if (language == null)
|
|
|
{
|
|
|
var d = m.GetLinkDefinition("default_syntax");
|
|
|
if (d != null)
|
|
|
language = d.title;
|
|
|
}
|
|
|
|
|
|
// Common replacements
|
|
|
if (language == "C#")
|
|
|
language = "csharp";
|
|
|
if (language == "C++")
|
|
|
language = "cpp";
|
|
|
|
|
|
// Wrap code in pre/code tags and add PrettyPrint attributes if necessary
|
|
|
if (string.IsNullOrEmpty(language))
|
|
|
return string.Format("<pre><code>{0}</code></pre>\n", code);
|
|
|
else
|
|
|
return string.Format("<pre class=\"prettyprint lang-{0}\"><code>{1}</code></pre>\n",
|
|
|
language.ToLowerInvariant(), code);
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// Transforms a string of Markdown into HTML.
|
|
|
/// </summary>
|
|
|
/// <param name="helper">HtmlHelper - Not used, but required to make this an extension method.</param>
|
|
|
/// <param name="text">The Markdown that should be transformed.</param>
|
|
|
/// <param name="urlBaseLocation">The url Base Location.</param>
|
|
|
/// <returns>The HTML representation of the supplied Markdown.</returns>
|
|
|
public string Markdown(string text, string urlBaseLocation = "")
|
|
|
{
|
|
|
// Transform the supplied text (Markdown) into HTML.
|
|
|
var markdownTransformer = GetMarkdownTransformer();
|
|
|
markdownTransformer.UrlBaseLocation = urlBaseLocation;
|
|
|
string html = markdownTransformer.Transform(text);
|
|
|
// Wrap the html in an MvcHtmlString otherwise it'll be HtmlEncoded and displayed to the user as HTML :(
|
|
|
return html;
|
|
|
}
|
|
|
|
|
|
internal Markdown GetMarkdownTransformer()
|
|
|
{
|
|
|
var markdownTransformer = new Markdown();
|
|
|
markdownTransformer.ExtraMode = true;
|
|
|
markdownTransformer.NoFollowLinks = true;
|
|
|
markdownTransformer.SafeMode = false;
|
|
|
markdownTransformer.FormatCodeBlock = FormatCodePrettyPrint;
|
|
|
markdownTransformer.ExtractHeadBlocks = true;
|
|
|
markdownTransformer.UserBreaks = true;
|
|
|
return markdownTransformer;
|
|
|
}
|
|
|
|
|
|
public ModelExpression Content { get; set; }
|
|
|
public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
|
|
{
|
|
|
if (output.TagName == "markdown")
|
|
|
{
|
|
|
output.TagName = null;
|
|
|
}
|
|
|
output.Attributes.RemoveAll("markdown");
|
|
|
|
|
|
var content = await GetContent(output);
|
|
|
var markdown = content;
|
|
|
var basePath = Base?.StartsWith("~") ?? false ?
|
|
|
Constants.UserFilesDir +
|
|
|
Base.Substring(1) : Base;
|
|
|
var html = Markdown(markdown, basePath);
|
|
|
output.Content.SetHtmlContent(html ?? "");
|
|
|
}
|
|
|
private async Task<string> GetContent(TagHelperOutput output)
|
|
|
{
|
|
|
if (MarkdownContent != null)
|
|
|
return MarkdownContent;
|
|
|
if (Content != null)
|
|
|
return Content.Model?.ToString();
|
|
|
return (await output.GetChildContentAsync(false)).GetContent();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|