Compare commits
No commits in common. "720b6364394bb896e496755e474b07aeaf82a960" and "ba09fa22df9c98635a6e0a5c8d46bb18b104f5c6" have entirely different histories.
720b636439
...
ba09fa22df
@ -1,14 +0,0 @@
|
|||||||
namespace OliverBooth.Data.Web;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
internal sealed class CodeSnippet : ICodeSnippet
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Content { get; } = string.Empty;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public int Id { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Language { get; } = string.Empty;
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
||||||
|
|
||||||
namespace OliverBooth.Data.Web.Configuration;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the configuration for the <see cref="Book" /> entity.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class CodeSnippetConfiguration : IEntityTypeConfiguration<CodeSnippet>
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Configure(EntityTypeBuilder<CodeSnippet> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable("CodeSnippet");
|
|
||||||
builder.HasKey(e => new { e.Id, e.Language });
|
|
||||||
|
|
||||||
builder.Property(e => e.Id);
|
|
||||||
builder.Property(e => e.Language);
|
|
||||||
builder.Property(e => e.Content);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
namespace OliverBooth.Data.Web;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a code snippet.
|
|
||||||
/// </summary>
|
|
||||||
public interface ICodeSnippet
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the content for this snippet.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The content for this snippet</value>
|
|
||||||
string Content { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the ID for this snippet.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The ID for this snippet</value>
|
|
||||||
int Id { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the language for this snippet.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The language for this snippet</value>
|
|
||||||
string Language { get; }
|
|
||||||
}
|
|
@ -25,12 +25,6 @@ internal sealed class WebContext : DbContext
|
|||||||
/// <value>The collection of books.</value>
|
/// <value>The collection of books.</value>
|
||||||
public DbSet<Book> Books { get; private set; } = null!;
|
public DbSet<Book> Books { get; private set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the collection of code snippets in the database.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The collection of code snippets.</value>
|
|
||||||
public DbSet<CodeSnippet> CodeSnippets { get; private set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the collection of blacklist entries in the database.
|
/// Gets the collection of blacklist entries in the database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -86,7 +80,6 @@ internal sealed class WebContext : DbContext
|
|||||||
{
|
{
|
||||||
modelBuilder.ApplyConfiguration(new BlacklistEntryConfiguration());
|
modelBuilder.ApplyConfiguration(new BlacklistEntryConfiguration());
|
||||||
modelBuilder.ApplyConfiguration(new BookConfiguration());
|
modelBuilder.ApplyConfiguration(new BookConfiguration());
|
||||||
modelBuilder.ApplyConfiguration(new CodeSnippetConfiguration());
|
|
||||||
modelBuilder.ApplyConfiguration(new ProgrammingLanguageConfiguration());
|
modelBuilder.ApplyConfiguration(new ProgrammingLanguageConfiguration());
|
||||||
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
|
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
|
||||||
modelBuilder.ApplyConfiguration(new TemplateConfiguration());
|
modelBuilder.ApplyConfiguration(new TemplateConfiguration());
|
||||||
|
@ -1,139 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
using Markdig;
|
|
||||||
using OliverBooth.Data.Web;
|
|
||||||
using OliverBooth.Services;
|
|
||||||
|
|
||||||
namespace OliverBooth.Markdown.Template;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a custom template renderer which renders the <c>{{Snippet}}</c> template.
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class CodeSnippetTemplateRenderer : CustomTemplateRenderer
|
|
||||||
{
|
|
||||||
private readonly ICodeSnippetService _codeSnippetService;
|
|
||||||
private readonly Lazy<MarkdownPipeline> _markdownPipeline;
|
|
||||||
private readonly IProgrammingLanguageService _programmingLanguageService;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="CodeSnippetTemplateRenderer" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serviceProvider">The service provider.</param>
|
|
||||||
public CodeSnippetTemplateRenderer(IServiceProvider serviceProvider) : base(serviceProvider)
|
|
||||||
{
|
|
||||||
// lazily evaluate to avoid circular dependency problem causing tremendous stack overflow
|
|
||||||
_markdownPipeline = new Lazy<MarkdownPipeline>(serviceProvider.GetRequiredService<MarkdownPipeline>);
|
|
||||||
_codeSnippetService = serviceProvider.GetRequiredService<ICodeSnippetService>();
|
|
||||||
_programmingLanguageService = serviceProvider.GetRequiredService<IProgrammingLanguageService>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override string Render(TemplateInline template)
|
|
||||||
{
|
|
||||||
Debug.Assert(template.Name == "Snippet");
|
|
||||||
Trace.Assert(template.Name == "Snippet");
|
|
||||||
|
|
||||||
IReadOnlyList<string> argumentList = template.ArgumentList;
|
|
||||||
|
|
||||||
if (argumentList.Count < 1)
|
|
||||||
{
|
|
||||||
return DefaultRender(template);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!int.TryParse(argumentList[0], out int snippetId))
|
|
||||||
{
|
|
||||||
return DefaultRender(template);
|
|
||||||
}
|
|
||||||
|
|
||||||
var identifier = Guid.NewGuid();
|
|
||||||
var snippets = new List<ICodeSnippet>();
|
|
||||||
|
|
||||||
IReadOnlyList<string> languages = argumentList.Count > 1
|
|
||||||
? argumentList[1].Split(';')
|
|
||||||
: _codeSnippetService.GetLanguagesForSnippet(snippetId);
|
|
||||||
|
|
||||||
foreach (string language in languages)
|
|
||||||
{
|
|
||||||
if (_codeSnippetService.TryGetCodeSnippetForLanguage(snippetId, language, out ICodeSnippet? snippet))
|
|
||||||
{
|
|
||||||
snippets.Add(snippet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snippets.Count == 1)
|
|
||||||
{
|
|
||||||
ICodeSnippet snippet = snippets[0];
|
|
||||||
return RenderHtml(snippet);
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
builder.AppendLine($"""
|
|
||||||
<ul class="nav nav-tabs mb-3" id="snp-{identifier:N}" data-identifier="{identifier:N}" role="tablist"
|
|
||||||
style="margin-bottom: -0.5em !important;">
|
|
||||||
""");
|
|
||||||
|
|
||||||
for (var index = 0; index < languages.Count; index++)
|
|
||||||
{
|
|
||||||
var language = languages[index];
|
|
||||||
string classList = "";
|
|
||||||
if (index == 0)
|
|
||||||
{
|
|
||||||
classList = " active";
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.AppendLine("""<li class="nav-item" role="presentation">""");
|
|
||||||
builder.AppendLine($"""
|
|
||||||
<a
|
|
||||||
data-tab-init
|
|
||||||
class="nav-link{classList}"
|
|
||||||
id="snp-{snippetId}-{identifier:N}-{language}-l"
|
|
||||||
href="#snp-{snippetId}-{identifier:N}-{language}"
|
|
||||||
role="tab"
|
|
||||||
data-tabs="snp-{snippetId}-{identifier:N}"
|
|
||||||
aria-controls="snp-{snippetId}-{identifier:N}-{language}"
|
|
||||||
aria-selected="true"
|
|
||||||
>{_programmingLanguageService.GetLanguageName(language)}</a
|
|
||||||
>
|
|
||||||
""");
|
|
||||||
builder.AppendLine("</li>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.AppendLine("</ul>");
|
|
||||||
|
|
||||||
builder.AppendLine($"""<div class="tab-content" id="snp-{snippetId}-{identifier:N}">""");
|
|
||||||
|
|
||||||
for (var index = 0; index < snippets.Count; index++)
|
|
||||||
{
|
|
||||||
string classList = "";
|
|
||||||
if (index == 0)
|
|
||||||
{
|
|
||||||
classList = " show active";
|
|
||||||
}
|
|
||||||
|
|
||||||
var snippet = snippets[index];
|
|
||||||
string html = RenderHtml(snippet);
|
|
||||||
builder.AppendLine($"""
|
|
||||||
<div class="tab-pane fade{classList}" id="snp-{snippetId}-{identifier:N}-{snippet.Language}" data-identifier="{identifier:N}" role="tabpanel"
|
|
||||||
aria-labelledby="snp-{snippetId}-{identifier:N}-{snippet.Language}">
|
|
||||||
""");
|
|
||||||
builder.AppendLine(html);
|
|
||||||
builder.AppendLine("</div>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.AppendLine("</div>");
|
|
||||||
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string RenderHtml(ICodeSnippet snippet)
|
|
||||||
{
|
|
||||||
return Markdig.Markdown.ToHtml($"```{snippet.Language}\n{snippet.Content}\n```", _markdownPipeline.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string DefaultRender(TemplateInline template)
|
|
||||||
{
|
|
||||||
return template.ArgumentList.Count == 0
|
|
||||||
? $"{{{{{template.Name}}}}}"
|
|
||||||
: $"{{{{{template.Name}|{string.Join('|', template.ArgumentList)}}}}}";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using OliverBooth.Data.Web;
|
|
||||||
|
|
||||||
namespace OliverBooth.Markdown.Template;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a custom renderer which overrides the default behaviour of the template engine.
|
|
||||||
/// </summary>
|
|
||||||
internal abstract class CustomTemplateRenderer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="CustomTemplateRenderer" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serviceProvider">The service provider.</param>
|
|
||||||
protected CustomTemplateRenderer(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
DbContextFactory = serviceProvider.GetRequiredService<IDbContextFactory<WebContext>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="WebContext" /> factory that was injected into this instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>An <see cref="IDbContextFactory{TContext}" /> for <see cref="WebContext" />.</value>
|
|
||||||
protected IDbContextFactory<WebContext> DbContextFactory { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Renders the specified template.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="template">The template to render.</param>
|
|
||||||
/// <returns>The rendered result of the template.</returns>
|
|
||||||
public abstract string Render(TemplateInline template);
|
|
||||||
}
|
|
@ -33,12 +33,10 @@ builder.Services.AddSingleton(provider => new MarkdownPipelineBuilder()
|
|||||||
builder.Services.AddDbContextFactory<BlogContext>();
|
builder.Services.AddDbContextFactory<BlogContext>();
|
||||||
builder.Services.AddDbContextFactory<WebContext>();
|
builder.Services.AddDbContextFactory<WebContext>();
|
||||||
builder.Services.AddHttpClient();
|
builder.Services.AddHttpClient();
|
||||||
builder.Services.AddSingleton<ICodeSnippetService, CodeSnippetService>();
|
|
||||||
builder.Services.AddSingleton<IContactService, ContactService>();
|
builder.Services.AddSingleton<IContactService, ContactService>();
|
||||||
builder.Services.AddSingleton<ITemplateService, TemplateService>();
|
builder.Services.AddSingleton<ITemplateService, TemplateService>();
|
||||||
builder.Services.AddSingleton<IBlogPostService, BlogPostService>();
|
builder.Services.AddSingleton<IBlogPostService, BlogPostService>();
|
||||||
builder.Services.AddSingleton<IBlogUserService, BlogUserService>();
|
builder.Services.AddSingleton<IBlogUserService, BlogUserService>();
|
||||||
builder.Services.AddSingleton<IProgrammingLanguageService, ProgrammingLanguageService>();
|
|
||||||
builder.Services.AddSingleton<IProjectService, ProjectService>();
|
builder.Services.AddSingleton<IProjectService, ProjectService>();
|
||||||
builder.Services.AddSingleton<IMastodonService, MastodonService>();
|
builder.Services.AddSingleton<IMastodonService, MastodonService>();
|
||||||
builder.Services.AddSingleton<ITutorialService, TutorialService>();
|
builder.Services.AddSingleton<ITutorialService, TutorialService>();
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using OliverBooth.Data.Web;
|
|
||||||
|
|
||||||
namespace OliverBooth.Services;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
internal sealed class CodeSnippetService : ICodeSnippetService
|
|
||||||
{
|
|
||||||
private readonly IDbContextFactory<WebContext> _dbContextFactory;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="CodeSnippetService" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dbContextFactory">The <see cref="WebContext" /> factory.</param>
|
|
||||||
public CodeSnippetService(IDbContextFactory<WebContext> dbContextFactory)
|
|
||||||
{
|
|
||||||
_dbContextFactory = dbContextFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IReadOnlyList<string> GetLanguagesForSnippet(int id)
|
|
||||||
{
|
|
||||||
var languages = new HashSet<string>();
|
|
||||||
using WebContext context = _dbContextFactory.CreateDbContext();
|
|
||||||
|
|
||||||
foreach (CodeSnippet snippet in context.CodeSnippets.Where(s => s.Id == id))
|
|
||||||
{
|
|
||||||
languages.Add(snippet.Language);
|
|
||||||
}
|
|
||||||
|
|
||||||
return languages.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool TryGetCodeSnippetForLanguage(int id, string language, [NotNullWhen(true)] out ICodeSnippet? snippet)
|
|
||||||
{
|
|
||||||
if (language is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(language));
|
|
||||||
}
|
|
||||||
|
|
||||||
using WebContext context = _dbContextFactory.CreateDbContext();
|
|
||||||
IQueryable<CodeSnippet> snippets = context.CodeSnippets.Where(s => s.Id == id);
|
|
||||||
snippet = snippets.FirstOrDefault(s => s.Language == language);
|
|
||||||
return snippet is not null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using OliverBooth.Data.Web;
|
|
||||||
|
|
||||||
namespace OliverBooth.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a service which can fetch multi-language code snippets.
|
|
||||||
/// </summary>
|
|
||||||
public interface ICodeSnippetService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns all the languages which apply to the specified snippet.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The ID of the snippet whose languages should be returned.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// A read-only view of the languages that apply to the snippet. This list may be empty if the snippet ID is invalid.
|
|
||||||
/// </returns>
|
|
||||||
IReadOnlyList<string> GetLanguagesForSnippet(int id);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to find a code snippet by the specified ID, in the specified language.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The ID of the snippet to search for.</param>
|
|
||||||
/// <param name="language">The language to search for.</param>
|
|
||||||
/// <param name="snippet">
|
|
||||||
/// When this method returns, contains the code snippet matching the specified criteria, if such a snippet was found;
|
|
||||||
/// otherwise, <see langword="null" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns><see langword="true" /> if the snippet was found; otherwise, <see langword="false" />.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="language" /> is <see langword="null" />.</exception>
|
|
||||||
bool TryGetCodeSnippetForLanguage(int id, string language, [NotNullWhen(true)] out ICodeSnippet? snippet);
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using OliverBooth.Data.Web;
|
|
||||||
|
|
||||||
namespace OliverBooth.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a service which can perform programming language lookup.
|
|
||||||
/// </summary>
|
|
||||||
public interface IProgrammingLanguageService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the human-readable name of a language.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="alias">The alias of the language.</param>
|
|
||||||
/// <returns>The human-readable name, or <paramref name="alias" /> if the name could not be found.</returns>
|
|
||||||
string GetLanguageName(string alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
internal sealed class ProgrammingLanguageService : IProgrammingLanguageService
|
|
||||||
{
|
|
||||||
private readonly IDbContextFactory<WebContext> _dbContextFactory;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ProgrammingLanguageService" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dbContextFactory">The <see cref="WebContext" /> factory.</param>
|
|
||||||
public ProgrammingLanguageService(IDbContextFactory<WebContext> dbContextFactory)
|
|
||||||
{
|
|
||||||
_dbContextFactory = dbContextFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string GetLanguageName(string alias)
|
|
||||||
{
|
|
||||||
using WebContext context = _dbContextFactory.CreateDbContext();
|
|
||||||
ProgrammingLanguage? language = context.ProgrammingLanguages.FirstOrDefault(l => l.Key == alias);
|
|
||||||
return language?.Name ?? alias;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Markdig;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using OliverBooth.Data.Web;
|
using OliverBooth.Data.Web;
|
||||||
using OliverBooth.Formatting;
|
using OliverBooth.Formatting;
|
||||||
@ -16,51 +14,31 @@ namespace OliverBooth.Services;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class TemplateService : ITemplateService
|
internal sealed class TemplateService : ITemplateService
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, CustomTemplateRenderer> _customTemplateRendererOverrides = new();
|
|
||||||
private static readonly Random Random = new();
|
private static readonly Random Random = new();
|
||||||
private readonly ILogger<TemplateService> _logger;
|
|
||||||
private readonly IDbContextFactory<WebContext> _webContextFactory;
|
private readonly IDbContextFactory<WebContext> _webContextFactory;
|
||||||
private readonly SmartFormatter _formatter;
|
private readonly SmartFormatter _formatter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TemplateService" /> class.
|
/// Initializes a new instance of the <see cref="TemplateService" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logger">The logger.</param>
|
|
||||||
/// <param name="serviceProvider">The <see cref="IServiceProvider" />.</param>
|
/// <param name="serviceProvider">The <see cref="IServiceProvider" />.</param>
|
||||||
/// <param name="webContextFactory">The <see cref="WebContext" /> factory.</param>
|
/// <param name="webContextFactory">The <see cref="WebContext" /> factory.</param>
|
||||||
public TemplateService(ILogger<TemplateService> logger,
|
public TemplateService(IServiceProvider serviceProvider,
|
||||||
IServiceProvider serviceProvider,
|
|
||||||
IDbContextFactory<WebContext> webContextFactory)
|
IDbContextFactory<WebContext> webContextFactory)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
_formatter = Smart.CreateDefaultSmartFormat();
|
_formatter = Smart.CreateDefaultSmartFormat();
|
||||||
_formatter.AddExtensions(new DefaultSource());
|
_formatter.AddExtensions(new DefaultSource());
|
||||||
_formatter.AddExtensions(new ReflectionSource());
|
_formatter.AddExtensions(new ReflectionSource());
|
||||||
_formatter.AddExtensions(new DateFormatter());
|
_formatter.AddExtensions(new DateFormatter());
|
||||||
_formatter.AddExtensions(new MarkdownFormatter(serviceProvider));
|
_formatter.AddExtensions(new MarkdownFormatter(serviceProvider));
|
||||||
|
|
||||||
_logger.LogDebug("Registering template override Snippet to CodeSnippetTemplateRenderer");
|
|
||||||
AddRendererOverride("Snippet", new CodeSnippetTemplateRenderer(serviceProvider));
|
|
||||||
|
|
||||||
_webContextFactory = webContextFactory;
|
_webContextFactory = webContextFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string RenderGlobalTemplate(TemplateInline templateInline)
|
public string RenderGlobalTemplate(TemplateInline templateInline)
|
||||||
{
|
{
|
||||||
if (templateInline is null)
|
if (templateInline is null) throw new ArgumentNullException(nameof(templateInline));
|
||||||
{
|
|
||||||
_logger.LogWarning("Attempting to render null inline template!");
|
|
||||||
throw new ArgumentNullException(nameof(templateInline));
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogDebug("Inline name is {Name}", templateInline.Name);
|
|
||||||
if (_customTemplateRendererOverrides.TryGetValue(templateInline.Name, out CustomTemplateRenderer? renderer))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("This matches renderer {Name}", renderer.GetType().Name);
|
|
||||||
return renderer.Render(templateInline);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TryGetTemplate(templateInline.Name, templateInline.Variant, out ITemplate? template)
|
return TryGetTemplate(templateInline.Name, templateInline.Variant, out ITemplate? template)
|
||||||
? RenderTemplate(templateInline, template)
|
? RenderTemplate(templateInline, template)
|
||||||
@ -111,12 +89,6 @@ internal sealed class TemplateService : ITemplateService
|
|||||||
return template is not null;
|
return template is not null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRendererOverride(string templateName, CustomTemplateRenderer renderer)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Registering template override {Name} to {Renderer}", templateName, renderer.GetType().Name);
|
|
||||||
_customTemplateRendererOverrides[templateName] = renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetDefaultRender(TemplateInline templateInline)
|
private static string GetDefaultRender(TemplateInline templateInline)
|
||||||
{
|
{
|
||||||
return string.IsNullOrWhiteSpace(templateInline.ArgumentString)
|
return string.IsNullOrWhiteSpace(templateInline.ArgumentString)
|
||||||
|
28
src/ts/UI.ts
28
src/ts/UI.ts
@ -77,7 +77,6 @@ class UI {
|
|||||||
UI.addHighlighting(element);
|
UI.addHighlighting(element);
|
||||||
UI.addBootstrapTooltips(element);
|
UI.addBootstrapTooltips(element);
|
||||||
UI.renderSpoilers(element);
|
UI.renderSpoilers(element);
|
||||||
UI.renderTabs(element);
|
|
||||||
UI.renderTeX(element);
|
UI.renderTeX(element);
|
||||||
UI.renderTimestamps(element);
|
UI.renderTimestamps(element);
|
||||||
UI.updateProjectCards(element);
|
UI.updateProjectCards(element);
|
||||||
@ -147,33 +146,6 @@ class UI {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders tabs in the document.
|
|
||||||
* @param element The element to search for tabs in.
|
|
||||||
*/
|
|
||||||
public static renderTabs(element?: Element) {
|
|
||||||
element = element || document.body;
|
|
||||||
element.querySelectorAll("[role=\"tablist\"]").forEach(function (tabList: HTMLElement) {
|
|
||||||
const identifier = tabList.dataset.identifier;
|
|
||||||
const tabLinks = tabList.querySelectorAll(".nav-link");
|
|
||||||
const tabPanes = element.querySelectorAll(`.tab-pane[data-identifier="${identifier}"]`);
|
|
||||||
|
|
||||||
tabLinks.forEach(function (tabLink: Element) {
|
|
||||||
tabLink.addEventListener("click", () => {
|
|
||||||
const controls = document.getElementById(tabLink.getAttribute("aria-controls"));
|
|
||||||
|
|
||||||
// switch "active" tab link
|
|
||||||
tabLinks.forEach(e => e.classList.remove("active"));
|
|
||||||
tabLink.classList.add("active");
|
|
||||||
|
|
||||||
// switch active tab itself
|
|
||||||
tabPanes.forEach(e => e.classList.remove("show", "active"));
|
|
||||||
controls.classList.add("show", "active");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders all TeX in the document.
|
* Renders all TeX in the document.
|
||||||
* @param element The element to search for TeX in.
|
* @param element The element to search for TeX in.
|
||||||
|
Loading…
Reference in New Issue
Block a user