diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 000000000..34c3e19a0 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "7.0.5", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..9b5784fb0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +**/bin +**/obj +**/node_modules +Dockerfile +build.sh diff --git a/.editorconfig b/.editorconfig index 6eecb086e..8b2395878 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,22 +1,154 @@ -# editorconfig.org -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_style = space -indent_size = 4 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.{css,scss,js,json,yml}] -indent_size = 2 - -[*.{xml,csproj,config,*proj,targets,props}] -indent_size = 2 - -[*.md] -trim_trailing_whitespace = false - -[*.sln] -indent_style = tab +# editorconfig.org +root = true + +[*] +charset = utf-8 +# end_of_line = crlf +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +tab_width = 2 +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_predefined_type_for_locals_parameters_members = true:silent + +[*.{cshtml}] +indent_size = 2 + +[*.{razor}] +indent_size = 2 + +[*.{css,scss,js,json,yml}] +indent_size = 2 + +[*.{xml,csproj,config,*proj,targets,props}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.sln] +indent_style = tab + +[*.cs] +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# 命名样式 + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +csharp_using_directive_placement = outside_namespace:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = false:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_space_around_binary_operators = before_and_after +csharp_indent_labels = one_less_than_current +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index f19ea3857..1baadf4bd 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -19,19 +19,20 @@ jobs: key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} restore-keys: | ${{ runner.os }}-nuget- + - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.x' + dotnet-version: '7.0.x' - name: Publish Blogifier - run: dotnet publish -c Release -o release --nologo + run: dotnet publish -c Release /p:RuntimeIdentifier=linux-x64 ./src/Blogifier/Blogifier.csproj --output dist - name: Add .nojekyll file - run: touch release/.nojekyll + run: touch dist/.nojekyll - - name: Deploy - uses: JamesIves/github-pages-deploy-action@4.1.4 - with: - branch: demo - folder: release + #- name: Deploy + # uses: JamesIves/github-pages-deploy-action@4.1.4 + # with: + # branch: demo + # folder: release diff --git a/.gitignore b/.gitignore index 9b70681f6..9d8d3b095 100644 --- a/.gitignore +++ b/.gitignore @@ -1,352 +1,360 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -.DS_Store +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +.DS_Store +Blog.db-shm +Blog.db-wal + +App_Data/ + +appsettings.Development.json + +dist diff --git a/.vscode/launch.json b/.vscode/launch.json index e47c0c29c..b90bfb380 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,31 +1,35 @@ { - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md - "version": "0.2.0", - "configurations": [ - { - "name": "Launch and Debug Blazor WebAssembly App", - "type": "coreclr", - "request": "launch", - "program": "dotnet", - "args":["run"], - "cwd": "${workspaceFolder}/src/Blogifier", - "env": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "launchBrowser": { - "enabled": true, - "args": "${auto-detect-url}", - "windows": { - "command": "cmd.exe", - "args": "/C start ${auto-detect-url}", - }, - "osx": { - "command": "open" - }, - "linux": { - "command": "xdg-open" - } - } + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": "Launch and Debug", + "type": "coreclr", + "request": "launch", + "program": "dotnet", + "args": [ + "watch" + ], + "cwd": "${workspaceFolder}/src/Blogifier", + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "launchBrowser": { + "enabled": true, + "args": "${auto-detect-url}", + "windows": { + "command": "cmd.exe", + "args": "/C start ${auto-detect-url}", + }, + "osx": { + "command": "open" + }, + "linux": { + "command": "xdg-open" } - ] -} \ No newline at end of file + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 58642d74a..0bf8b754e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,42 +1,42 @@ { - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "${workspaceFolder}/src/Blogifier/Blogifier.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "publish", - "command": "dotnet", - "type": "process", - "args": [ - "publish", - "${workspaceFolder}/src/Blogifier/Blogifier.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "watch", - "command": "dotnet", - "type": "process", - "args": [ - "watch", - "run", - "${workspaceFolder}/src/Blogifier/Blogifier.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/Blogifier/Blogifier.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/src/Blogifier/Blogifier.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/src/Blogifier/Blogifier.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} diff --git a/Blogifier.sln b/Blogifier.sln index e10a69835..0482bd743 100644 --- a/Blogifier.sln +++ b/Blogifier.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.0.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 MinimumVisualStudioVersion = 16.0.0.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blogifier", "src\Blogifier\Blogifier.csproj", "{30113938-1040-459A-A0B6-3663E9534C8B}" EndProject @@ -8,8 +8,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blogifier.Admin", "src\Blog EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blogifier.Shared", "src\Blogifier.Shared\Blogifier.Shared.csproj", "{A0173B1A-FDC9-4E48-94E1-21D6C0B19257}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blogifier.Core", "src\Blogifier.Core\Blogifier.Core.csproj", "{99296F5E-F267-4483-97BF-647080D21C35}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{512E41FF-25A7-49D6-B126-EC1450E17B72}" ProjectSection(SolutionItems) = preProject docs\01-Authentication.md = docs\01-Authentication.md @@ -18,22 +16,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{512E41FF-2 docs\04-Localization.md = docs\04-Localization.md docs\05-Emails.md = docs\05-Emails.md docs\06-Newsletters.md = docs\06-Newsletters.md + README.md = README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8DDBCDEC-BFD8-4737-93BD-5259C3AE9CAE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blogifier.Tests", "tests\Blogifier.Tests\Blogifier.Tests.csproj", "{27EB38D0-E275-4033-93B6-3ED6E6B7B442}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "data", "data", "{84FCDE15-5F12-484E-A8C2-DA9A1D3122BB}" - ProjectSection(SolutionItems) = preProject - tests\data\test3.xml = tests\data\test3.xml - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "items", "items", "{D2E9C8BA-AC43-4A35-88D9-8D63D26884B8}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore Dockerfile = Dockerfile EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blogifier.Themes.Standard", "src\Blogifier.Themes.Standard\Blogifier.Themes.Standard.csproj", "{7784DF02-6BCF-40A9-B327-C1F173975714}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "themes", "themes", "{4155E512-4F91-48D3-823A-45B7C71125A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -80,18 +80,6 @@ Global {A0173B1A-FDC9-4E48-94E1-21D6C0B19257}.Release|x64.Build.0 = Release|Any CPU {A0173B1A-FDC9-4E48-94E1-21D6C0B19257}.Release|x86.ActiveCfg = Release|Any CPU {A0173B1A-FDC9-4E48-94E1-21D6C0B19257}.Release|x86.Build.0 = Release|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Debug|Any CPU.Build.0 = Debug|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Debug|x64.ActiveCfg = Debug|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Debug|x64.Build.0 = Debug|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Debug|x86.ActiveCfg = Debug|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Debug|x86.Build.0 = Debug|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Release|Any CPU.ActiveCfg = Release|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Release|Any CPU.Build.0 = Release|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Release|x64.ActiveCfg = Release|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Release|x64.Build.0 = Release|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Release|x86.ActiveCfg = Release|Any CPU - {99296F5E-F267-4483-97BF-647080D21C35}.Release|x86.Build.0 = Release|Any CPU {27EB38D0-E275-4033-93B6-3ED6E6B7B442}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {27EB38D0-E275-4033-93B6-3ED6E6B7B442}.Debug|Any CPU.Build.0 = Debug|Any CPU {27EB38D0-E275-4033-93B6-3ED6E6B7B442}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -104,13 +92,25 @@ Global {27EB38D0-E275-4033-93B6-3ED6E6B7B442}.Release|x64.Build.0 = Release|Any CPU {27EB38D0-E275-4033-93B6-3ED6E6B7B442}.Release|x86.ActiveCfg = Release|Any CPU {27EB38D0-E275-4033-93B6-3ED6E6B7B442}.Release|x86.Build.0 = Release|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Debug|x64.ActiveCfg = Debug|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Debug|x64.Build.0 = Debug|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Debug|x86.ActiveCfg = Debug|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Debug|x86.Build.0 = Debug|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Release|Any CPU.Build.0 = Release|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Release|x64.ActiveCfg = Release|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Release|x64.Build.0 = Release|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Release|x86.ActiveCfg = Release|Any CPU + {7784DF02-6BCF-40A9-B327-C1F173975714}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {27EB38D0-E275-4033-93B6-3ED6E6B7B442} = {8DDBCDEC-BFD8-4737-93BD-5259C3AE9CAE} - {84FCDE15-5F12-484E-A8C2-DA9A1D3122BB} = {8DDBCDEC-BFD8-4737-93BD-5259C3AE9CAE} + {7784DF02-6BCF-40A9-B327-C1F173975714} = {4155E512-4F91-48D3-823A-45B7C71125A0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8AA706E7-D820-4892-9F60-FCEEDC51FF5E} diff --git a/Dockerfile b/Dockerfile index 9302a36e6..9b213ead1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,14 @@ -FROM mcr.microsoft.com/dotnet/aspnet:6.0 as base - +FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine as sdk # Copy everything else and build COPY ./ /opt/blogifier WORKDIR /opt/blogifier +RUN ["dotnet","publish", "-c", "Release","/p:RuntimeIdentifier=linux-musl-x64", "./src/Blogifier/Blogifier.csproj","-o","dist" ] -RUN ["dotnet","publish","./src/Blogifier/Blogifier.csproj","-o","./outputs" ] - -FROM mcr.microsoft.com/dotnet/aspnet:6.0 as run -COPY --from=base /opt/blogifier/outputs /opt/blogifier/outputs -WORKDIR /opt/blogifier/outputs -ENTRYPOINT ["dotnet", "Blogifier.dll"] \ No newline at end of file +FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine as run +# TOTO zh-CH +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories +RUN apk add --no-cache icu-libs +ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false +COPY --from=sdk /opt/blogifier/dist /opt/blogifier/ +WORKDIR /opt/blogifier +ENTRYPOINT ["dotnet", "Blogifier.dll"] diff --git a/README.md b/README.md index 893e2b50e..761adafde 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@

Blogifier

- Blogifier is a self-hosted open source publishing platform written in ASP.NET and Blazor WebAssembly. It can be used to quickly and easily set up a lightweight, but fully functional personal or group blog. + Blogifier is a self-hosted open source publishing platform written in ASP.NET and Blazor WebAssembly. + It can be used to quickly and easily set up a lightweight, but fully functional personal or group blog.



@@ -9,7 +10,7 @@ Steps to install compiled application on the server for a self-hosting: -1. .NET Core Runtime (currently 6.0) must be installed on your host server. +1. .NET Core Runtime (currently 7.0) must be installed on your host server. 2. [Download](https://github.com/blogifierdotnet/Blogifier/releases) the latest release. 3. Unzip and copy to your host server.
4. Restart your website. diff --git a/build.cmd b/build.cmd new file mode 100644 index 000000000..a71b5741b --- /dev/null +++ b/build.cmd @@ -0,0 +1,2 @@ +dotnet clean +dotnet build -c Debug /p:RuntimeIdentifier=linux-x64 ./src/Blogifier/Blogifier.csproj --output dist diff --git a/build.sh b/build.sh new file mode 100644 index 000000000..b0214ddf2 --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +dotnet clean +dotnet build -c Debug /p:RuntimeIdentifier=win-x64 ./src/Blogifier/Blogifier.csproj --output dist diff --git a/docs/02-Database.md b/docs/02-Database.md index 34f25f34f..87fef7c58 100644 --- a/docs/02-Database.md +++ b/docs/02-Database.md @@ -11,14 +11,18 @@ ``` Valid providers: `SQLite`, `SqlServer`, `Postgres`, `MySql` (you'll need to supply valid connection string) -2. Remove `Blogifier.Core/Data/Migrations` folder with existing migrations -3. In the Visual Studio, open `Package Manager Console`, set `Blogifier.Core` +2. Remove `Blogifier/Data/Migrations` folder with existing migrations +3. In the Visual Studio, open `Package Manager Console`, set `Blogifier` as Default project and run these commands: ``` Add-Migration Init -o Data\Migrations Update-Database + +# cil +dotnet ef migrations Init -o Data\Migrations +dotnet ef migrations remove ``` First command should re-generate provider specific code migrations and second will -execute them and create database specified in the connection string. \ No newline at end of file +execute them and create database specified in the connection string. diff --git a/publish.cmd b/publish.cmd new file mode 100644 index 000000000..4eec9c5bd --- /dev/null +++ b/publish.cmd @@ -0,0 +1 @@ +dotnet publish -c Release /p:RuntimeIdentifier=win-x64 ./src/Blogifier/Blogifier.csproj -v minimal --output dist diff --git a/publish.sh b/publish.sh new file mode 100644 index 000000000..39fb990e8 --- /dev/null +++ b/publish.sh @@ -0,0 +1,7 @@ +# Local machine +# rm -fr dist +# dotnet publish -c Release /p:RuntimeIdentifier=linux-x64 ./src/Blogifier/Blogifier.csproj --output dist + +# docker +docker build -t dorthl/blogifier:latest . +docker push dorthl/blogifier:latest diff --git a/src/Blogifier.Admin/App.razor b/src/Blogifier.Admin/App.razor index 3772d469f..c6d65f3b3 100644 --- a/src/Blogifier.Admin/App.razor +++ b/src/Blogifier.Admin/App.razor @@ -1,12 +1,13 @@ - - - - - - - -

Sorry, there's nothing at this address.

-
-
-
-
\ No newline at end of file + + + + + + + +

Sorry, there's nothing at this address.

+
+
+
+
+ diff --git a/src/Blogifier.Admin/BlogAuthStateProvider.cs b/src/Blogifier.Admin/BlogAuthStateProvider.cs new file mode 100644 index 000000000..84a836e59 --- /dev/null +++ b/src/Blogifier.Admin/BlogAuthStateProvider.cs @@ -0,0 +1,46 @@ +using Blogifier.Identity; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.Extensions.Logging; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Blogifier.Admin; + +public class BlogAuthStateProvider : AuthenticationStateProvider +{ + private readonly ILogger _logger; + protected readonly HttpClient _httpClient; + protected AuthenticationState? _state; + + public BlogAuthStateProvider(ILogger logger, HttpClient httpClient) + { + _logger = logger; + _httpClient = httpClient; + } + + public override async Task GetAuthenticationStateAsync() + { + if (_state == null) + { + var response = await _httpClient.GetAsync("/api/token/userinfo"); + BlogifierClaims? claims = null; + if (response.IsSuccessStatusCode) + { + var stream = await response.Content.ReadAsStreamAsync(); + if (stream.Length > 0) + { + claims = JsonSerializer.Deserialize(stream, BlogifierConstant.DefaultJsonSerializerOptionss)!; + _logger.LogInformation("claims success userName:{UserName}", claims.UserName); + } + } + else + { + _logger.LogError("claims http error StatusCode:{StatusCode}", response.StatusCode); + } + var principal = BlogifierClaims.Generate(claims); + _state = new AuthenticationState(principal); + } + return _state; + } +} diff --git a/src/Blogifier.Admin/BlogAuthenticationStateProvider.cs b/src/Blogifier.Admin/BlogAuthenticationStateProvider.cs deleted file mode 100644 index 5073e23a5..000000000 --- a/src/Blogifier.Admin/BlogAuthenticationStateProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Blogifier.Shared; -using Microsoft.AspNetCore.Components.Authorization; -using System.Net.Http; -using System.Net.Http.Json; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace Blogifier.Admin -{ - public class BlogAuthenticationStateProvider : AuthenticationStateProvider - { - private readonly HttpClient _httpClient; - - public BlogAuthenticationStateProvider(HttpClient httpClient) - { - _httpClient = httpClient; - } - - public override async Task GetAuthenticationStateAsync() - { - Author author = await _httpClient.GetFromJsonAsync("api/author/getcurrent"); - - if (author != null && author.Email != null) - { - var claim = new Claim(ClaimTypes.Name, author.Email); - var claimsIdentity = new ClaimsIdentity(new[] { claim }, "serverAuth"); - var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); - - return new AuthenticationState(claimsPrincipal); - } - else - return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); - } - } -} diff --git a/src/Blogifier.Admin/BlogStateProvider.cs b/src/Blogifier.Admin/BlogStateProvider.cs deleted file mode 100644 index 27e8bce7e..000000000 --- a/src/Blogifier.Admin/BlogStateProvider.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Blogifier.Shared; -using System; - -namespace Blogifier.Admin -{ - public class BlogStateProvider - { - public PostType PostType { get; set; } = PostType.Post; - - public event Action OnChange; - - public void SetPostType(PostType postType) - { - PostType = postType; - NotifyStateChanged(); - } - - private void NotifyStateChanged() => OnChange?.Invoke(); - } -} diff --git a/src/Blogifier.Admin/Blogifier.Admin.csproj b/src/Blogifier.Admin/Blogifier.Admin.csproj index a6a78d806..e9e15754a 100644 --- a/src/Blogifier.Admin/Blogifier.Admin.csproj +++ b/src/Blogifier.Admin/Blogifier.Admin.csproj @@ -1,19 +1,87 @@ - net6.0 + net7.0 + enable + false + assets\ + - + + + - - - - - - + + + + + + - \ No newline at end of file + + + + + + + + + + + $(ClientAssetsDirectory)package-lock.json;$(ClientAssetsDirectory)package.json + $(ClientAssetsDirectory)node_modules\.package-lock.json + $(MSBuildProjectFile);$(ClientAssetsRestoreInputs) + + DispatchToInnerBuilds + + true + $(ClientAssetsDirectory)\dist\ + + + + + + + + + + + + + + + + + + + + + <_ClientAssetsOutputFullPath>$([System.IO.Path]::GetFullPath('$(ClientAssetsBuildOutput)'))/ + + + <_ClientAssetsBuildOutput Include="$(ClientAssetsBuildOutput)**"> + + + + + + + + + + + + + + + + + diff --git a/src/Blogifier.Admin/Components/Blog/CategoriesComponent.razor b/src/Blogifier.Admin/Components/Blog/CategoriesComponent.razor deleted file mode 100644 index 425918bed..000000000 --- a/src/Blogifier.Admin/Components/Blog/CategoriesComponent.razor +++ /dev/null @@ -1,61 +0,0 @@ -@inject HttpClient _http -@inject IStringLocalizer _localizer - -
- @if (PostCategories != null) - { - foreach (var item in PostCategories) - { -
- - - - -
- } - } - -
- -@code { - [Parameter] public Post Post { get; set; } - - protected string Tag { get; set; } - public List PostCategories { get; set; } - - protected override async Task OnInitializedAsync() - { - await Load(); - } - - protected async Task Load() - { - Tag = ""; - PostCategories = (await _http.GetFromJsonAsync>($"api/category/{Post.Id}")).ToList(); - } - - protected async Task KeyPressed(KeyboardEventArgs eventArgs) - { - if (eventArgs.Code == "Enter") - { - PostCategories.Add(new Category { Content = Tag, DateUpdated = DateTime.UtcNow }); - Tag = await Task.FromResult(""); - } - } - - protected async Task Remove(string tag) - { - var tagToRemove = await Task.FromResult(PostCategories.Where(c => c.Content == tag).FirstOrDefault()); - PostCategories.Remove(tagToRemove); - } -} diff --git a/src/Blogifier.Admin/Components/CategoriesComponent.razor b/src/Blogifier.Admin/Components/CategoriesComponent.razor new file mode 100644 index 000000000..192cb7870 --- /dev/null +++ b/src/Blogifier.Admin/Components/CategoriesComponent.razor @@ -0,0 +1,51 @@ +@inject IStringLocalizer _localizer + +@code { + [Parameter] public List Categories { get; set; } = default!; + + protected string Tag { get; set; } = default!; + + protected override void OnInitialized() + { + Tag = string.Empty; + } + + protected void KeyPressed(KeyboardEventArgs eventArgs) + { + if (eventArgs.Code == "Enter") + { + Categories.Add(new CategoryDto { Content = Tag }); + Tag = string.Empty; + } + } + + protected void Remove(string tag) + { + Categories!.Remove(Categories.Where(c => c.Content == tag).First()); + } +} + +
+ @foreach (var item in Categories) + { +
+ + + + +
+ } + +
+ diff --git a/src/Blogifier.Admin/Components/Customize/MenusComponent.razor b/src/Blogifier.Admin/Components/Customize/MenusComponent.razor deleted file mode 100644 index e351d2305..000000000 --- a/src/Blogifier.Admin/Components/Customize/MenusComponent.razor +++ /dev/null @@ -1,5 +0,0 @@ -@inject IStringLocalizer _localizer -
- @_localizer["under-development"]. - @_localizer["more-info"] -
diff --git a/src/Blogifier.Admin/Components/Customize/SettingsComponent.razor b/src/Blogifier.Admin/Components/Customize/SettingsComponent.razor deleted file mode 100644 index 19970b08c..000000000 --- a/src/Blogifier.Admin/Components/Customize/SettingsComponent.razor +++ /dev/null @@ -1,98 +0,0 @@ -@inject HttpClient _http -@inject IStringLocalizer _localizer -@inject IJSRuntime JSRuntime - -@if (Settings != null) -{ -
- @foreach (var item in Settings.Sections) - { - var sec = item.Label.GetHashCode(); - -
-

- -

-
- -
- @foreach (var field in item.Fields) - { -
- @if (field.Type == "select") - { - - - } - else if (field.Type == "checkbox") - { -
- - -
- } - else if (field.Type == "textarea") - { - - - } - else - { - - - } -
- } -
-
-
- } -
-} - -@code { - protected Blog Blog { get; set; } - protected ThemeItem CurrentTheme { get; set; } - protected ToasterComponent Toaster; - protected ThemeSettings Settings { get; set; } - - protected override async Task OnInitializedAsync() - { - await Load(); - } - - public async Task Load() - { - Blog = await _http.GetFromJsonAsync("api/blog"); - var allThemes = await _http.GetFromJsonAsync>($"api/storage/themes"); - - Settings = await _http.GetFromJsonAsync($"api/theme/{Blog.Theme}"); - } - - public async Task Save() - { - foreach (var section in Settings.Sections) - { - foreach (var field in section.Fields) - { - var val = await JSRuntime.InvokeAsync("commonJsFunctions.getFieldValue", field); - field.Value = val.ToString(); - } - } - return await _http.PostAsJsonAsync($"api/theme/{Blog.Theme}", Settings); - } -} diff --git a/src/Blogifier.Admin/Components/Customize/WidgetsComponent.razor b/src/Blogifier.Admin/Components/Customize/WidgetsComponent.razor deleted file mode 100644 index e351d2305..000000000 --- a/src/Blogifier.Admin/Components/Customize/WidgetsComponent.razor +++ /dev/null @@ -1,5 +0,0 @@ -@inject IStringLocalizer _localizer -
- @_localizer["under-development"]. - @_localizer["more-info"] -
diff --git a/src/Blogifier.Admin/Components/Dashboard/AnalyticsComponent.razor b/src/Blogifier.Admin/Components/Dashboard/AnalyticsComponent.razor deleted file mode 100644 index 3cc9a926a..000000000 --- a/src/Blogifier.Admin/Components/Dashboard/AnalyticsComponent.razor +++ /dev/null @@ -1,217 +0,0 @@ -@using ChartJs.Blazor -@using ChartJs.Blazor.Common -@using ChartJs.Blazor.Common.Enums -@using ChartJs.Blazor.Util -@using ChartJs.Blazor.BarChart -@using System.Drawing - -@inject HttpClient _http -@inject IStringLocalizer _localizer - -
-
-
@_localizer["analytics"]
- -
- - - - - - - -
- - -@code { - protected ToasterComponent Toaster; - private BarConfig _config; - protected List _visits; - - protected bool _hideGraph = false; - protected bool _hideList = true; - - protected List _dateOptions; - protected AnalyticsModel _model; - - protected override async Task OnInitializedAsync() - { - _config = GetConfig(); - - _dateOptions = new List(); - _dateOptions.Add(new OptionItem { Id = 1, Title = _localizer["today"] }); - _dateOptions.Add(new OptionItem { Id = 2, Title = _localizer["yesterday"] }); - _dateOptions.Add(new OptionItem { Id = 3, Title = _localizer["7-days"] }); - _dateOptions.Add(new OptionItem { Id = 4, Title = _localizer["30-days"] }); - _dateOptions.Add(new OptionItem { Id = 5, Title = _localizer["90-days"] }); - - await Load(); - } - - protected async Task Load() - { - IDataset dataset = new BarDataset() - { - Label = "Latest Post Views", - BackgroundColor = ColorUtil.FromDrawingColor(Color.FromArgb(98, 42, 255)), - BorderWidth = 0 - }; - - _model = await _http.GetFromJsonAsync("api/analytics"); - - if (_model == null || _model.LatestPostViews == null) - { - LoadData(dataset, TestData()); - } - else - { - _hideList = _model.DisplayType == AnalyticsListType.Graph; - _hideGraph = _model.DisplayType == AnalyticsListType.List; - - LoadData(dataset, _model.LatestPostViews); - } - - if (_config.Data.Datasets.Count > 0) - { - _config.Data.Datasets.Clear(); - } - - _config.Data.Datasets.Add(dataset); - } - - protected BarConfig GetConfig() - { - return new BarConfig - { - Options = new BarOptions - { - Responsive = true, - Legend = new Legend - { - Position = Position.Top - } - } - }; - } - - protected void LoadData(IDataset dataset, BarChartModel model) - { - _visits = new List(); - var labels = model.Labels.ToList(); - var values = model.Data.ToList(); - - _config.Data.Labels.Clear(); - - for (int i = 0; i < labels.Count; i++) - { - _config.Data.Labels.Add(labels[i]); - dataset.Add(values[i]); - - _visits.Add(new PostVisit { Name = labels[i], Value = values[i] }); - } - _visits = _visits.OrderByDescending(v => v.Value).ToList(); - } - - protected async Task ToggleAnalyticsView(bool isGraph) - { - _hideGraph = await Task.FromResult(!isGraph); - _hideList = isGraph; - - int typeId = isGraph ? 2 : 1; - - Toaster.Toast(await _http.PutAsJsonAsync($"api/analytics/displayType/{typeId}", typeId)); - } - - protected async Task DateOptionSelect(int id) - { - Blog blog = await _http.GetFromJsonAsync("api/blog"); - blog.AnalyticsPeriod = id; - _model.DisplayPeriod = (AnalyticsPeriod)id; - Toaster.Toast(await _http.PutAsJsonAsync("api/blog", blog)); - await Load(); - } - - protected BarChartModel TestData() - { - return new BarChartModel() - { - Labels = new List() { "Post One", "Post Two", "Post Three", "Post Four", "Post Five", "Post Six" }, - Data = new List() { 12036, 15350, 9457, 11104, 7938, 8136 } - }; - } - - protected string PeriodLabel() - { - if (_model == null) - return ""; - return _model.DisplayPeriod.ToString(); - } -} diff --git a/src/Blogifier.Admin/Components/Dashboard/TotalsComponent.razor b/src/Blogifier.Admin/Components/Dashboard/TotalsComponent.razor deleted file mode 100644 index 60357d067..000000000 --- a/src/Blogifier.Admin/Components/Dashboard/TotalsComponent.razor +++ /dev/null @@ -1,35 +0,0 @@ -@inject HttpClient _http -@inject IStringLocalizer _localizer - -@if (_model != null) -{ - -} - -@code { - protected AnalyticsModel _model; - - protected override async Task OnInitializedAsync() - { - _model = await _http.GetFromJsonAsync("api/analytics"); - } -} diff --git a/src/Blogifier.Admin/Components/EditorComponent.razor b/src/Blogifier.Admin/Components/EditorComponent.razor index 6d584a00f..a75ff1054 100644 --- a/src/Blogifier.Admin/Components/EditorComponent.razor +++ b/src/Blogifier.Admin/Components/EditorComponent.razor @@ -1,20 +1,52 @@ -@inject IJSRuntime JSRuntime +@implements IAsyncDisposable @inject IStringLocalizer _localizer +@inject IJSRuntime _jsRuntime +
- +
+ @code { - [Parameter] public string Content { get; set; } - [Parameter] public string Toolbar { get; set; } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await JSRuntime.InvokeAsync("commonJsFunctions.loadEditor", Toolbar); - } + [Parameter] public string Toolbar { get; set; } = default!; - await JSRuntime.InvokeAsync("commonJsFunctions.setEditorValue", Content ); + private ValueTask _taskModule; + private ElementReference? _textareaReference; + private InputFile? inputImageFiles; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + _taskModule = _jsRuntime.InvokeAsync("import", "./admin/js/editor.min.js"); + var module = await _taskModule; + await module.InvokeVoidAsync("loadEditor", Toolbar, _textareaReference, inputImageFiles?.Element); } + } + + protected async Task LoadImageFiles(InputFileChangeEventArgs args) + { + var module = await _taskModule; + await module.InvokeVoidAsync("previewImage", inputImageFiles?.Element); + } + + public async ValueTask SetValueAsync(string value) + { + var module = await _taskModule; + await module.InvokeVoidAsync("setEditorValue", value); + } + + public async ValueTask GetValueAsync() + { + var module = await _taskModule; + var content = await module.InvokeAsync("getEditorValue"); + return content; + } + + async ValueTask IAsyncDisposable.DisposeAsync() + { + var module = await _taskModule; + await module.DisposeAsync(); + } } diff --git a/src/Blogifier.Admin/Components/NavMenuComponent.razor b/src/Blogifier.Admin/Components/NavMenuComponent.razor new file mode 100644 index 000000000..a139ee4e9 --- /dev/null +++ b/src/Blogifier.Admin/Components/NavMenuComponent.razor @@ -0,0 +1,126 @@ +@inject HttpClient _http +@inject NavigationManager _navigationManager +@inject AuthenticationStateProvider _stateProvider +@inject IJSRuntime _jsRuntime +@inject IStringLocalizer _localizer + +@code { + + protected BlogifierClaims? _claims; + + + protected override async Task OnInitializedAsync() + { + var state = await _stateProvider.GetAuthenticationStateAsync(); + var identity = state.User.Identity; + _claims = BlogifierClaims.Analysis(state.User); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await _jsRuntime.InvokeAsync("commonJsFunctions.setTooltip", ""); + } + } +} + + diff --git a/src/Blogifier.Admin/Components/PageTitleComponent.razor b/src/Blogifier.Admin/Components/PageTitleComponent.razor new file mode 100644 index 000000000..fba394574 --- /dev/null +++ b/src/Blogifier.Admin/Components/PageTitleComponent.razor @@ -0,0 +1,11 @@ +@inject IJSRuntime _jsRuntime; + +@code { + [Parameter] public string Title { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + await SetTitleAsync(); + } + private async Task SetTitleAsync() => await _jsRuntime.InvokeVoidAsync("commonJsFunctions.setTitle", Title); +} diff --git a/src/Blogifier.Admin/Components/RedirectComponent.razor b/src/Blogifier.Admin/Components/RedirectComponent.razor new file mode 100644 index 000000000..85d57ba8f --- /dev/null +++ b/src/Blogifier.Admin/Components/RedirectComponent.razor @@ -0,0 +1,8 @@ +@inject NavigationManager _navigationManager + +@code { + protected override void OnInitialized() + { + _navigationManager.NavigateTo($"account?redirectUri={Uri.EscapeDataString(_navigationManager.Uri)}", true, true); + } +} diff --git a/src/Blogifier.Admin/Components/ToasterComponent.razor b/src/Blogifier.Admin/Components/ToasterComponent.razor deleted file mode 100644 index 38531443c..000000000 --- a/src/Blogifier.Admin/Components/ToasterComponent.razor +++ /dev/null @@ -1,12 +0,0 @@ -@inject IToaster _toaster -@inject IStringLocalizer _localizer - -@code { - public void Toast(HttpResponseMessage msg) - { - if (msg.IsSuccessStatusCode) - _toaster.Success(_localizer["completed"]); - else - _toaster.Error(_localizer["generic-error"]); - } -} \ No newline at end of file diff --git a/src/Blogifier.Admin/Dtos/FrontCategoryItemDto.cs b/src/Blogifier.Admin/Dtos/FrontCategoryItemDto.cs new file mode 100644 index 000000000..5cd40f3b5 --- /dev/null +++ b/src/Blogifier.Admin/Dtos/FrontCategoryItemDto.cs @@ -0,0 +1,6 @@ +namespace Blogifier.Shared; + +public class FrontCategoryItemDto : CategoryItemDto +{ + public bool Selected { get; set; } +} diff --git a/src/Blogifier.Admin/Dtos/FrontImportDto.cs b/src/Blogifier.Admin/Dtos/FrontImportDto.cs new file mode 100644 index 000000000..51a3e21be --- /dev/null +++ b/src/Blogifier.Admin/Dtos/FrontImportDto.cs @@ -0,0 +1,9 @@ +using Blogifier.Shared; +using System.Collections.Generic; + +namespace Blogifier.Admin; + +public class FrontImportDto : ImportDto +{ + public new List Posts { get; set; } = default!; +} diff --git a/src/Blogifier.Admin/Dtos/FrontPostEditorDto.cs b/src/Blogifier.Admin/Dtos/FrontPostEditorDto.cs new file mode 100644 index 000000000..0952133c6 --- /dev/null +++ b/src/Blogifier.Admin/Dtos/FrontPostEditorDto.cs @@ -0,0 +1,9 @@ +using Blogifier.Shared; + +namespace Blogifier.Admin; + +public class FrontPostEditorDto : PostEditorDto +{ + public bool Selected { get; set; } + public bool? ImportComplete { get; set; } +} diff --git a/src/Blogifier.Admin/Dtos/FrontPostItemDto.cs b/src/Blogifier.Admin/Dtos/FrontPostItemDto.cs new file mode 100644 index 000000000..a3fb239a3 --- /dev/null +++ b/src/Blogifier.Admin/Dtos/FrontPostItemDto.cs @@ -0,0 +1,8 @@ +using Blogifier.Shared; + +namespace Blogifier.Admin; + +public class FrontPostItemDto : PostItemDto +{ + public bool Selected { get; set; } +} diff --git a/src/Blogifier.Admin/Dtos/FrontUserInfoDto.cs b/src/Blogifier.Admin/Dtos/FrontUserInfoDto.cs new file mode 100644 index 000000000..bbf1ac17f --- /dev/null +++ b/src/Blogifier.Admin/Dtos/FrontUserInfoDto.cs @@ -0,0 +1,8 @@ +using Blogifier.Shared; + +namespace Blogifier.Admin; + +public class FrontUserInfoDto : UserInfoDto +{ + public bool Selected { get; set; } +} diff --git a/src/Blogifier.Admin/Pages/Account/Login.razor b/src/Blogifier.Admin/Pages/Account/Login.razor deleted file mode 100644 index 8dbeaea02..000000000 --- a/src/Blogifier.Admin/Pages/Account/Login.razor +++ /dev/null @@ -1,30 +0,0 @@ -@layout AccountLayout -@page "/admin/login/" -@inject HttpClient Http -@inject NavigationManager _navigationManager -@inject IStringLocalizer _localizer - - - -@if (showError) -{ - -} - -@if (model != null) -{ - - -
- - - -
-
- - - -
- -
-} diff --git a/src/Blogifier.Admin/Pages/Account/Login.razor.cs b/src/Blogifier.Admin/Pages/Account/Login.razor.cs deleted file mode 100644 index 4d9ae4c2b..000000000 --- a/src/Blogifier.Admin/Pages/Account/Login.razor.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Blogifier.Shared; -using Microsoft.AspNetCore.WebUtilities; -using System; -using System.Linq; -using System.Net.Http.Json; -using System.Threading.Tasks; - -namespace Blogifier.Admin.Pages.Account -{ - public partial class Login - { - public bool showError = false; - public LoginModel model = new LoginModel { Email = "", Password = "" }; - - public async Task LoginUser() - { - var returnUrl = "admin/"; - var uri = _navigationManager.ToAbsoluteUri(_navigationManager.Uri); - - if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("returnUrl", out var param)) - returnUrl = param.First(); - - if(!IsLocalUrl(returnUrl)) - returnUrl = "admin/"; - - var result = await Http.PostAsJsonAsync("api/author/login", model); - - if (result.IsSuccessStatusCode) - { - showError = false; - _navigationManager.NavigateTo(returnUrl, true); - } - else - { - showError = true; - StateHasChanged(); - } - } - - static bool IsLocalUrl(string url) - { - if(url.Contains("//")) - return false; - - Uri result; - return Uri.TryCreate(url, UriKind.Relative, out result); - } - } -} diff --git a/src/Blogifier.Admin/Pages/Account/LogoutView.razor b/src/Blogifier.Admin/Pages/Account/LogoutView.razor deleted file mode 100644 index dac9fbd41..000000000 --- a/src/Blogifier.Admin/Pages/Account/LogoutView.razor +++ /dev/null @@ -1,14 +0,0 @@ -@page "/admin/logout/" -@inject HttpClient _http -@inject NavigationManager _navigationManager -@inject IStringLocalizer _localizer - - - -@code{ - protected override async Task OnInitializedAsync() - { - await _http.GetFromJsonAsync("api/author/logout"); - _navigationManager.NavigateTo(_navigationManager.BaseUri, true); - } -} diff --git a/src/Blogifier.Admin/Pages/Account/PasswordView.razor b/src/Blogifier.Admin/Pages/Account/PasswordView.razor deleted file mode 100644 index dc54a2f2c..000000000 --- a/src/Blogifier.Admin/Pages/Account/PasswordView.razor +++ /dev/null @@ -1,58 +0,0 @@ -@layout ProfileLayout -@page "/admin/profile/password/" -@inject HttpClient _http -@inject IStringLocalizer _localizer -@inject IToaster _toaster - - - -

@_localizer["change-password"]

-
- @if (Author != null) - { - - -
- - - -
-
- - - -
-
- -
-
- } -
- - -@code { - protected Author Author { get; set; } - protected RegisterModel Model { get; set; } - protected ToasterComponent Toaster; - - protected override async Task OnInitializedAsync() - { - await Load(); - } - - protected async Task Load() - { - Author = await _http.GetFromJsonAsync("api/author/getcurrent"); - Model = new RegisterModel - { - Name = Author.DisplayName, - Email = Author.Email - }; - } - - protected async Task SavePassword() - { - Toaster.Toast(await _http.PutAsJsonAsync("api/author/changepassword", Model)); - await Load(); - } -} diff --git a/src/Blogifier.Admin/Pages/Account/ProfileView.razor b/src/Blogifier.Admin/Pages/Account/ProfileView.razor deleted file mode 100644 index 5e8ee2454..000000000 --- a/src/Blogifier.Admin/Pages/Account/ProfileView.razor +++ /dev/null @@ -1,87 +0,0 @@ -@layout ProfileLayout -@page "/admin/profile/" -@inject HttpClient _http -@inject IStringLocalizer _localizer -@inject IToaster _toaster - - - -

@_localizer["edit-profile"]

-
- @if (Author != null) - { - - -
- -
- @Author.DisplayName - - -
-
-
- - - -
-
- - - -
-
- - -
-
- - -
-
- -} - - - -@code { - protected ToasterComponent Toaster; - protected List Categories { get; set; } - protected Category CurrentCategory { get; set; } - protected string SearchTerm { get; set; } - protected bool IsEdit = false; - - protected override async Task OnInitializedAsync() - { - await Load(); - } - - protected async Task Load() - { - Categories = await _http.GetFromJsonAsync>($"api/category"); - } - - protected async Task ShowEdit(int categoryId) - { - CurrentCategory = await _http.GetFromJsonAsync($"api/category/byId/{categoryId}"); - IsEdit = true; - } - - protected void CancelEdit() - { - CurrentCategory = null; - IsEdit = false; - } - - protected async Task SaveEdit() - { - Toaster.Toast(await _http.PutAsJsonAsync("api/category", CurrentCategory)); - CurrentCategory = null; - IsEdit = false; - await Load(); - } - - public void CheckAll(object checkValue) - { - bool isChecked = (bool)checkValue; - Categories.ForEach(p => p.Selected = isChecked); - StateHasChanged(); - } - - public async Task RunAction(GroupAction action) - { - string confirm = _localizer["confirm-delete"]; - bool confirmed = false; - - if (action == GroupAction.Delete) - { - confirmed = await _jsruntime.InvokeAsync("confirm", confirm); - if (!confirmed) - return; - } - - foreach (var category in Categories) - { - if (category.Selected) - { - await _http.DeleteAsync($"api/category/{category.Id}"); - } - } - await Load(); - } - - protected async Task SearchKeyPress(KeyboardEventArgs e) - { - if (e.Key == "Enter") - await SearchCategories(); - } - - protected async Task SearchCategories() - { - if (string.IsNullOrEmpty(SearchTerm)) - SearchTerm = "*"; - - Categories = await _http.GetFromJsonAsync>($"api/category/{SearchTerm}"); - SearchTerm = ""; - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await _jsruntime.InvokeVoidAsync("commonJsFunctions.setTooltip"); - } - } -} diff --git a/src/Blogifier.Admin/Pages/Blog/Editor.razor b/src/Blogifier.Admin/Pages/Blog/Editor.razor deleted file mode 100644 index 11133bb9d..000000000 --- a/src/Blogifier.Admin/Pages/Blog/Editor.razor +++ /dev/null @@ -1,245 +0,0 @@ -@page "/admin/editor/{Slug?}/" -@inject HttpClient _http -@inject IStringLocalizer _localizer -@inject IJSRuntime _jsruntime -@inject IToaster _toaster -@inject NavigationManager _navigation -@inject BlogStateProvider _stateprovider -@implements IDisposable - - - -@if (Post != null) -{ -
-
- @_localizer[ -
-
- @if (Post.Id == 0) - { - - - } - else - { - if (Post.Published > DateTime.MinValue) - { - - - - - - - - @_localizer["view"] - - } - else - { - - - - } - } -
-
-
-
- - -
- - -
-
-
-
- -
-} - - - -@code -{ - protected Author Author { get; set; } - protected Post Post { get; set; } - protected ToasterComponent Toaster; - protected CategoriesComponent Categories; - protected string Confirm { get; set; } - protected string PostTile - { - get - { - return _stateprovider.PostType == PostType.Post ? _localizer["post-title"] : _localizer["page-title"]; - } - } - - [Parameter] -#nullable enable - public string? Slug { get; set; } -#nullable disable - - protected override async Task OnInitializedAsync() - { - Author = await _http.GetFromJsonAsync("api/author/getcurrent"); - Confirm = _localizer["confirm-delete"]; - _stateprovider.OnChange += StateHasChanged; - await Load(); - } - - protected async Task Load() - { - Post = NewPost(); - - if (!string.IsNullOrEmpty(Slug)) - { - Post = await _http.GetFromJsonAsync($"api/post/byslug/{Slug}"); - var headTitle = _localizer["edit"] + " - " + Post.Title; - await _jsruntime.InvokeVoidAsync("commonJsFunctions.setTitle", headTitle); - } - } - - protected async Task SavePost(PostAction postAction) - { - Post.Content = await _jsruntime.InvokeAsync("commonJsFunctions.getEditorValue", ""); - Post.PostType = _stateprovider.PostType; - - Post.Cover = await _jsruntime.InvokeAsync("commonJsFunctions.getSrcValue", "postCover"); - Post.Cover = Post.Cover.Replace(_navigation.BaseUri, ""); - - if(string.IsNullOrEmpty(Post.Cover)) - Post.Cover = Constants.DefaultCover; - - if (string.IsNullOrEmpty(Post.Description)) - Post.Description = Post.Title; - - if (string.IsNullOrEmpty(Post.Title) || string.IsNullOrEmpty(Post.Content)) - { - _toaster.Error(_localizer["title-content-required"]); - return; - } - - if (postAction == PostAction.Publish) - Post.Published = DateTime.UtcNow; - - if (postAction == PostAction.Unpublish) - Post.Published = DateTime.MinValue; - - if (Post.Id == 0) - { - Post.Slug = await _http.GetStringAsync($"api/post/getslug/{Post.Title}"); - HttpResponseMessage result = await _http.PostAsJsonAsync($"api/post/add", Post); - - if(result.IsSuccessStatusCode) - { - Post = await _http.GetFromJsonAsync($"api/post/byslug/{Post.Slug}"); - await _http.PutAsJsonAsync>($"api/category/{Post.Id}", Categories.PostCategories); - - if (postAction == PostAction.Publish) - { - await _http.GetFromJsonAsync($"api/newsletter/send/{Post.Id}"); - } - - _navigation.NavigateTo($"/admin/editor/{Post.Slug}"); - await Load(); - } - - Toaster.Toast(result); - } - else - { - Toaster.Toast(await _http.PutAsJsonAsync($"api/post/update", Post)); - - await _http.PutAsJsonAsync>($"api/category/{Post.Id}", Categories.PostCategories); - - await _http.GetFromJsonAsync($"api/newsletter/send/{Post.Id}"); - await Load(); - } - } - - protected async Task Save() - { - await SavePost(PostAction.Save); - } - - protected async Task Publish() - { - await SavePost(PostAction.Publish); - } - - protected async Task Unpublish() - { - await SavePost(PostAction.Unpublish); - } - - protected async Task Remove(int id) - { - if (await _jsruntime.InvokeAsync("confirm", Confirm)) - { - Toaster.Toast(await _http.DeleteAsync($"api/post/{id}")); - _navigation.NavigateTo($"admin"); - } - } - - protected async Task ResetCover() - { - Post.Cover = Constants.DefaultCover; - await Save(); - } - - protected async Task RemoveCover() - { - Post.Cover = null; - await Save(); - } - - protected Post NewPost() - { - return new Post - { - Id = 0, - Title = "", - Description = "", - Content = "", - AuthorId = Author.Id, - PostType = PostType.Post, - Cover = Constants.DefaultCover - }; - } - - public void Dispose() - { - _stateprovider.OnChange -= StateHasChanged; - } -} diff --git a/src/Blogifier.Admin/Pages/Blog/ImportView.razor b/src/Blogifier.Admin/Pages/Blog/ImportView.razor deleted file mode 100644 index 27622d2bb..000000000 --- a/src/Blogifier.Admin/Pages/Blog/ImportView.razor +++ /dev/null @@ -1,134 +0,0 @@ -@layout BlogLayout -@page "/admin/blog/import/" -@inject HttpClient _http -@inject IStringLocalizer _localizer -@inject IToaster _toaster -@inject IJSRuntime JSRuntime - - - -@if (Posts == null) -{ -

@_localizer["import"]

-
- - -
- - - -
-
- - - -
-
- -
-
-
-} -else { -
-
@Posts.Count @_localizer["import-message-found"].
-
- -
-
    - @foreach (var post in Posts) - { -
  • - - - -
    @post.Title
    -
    @post.Published.ToFriendlyShortDateString()
    -
  • - } -
-
@StatusMsg
-
- - -
-
-} - -@code { - protected ImportModel ImportModel { get; set; } - protected List Posts { get; set; } - protected string StatusMsg { get; set; } - protected string StatusMsgCss { get; set; } - - protected override void OnInitialized() - { - Load(); - } - - protected void Load() - { - ImportModel = new ImportModel { FeedUrl = "" }; - Posts = null; - StatusMsg = ""; - StatusMsgCss = "d-none"; - } - - protected async Task GetEntries() - { - Posts = await _http.GetFromJsonAsync>($"api/syndication/getitems?feedUrl={ImportModel.FeedUrl}&baseUrl={ImportModel.BaseUrl}"); - } - - protected async Task Import() - { - int successCnt = 0; - int failedCnt = 0; - - foreach (var post in Posts) - { - if (!post.Selected) - continue; - - var result = await _http.PostAsJsonAsync("api/syndication/import", post); - if (result.IsSuccessStatusCode) - { - await JSRuntime.InvokeAsync("commonJsFunctions.replaceElement", post.Slug, true); - successCnt++; - } - else - { - await JSRuntime.InvokeAsync("commonJsFunctions.replaceElement", post.Slug, false); - failedCnt++; - } - } - - if (failedCnt == 0 && successCnt > 0) { - StatusMsg = $"Imported {successCnt} posts."; - StatusMsgCss = $"alert-success"; - } - else { - StatusMsg = $"Imported {successCnt} posts out of {successCnt + failedCnt}. Please check logs for errors."; - StatusMsgCss = $"alert-warning"; - } - } - - public void CheckAll(object checkValue) - { - bool isChecked = (bool)checkValue; - Posts.ForEach(p => p.Selected = isChecked); - StateHasChanged(); - } -} diff --git a/src/Blogifier.Admin/Pages/Blog/PostsView.razor b/src/Blogifier.Admin/Pages/Blog/PostsView.razor deleted file mode 100644 index ea7167d36..000000000 --- a/src/Blogifier.Admin/Pages/Blog/PostsView.razor +++ /dev/null @@ -1,321 +0,0 @@ -@layout BlogLayout -@page "/admin/blog/" -@using Blogifier.Shared -@inject HttpClient _http -@inject IStringLocalizer _localizer -@inject IJSRuntime _jsruntime -@inject IToaster _toaster -@inject NavigationManager _navigation -@inject BlogStateProvider _stateprovider -@implements IDisposable - - - - -
- - - - @_localizer["new-post"] - - - - - - - -
- - - -@if (Posts != null && Posts.Count > 0) -{ -
-
    - - - @{ - string pubDate = post.Published > DateTime.MinValue ? post.Published.ToFriendlyShortDateString() : @_localizer["draft"]; - string pubStatus = post.Published > DateTime.MinValue ? "published" : ""; - string featured = post.IsFeatured ? "featured" : ""; - } - -
  • - - - @post.Title - - - @pubDate - - - - - - - - - - -
  • -
    -
-
-} -else -{ -
- - - - -

@_localizer["list-is-empty"]

-
-} - - -@code { - protected List Posts { get; set; } - protected Author Author { get; set; } - - protected string SearchTerm { get; set; } - - protected string FilterLabel { get; set; } - protected PublishedStatus FilterValue { get; set; } - - protected string PostTypeLabel { get; set; } - protected string PostTypeButton { get; set; } - protected PostType PostTypeValue { get; set; } - - protected override async Task OnInitializedAsync() - { - Author = await _http.GetFromJsonAsync("api/author/getcurrent"); - - FilterLabel = _localizer["all"]; - PostTypeLabel = _localizer["posts"]; - PostTypeButton = _localizer["post"]; - - _stateprovider.OnChange += StateHasChanged; - _stateprovider.PostType = PostType.Post; - PostTypeValue = PostType.Post; - - await Load(); - } - - protected async Task Load() - { - Posts = await _http.GetFromJsonAsync>($"api/post/list/{FilterValue}/{PostTypeValue}"); - } - - public void CheckAll(object checkValue) - { - bool isChecked = (bool)checkValue; - Posts.ForEach(p => p.Selected = isChecked); - StateHasChanged(); - } - - public async Task RunAction(GroupAction action) - { - string confirm = _localizer["confirm-delete"]; - bool confirmed = false; - - if (action == GroupAction.Delete) - { - confirmed = await _jsruntime.InvokeAsync("confirm", confirm); - if (!confirmed) - return; - } - - foreach (var post in Posts) - { - if (post.Selected) - { - switch (action) - { - case GroupAction.Publish: - await _http.PutAsJsonAsync($"api/post/publish/{post.Id}", true); - break; - case GroupAction.Unpublish: - await _http.PutAsJsonAsync($"api/post/publish/{post.Id}", false); - break; - case GroupAction.Feature: - await _http.PutAsJsonAsync($"api/post/featured/{post.Id}", !post.IsFeatured); - break; - case GroupAction.Delete: - await _http.DeleteAsync($"api/post/{post.Id}"); - break; - } - } - } - await Load(); - } - - protected async Task SearchKeyPress(KeyboardEventArgs e) - { - if (e.Key == "Enter") - await SearchPosts(); - } - - protected async Task SearchPosts() - { - if (string.IsNullOrEmpty(SearchTerm)) - SearchTerm = "*"; - - Posts = await _http.GetFromJsonAsync>($"api/post/list/search/{SearchTerm}"); - SearchTerm = ""; - } - - protected async Task GetPosts() - { - PostTypeLabel = _localizer["posts"]; - PostTypeButton = _localizer["post"]; - PostTypeValue = PostType.Post; - _stateprovider.SetPostType(PostType.Post); - await Load(); - } - - protected async Task GetPages() - { - PostTypeLabel = _localizer["pages"]; - PostTypeButton = _localizer["page"]; - PostTypeValue = PostType.Page; - _stateprovider.SetPostType(PostType.Page); - await Load(); - } - - public async Task Filter(PublishedStatus filter) - { - FilterValue = filter; - switch (filter) - { - case PublishedStatus.Published: - FilterLabel = _localizer["published"]; - break; - case PublishedStatus.Drafts: - FilterLabel = _localizer["draft", true]; - break; - case PublishedStatus.Featured: - FilterLabel = _localizer["featured"]; - break; - default: - FilterLabel = _localizer["all"]; - break; - } - await Load(); - } - - public async Task Publish(Post post) - { - Toast(await _http.PutAsJsonAsync($"api/post/publish/{post.Id}", (post.Published == DateTime.MinValue))); - await Load(); - } - - public async Task Featured(Post post) - { - Toast(await _http.PutAsJsonAsync($"api/post/featured/{post.Id}", !post.IsFeatured)); - await Load(); - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await _jsruntime.InvokeVoidAsync("commonJsFunctions.setTooltip"); - } - } - - protected void Toast(HttpResponseMessage msg) - { - if (msg.IsSuccessStatusCode) - _toaster.Success(_localizer["completed"]); - else - _toaster.Error(_localizer["generic-error"]); - } - - protected void Open() { - } - - public void Dispose() - { - _stateprovider.OnChange -= StateHasChanged; - } -} diff --git a/src/Blogifier.Admin/Pages/Blog/SettingsView.razor b/src/Blogifier.Admin/Pages/Blog/SettingsView.razor deleted file mode 100644 index d1babb613..000000000 --- a/src/Blogifier.Admin/Pages/Blog/SettingsView.razor +++ /dev/null @@ -1,48 +0,0 @@ -@layout BlogLayout -@page "/admin/blog/settings/" -@inject HttpClient _http -@inject IStringLocalizer _localizer -@inject IToaster _toaster -@inject IJSRuntime JSRuntime - - - -@if (Blog != null) -{ -

@_localizer["blog-settings"]

-
- - - -
-
- - -
-
-
- - -
-
- -
-
-
-} - - -@code { - protected Blog Blog { get; set; } - protected ToasterComponent Toaster; - - protected override async Task OnInitializedAsync() - { - Blog = await _http.GetFromJsonAsync("api/blog"); - } - - protected async Task Save() - { - Toaster.Toast(await _http.PutAsJsonAsync("api/blog", Blog)); - } -} diff --git a/src/Blogifier.Admin/Pages/Blogs/BlogsView.razor b/src/Blogifier.Admin/Pages/Blogs/BlogsView.razor new file mode 100644 index 000000000..c5eb0e836 --- /dev/null +++ b/src/Blogifier.Admin/Pages/Blogs/BlogsView.razor @@ -0,0 +1,249 @@ +@page "/admin/blogs" + +@layout BlogsLayout + +@inject HttpClient _http +@inject IMapper _mapper +@inject NavigationManager _navigation +@inject IStringLocalizer _localizer +@inject IJSRuntime _jsruntime +@inject IToaster _toaster + +@code { + + protected List? Posts { get; set; } + protected string? SearchTerm { get; set; } + protected PublishedStatus FilterValue { get; set; } = PublishedStatus.All; + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + protected async Task LoadAsync() + { + var result = await _http.GetFromJsonAsync>($"api/post/items/{(int)FilterValue}/{(int)PostType.Post}"); + Posts = _mapper.Map>(result); + } + + public void CheckAll(object? checkValue) + { + if (checkValue != null) + { + var isChecked = (bool)checkValue; + if (Posts != null) Posts.ForEach(item => item.Selected = isChecked); + StateHasChanged(); + } + } + + private async Task RunAction(GroupAction action) + { + if (action == GroupAction.Delete) + { + var confirmedString = _localizer["confirm-delete"].Value; + var confirmed = await _jsruntime.InvokeAsync("confirm", confirmedString); + if (!confirmed) return; + } + + if (Posts != null) + { + var ids = Posts.Where(m => m.Selected).Select(m => m.Id); + if (ids.Any()) + { + var idsString = string.Join(",", ids); + switch (action) + { + case GroupAction.Publish: + await _http.PutAsJsonAsync($"api/post/state/{idsString}", PostState.Release); + break; + case GroupAction.Unpublish: + await _http.PutAsJsonAsync($"api/post/state/{idsString}", PostState.Draft); + break; + case GroupAction.Delete: + await _http.DeleteAsync($"api/post/{idsString}"); + break; + } + } + } + await LoadAsync(); + } + + protected async Task SearchKeyPress(KeyboardEventArgs e) + { + if (e.Key == "Enter") + await SearchPosts(); + } + + protected async Task SearchPosts() + { + if (string.IsNullOrEmpty(SearchTerm)) SearchTerm = "*"; + var result = await _http.GetFromJsonAsync>($"api/post/items/search/{SearchTerm}"); + Posts = _mapper.Map>(result); + } + + public async Task Filter(PublishedStatus filter) + { + FilterValue = filter; + await LoadAsync(); + } + + public async Task Publish(PostItemDto post) + { + var state = post.State != PostState.Release ? PostState.Release : PostState.Draft; + Toast(await _http.PutAsJsonAsync($"api/post/state/{post.Id}", state)); + await LoadAsync(); + } + + public async Task Featured(PostItemDto post) + { + var state = post.State != PostState.Featured ? PostState.Featured : PostState.Release; + Toast(await _http.PutAsJsonAsync($"api/post/state/{post.Id}", state)); + await LoadAsync(); + } + + protected void Toast(HttpResponseMessage msg) + { + if (msg.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } +} + + + +
+ + @_localizer["new-post"] + + + + +
+ + + +@if (Posts != null && Posts.Count > 0) +{ +
+
    + +
  • + + + @post.Title + + + @DateTimeHelper.ToFriendlyShortDateString(post.PublishedAt, _localizer["draft"]) + + + + + + + + + + +
  • +
    +
+
+} +else +{ +
+ + + + +

@_localizer["list-is-empty"]

+
+} diff --git a/src/Blogifier.Admin/Pages/Blogs/CategoryEditorView.razor b/src/Blogifier.Admin/Pages/Blogs/CategoryEditorView.razor new file mode 100644 index 000000000..032f65a90 --- /dev/null +++ b/src/Blogifier.Admin/Pages/Blogs/CategoryEditorView.razor @@ -0,0 +1,68 @@ +@page "/admin/blogs/category/{Id:int?}" + +@layout BlogsLayout + +@inject HttpClient _http +@inject IMapper _mapper +@inject NavigationManager _navigation +@inject IStringLocalizer _localizer +@inject IJSRuntime _jsruntime +@inject IToaster _toaster + +

@_localizer["edit-category"]

+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +@code { + + [Parameter] public int? Id { get; set; } + + protected CategoryEitorDto Category { get; set; } = new CategoryEitorDto(); + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + protected async Task LoadAsync() + { + var result = await _http.GetFromJsonAsync($"api/category/byId/{Id}"); + if (result != null) Category = result; + } + + protected void Cancel() + { + _navigation.NavigateTo("/admin/blogs/category"); + } + + protected async Task Save() + { + Toast(await _http.PutAsJsonAsync("api/category", Category)); + Cancel(); + } + + protected void Toast(HttpResponseMessage msg) + { + if (msg.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } + +} diff --git a/src/Blogifier.Admin/Pages/Blogs/CategoryView.razor b/src/Blogifier.Admin/Pages/Blogs/CategoryView.razor new file mode 100644 index 000000000..c1111b43f --- /dev/null +++ b/src/Blogifier.Admin/Pages/Blogs/CategoryView.razor @@ -0,0 +1,156 @@ +@page "/admin/blogs/category" + +@layout BlogsLayout + +@inject HttpClient _http +@inject IMapper _mapper +@inject NavigationManager _navigation +@inject IStringLocalizer _localizer +@inject IJSRuntime _jsruntime +@inject IToaster _toaster + + +@code { + + protected List? Categories { get; set; } + protected string? SearchTerm { get; set; } + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + protected async Task LoadAsync() + { + var result = await _http.GetFromJsonAsync>($"api/category/items"); + Categories = _mapper.Map>(result); + } + + protected void CheckAll(object? checkValue) + { + if (checkValue != null && Categories != null) + { + var isChecked = (bool)checkValue; + Categories.ForEach(p => p.Selected = isChecked); + StateHasChanged(); + } + } + + public async Task RunAction(GroupAction action) + { + if (action == GroupAction.Delete) + { + var confirm = _localizer["confirm-delete"]; + var confirmed = await _jsruntime.InvokeAsync("confirm", confirm.Value); + if (!confirmed) return; + } + + if (Categories != null) + { + var categoryeId = Categories.Where(m => m.Selected).Select(m => m.Id); + if (categoryeId.Any()) + { + var categoryeIdsString = string.Join(",", categoryeId); + await _http.DeleteAsync($"api/category/{categoryeIdsString}"); + } + } + await LoadAsync(); + } + + protected async Task SearchKeyPress(KeyboardEventArgs e) + { + if (e.Key == "Enter") + await SearchCategories(); + } + + protected async Task SearchCategories() + { + if (string.IsNullOrEmpty(SearchTerm)) + SearchTerm = "*"; + + Categories = await _http.GetFromJsonAsync>($"api/category/{SearchTerm}"); + SearchTerm = ""; + } + + protected void OnEdit(int categoryId) + { + _navigation.NavigateTo($"/admin/blogs/category/{categoryId}"); + } +} + + + +
+
+ + @*@_localizer["new-category"]*@ + @if (Categories != null && Categories.Count > 0) + { + + } + +
+ + + + @if (Categories != null && Categories.Any()) + { + + } + else + { +
+ + + + +

@_localizer["list-is-empty"]

+
+ } +
+ + diff --git a/src/Blogifier.Admin/Pages/Blogs/EditorView.razor b/src/Blogifier.Admin/Pages/Blogs/EditorView.razor new file mode 100644 index 000000000..861dfe9b8 --- /dev/null +++ b/src/Blogifier.Admin/Pages/Blogs/EditorView.razor @@ -0,0 +1,181 @@ +@page "/admin/blogs/editor/{Slug?}" + +@inject HttpClient _httpClient +@inject IStringLocalizer _localizer +@inject NavigationManager _navigation +@inject IJSRuntime _jsruntime +@inject IToaster _toaster +@inject ToasterService _toasterService +@inject ILogger _logger + + + +
+
+ @_localizer[ +
+
+ @if (string.IsNullOrEmpty(Post.Slug)) + { + + + } + else if (Post.State >= PostState.Release) + { + + + + + + + + @_localizer["view"] + + } + else + { + + + + } +
+
+
+
+ + +
+ + +
+
+
+
+ +
+ +@code { + + [Parameter] public string? Slug { get; set; } + + private EditorComponent _editorComponent = default!; + + protected PostEditorDto Post { get; set; } = new PostEditorDto + { + Title = string.Empty, + Description = string.Empty, + Content = string.Empty, + PostType = PostType.Post, + Cover = BlogifierConstant.DefaultCover, + Categories = new List(), + }; + + protected override async Task OnInitializedAsync() + { + if (!string.IsNullOrEmpty(Slug)) + { + Post = (await _httpClient.GetFromJsonAsync($"api/post/byslug/{Slug}"))!; + if (Post.Categories == null) Post.Categories = new List(); + await SetJsPostAsync(Post); + } + } + + protected async Task SavePostAsync(PostState postState) + { + var content = await _editorComponent.GetValueAsync(); + if (string.IsNullOrEmpty(Post.Title) || string.IsNullOrEmpty(content)) + { + _toaster.Error(_localizer["title-content-required"]); + return; + } + Post.Content = content; + Post.Cover = await _jsruntime.InvokeAsync("commonJsFunctions.getSrcValue", "postCover"); + Post.Cover = Post.Cover.Replace(_navigation.BaseUri, ""); + if (string.IsNullOrEmpty(Post.Cover)) Post.Cover = BlogifierConstant.DefaultCover; + if (string.IsNullOrEmpty(Post.Description)) Post.Description = Post.Title; + Post.State = postState; + if (Post.Id == 0) + { + var response = await _httpClient.PostAsJsonAsync($"api/post/add", Post); + _toasterService.CheckResponse(response); + } + else + { + var response = await _httpClient.PutAsJsonAsync($"api/post/update", Post); + _toasterService.CheckResponse(response); + } + } + + protected async Task SaveAsync() + { + await SavePostAsync(PostState.Draft); + } + + protected async Task PublishAsync() + { + await SavePostAsync(PostState.Release); + } + + protected async Task UnpublishAsync() + { + await SavePostAsync(PostState.Draft); + } + + protected async Task RemoveAsync(int id) + { + if (await _jsruntime.InvokeAsync("confirm", _localizer["confirm-delete"])) + { + var response = await _httpClient.DeleteAsync($"api/post/{id}"); + _toasterService.CheckResponse(response); + _navigation.NavigateTo($"admin"); + } + } + + protected async Task ResetCoverAsync() + { + Post.Cover = BlogifierConstant.DefaultCover; + await SaveAsync(); + } + + protected async Task RemoveCoverAsync() + { + Post.Cover = null; + await SaveAsync(); + } + + private async Task SetJsPostAsync(PostEditorDto post) + { + var headTitle = _localizer["edit"] + " - " + Post.Title; + await _jsruntime.InvokeVoidAsync("commonJsFunctions.setTitle", headTitle); + await _editorComponent.SetValueAsync(Post.Content); + } +} diff --git a/src/Blogifier.Admin/Pages/Blogs/ImportView.razor b/src/Blogifier.Admin/Pages/Blogs/ImportView.razor new file mode 100644 index 000000000..06b3081a3 --- /dev/null +++ b/src/Blogifier.Admin/Pages/Blogs/ImportView.razor @@ -0,0 +1,155 @@ +@page "/admin/blogs/import" +@layout BlogsLayout +@inject HttpClient _http +@inject IStringLocalizer _localizer +@inject IToaster _toaster +@inject IJSRuntime _jsRuntime +@inject IMapper _mapper + + + +@if (Status != null) +{ +
@Status.Msg
+} +@if (Import == null) +{ +

@_localizer["import"]

+
+ + +
+ + + +
+
+ +
+
+
+} +else +{ +
+
@Import.Posts.Count @_localizer["import-message-found"].
+
+ +
+
    + @foreach (var post in Import.Posts) + { +
  • + + @if (!post.ImportComplete.HasValue) + { + + } + else if (post.ImportComplete.Value) + { + + + + + + } + else + { + + + + + + } +
    @post.Title
    +
    @DateTimeHelper.ToFriendlyShortDateString(post.PublishedAt)
    +
  • + } +
+
+ + +
+
+} + +@code { + protected ImportRssDto ImportRss { get; set; } = default!; + protected FrontImportDto? Import { get; set; } + protected AlertStatus? Status { get; set; } + + protected override void OnInitialized() + { + OnLoad(); + } + + protected void OnLoad() + { + ImportRss = new ImportRssDto { FeedUrl = string.Empty }; + Import = null; + Status = null; + } + + protected async Task OnAnalysis() + { + Status = null; + var result = await _http.GetFromJsonAsync($"api/import/rss?feedUrl={ImportRss.FeedUrl}"); + var front = _mapper.Map(result); + Import = front; + } + + protected async Task OnImport() + { + var request = new ImportDto { BaseUrl = Import!.BaseUrl, Posts = new List() }; + foreach (var post in Import!.Posts!) + { + if (!post.Selected) continue; + var inputPost = _mapper.Map(post); + request.Posts.Add(inputPost); + } + if (!request.Posts.Any()) return; + + var response = await _http.PostAsJsonAsync("api/import/write", request); + if (response.IsSuccessStatusCode) + { + var stream = await response.Content.ReadAsStreamAsync(); + var inputPosts = (await JsonSerializer.DeserializeAsync>(stream, BlogifierConstant.DefaultJsonSerializerOptionss))!; + var successCount = 0; + foreach (var post in inputPosts) + { + var importPost = Import.Posts.First(m => m.Title.Equals(post.Title, StringComparison.Ordinal)); + importPost.ImportComplete = true; + successCount++; + } + Status = new AlertStatus($"Imported {successCount} posts.", "alert-success"); + } + else + { + Status = new AlertStatus("import posts errors.", "alert-warning"); + } + } + + protected void CheckAll(object? checkValue) + { + bool isChecked = checkValue != null ? (bool)checkValue : false; + Import!.Posts.ForEach(p => p.Selected = isChecked); + StateHasChanged(); + } + + protected class AlertStatus + { + public string Msg { get; set; } + public string MsgCss { get; set; } + + public AlertStatus(string msg, string msgCss) + { + Msg = msg; + MsgCss = msgCss; + } + } +} diff --git a/src/Blogifier.Admin/Pages/Blogs/SettingsView.razor b/src/Blogifier.Admin/Pages/Blogs/SettingsView.razor new file mode 100644 index 000000000..05216bf85 --- /dev/null +++ b/src/Blogifier.Admin/Pages/Blogs/SettingsView.razor @@ -0,0 +1,56 @@ +@page "/admin/blogs/settings" +@layout BlogsLayout + +@inject HttpClient _http +@inject IStringLocalizer _localizer +@inject IToaster _toaster + +@code { + protected BlogEitorDto? Blog { get; set; } + + protected override async Task OnInitializedAsync() + { + Blog = await _http.GetFromJsonAsync("api/blog"); + } + + protected async Task Save() + { + Toast(await _http.PutAsJsonAsync("api/blog", Blog!)); + } + + protected void Toast(HttpResponseMessage msg) + { + if (msg.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } +} + + + +@if (Blog != null) +{ +

@_localizer["blog-settings"]

+
+ + + +
+
+ + +
+
+
+ + +
+
+ +
+
+
+} + + diff --git a/src/Blogifier.Admin/Pages/Dashboard/DashboardView.razor b/src/Blogifier.Admin/Pages/Dashboard/DashboardView.razor deleted file mode 100644 index b32b22d6d..000000000 --- a/src/Blogifier.Admin/Pages/Dashboard/DashboardView.razor +++ /dev/null @@ -1,34 +0,0 @@ -@page "/admin/" -@inject HttpClient _http -@inject IStringLocalizer _localizer - - - diff --git a/src/Blogifier.Admin/Pages/Drive/DriveView.razor b/src/Blogifier.Admin/Pages/Drive/DriveView.razor index ba6418438..9edbd18c3 100644 --- a/src/Blogifier.Admin/Pages/Drive/DriveView.razor +++ b/src/Blogifier.Admin/Pages/Drive/DriveView.razor @@ -1,14 +1,17 @@ @page "/admin/drive/" @inject IStringLocalizer _localizer + + +
-

@_localizer["Drive"]

-
-

Drive is a file storage that you are able to upload and manage your files and use throughout the website.

-
- @_localizer["under-development"]. - - @_localizer["more-info"] - -
+

@_localizer["drive"]

+
+

@_localizer["drive-describe"]

+
+ @_localizer["under-development"]. + + @_localizer["more-info"] +
+
diff --git a/src/Blogifier.Admin/Pages/HomeView.razor b/src/Blogifier.Admin/Pages/HomeView.razor new file mode 100644 index 000000000..6eb030280 --- /dev/null +++ b/src/Blogifier.Admin/Pages/HomeView.razor @@ -0,0 +1,282 @@ +@using ChartJs.Blazor +@using ChartJs.Blazor.Common +@using ChartJs.Blazor.Common.Enums +@using ChartJs.Blazor.Util +@using ChartJs.Blazor.BarChart +@using System.Drawing + +@page "/admin" + +@inject HttpClient _http +@inject IStringLocalizer _localizer +@inject IToaster _toaster + +@code { + + protected BarConfig _config = default!; + protected List _dateOptions = default!; + protected List _visits = default!; + protected bool _hideGraph = false; + protected bool _hideList = true; + protected AnalyticsDto? _analytics; + + protected override async Task OnInitializedAsync() + { + _config = new BarConfig + { + Options = new BarOptions + { + Responsive = true, + Legend = new Legend + { + Position = Position.Top + } + } + }; + _dateOptions = new List + { + new OptionItem { Id = 1, Title = _localizer["today"] }, + new OptionItem { Id = 2, Title = _localizer["yesterday"] }, + new OptionItem { Id = 3, Title = _localizer["7-days"] }, + new OptionItem { Id = 4, Title = _localizer["30-days"] }, + new OptionItem { Id = 5, Title = _localizer["90-days"] }, + }; + Load(); + _analytics = await _http.GetFromJsonAsync("api/analytics"); + } + + protected void Load() + { + var dataset = new BarDataset() + { + Label = "Latest Post Views", + BackgroundColor = ColorUtil.FromDrawingColor(Color.FromArgb(98, 42, 255)), + BorderWidth = 0 + }; + + if (_analytics == null || _analytics.LatestPostViews == null) + { + LoadData(dataset, TestData()); + } + else + { + _hideList = _analytics.DisplayType == AnalyticsListType.Graph; + _hideGraph = _analytics.DisplayType == AnalyticsListType.List; + + LoadData(dataset, _analytics.LatestPostViews); + } + + if (_config.Data.Datasets.Count > 0) + { + _config.Data.Datasets.Clear(); + } + + _config.Data.Datasets.Add(dataset); + } + + + + protected void LoadData(IDataset dataset, BarChartModel model) + { + _visits = new List(); + var labels = model.Labels.ToList(); + var values = model.Data.ToList(); + + _config.Data.Labels.Clear(); + + for (int i = 0; i < labels.Count; i++) + { + _config.Data.Labels.Add(labels[i]); + dataset.Add(values[i]); + + _visits.Add(new PostVisit { Name = labels[i], Value = values[i] }); + } + _visits = _visits.OrderByDescending(v => v.Value).ToList(); + } + + protected async Task ToggleAnalyticsView(bool isGraph) + { + _hideGraph = await Task.FromResult(!isGraph); + _hideList = isGraph; + + int typeId = isGraph ? 2 : 1; + + Toast(await _http.PutAsJsonAsync($"api/analytics/displayType/{typeId}", typeId)); + } + + protected void DateOptionSelect(int id) + { + //var blog = await _http.GetFromJsonAsync("api/blog"); + //blog.AnalyticsPeriod = id; + //_analytics.DisplayPeriod = (AnalyticsPeriod)id; + //Toast(await _http.PutAsJsonAsync("api/blog", blog)); + //Load(); + } + + protected BarChartModel TestData() + { + return new BarChartModel() + { + Labels = new List() { "Post One", "Post Two", "Post Three", "Post Four", "Post Five", "Post Six" }, + Data = new List() { 12036, 15350, 9457, 11104, 7938, 8136 } + }; + } + + protected string PeriodLabel() + { + if (_analytics == null) + return ""; + return _analytics.DisplayPeriod.ToString(); + } + + protected void Toast(HttpResponseMessage msg) + { + if (msg.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } +} + + + +
+
+ +
+
+
+
@_localizer["analytics"]
+ +
+ + + + + + + +
+ +
+
+
diff --git a/src/Blogifier.Admin/Pages/Newsletter/NewsletterView.razor b/src/Blogifier.Admin/Pages/Newsletter/NewsletterView.razor index 6ba85d109..906898584 100644 --- a/src/Blogifier.Admin/Pages/Newsletter/NewsletterView.razor +++ b/src/Blogifier.Admin/Pages/Newsletter/NewsletterView.razor @@ -1,83 +1,87 @@ -@layout NewsletterLayout @page "/admin/newsletter/" + +@layout NewsletterLayout + @inject HttpClient _http @inject IStringLocalizer _localizer @inject IToaster _toaster - +

@_localizer["newsletters"]

- @if (Newsletters == null || Newsletters.Count == 0) - { -

@_localizer["not-found"]

- } - else - { -
    - @foreach (var newsletter in Newsletters) + @if (Newsletters != null && Newsletters.Any()) + { +
      + @foreach (var item in Newsletters) + { +
    • + @item.Post.Title + - @pubDate - -
    • + + + } -
    - } + + @DateTimeHelper.ToFriendlyShortDateString(item.CreatedAt) + + + } +
+ } + else + { +

@_localizer["not-found"]

+ }
- + @code { - protected ToasterComponent Toaster; - protected List Newsletters; + protected List? Newsletters; + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } - protected override async Task OnInitializedAsync() - { - await Load(); - } + protected async Task LoadAsync() + { + Newsletters = await _http.GetFromJsonAsync>($"api/newsletter/items"); + } - protected async Task Load() - { - Newsletters = await _http.GetFromJsonAsync>($"api/newsletter/newsletters"); - } + protected async Task DeleteAsync(int id) + { + Toast(await _http.DeleteAsync($"api/newsletter/remove/{id}")); + await LoadAsync(); + } - protected async Task RemoveNewsletter(int id) - { - Toaster.Toast(await _http.DeleteAsync($"api/newsletter/remove/{id}")); - await Load(); - } + protected async Task ResendAsync(int postId) + { + bool success = await _http.GetFromJsonAsync($"api/newsletter/send/{postId}"); + if (success) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + await LoadAsync(); + } - protected async Task Resend(int postId) - { - bool success = await _http.GetFromJsonAsync($"api/newsletter/send/{postId}"); - if (success) - _toaster.Success(_localizer["completed"]); - else - _toaster.Error(_localizer["generic-error"]); - await Load(); - } + protected void Toast(HttpResponseMessage msg) + { + if (msg.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } } diff --git a/src/Blogifier.Admin/Pages/Newsletter/SettingsView.razor b/src/Blogifier.Admin/Pages/Newsletter/SettingsView.razor index ce7ea4f81..f7ffa8ac1 100644 --- a/src/Blogifier.Admin/Pages/Newsletter/SettingsView.razor +++ b/src/Blogifier.Admin/Pages/Newsletter/SettingsView.razor @@ -1,77 +1,86 @@ -@layout NewsletterLayout +@layout NewsletterLayout + @page "/admin/newsletter/settings/" + @inject HttpClient _http @inject IStringLocalizer _localizer +@inject IToaster _toaster - +

@_localizer["newsletter-settings"]

- @if (Mail != null) - { - - - -
- - -
-
- - -
-
- - -
-
- - -
- -
- - -
-
- - -
-
- - -
-
-
- - -
-
-
- -
-
- } + + + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ +
+
- @code { - protected ToasterComponent Toaster; - protected Blog Blog { get; set; } - protected MailSetting Mail { get; set; } + protected MailSettingDto Mail { get; set; } = new MailSettingDto(); - protected override async Task OnInitializedAsync() + protected override async Task OnInitializedAsync() + { + var response = await _http.GetAsync("api/mail/settings"); + if (response.IsSuccessStatusCode) { - Blog = await _http.GetFromJsonAsync("api/blog"); - Mail = await _http.GetFromJsonAsync("api/newsletter/mailsettings"); - if (Mail == null) - Mail = new MailSetting(); + var stream = await response.Content.ReadAsStreamAsync(); + if (stream.Length > 0) + { + Mail = (await JsonSerializer.DeserializeAsync(stream, BlogifierConstant.DefaultJsonSerializerOptionss))!; + } } + } - protected async Task Save() - { - Toaster.Toast(await _http.PutAsJsonAsync("api/newsletter/mailsettings", Mail)); - } + protected async Task SaveAsync() + { + Toast(await _http.PutAsJsonAsync("api/mail/settings", Mail)); + } + + protected void Toast(HttpResponseMessage response) + { + if (response.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } } diff --git a/src/Blogifier.Admin/Pages/Newsletter/SubscribersView.razor b/src/Blogifier.Admin/Pages/Newsletter/SubscribersView.razor index 8d099f9b4..f6aaf2d99 100644 --- a/src/Blogifier.Admin/Pages/Newsletter/SubscribersView.razor +++ b/src/Blogifier.Admin/Pages/Newsletter/SubscribersView.razor @@ -1,58 +1,67 @@ -@layout NewsletterLayout @page "/admin/newsletter/subscribers/" + +@layout NewsletterLayout + @inject HttpClient _http @inject IStringLocalizer _localizer +@inject IToaster _toaster - +

@_localizer["subscribers"]

- - @if (Subscribers == null || Subscribers.Count == 0) - { -

@_localizer["not-found"]

- } - else - { -
    - @foreach (var subscriber in Subscribers) - { -
  • - @{ - string title = $"{subscriber.Email} / {subscriber.Country} / {subscriber.Region} / {subscriber.Ip}"; - string pubDate = subscriber.DateCreated.ToFriendlyDateTimeString(); - } - @title - @pubDate - -
  • - } -
- } + @if (Subscribers != null && Subscribers.Any()) + { +
    + @foreach (var item in Subscribers) + { +
  • + @{ + string title = $"{item.Email} / {item.Country} / {item.Region} / {item.Ip}"; + string pubDate = item.CreatedAt.ToFriendlyDateTimeString(); + } + @title + @pubDate + +
  • + } +
+ } + else + { +

@_localizer["not-found"]

+ }
- @code { - protected ToasterComponent Toaster; - protected List Subscribers; - - protected override async Task OnInitializedAsync() - { - await Load(); - } - - protected async Task Load() - { - Subscribers = await _http.GetFromJsonAsync>($"api/newsletter/subscribers"); - } - - protected async Task RemoveSubscription(int id) - { - Toaster.Toast(await _http.DeleteAsync($"api/newsletter/unsubscribe/{id}")); - await Load(); - } + + protected List? Subscribers; + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + protected async Task LoadAsync() + { + Subscribers = await _http.GetFromJsonAsync>($"api/subscriber/items"); + } + + protected async Task DeleteAsync(int id) + { + Toast(await _http.DeleteAsync($"api/subscriber/{id}")); + await LoadAsync(); + } + + protected void Toast(HttpResponseMessage msg) + { + if (msg.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } } diff --git a/src/Blogifier.Admin/Pages/Pages/Editor.razor b/src/Blogifier.Admin/Pages/Pages/Editor.razor deleted file mode 100644 index d39932483..000000000 --- a/src/Blogifier.Admin/Pages/Pages/Editor.razor +++ /dev/null @@ -1,210 +0,0 @@ -@page "/admin/pages/editor/{Slug?}/" -@inject HttpClient _http -@inject IStringLocalizer _localizer -@inject IJSRuntime _jsruntime -@inject IToaster _toaster -@inject NavigationManager _navigation - - - -
- @if (Post != null) - { - -
- @_localizer[ -
-
- @if (Post.Id == 0) - { - - - } - else - { - if (Post.Published > DateTime.MinValue) - { - - - - - - - - @_localizer["view"] - - } - else - { - - - - } - } -
-
-
-
- - -
- -
-
-
-
- - } -
- - -@code{ - protected Author Author { get; set; } - protected Post Post { get; set; } - protected ToasterComponent Toaster; - protected string Confirm { get; set; } - - [Parameter] -#nullable enable - public string? Slug { get; set; } -#nullable disable - - protected override async Task OnInitializedAsync() - { - Author = await _http.GetFromJsonAsync("api/author/getcurrent"); - Confirm = _localizer["confirm-delete"]; - await Load(); - } - - protected async Task Load() - { - Post = NewPost(); - - if (!string.IsNullOrEmpty(Slug)) - { - Post = await _http.GetFromJsonAsync($"api/post/byslug/{Slug}"); - var headTitle = _localizer["edit"] + " - " + Post.Title; - await _jsruntime.InvokeVoidAsync("commonJsFunctions.setTitle", headTitle); - } - } - - protected async Task SavePost(PostAction postAction) - { - Post.Content = await _jsruntime.InvokeAsync("commonJsFunctions.getEditorValue", ""); - Post.PostType = PostType.Page; - - Post.Cover = await _jsruntime.InvokeAsync("commonJsFunctions.getSrcValue", "postCover"); - if(string.IsNullOrEmpty(Post.Cover)) - Post.Cover = Constants.DefaultCover; - - if (string.IsNullOrEmpty(Post.Description)) - Post.Description = Post.Title; - - if (string.IsNullOrEmpty(Post.Title) || string.IsNullOrEmpty(Post.Content)) - { - _toaster.Error(_localizer["title-content-required"]); - return; - } - - if (postAction == PostAction.Publish) - Post.Published = DateTime.UtcNow; - - if (postAction == PostAction.Unpublish) - Post.Published = DateTime.MinValue; - - if (Post.Id == 0) - { - Post.Slug = await _http.GetStringAsync($"api/post/getslug/{Post.Title}"); - Toaster.Toast(await _http.PostAsJsonAsync($"api/post/add", Post)); - - Post = await _http.GetFromJsonAsync($"api/post/byslug/{Post.Slug}"); - - _navigation.NavigateTo($"/admin/pages/editor/{Post.Slug}"); - await Load(); - } - else - { - Toaster.Toast(await _http.PutAsJsonAsync($"api/post/update", Post)); - await Load(); - } - } - - protected async Task Save() - { - await SavePost(PostAction.Save); - } - - protected async Task Publish() - { - await SavePost(PostAction.Publish); - } - - protected async Task Unpublish() - { - await SavePost(PostAction.Unpublish); - } - - protected async Task Remove(int id) - { - if (await _jsruntime.InvokeAsync("confirm", Confirm)) - { - Toaster.Toast(await _http.DeleteAsync($"api/post/{id}")); - _navigation.NavigateTo($"admin"); - } - } - - protected async Task ResetCover() - { - Post.Cover = Constants.DefaultCover; - await Save(); - } - - protected async Task RemoveCover() - { - Post.Cover = null; - await Save(); - } - - protected Post NewPost() - { - return new Post - { - Id = 0, - Title = "", - Description = "", - Content = "", - AuthorId = Author.Id, - PostType = PostType.Page, - Cover = Constants.DefaultCover - }; - } -} diff --git a/src/Blogifier.Admin/Pages/Pages/EditorView.razor b/src/Blogifier.Admin/Pages/Pages/EditorView.razor new file mode 100644 index 000000000..41f10b742 --- /dev/null +++ b/src/Blogifier.Admin/Pages/Pages/EditorView.razor @@ -0,0 +1,199 @@ +@page "/admin/pages/editor/{Slug?}/" + +@inject HttpClient _httpClient +@inject IStringLocalizer _localizer +@inject IJSRuntime _jsruntime +@inject IToaster _toaster +@inject NavigationManager _navigation + + +
+ @if (Post != null) + { +
+ @_localizer[ +
+
+ @if (string.IsNullOrEmpty(Post.Slug)) + { + + + } + else if (Post.State >= PostState.Release) + { + + + + + + + + @_localizer["view"] + + } + else + { + + + + } +
+
+
+
+ + +
+ +
+
+
+
+ + } +
+ +@code { + + [Parameter] public string? Slug { get; set; } + + private EditorComponent _editorComponent = default!; + + protected PostEditorDto Post { get; set; } = new PostEditorDto + { + Title = string.Empty, + Description = string.Empty, + Content = string.Empty, + PostType = PostType.Page, + Cover = BlogifierConstant.DefaultCover, + Categories = new List(), + }; + + protected override async Task OnInitializedAsync() + { + if (!string.IsNullOrEmpty(Slug)) + { + Post = (await _httpClient.GetFromJsonAsync($"api/post/byslug/{Slug}"))!; + if (Post.Categories == null) Post.Categories = new List(); + await SetJsPostAsync(Post); + } + } + + protected async Task SavePostAsync(PostState postState) + { + Post.State = postState; + Post.Content = await _jsruntime.InvokeAsync("commonJsFunctions.getEditorValue", ""); + if (string.IsNullOrEmpty(Post.Title) || string.IsNullOrEmpty(Post.Content)) + { + _toaster.Error(_localizer["title-content-required"]); + return; + } + + Post.Cover = await _jsruntime.InvokeAsync("commonJsFunctions.getSrcValue", "postCover"); + Post.Cover = Post.Cover.Replace(_navigation.BaseUri, ""); + if (string.IsNullOrEmpty(Post.Cover)) Post.Cover = BlogifierConstant.DefaultCover; + + if (string.IsNullOrEmpty(Post.Description)) Post.Description = Post.Title; + + if (Post.Id == 0) + { + var response = await _httpClient.PostAsJsonAsync($"api/post/add", Post); + await CheckSetEditorResponseAsync(response); + } + else + { + var response = await _httpClient.PutAsJsonAsync($"api/post/update", Post); + await CheckSetEditorResponseAsync(response); + } + } + + protected async Task SaveAsync() + { + await SavePostAsync(PostState.Draft); + } + + protected async Task PublishAsync() + { + await SavePostAsync(PostState.Release); + } + + protected async Task UnpublishAsync() + { + await SavePostAsync(PostState.Draft); + } + + protected async Task RemoveAsync(int id) + { + if (await _jsruntime.InvokeAsync("confirm", _localizer["confirm-delete"])) + { + var result = await _httpClient.DeleteAsync($"api/post/{id}"); + if (result.IsSuccessStatusCode) _toaster.Success(_localizer["completed"]); + else _toaster.Error(_localizer["generic-error"]); + _navigation.NavigateTo($"admin"); + } + } + + protected async Task ResetCoverAsync() + { + Post.Cover = BlogifierConstant.DefaultCover; + await SaveAsync(); + } + + protected async Task RemoveCoverAsync() + { + Post.Cover = null; + await SaveAsync(); + } + + private async Task SetJsPostAsync(PostEditorDto post) + { + var headTitle = _localizer["edit"] + " - " + Post.Title; + await _jsruntime.InvokeVoidAsync("commonJsFunctions.setTitle", headTitle); + await _editorComponent.SetValueAsync(Post.Content); + } + + private async Task CheckSetEditorResponseAsync(HttpResponseMessage response) + { + if (response.IsSuccessStatusCode) + { + var stream = await response.Content.ReadAsStreamAsync(); + Post = (await JsonSerializer.DeserializeAsync(stream, BlogifierConstant.DefaultJsonSerializerOptionss))!; + await SetJsPostAsync(Post); + _toaster.Success(_localizer["completed"]); + } + else + { + _toaster.Error(_localizer["generic-error"]); + } + } + +} diff --git a/src/Blogifier.Admin/Pages/Pages/PagesView.razor b/src/Blogifier.Admin/Pages/Pages/PagesView.razor index 56017d03b..558d73090 100644 --- a/src/Blogifier.Admin/Pages/Pages/PagesView.razor +++ b/src/Blogifier.Admin/Pages/Pages/PagesView.razor @@ -1,276 +1,237 @@ @page "/admin/pages/" -@using Blogifier.Shared + @inject HttpClient _http +@inject IMapper _mapper +@inject NavigationManager _navigation @inject IStringLocalizer _localizer @inject IJSRuntime _jsruntime @inject IToaster _toaster -@inject NavigationManager _navigation -@inject BlogStateProvider _stateprovider -@implements IDisposable - - - -
-
- - - @_localizer["new-page"] - - @if (Posts != null && Posts.Count > 0) - { - - } - - - - - -
- - - - @if (Posts != null && Posts.Count > 0) - { -
    - - - @{ - string pubDate = post.Published > DateTime.MinValue ? post.Published.ToFriendlyShortDateString() : _localizer["draft"]; - string pubStatus = post.Published > DateTime.MinValue ? "published" : ""; - } - -
  • - - - @post.Title - - - @pubDate - - - - - - - - - -
  • -
    -
- } - else - { -
- - - - -

@_localizer["list-is-empty"]

-
- } -
@code { - protected List Posts { get; set; } - protected Author Author { get; set; } - - protected string SearchTerm { get; set; } - - protected string FilterLabel { get; set; } - protected PublishedStatus FilterValue { get; set; } - - protected string PostTypeLabel { get; set; } - protected string PostTypeButton { get; set; } - - protected override async Task OnInitializedAsync() - { - Author = await _http.GetFromJsonAsync("api/author/getcurrent"); - FilterLabel = _localizer["all"]; - PostTypeLabel = _localizer["posts"]; - PostTypeButton = _localizer["post"]; + protected List? Posts { get; set; } + protected string? SearchTerm { get; set; } + protected PublishedStatus FilterValue { get; set; } = PublishedStatus.All; - _stateprovider.OnChange += StateHasChanged; - _stateprovider.PostType = PostType.Post; + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } - await Load(); - } + protected async Task LoadAsync() + { + var result = await _http.GetFromJsonAsync>($"api/post/items/{(int)FilterValue}/{(int)PostType.Page}"); + Posts = _mapper.Map>(result); + } - protected async Task Load() + public void CheckAll(object? checkValue) + { + if (checkValue != null) { - Posts = await _http.GetFromJsonAsync>($"api/post/list/{FilterValue}/{PostType.Page}"); + var isChecked = (bool)checkValue; + foreach (var item in Posts!) + { + item.Selected = isChecked; + } + StateHasChanged(); } + } - public void CheckAll(object checkValue) + private async Task RunAction(GroupAction action) + { + if (action == GroupAction.Delete) { - bool isChecked = (bool)checkValue; - Posts.ForEach(p => p.Selected = isChecked); - StateHasChanged(); + var confirmed = await _jsruntime.InvokeAsync("confirm", _localizer["confirm-delete"]); + if (!confirmed) return; } - public async Task RunAction(GroupAction action) + if (Posts != null) { - string confirm = _localizer["confirm-delete"]; - bool confirmed = false; - - if (action == GroupAction.Delete) + var ids = Posts.Where(m => m.Selected).Select(m => m.Id); + if (ids.Any()) + { + var idsString = string.Join(",", ids); + switch (action) { - confirmed = await _jsruntime.InvokeAsync("confirm", confirm); - if (!confirmed) - return; + case GroupAction.Publish: + await _http.PutAsJsonAsync($"api/post/state/{idsString}", PostState.Release); + break; + case GroupAction.Unpublish: + await _http.PutAsJsonAsync($"api/post/state/{idsString}", PostState.Draft); + break; + case GroupAction.Delete: + await _http.DeleteAsync($"api/post/{idsString}"); + break; } - - foreach (var post in Posts) - { - if (post.Selected) - { - switch (action) - { - case GroupAction.Publish: - await _http.PutAsJsonAsync($"api/post/publish/{post.Id}", true); - break; - case GroupAction.Unpublish: - await _http.PutAsJsonAsync($"api/post/publish/{post.Id}", false); - break; - case GroupAction.Delete: - await _http.DeleteAsync($"api/post/{post.Id}"); - break; - } - } - } - await Load(); + } } + await LoadAsync(); + } + + protected async Task SearchKeyPress(KeyboardEventArgs e) + { + if (e.Key == "Enter") + await SearchPosts(); + } + + protected async Task SearchPosts() + { + if (string.IsNullOrEmpty(SearchTerm)) SearchTerm = "*"; + var result = await _http.GetFromJsonAsync>($"api/post/items/search/{SearchTerm}"); + Posts = _mapper.Map>(result); + } + + public async Task Filter(PublishedStatus filter) + { + FilterValue = filter; + await LoadAsync(); + } + + public async Task Publish(PostItemDto post) + { + Toast(await _http.PutAsJsonAsync($"api/post/state/{post.Id}", PostState.Release)); + await LoadAsync(); + } + + protected void Toast(HttpResponseMessage msg) + { + if (msg.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } +} - protected async Task SearchKeyPress(KeyboardEventArgs e) - { - if (e.Key == "Enter") - await SearchPosts(); - } - protected async Task SearchPosts() - { - if (string.IsNullOrEmpty(SearchTerm)) - SearchTerm = "*"; + - Posts = await _http.GetFromJsonAsync>($"api/post/list/search/{SearchTerm}"); - SearchTerm = ""; - } +
+
- protected async Task GetPages() - { - PostTypeLabel = _localizer["pages"]; - PostTypeButton = _localizer["page"]; - _stateprovider.SetPostType(PostType.Page); - await Load(); - } + - public async Task Filter(PublishedStatus filter) - { - FilterValue = filter; - switch (filter) - { - case PublishedStatus.Published: - FilterLabel = _localizer["published"]; - break; - case PublishedStatus.Drafts: - FilterLabel = _localizer["draft", true]; - break; - default: - FilterLabel = _localizer["all"]; - break; - } - await Load(); - } + @_localizer["new-page"] - public async Task Publish(Post post) + @if (Posts != null && Posts.Count > 0) { - Toast(await _http.PutAsJsonAsync($"api/post/publish/{post.Id}", (post.Published == DateTime.MinValue))); - await Load(); + } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) + - protected void Open() - { - } + + +
+ + + + @if (Posts != null && Posts.Count > 0) + { +
    + +
  • + + + @post.Title + + + @DateTimeHelper.ToFriendlyShortDateString(post.PublishedAt, _localizer["draft"]) + + + + + + + + + +
  • +
    +
+ } + else + { +
+ + + + +

@_localizer["list-is-empty"]

+
+ } +
- public void Dispose() - { - _stateprovider.OnChange -= StateHasChanged; - } -} diff --git a/src/Blogifier.Admin/Pages/Settings/AboutView.razor b/src/Blogifier.Admin/Pages/Settings/AboutView.razor index afc3a6e75..8dad57b74 100644 --- a/src/Blogifier.Admin/Pages/Settings/AboutView.razor +++ b/src/Blogifier.Admin/Pages/Settings/AboutView.razor @@ -1,72 +1,74 @@ -@layout SettingsLayout @page "/admin/settings/about/" + +@layout SettingsLayout + @inject HttpClient _http @inject IStringLocalizer _localizer -@inject IJSRuntime JSRuntime - +@code { + protected AboutDto? About { get; set; } + protected override async Task OnInitializedAsync() + { + About = await _http.GetFromJsonAsync($"api/blog/about"); + } +} + + +

@_localizer["about-blogifier"]

- -@if (AboutModel != null) +@if (About != null) { -

@_localizer["specification"]

-
-
    -
  • - -
    @AboutModel.Version
    -
  • -
  • - -
    @AboutModel.OperatingSystem
    -
  • -
  • - -
    @AboutModel.DatabaseProvider
    -
  • -
-
+

@_localizer["specification"]

+
+
    +
  • + +
    @About.Version
    +
  • +
  • + +
    @About.OperatingSystem
    +
  • +
  • + +
    @About.DatabaseProvider
    +
  • +
+
} -@code { - protected AboutModel AboutModel { get; set; } - - protected override async Task OnInitializedAsync() - { - AboutModel = await _http.GetFromJsonAsync($"api/about"); - } -} diff --git a/src/Blogifier.Admin/Pages/Settings/AdvancedView.razor b/src/Blogifier.Admin/Pages/Settings/AdvancedView.razor index 095404f4f..9d55bf830 100644 --- a/src/Blogifier.Admin/Pages/Settings/AdvancedView.razor +++ b/src/Blogifier.Admin/Pages/Settings/AdvancedView.razor @@ -1,14 +1,15 @@ @layout SettingsLayout @page "/admin/settings/advanced/" @inject IStringLocalizer _localizer - + +

@_localizer["advanced-settings"]

-
- @_localizer["under-development"]. - - @_localizer["more-info"] - -
+
+ @_localizer["under-development"]. + + @_localizer["more-info"] + +
diff --git a/src/Blogifier.Admin/Pages/Settings/BasicView.razor b/src/Blogifier.Admin/Pages/Settings/BasicView.razor index 7a49817ee..f0550ea33 100644 --- a/src/Blogifier.Admin/Pages/Settings/BasicView.razor +++ b/src/Blogifier.Admin/Pages/Settings/BasicView.razor @@ -1,49 +1,57 @@ -@layout SettingsLayout -@page "/admin/settings/" +@layout SettingsLayout +@page "/admin/settings" @inject HttpClient _http +@inject IToaster _toaster @inject IStringLocalizer _localizer - + +

@_localizer["basic-settings"]

- @if (Blog != null) - { - - - -
- - -
-
- - -
-
- - -
-
- -
-
- } + @if (Blog != null) + { + + + +
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ }
- @code { - protected Blog Blog { get; set; } - protected ToasterComponent Toaster; + protected BlogEitorDto? Blog { get; set; } + + protected override async Task OnInitializedAsync() + { + Blog = await _http.GetFromJsonAsync("api/blog"); + } - protected override async Task OnInitializedAsync() - { - Blog = await _http.GetFromJsonAsync("api/blog"); - } + protected async Task Save() + { + Toast(await _http.PutAsJsonAsync("api/blog", Blog!)); + } - protected async Task Save() - { - Toaster.Toast(await _http.PutAsJsonAsync("api/blog", Blog)); - } + protected void Toast(HttpResponseMessage msg) + { + if (msg.IsSuccessStatusCode) + _toaster.Success(_localizer["completed"]); + else + _toaster.Error(_localizer["generic-error"]); + } } diff --git a/src/Blogifier.Admin/Pages/Settings/CommentsView.razor b/src/Blogifier.Admin/Pages/Settings/CommentsView.razor index 446fbcb37..44b70f5a0 100644 --- a/src/Blogifier.Admin/Pages/Settings/CommentsView.razor +++ b/src/Blogifier.Admin/Pages/Settings/CommentsView.razor @@ -4,14 +4,14 @@ @inject IStringLocalizer _localizer @inject IToaster _toaster - +

@_localizer["comments-settings"]

-
- @_localizer["under-development"]. - - @_localizer["more-info"] - -
+
+ @_localizer["under-development"]. + + @_localizer["more-info"] + +
diff --git a/src/Blogifier.Admin/Pages/Settings/CustomizeView.razor b/src/Blogifier.Admin/Pages/Settings/CustomizeView.razor index e5764664f..ef1f99d0e 100644 --- a/src/Blogifier.Admin/Pages/Settings/CustomizeView.razor +++ b/src/Blogifier.Admin/Pages/Settings/CustomizeView.razor @@ -1,27 +1,22 @@ -@layout SettingsLayout @page "/admin/settings/customize/" + +@layout SettingsLayout + @inject HttpClient _http @inject IStringLocalizer _localizer -@inject IJSRuntime JSRuntime +@inject IJSRuntime _jsRuntime +@inject IToaster _toaster - + -

@_localizer["customization"]

+

@_localizer["menus"]

@_localizer["theme-customization-desc"]

- - - +
+ @_localizer["under-development"]. + + @_localizer["more-info"] + +
- - -@code { - protected ToasterComponent Toaster; - SettingsComponent ChildSettings; - - async Task SaveSettings() - { - Toaster.Toast(await ChildSettings.Save()); - } -} diff --git a/src/Blogifier.Admin/Pages/Settings/MenusView.razor b/src/Blogifier.Admin/Pages/Settings/MenusView.razor index c2cb73184..17a1e737b 100644 --- a/src/Blogifier.Admin/Pages/Settings/MenusView.razor +++ b/src/Blogifier.Admin/Pages/Settings/MenusView.razor @@ -1,15 +1,16 @@ @layout SettingsLayout @page "/admin/settings/customize/menus/" @inject IStringLocalizer _localizer - + +

@_localizer["menus"]

@_localizer["theme-customization-desc"]

-
- @_localizer["under-development"]. - - @_localizer["more-info"] - -
+
+ @_localizer["under-development"]. + + @_localizer["more-info"] + +
diff --git a/src/Blogifier.Admin/Pages/Settings/ScriptsView.razor b/src/Blogifier.Admin/Pages/Settings/ScriptsView.razor index e11501a0c..2c72d77ad 100644 --- a/src/Blogifier.Admin/Pages/Settings/ScriptsView.razor +++ b/src/Blogifier.Admin/Pages/Settings/ScriptsView.razor @@ -1,44 +1,51 @@ @layout SettingsLayout @page "/admin/settings/scripts/" @inject HttpClient _http +@inject IToaster _toaster @inject IStringLocalizer _localizer - +

@_localizer["script-settings"]

@_localizer["include-scripts"]

- @if (Blog != null) - { - -
- - + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+ + diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml new file mode 100644 index 000000000..51c5a06a8 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml @@ -0,0 +1,38 @@ +@inject IStringLocalizer _localizer + + + + + + + + @await RenderSectionAsync("HeadMeta",false) + + + + + +
+ + +
+ + + + diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_base.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_base.cshtml new file mode 100644 index 000000000..307159b3d --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_base.cshtml @@ -0,0 +1,35 @@ + + + + + + + @await RenderSectionAsync("HeadMeta",false) + + + + + + + @RenderBody() + + + + diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_main.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_main.cshtml new file mode 100644 index 000000000..de7ca6893 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_main.cshtml @@ -0,0 +1,44 @@ +@model MainModel + + + + + + + + @Model.Main.Title + + + + + + + @Html.Raw(Model.Main.HeaderScript) + + + + @RenderBody() + + + @Html.Raw(Model.Main.FooterScript) + + + + diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_profile.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_profile.cshtml new file mode 100644 index 000000000..48e1f3114 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_profile.cshtml @@ -0,0 +1,60 @@ +@model AccountProfileModel +@inject IStringLocalizer _localizer + + + + + + + + @await RenderSectionAsync("HeadMeta",false) + + + + + +
+ +
+
+ +
+ @RenderBody() +
+
+
+
+ + + + diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml new file mode 100644 index 000000000..1c2e746db --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml @@ -0,0 +1,28 @@ +@model AccountLoginModel +@{ + Layout = "layouts/_account.cshtml"; +} +@inject IStringLocalizer _localizer + +@section HeadMeta{ + @_localizer["login"] +} + +@if (Model.ShowError) +{ + +} +
+ +
+ + + +
+
+ + + +
+ +
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml new file mode 100644 index 000000000..fa9b93cad --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml @@ -0,0 +1,42 @@ + +@model PostModel + +@inject IStringLocalizer _localizer + +@{ + Layout = "layouts/_main.cshtml"; +} + +
+
+
+ @Model.PostSlug.Post.Title +
+
+

@Model.PostSlug.Post.Title

+ +
+
+ @Html.Raw(Model.PostSlug.Post.ContentHtml) +
+
+ +
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/password.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/password.cshtml new file mode 100644 index 000000000..2703d0e87 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/password.cshtml @@ -0,0 +1,34 @@ +@model AccountProfilePasswordModel + +@{ + Layout = "layouts/_profile.cshtml"; +} + +@inject IStringLocalizer _localizer + +@section HeadMeta{ + @_localizer["edit-profile"] +} + +

@_localizer["change-password"]

+
+ @if (!string.IsNullOrEmpty(Model.Error)) + { + + } +
+
+ + + +
+
+ + + +
+
+ +
+
+
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml new file mode 100644 index 000000000..6d7e7d391 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml @@ -0,0 +1,92 @@ +@model PostModel + +@inject IStringLocalizer _localizer +@{ + Layout = "layouts/_main.cshtml"; ; +} + +
+
+
+ +
+
+

@Model.PostSlug.Post.Title

+ +
+ +
+ @Html.Raw(Model.PostSlug.Post.ContentHtml) +
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/author.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/author.cshtml new file mode 100644 index 000000000..62c226396 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/author.cshtml @@ -0,0 +1,11 @@ +@model PostModel + + diff --git a/src/Blogifier/Views/Themes/standard/post/comments.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/comments.cshtml similarity index 100% rename from src/Blogifier/Views/Themes/standard/post/comments.cshtml rename to src/Blogifier.Themes.Standard/Views/Themes/standard/post/comments.cshtml diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/featured.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/featured.cshtml new file mode 100644 index 000000000..5e8b9d528 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/featured.cshtml @@ -0,0 +1,89 @@ +@model PostPagerModel + +@{ + var featured = Model.Pager.Items.Where(p => p.State == PostState.Featured).Take(3).ToList(); +} + +@if (featured.Any()) +{ + +} diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/nav.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/nav.cshtml new file mode 100644 index 000000000..8ed0e9a73 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/nav.cshtml @@ -0,0 +1,26 @@ +@model PostModel + +@inject IStringLocalizer _localizer + + diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/related.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/related.cshtml new file mode 100644 index 000000000..40714d090 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/related.cshtml @@ -0,0 +1,81 @@ +@model PostModel + +@inject IStringLocalizer _localizer + + +@if (Model.PostSlug.Related.Any()) +{ + +} diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/share.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/share.cshtml new file mode 100644 index 000000000..6b8704b47 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/share.cshtml @@ -0,0 +1,56 @@ +@model PostModel + +@{ + var postSlug = Model.Main.AbsoluteUrl + "/posts/" + Model.PostSlug.Post.Slug; + var share_facebook = "https://www.facebook.com/sharer/sharer.php?u=" + postSlug; + var share_twitter = "https://twitter.com/intent/tweet?text=" + Model.PostSlug.Post.Title + "&url=" + postSlug; + var share_email = "mailto:?&subject=" + Model.PostSlug.Post.Title + "&cc=&bcc=&body=" + Model.PostSlug.Post.Title + "%0" + postSlug; +} + + diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-grid.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-grid.cshtml new file mode 100644 index 000000000..fed09c2f2 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-grid.cshtml @@ -0,0 +1,59 @@ +@model PostPagerModel + +@inject IStringLocalizer _localizer + +@if (Model.Pager.Items.Any()) +{ +
+ @foreach (var post in Model.Pager.Items) + { +
+
+
+ @post.Title +
+ @if (post.Categories != null) + { +
+ @foreach (var cat in post.Categories) + { + @cat.Content + } +
+ } +

+ @post.Title +

+

@Html.Raw(post.Description)

+
+
+ @post.User.NickName + @post.User.NickName +
+
+ + + + + +
+ + Read + + + + +
+ +
+
+ } +
+} +else +{ +
@_localizer["empty"]!
+} diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-list.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-list.cshtml new file mode 100644 index 000000000..bcbf53694 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-list.cshtml @@ -0,0 +1,60 @@ +@model PostPagerModel +@inject IStringLocalizer _localizer + +@if (Model.Pager.Items.Any()) +{ + @foreach (var post in Model.Pager.Items) + { +
+
+ @post.Title +
+
+

+ @post.Title +

+
+
+ @post.User.NickName + @post.User.NickName +
+
+ + + + + +
+ @if (post.Categories != null) + { +
+ + + + @foreach (var cat in post.Categories) + { + @cat.Content + } +
+ } +
+

@Html.Raw(post.Description)

+ + Read More + + + + +
+
+ } +} +else +{ +
@_localizer["empty"]!
+} diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml new file mode 100644 index 000000000..ece83fb54 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml @@ -0,0 +1,57 @@ +@model AccountProfileEditModel +@{ + Layout = "layouts/_profile.cshtml"; +} +@inject IStringLocalizer _localizer + +@section HeadMeta{ + @_localizer["edit-profile"] +} + +

@_localizer["edit-profile"]

+
+ @if (!string.IsNullOrEmpty(Model.Error)) + { + + } +
+ + + +
+ +
+ @Model.NickName + + +
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+ +
+
+
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml new file mode 100644 index 000000000..eedc8a484 --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml @@ -0,0 +1,47 @@ +@model AccountRegisterModel +@inject IStringLocalizer _localizer +@{ + Layout = "layouts/_account.cshtml"; +} + +@section HeadMeta{ + @_localizer["register"] +} + + + +@if (Model.ShowError) +{ + +} +
+ +
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+ +
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/search.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/search.cshtml new file mode 100644 index 000000000..bfa5c8e0c --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/search.cshtml @@ -0,0 +1,24 @@ +@model SearchModel +@inject IStringLocalizer _localizer +@{ + Layout = "layouts/_main.cshtml"; +} + +
+
+ +
+

Search Results

+ @foreach (var post in Model.Pager.Items) + { +
+
+ @post.Title +
+
+ @post.Title +

@post.Description

+
+
+ } +
diff --git a/src/Blogifier.Themes.Standard/Views/_ViewImports.cshtml b/src/Blogifier.Themes.Standard/Views/_ViewImports.cshtml new file mode 100644 index 000000000..9baf9fe2b --- /dev/null +++ b/src/Blogifier.Themes.Standard/Views/_ViewImports.cshtml @@ -0,0 +1,7 @@ +@using Microsoft.Extensions.Localization +@using Blogifier.Shared +@using Blogifier.Shared.Resources +@using Blogifier.Models; +@using Blogifier.Helper; +@using Blogifier; +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/Blogifier/wwwroot/themes/standard/README.md b/src/Blogifier.Themes.Standard/assets/README.md similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/README.md rename to src/Blogifier.Themes.Standard/assets/README.md diff --git a/src/Blogifier/wwwroot/themes/standard/js/newsletter.js b/src/Blogifier.Themes.Standard/assets/js/index.js similarity index 74% rename from src/Blogifier/wwwroot/themes/standard/js/newsletter.js rename to src/Blogifier.Themes.Standard/assets/js/index.js index 8b34eadd1..f016782b1 100644 --- a/src/Blogifier/wwwroot/themes/standard/js/newsletter.js +++ b/src/Blogifier.Themes.Standard/assets/js/index.js @@ -1,3 +1,6 @@ +import "bootstrap/dist/js/bootstrap.bundle.min.js"; +import '../lib/highlight.js' + // get the newsletter form elements const form = document.getElementById("newsletter"); const form_email = document.getElementById("newsletter_email"); @@ -69,3 +72,29 @@ form.addEventListener("submit", function (e) { subscribeNewsletter(form.action, subscriber_data); }); }); + + +// search modal auto focus +var myModal = document.getElementById('searchModal'); +var myInput = document.getElementById('searchFormInput'); +if (myModal) { + myModal.addEventListener('shown.bs.modal', function () { + myInput.focus() + }) +} + +// copy input +function copyInput(elm) { + var copyText = document.getElementById(elm); + var copyTextStore = copyText.dataset.link; + copyText.select(); + copyText.setSelectionRange(0, 99999); + document.execCommand("copy"); + copyText.value = "Copied!"; + copyText.classList.add("copied"); + setTimeout(function () { + copyText.value = copyTextStore; + copyText.classList.remove("copied"); + }, 500); +} + diff --git a/src/Blogifier.Themes.Standard/assets/lib/highlight.js b/src/Blogifier.Themes.Standard/assets/lib/highlight.js new file mode 100644 index 000000000..b501b6605 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/lib/highlight.js @@ -0,0 +1,2646 @@ +/* + Highlight.js 10.5.0 (af20048d) + License: BSD-3-Clause + Copyright (c) 2006-2020, Ivan Sagalaev +*/ +var hljs = function () { + "use strict"; function e(t) { + return t instanceof Map ? t.clear = t.delete = t.set = () => { + throw Error("map is read-only") + } : t instanceof Set && (t.add = t.clear = t.delete = () => { + throw Error("set is read-only") + }), Object.freeze(t), Object.getOwnPropertyNames(t).forEach((n => { + var s = t[n] + ; "object" != typeof s || Object.isFrozen(s) || e(s) + })), t + } var t = e, n = e; t.default = n + ; class s { + constructor(e) { void 0 === e.data && (e.data = {}), this.data = e.data } + ignoreMatch() { this.ignore = !0 } + } function r(e) { + return e.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'") + } function a(e, ...t) { + const n = Object.create(null); for (const t in e) n[t] = e[t] + ; return t.forEach((e => { for (const t in e) n[t] = e[t] })), n + } const i = e => !!e.kind + ; class o { + constructor(e, t) { + this.buffer = "", this.classPrefix = t.classPrefix, e.walk(this) + } addText(e) { + this.buffer += r(e) + } openNode(e) { + if (!i(e)) return; let t = e.kind + ; e.sublanguage || (t = `${this.classPrefix}${t}`), this.span(t) + } closeNode(e) { + i(e) && (this.buffer += "") + } value() { return this.buffer } span(e) { + this.buffer += `` + } + } class l { + constructor() { + this.rootNode = { + children: [] + }, this.stack = [this.rootNode] + } get top() { + return this.stack[this.stack.length - 1] + } get root() { return this.rootNode } add(e) { + this.top.children.push(e) + } openNode(e) { + const t = { kind: e, children: [] } + ; this.add(t), this.stack.push(t) + } closeNode() { + if (this.stack.length > 1) return this.stack.pop() + } closeAllNodes() { + for (; this.closeNode();); + } toJSON() { return JSON.stringify(this.rootNode, null, 4) } + walk(e) { return this.constructor._walk(e, this.rootNode) } static _walk(e, t) { + return "string" == typeof t ? e.addText(t) : t.children && (e.openNode(t), + t.children.forEach((t => this._walk(e, t))), e.closeNode(t)), e + } static _collapse(e) { + "string" != typeof e && e.children && (e.children.every((e => "string" == typeof e)) ? e.children = [e.children.join("")] : e.children.forEach((e => { + l._collapse(e) + }))) + } + } class c extends l { + constructor(e) { super(), this.options = e } + addKeyword(e, t) { "" !== e && (this.openNode(t), this.addText(e), this.closeNode()) } + addText(e) { "" !== e && this.add(e) } addSublanguage(e, t) { + const n = e.root + ; n.kind = t, n.sublanguage = !0, this.add(n) + } toHTML() { + return new o(this, this.options).value() + } finalize() { return !0 } + } function u(e) { + return e ? "string" == typeof e ? e : e.source : null + } + const g = "[a-zA-Z]\\w*", d = "[a-zA-Z_]\\w*", h = "\\b\\d+(\\.\\d+)?", f = "(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)", p = "\\b(0b[01]+)", m = { + begin: "\\\\[\\s\\S]", relevance: 0 + }, b = { + className: "string", begin: "'", end: "'", + illegal: "\\n", contains: [m] + }, x = { + className: "string", begin: '"', end: '"', + illegal: "\\n", contains: [m] + }, E = { + begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ + }, v = (e, t, n = {}) => { + const s = a({ className: "comment", begin: e, end: t, contains: [] }, n) + ; return s.contains.push(E), s.contains.push({ + className: "doctag", + begin: "(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):", relevance: 0 + }), s + }, N = v("//", "$"), w = v("/\\*", "\\*/"), R = v("#", "$"); var y = Object.freeze({ + __proto__: null, IDENT_RE: g, UNDERSCORE_IDENT_RE: d, NUMBER_RE: h, C_NUMBER_RE: f, + BINARY_NUMBER_RE: p, + RE_STARTERS_RE: "!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", + SHEBANG: (e = {}) => { + const t = /^#![ ]*\// + ; return e.binary && (e.begin = ((...e) => e.map((e => u(e))).join(""))(t, /.*\b/, e.binary, /\b.*/)), + a({ + className: "meta", begin: t, end: /$/, relevance: 0, "on:begin": (e, t) => { + 0 !== e.index && t.ignoreMatch() + } + }, e) + }, BACKSLASH_ESCAPE: m, APOS_STRING_MODE: b, + QUOTE_STRING_MODE: x, PHRASAL_WORDS_MODE: E, COMMENT: v, C_LINE_COMMENT_MODE: N, + C_BLOCK_COMMENT_MODE: w, HASH_COMMENT_MODE: R, NUMBER_MODE: { + className: "number", + begin: h, relevance: 0 + }, C_NUMBER_MODE: { className: "number", begin: f, relevance: 0 }, + BINARY_NUMBER_MODE: { className: "number", begin: p, relevance: 0 }, CSS_NUMBER_MODE: { + className: "number", + begin: h + "(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", + relevance: 0 + }, REGEXP_MODE: { + begin: /(?=\/[^/\n]*\/)/, contains: [{ + className: "regexp", + begin: /\//, end: /\/[gimuy]*/, illegal: /\n/, contains: [m, { + begin: /\[/, end: /\]/, + relevance: 0, contains: [m] + }] + }] + }, TITLE_MODE: { + className: "title", begin: g, relevance: 0 + }, UNDERSCORE_TITLE_MODE: { className: "title", begin: d, relevance: 0 }, METHOD_GUARD: { + begin: "\\.\\s*[a-zA-Z_]\\w*", relevance: 0 + }, END_SAME_AS_BEGIN: e => Object.assign(e, { + "on:begin": (e, t) => { t.data._beginMatch = e[1] }, "on:end": (e, t) => { + t.data._beginMatch !== e[1] && t.ignoreMatch() + } + }) + }); function _(e, t) { + "." === e.input[e.index - 1] && t.ignoreMatch() + } function k(e, t) { + t && e.beginKeywords && (e.begin = "\\b(" + e.beginKeywords.split(" ").join("|") + ")(?!\\.)(?=\\b|\\s)", + e.__beforeBegin = _, e.keywords = e.keywords || e.beginKeywords, delete e.beginKeywords) + } function M(e, t) { + Array.isArray(e.illegal) && (e.illegal = ((...e) => "(" + e.map((e => u(e))).join("|") + ")")(...e.illegal)) + } function O(e, t) { + if (e.match) { + if (e.begin || e.end) throw Error("begin & end are not supported with match") + ; e.begin = e.match, delete e.match + } + } function A(e, t) { + void 0 === e.relevance && (e.relevance = 1) + } + const L = ["of", "and", "for", "in", "not", "or", "if", "then", "parent", "list", "value"] + ; function B(e, t) { return t ? Number(t) : (e => L.includes(e.toLowerCase()))(e) ? 0 : 1 } + function I(e, { plugins: t }) { + function n(t, n) { + return RegExp(u(t), "m" + (e.case_insensitive ? "i" : "") + (n ? "g" : "")) + } class s { + constructor() { + this.matchIndexes = {}, this.regexes = [], this.matchAt = 1, this.position = 0 + } + addRule(e, t) { + t.position = this.position++, this.matchIndexes[this.matchAt] = t, this.regexes.push([t, e]), + this.matchAt += (e => RegExp(e.toString() + "|").exec("").length - 1)(e) + 1 + } compile() { + 0 === this.regexes.length && (this.exec = () => null) + ; const e = this.regexes.map((e => e[1])); this.matcherRe = n(((e, t = "|") => { + const n = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./; let s = 0, r = "" + ; for (let a = 0; a < e.length; a++) { + s += 1; const i = s; let o = u(e[a]) + ; for (a > 0 && (r += t), r += "("; o.length > 0;) { + const e = n.exec(o); if (null == e) { r += o; break } + r += o.substring(0, e.index), + o = o.substring(e.index + e[0].length), "\\" === e[0][0] && e[1] ? r += "\\" + (Number(e[1]) + i) : (r += e[0], + "(" === e[0] && s++) + } r += ")" + } return r + })(e), !0), this.lastIndex = 0 + } exec(e) { + this.matcherRe.lastIndex = this.lastIndex; const t = this.matcherRe.exec(e) + ; if (!t) return null + ; const n = t.findIndex(((e, t) => t > 0 && void 0 !== e)), s = this.matchIndexes[n] + ; return t.splice(0, n), Object.assign(t, s) + } + } class r { + constructor() { + this.rules = [], this.multiRegexes = [], + this.count = 0, this.lastIndex = 0, this.regexIndex = 0 + } getMatcher(e) { + if (this.multiRegexes[e]) return this.multiRegexes[e]; const t = new s + ; return this.rules.slice(e).forEach((([e, n]) => t.addRule(e, n))), + t.compile(), this.multiRegexes[e] = t, t + } resumingScanAtSamePosition() { + return 0 !== this.regexIndex + } considerAll() { this.regexIndex = 0 } addRule(e, t) { + this.rules.push([e, t]), "begin" === t.type && this.count++ + } exec(e) { + const t = this.getMatcher(this.regexIndex); t.lastIndex = this.lastIndex + ; let n = t.exec(e) + ; if (this.resumingScanAtSamePosition()) if (n && n.index === this.lastIndex); else { + const t = this.getMatcher(0); t.lastIndex = this.lastIndex + 1, n = t.exec(e) + } + return n && (this.regexIndex += n.position + 1, + this.regexIndex === this.count && this.considerAll()), n + } + } + if (e.compilerExtensions || (e.compilerExtensions = []), + e.contains && e.contains.includes("self")) throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") + ; return e.classNameAliases = a(e.classNameAliases || {}), function t(s, i) { + const o = s + ; if (s.compiled) return o + ;[O].forEach((e => e(s, i))), e.compilerExtensions.forEach((e => e(s, i))), + s.__beforeBegin = null, [k, M, A].forEach((e => e(s, i))), s.compiled = !0; let l = null + ; if ("object" == typeof s.keywords && (l = s.keywords.$pattern, + delete s.keywords.$pattern), s.keywords && (s.keywords = ((e, t) => { + const n = {} + ; return "string" == typeof e ? s("keyword", e) : Object.keys(e).forEach((t => { + s(t, e[t]) + })), n; function s(e, s) { + t && (s = s.toLowerCase()), s.split(" ").forEach((t => { + const s = t.split("|"); n[s[0]] = [e, B(s[0], s[1])] + })) + } + })(s.keywords, e.case_insensitive)), + s.lexemes && l) throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ") + ; return l = l || s.lexemes || /\w+/, + o.keywordPatternRe = n(l, !0), i && (s.begin || (s.begin = /\B|\b/), + o.beginRe = n(s.begin), s.endSameAsBegin && (s.end = s.begin), + s.end || s.endsWithParent || (s.end = /\B|\b/), + s.end && (o.endRe = n(s.end)), o.terminatorEnd = u(s.end) || "", + s.endsWithParent && i.terminatorEnd && (o.terminatorEnd += (s.end ? "|" : "") + i.terminatorEnd)), + s.illegal && (o.illegalRe = n(s.illegal)), + s.contains || (s.contains = []), s.contains = [].concat(...s.contains.map((e => (e => (e.variants && !e.cachedVariants && (e.cachedVariants = e.variants.map((t => a(e, { + variants: null + }, t)))), e.cachedVariants ? e.cachedVariants : T(e) ? a(e, { + starts: e.starts ? a(e.starts) : null + }) : Object.isFrozen(e) ? a(e) : e))("self" === e ? s : e)))), s.contains.forEach((e => { + t(e, o) + })), s.starts && t(s.starts, i), o.matcher = (e => { + const t = new r + ; return e.contains.forEach((e => t.addRule(e.begin, { + rule: e, type: "begin" + }))), e.terminatorEnd && t.addRule(e.terminatorEnd, { + type: "end" + }), e.illegal && t.addRule(e.illegal, { type: "illegal" }), t + })(o), o + }(e) + } function T(e) { + return !!e && (e.endsWithParent || T(e.starts)) + } function j(e) { + const t = { + props: ["language", "code", "autodetect"], data: () => ({ + detectedLanguage: "", + unknownLanguage: !1 + }), computed: { + className() { + return this.unknownLanguage ? "" : "hljs " + this.detectedLanguage + }, highlighted() { + if (!this.autoDetect && !e.getLanguage(this.language)) return console.warn(`The language "${this.language}" you specified could not be found.`), + this.unknownLanguage = !0, r(this.code); let t = {} + ; return this.autoDetect ? (t = e.highlightAuto(this.code), + this.detectedLanguage = t.language) : (t = e.highlight(this.language, this.code, this.ignoreIllegals), + this.detectedLanguage = this.language), t.value + }, autoDetect() { + return !(this.language && (e = this.autodetect, !e && "" !== e)); var e + }, + ignoreIllegals: () => !0 + }, render(e) { + return e("pre", {}, [e("code", { + class: this.className, domProps: { innerHTML: this.highlighted } + })]) + } + }; return { + Component: t, VuePlugin: { install(e) { e.component("highlightjs", t) } } + } + } const S = { + "after:highlightBlock": ({ block: e, result: t, text: n }) => { + const s = D(e) + ; if (!s.length) return; const a = document.createElement("div") + ; a.innerHTML = t.value, t.value = ((e, t, n) => { + let s = 0, a = ""; const i = []; function o() { + return e.length && t.length ? e[0].offset !== t[0].offset ? e[0].offset < t[0].offset ? e : t : "start" === t[0].event ? e : t : e.length ? e : t + } function l(e) { + a += "<" + P(e) + [].map.call(e.attributes, (function (e) { + return " " + e.nodeName + '="' + r(e.value) + '"' + })).join("") + ">" + } function c(e) { + a += "" + } function u(e) { ("start" === e.event ? l : c)(e.node) } + for (; e.length || t.length;) { + let t = o() + ; if (a += r(n.substring(s, t[0].offset)), s = t[0].offset, t === e) { + i.reverse().forEach(c) + ; do { u(t.splice(0, 1)[0]), t = o() } while (t === e && t.length && t[0].offset === s) + ; i.reverse().forEach(l) + } else "start" === t[0].event ? i.push(t[0].node) : i.pop(), u(t.splice(0, 1)[0]) + } + return a + r(n.substr(s)) + })(s, D(a), n) + } + }; function P(e) { + return e.nodeName.toLowerCase() + } function D(e) { + const t = []; return function e(n, s) { + for (let r = n.firstChild; r; r = r.nextSibling)3 === r.nodeType ? s += r.nodeValue.length : 1 === r.nodeType && (t.push({ + event: "start", offset: s, node: r + }), s = e(r, s), P(r).match(/br|hr|img|input/) || t.push({ + event: "stop", offset: s, node: r + })); return s + }(e, 0), t + } const C = e => { + console.error(e) + }, H = (e, ...t) => { console.log("WARN: " + e, ...t) }, $ = (e, t) => { + console.log(`Deprecated as of ${e}. ${t}`) + }, U = r, z = a, K = Symbol("nomatch") + ; return (e => { + const n = Object.create(null), r = Object.create(null), a = []; let i = !0 + ; const o = /(^(<[^>]+>|\t|)+|\n)/gm, l = "Could not find the language '{}', did you forget to load/include a language module?", u = { + disableAutodetect: !0, name: "Plain text", contains: [] + }; let g = { + noHighlightRe: /^(no-?highlight)$/i, + languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i, classPrefix: "hljs-", + tabReplace: null, useBR: !1, languages: null, __emitter: c + }; function d(e) { + return g.noHighlightRe.test(e) + } function h(e, t, n, s) { + const r = { code: t, language: e } + ; _("before:highlight", r); const a = r.result ? r.result : f(r.language, r.code, n, s) + ; return a.code = r.code, _("after:highlight", a), a + } function f(e, t, r, o) { + const c = t + ; function u(e, t) { + const n = w.case_insensitive ? t[0].toLowerCase() : t[0] + ; return Object.prototype.hasOwnProperty.call(e.keywords, n) && e.keywords[n] + } + function d() { + null != _.subLanguage ? (() => { + if ("" === O) return; let e = null + ; if ("string" == typeof _.subLanguage) { + if (!n[_.subLanguage]) return void M.addText(O) + ; e = f(_.subLanguage, O, !0, k[_.subLanguage]), k[_.subLanguage] = e.top + } else e = p(O, _.subLanguage.length ? _.subLanguage : null) + ; _.relevance > 0 && (A += e.relevance), M.addSublanguage(e.emitter, e.language) + })() : (() => { + if (!_.keywords) return void M.addText(O); let e = 0 + ; _.keywordPatternRe.lastIndex = 0; let t = _.keywordPatternRe.exec(O), n = ""; for (; t;) { + n += O.substring(e, t.index); const s = u(_, t); if (s) { + const [e, r] = s + ; M.addText(n), n = "", A += r; const a = w.classNameAliases[e] || e; M.addKeyword(t[0], a) + } else n += t[0]; e = _.keywordPatternRe.lastIndex, t = _.keywordPatternRe.exec(O) + } + n += O.substr(e), M.addText(n) + })(), O = "" + } function h(e) { + return e.className && M.openNode(w.classNameAliases[e.className] || e.className), + _ = Object.create(e, { parent: { value: _ } }), _ + } function m(e, t, n) { + let r = ((e, t) => { + const n = e && e.exec(t); return n && 0 === n.index + })(e.endRe, n); if (r) { + if (e["on:end"]) { + const n = new s(e); e["on:end"](t, n), n.ignore && (r = !1) + } if (r) { + for (; e.endsParent && e.parent;)e = e.parent; return e + } + } + if (e.endsWithParent) return m(e.parent, t, n) + } function b(e) { + return 0 === _.matcher.regexIndex ? (O += e[0], 1) : (T = !0, 0) + } function x(e) { + const t = e[0], n = c.substr(e.index), s = m(_, e, n); if (!s) return K; const r = _ + ; r.skip ? O += t : (r.returnEnd || r.excludeEnd || (O += t), d(), r.excludeEnd && (O = t)); do { + _.className && M.closeNode(), _.skip || _.subLanguage || (A += _.relevance), _ = _.parent + } while (_ !== s.parent) + ; return s.starts && (s.endSameAsBegin && (s.starts.endRe = s.endRe), + h(s.starts)), r.returnEnd ? 0 : t.length + } let E = {}; function v(t, n) { + const a = n && n[0] + ; if (O += t, null == a) return d(), 0 + ; if ("begin" === E.type && "end" === n.type && E.index === n.index && "" === a) { + if (O += c.slice(n.index, n.index + 1), !i) { + const t = Error("0 width match regex") + ; throw t.languageName = e, t.badRule = E.rule, t + } return 1 + } + if (E = n, "begin" === n.type) return function (e) { + const t = e[0], n = e.rule, r = new s(n), a = [n.__beforeBegin, n["on:begin"]] + ; for (const n of a) if (n && (n(e, r), r.ignore)) return b(t) + ; return n && n.endSameAsBegin && (n.endRe = RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"), "m")), + n.skip ? O += t : (n.excludeBegin && (O += t), + d(), n.returnBegin || n.excludeBegin || (O = t)), h(n), n.returnBegin ? 0 : t.length + }(n) + ; if ("illegal" === n.type && !r) { + const e = Error('Illegal lexeme "' + a + '" for mode "' + (_.className || "") + '"') + ; throw e.mode = _, e + } if ("end" === n.type) { const e = x(n); if (e !== K) return e } + if ("illegal" === n.type && "" === a) return 1 + ; if (B > 1e5 && B > 3 * n.index) throw Error("potential infinite loop, way more iterations than matches") + ; return O += a, a.length + } const w = N(e) + ; if (!w) throw C(l.replace("{}", e)), Error('Unknown language: "' + e + '"') + ; const R = I(w, { plugins: a }); let y = "", _ = o || R; const k = {}, M = new g.__emitter(g); (() => { + const e = []; for (let t = _; t !== w; t = t.parent)t.className && e.unshift(t.className) + ; e.forEach((e => M.openNode(e))) + })(); let O = "", A = 0, L = 0, B = 0, T = !1; try { + for (_.matcher.considerAll(); ;) { + B++, T ? T = !1 : _.matcher.considerAll(), _.matcher.lastIndex = L + ; const e = _.matcher.exec(c); if (!e) break; const t = v(c.substring(L, e.index), e) + ; L = e.index + t + } return v(c.substr(L)), M.closeAllNodes(), M.finalize(), y = M.toHTML(), { + relevance: A, value: y, language: e, illegal: !1, emitter: M, top: _ + } + } catch (t) { + if (t.message && t.message.includes("Illegal")) return { + illegal: !0, illegalBy: { + msg: t.message, context: c.slice(L - 100, L + 100), mode: t.mode + }, sofar: y, relevance: 0, + value: U(c), emitter: M + }; if (i) return { + illegal: !1, relevance: 0, value: U(c), emitter: M, + language: e, top: _, errorRaised: t + }; throw t + } + } function p(e, t) { + t = t || g.languages || Object.keys(n); const s = (e => { + const t = { + relevance: 0, + emitter: new g.__emitter(g), value: U(e), illegal: !1, top: u + } + ; return t.emitter.addText(e), t + })(e), r = t.filter(N).filter(R).map((t => f(t, e, !1))) + ; r.unshift(s); const a = r.sort(((e, t) => { + if (e.relevance !== t.relevance) return t.relevance - e.relevance + ; if (e.language && t.language) { + if (N(e.language).supersetOf === t.language) return 1 + ; if (N(t.language).supersetOf === e.language) return -1 + } return 0 + })), [i, o] = a, l = i + ; return l.second_best = o, l + } const m = { + "before:highlightBlock": ({ block: e }) => { + g.useBR && (e.innerHTML = e.innerHTML.replace(/\n/g, "").replace(//g, "\n")) + }, "after:highlightBlock": ({ result: e }) => { + g.useBR && (e.value = e.value.replace(/\n/g, "
")) + } + }, b = /^(<[^>]+>|\t)+/gm, x = { + "after:highlightBlock": ({ result: e }) => { + g.tabReplace && (e.value = e.value.replace(b, (e => e.replace(/\t/g, g.tabReplace)))) + } + } + ; function E(e) { + let t = null; const n = (e => { + let t = e.className + " " + ; t += e.parentNode ? e.parentNode.className : ""; const n = g.languageDetectRe.exec(t) + ; if (n) { + const t = N(n[1]) + ; return t || (H(l.replace("{}", n[1])), H("Falling back to no-highlight mode for this block.", e)), + t ? n[1] : "no-highlight" + } return t.split(/\s+/).find((e => d(e) || N(e))) + })(e) + ; if (d(n)) return; _("before:highlightBlock", { block: e, language: n }), t = e + ; const s = t.textContent, a = n ? h(n, s, !0) : p(s); _("after:highlightBlock", { + block: e, + result: a, text: s + }), e.innerHTML = a.value, ((e, t, n) => { + const s = t ? r[t] : n + ; e.classList.add("hljs"), s && e.classList.add(s) + })(e, n, a.language), e.result = { + language: a.language, re: a.relevance, relavance: a.relevance + }, a.second_best && (e.second_best = { + language: a.second_best.language, + re: a.second_best.relevance, relavance: a.second_best.relevance + }) + } const v = () => { + v.called || (v.called = !0, document.querySelectorAll("pre code").forEach(E)) + } + ; function N(e) { return e = (e || "").toLowerCase(), n[e] || n[r[e]] } + function w(e, { languageName: t }) { + "string" == typeof e && (e = [e]), e.forEach((e => { + r[e] = t + })) + } function R(e) { const t = N(e); return t && !t.disableAutodetect } function _(e, t) { + const n = e; a.forEach((e => { e[n] && e[n](t) })) + } Object.assign(e, { + highlight: h, + highlightAuto: p, fixMarkup: e => { + return $("10.2.0", "fixMarkup will be removed entirely in v11.0"), + $("10.2.0", "Please see https://github.com/highlightjs/highlight.js/issues/2534"), + t = e, + g.tabReplace || g.useBR ? t.replace(o, (e => "\n" === e ? g.useBR ? "
" : e : g.tabReplace ? e.replace(/\t/g, g.tabReplace) : e)) : t + ; var t + }, highlightBlock: E, configure: e => { + e.useBR && ($("10.3.0", "'useBR' will be removed entirely in v11.0"), + $("10.3.0", "Please see https://github.com/highlightjs/highlight.js/issues/2559")), + g = z(g, e) + }, initHighlighting: v, initHighlightingOnLoad: () => { + window.addEventListener("DOMContentLoaded", v, !1) + }, registerLanguage: (t, s) => { + let r = null; try { r = s(e) } catch (e) { + if (C("Language definition for '{}' could not be registered.".replace("{}", t)), + !i) throw e; C(e), r = u + } + r.name || (r.name = t), n[t] = r, r.rawDefinition = s.bind(null, e), r.aliases && w(r.aliases, { + languageName: t + }) + }, listLanguages: () => Object.keys(n), getLanguage: N, + registerAliases: w, requireLanguage: e => { + $("10.4.0", "requireLanguage will be removed entirely in v11."), + $("10.4.0", "Please see https://github.com/highlightjs/highlight.js/pull/2844") + ; const t = N(e); if (t) return t + ; throw Error("The '{}' language is required, but not loaded.".replace("{}", e)) + }, + autoDetection: R, inherit: z, addPlugin: e => { a.push(e) }, vuePlugin: j(e).VuePlugin + }), e.debugMode = () => { i = !1 }, e.safeMode = () => { i = !0 }, e.versionString = "10.5.0" + ; for (const e in y) "object" == typeof y[e] && t(y[e]) + ; return Object.assign(e, y), e.addPlugin(m), e.addPlugin(S), e.addPlugin(x), e + })({}) +}(); "object" == typeof exports && "undefined" != typeof module && (module.exports = hljs); hljs.registerLanguage("apache", (() => { + "use strict"; return e => { + const n = { + className: "number", begin: /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?/ + } + ; return { + name: "Apache config", aliases: ["apacheconf"], case_insensitive: !0, + contains: [e.HASH_COMMENT_MODE, { + className: "section", begin: /<\/?/, end: />/, + contains: [n, { + className: "number", begin: /:\d{1,5}/ + }, e.inherit(e.QUOTE_STRING_MODE, { relevance: 0 })] + }, { + className: "attribute", + begin: /\w+/, relevance: 0, keywords: { + nomarkup: "order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername" + }, starts: { + end: /$/, relevance: 0, keywords: { literal: "on off all deny allow" }, + contains: [{ className: "meta", begin: /\s\[/, end: /\]$/ }, { + className: "variable", + begin: /[\$%]\{/, end: /\}/, contains: ["self", { className: "number", begin: /[$%]\d+/ }] + }, n, { className: "number", begin: /\d+/ }, e.QUOTE_STRING_MODE] + } + }], illegal: /\S/ + } + } +})()); hljs.registerLanguage("properties", (() => { + "use strict"; return e => { + var n = "[ \\t\\f]*", a = n + "[:=]" + n, t = "(" + a + "|[ \\t\\f]+)", r = "([^\\\\\\W:= \\t\\f\\n]|\\\\.)+", s = "([^\\\\:= \\t\\f\\n]|\\\\.)+", i = { + end: t, relevance: 0, starts: { + className: "string", end: /$/, relevance: 0, contains: [{ + begin: "\\\\\\\\" + }, { begin: "\\\\\\n" }] + } + }; return { + name: ".properties", + case_insensitive: !0, illegal: /\S/, contains: [e.COMMENT("^\\s*[!#]", "$"), { + returnBegin: !0, variants: [{ begin: r + a, relevance: 1 }, { + begin: r + "[ \\t\\f]+", + relevance: 0 + }], contains: [{ className: "attr", begin: r, endsParent: !0, relevance: 0 }], + starts: i + }, { + begin: s + t, returnBegin: !0, relevance: 0, contains: [{ + className: "meta", + begin: s, endsParent: !0, relevance: 0 + }], starts: i + }, { + className: "attr", relevance: 0, + begin: s + n + "$" + }] + } + } +})()); hljs.registerLanguage("diff", (() => { + "use strict"; return e => ({ + name: "Diff", + aliases: ["patch"], contains: [{ + className: "meta", relevance: 10, variants: [{ + begin: /^@@ +-\d+,\d+ +\+\d+,\d+ +@@/ + }, { begin: /^\*\*\* +\d+,\d+ +\*\*\*\*$/ }, { + begin: /^--- +\d+,\d+ +----$/ + }] + }, { + className: "comment", variants: [{ + begin: /Index: /, + end: /$/ + }, { begin: /^index/, end: /$/ }, { begin: /={3,}/, end: /$/ }, { + begin: /^-{3}/, end: /$/ + }, { begin: /^\*{3} /, end: /$/ }, { begin: /^\+{3}/, end: /$/ }, { begin: /^\*{15}$/ }, { + begin: /^diff --git/, end: /$/ + }] + }, { className: "addition", begin: /^\+/, end: /$/ }, { + className: "deletion", begin: /^-/, end: /$/ + }, { + className: "addition", begin: /^!/, + end: /$/ + }] + }) +})()); hljs.registerLanguage("cpp", (() => { + "use strict"; function e(e) { + return ((...e) => e.map((e => (e => e ? "string" == typeof e ? e : e.source : null)(e))).join(""))("(", e, ")?") + } return t => { + const n = (t => { + const n = t.COMMENT("//", "$", { + contains: [{ begin: /\\\n/ }] + }), r = "[a-zA-Z_]\\w*::", a = "(decltype\\(auto\\)|" + e(r) + "[a-zA-Z_]\\w*" + e("<[^<>]+>") + ")", s = { + className: "keyword", begin: "\\b[a-z\\d_]*_t\\b" + }, i = { + className: "string", + variants: [{ + begin: '(u8?|U|L)?"', end: '"', illegal: "\\n", + contains: [t.BACKSLASH_ESCAPE] + }, { + begin: "(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", + end: "'", illegal: "." + }, t.END_SAME_AS_BEGIN({ + begin: /(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/, end: /\)([^()\\ ]{0,16})"/ + })] + }, c = { + className: "number", variants: [{ begin: "\\b(0b[01']+)" }, { + begin: "(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" + }, { + begin: "(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" + }], relevance: 0 + }, o = { + className: "meta", begin: /#\s*[a-z]+\b/, end: /$/, keywords: { + "meta-keyword": "if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" + }, contains: [{ begin: /\\\n/, relevance: 0 }, t.inherit(i, { className: "meta-string" }), { + className: "meta-string", begin: /<.*?>/, end: /$/, illegal: "\\n" + }, n, t.C_BLOCK_COMMENT_MODE] + }, l = { + className: "title", begin: e(r) + t.IDENT_RE, + relevance: 0 + }, d = e(r) + t.IDENT_RE + "\\s*\\(", u = { + keyword: "int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq", + built_in: "std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary", + literal: "true false nullptr NULL" + }, p = [o, s, n, t.C_BLOCK_COMMENT_MODE, c, i], m = { + variants: [{ begin: /=/, end: /;/ }, { begin: /\(/, end: /\)/ }, { + beginKeywords: "new throw return else", end: /;/ + }], keywords: u, contains: p.concat([{ + begin: /\(/, end: /\)/, keywords: u, contains: p.concat(["self"]), relevance: 0 + }]), + relevance: 0 + }, _ = { + className: "function", begin: "(" + a + "[\\*&\\s]+)+" + d, + returnBegin: !0, end: /[{;=]/, excludeEnd: !0, keywords: u, illegal: /[^\w\s\*&:<>.]/, + contains: [{ begin: "decltype\\(auto\\)", keywords: u, relevance: 0 }, { + begin: d, + returnBegin: !0, contains: [l], relevance: 0 + }, { + className: "params", begin: /\(/, + end: /\)/, keywords: u, relevance: 0, contains: [n, t.C_BLOCK_COMMENT_MODE, i, c, s, { + begin: /\(/, end: /\)/, keywords: u, relevance: 0, + contains: ["self", n, t.C_BLOCK_COMMENT_MODE, i, c, s] + }] + }, s, n, t.C_BLOCK_COMMENT_MODE, o] + }; return { + aliases: ["c", "cc", "h", "c++", "h++", "hpp", "hh", "hxx", "cxx"], keywords: u, + disableAutodetect: !0, illegal: "", keywords: u, contains: ["self", s] + }, { begin: t.IDENT_RE + "::", keywords: u }, { + className: "class", beginKeywords: "enum class struct union", end: /[{;:<>=]/, + contains: [{ beginKeywords: "final class struct" }, t.TITLE_MODE] + }]), exports: { + preprocessor: o, strings: i, keywords: u + } + } + })(t) + ; return n.disableAutodetect = !1, n.name = "C++", + n.aliases = ["cc", "c++", "h++", "hpp", "hh", "hxx", "cxx"], n + } +})()); hljs.registerLanguage("kotlin", (() => { + "use strict" + ; var e = "\\.([0-9](_*[0-9])*)", n = "[0-9a-fA-F](_*[0-9a-fA-F])*", a = { + className: "number", variants: [{ + begin: `(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` + }, { begin: `\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)` }, { + begin: `(${e})[fFdD]?\\b` + }, { begin: "\\b([0-9](_*[0-9])*)[fFdD]\\b" }, { + begin: `\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` + }, { begin: "\\b(0|[1-9](_*[0-9])*)[lL]?\\b" }, { begin: `\\b0[xX](${n})[lL]?\\b` }, { + begin: "\\b0(_*[0-7])*[lL]?\\b" + }, { begin: "\\b0[bB][01](_*[01])*[lL]?\\b" }], + relevance: 0 + }; return e => { + const n = { + keyword: "abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", + built_in: "Byte Short Char Int Long Boolean Float Double Void Unit Nothing", + literal: "true false null" + }, i = { + className: "symbol", begin: e.UNDERSCORE_IDENT_RE + "@" + }, s = { className: "subst", begin: /\$\{/, end: /\}/, contains: [e.C_NUMBER_MODE] }, t = { + className: "variable", begin: "\\$" + e.UNDERSCORE_IDENT_RE + }, r = { + className: "string", + variants: [{ begin: '"""', end: '"""(?=[^"])', contains: [t, s] }, { + begin: "'", end: "'", + illegal: /\n/, contains: [e.BACKSLASH_ESCAPE] + }, { + begin: '"', end: '"', illegal: /\n/, + contains: [e.BACKSLASH_ESCAPE, t, s] + }] + }; s.contains.push(r); const l = { + className: "meta", + begin: "@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*" + e.UNDERSCORE_IDENT_RE + ")?" + }, c = { + className: "meta", begin: "@" + e.UNDERSCORE_IDENT_RE, contains: [{ + begin: /\(/, + end: /\)/, contains: [e.inherit(r, { className: "meta-string" })] + }] + }, o = a, b = e.COMMENT("/\\*", "\\*/", { contains: [e.C_BLOCK_COMMENT_MODE] }), E = { + variants: [{ className: "type", begin: e.UNDERSCORE_IDENT_RE }, { + begin: /\(/, end: /\)/, + contains: [] + }] + }, d = E; return d.variants[1].contains = [E], E.variants[1].contains = [d], + { + name: "Kotlin", aliases: ["kt"], keywords: n, contains: [e.COMMENT("/\\*\\*", "\\*/", { + relevance: 0, contains: [{ className: "doctag", begin: "@[A-Za-z]+" }] + }), e.C_LINE_COMMENT_MODE, b, { + className: "keyword", + begin: /\b(break|continue|return|this)\b/, starts: { + contains: [{ + className: "symbol", + begin: /@\w+/ + }] + } + }, i, l, c, { + className: "function", beginKeywords: "fun", end: "[(]|$", + returnBegin: !0, excludeEnd: !0, keywords: n, relevance: 5, contains: [{ + begin: e.UNDERSCORE_IDENT_RE + "\\s*\\(", returnBegin: !0, relevance: 0, + contains: [e.UNDERSCORE_TITLE_MODE] + }, { + className: "type", begin: //, + keywords: "reified", relevance: 0 + }, { + className: "params", begin: /\(/, end: /\)/, + endsParent: !0, keywords: n, relevance: 0, contains: [{ + begin: /:/, end: /[=,\/]/, + endsWithParent: !0, contains: [E, e.C_LINE_COMMENT_MODE, b], relevance: 0 + }, e.C_LINE_COMMENT_MODE, b, l, c, r, e.C_NUMBER_MODE] + }, b] + }, { + className: "class", + beginKeywords: "class interface trait", end: /[:\{(]|$/, excludeEnd: !0, + illegal: "extends implements", contains: [{ + beginKeywords: "public protected internal private constructor" + }, e.UNDERSCORE_TITLE_MODE, { + className: "type", begin: //, excludeBegin: !0, + excludeEnd: !0, relevance: 0 + }, { + className: "type", begin: /[,:]\s*/, end: /[<\(,]|$/, + excludeBegin: !0, returnEnd: !0 + }, l, c] + }, r, { + className: "meta", begin: "^#!/usr/bin/env", + end: "$", illegal: "\n" + }, o] + } + } +})()); hljs.registerLanguage("ruby", (() => { + "use strict"; function e(...e) { + return e.map((e => { + return (n = e) ? "string" == typeof n ? n : n.source : null; var n + })).join("") + } return n => { + var a, i = "([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)", s = { + keyword: "and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor __FILE__", + built_in: "proc lambda", literal: "true false nil" + }, r = { + className: "doctag", + begin: "@[A-Za-z]+" + }, b = { begin: "#<", end: ">" }, t = [n.COMMENT("#", "$", { + contains: [r] + }), n.COMMENT("^=begin", "^=end", { + contains: [r], relevance: 10 + }), n.COMMENT("^__END__", "\\n$")], c = { + className: "subst", begin: /#\{/, end: /\}/, + keywords: s + }, d = { + className: "string", contains: [n.BACKSLASH_ESCAPE, c], variants: [{ + begin: /'/, end: /'/ + }, { begin: /"/, end: /"/ }, { begin: /`/, end: /`/ }, { + begin: /%[qQwWx]?\(/, + end: /\)/ + }, { begin: /%[qQwWx]?\[/, end: /\]/ }, { begin: /%[qQwWx]?\{/, end: /\}/ }, { + begin: /%[qQwWx]?/ + }, { begin: /%[qQwWx]?\//, end: /\// }, { + begin: /%[qQwWx]?%/, + end: /%/ + }, { begin: /%[qQwWx]?-/, end: /-/ }, { begin: /%[qQwWx]?\|/, end: /\|/ }, { + begin: /\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/ + }, { + begin: /<<[-~]?'?(\w+)\n(?:[^\n]*\n)*?\s*\1\b/, returnBegin: !0, contains: [{ + begin: /<<[-~]?'?/ + }, n.END_SAME_AS_BEGIN({ + begin: /(\w+)/, end: /(\w+)/, + contains: [n.BACKSLASH_ESCAPE, c] + })] + }] + }, g = "[0-9](_?[0-9])*", l = { + className: "number", + relevance: 0, variants: [{ + begin: `\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b` + }, { + begin: "\\b0[dD][0-9](_?[0-9])*r?i?\\b" + }, { + begin: "\\b0[bB][0-1](_?[0-1])*r?i?\\b" + }, { begin: "\\b0[oO][0-7](_?[0-7])*r?i?\\b" }, { + begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b" + }, { + begin: "\\b0(_?[0-7])+r?i?\\b" + }] + }, o = { + className: "params", begin: "\\(", end: "\\)", + endsParent: !0, keywords: s + }, _ = [d, { + className: "class", beginKeywords: "class module", + end: "$|;", illegal: /=/, contains: [n.inherit(n.TITLE_MODE, { + begin: "[A-Za-z_]\\w*(::\\w+)*(\\?|!)?" + }), { + begin: "<\\s*", contains: [{ + begin: "(" + n.IDENT_RE + "::)?" + n.IDENT_RE + }] + }].concat(t) + }, { + className: "function", + begin: e(/def\s*/, (a = i + "\\s*(\\(|;|$)", e("(?=", a, ")"))), keywords: "def", end: "$|;", + contains: [n.inherit(n.TITLE_MODE, { begin: i }), o].concat(t) + }, { + begin: n.IDENT_RE + "::" + }, { className: "symbol", begin: n.UNDERSCORE_IDENT_RE + "(!|\\?)?:", relevance: 0 }, { + className: "symbol", begin: ":(?!\\s)", contains: [d, { begin: i }], relevance: 0 + }, l, { + className: "variable", + begin: "(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])" + }, { + className: "params", begin: /\|/, end: /\|/, relevance: 0, keywords: s + }, { + begin: "(" + n.RE_STARTERS_RE + "|unless)\\s*", keywords: "unless", contains: [{ + className: "regexp", contains: [n.BACKSLASH_ESCAPE, c], illegal: /\n/, variants: [{ + begin: "/", end: "/[a-z]*" + }, { begin: /%r\{/, end: /\}[a-z]*/ }, { + begin: "%r\\(", + end: "\\)[a-z]*" + }, { begin: "%r!", end: "![a-z]*" }, { begin: "%r\\[", end: "\\][a-z]*" }] + }].concat(b, t), relevance: 0 + }].concat(b, t); c.contains = _, o.contains = _; var E = [{ + begin: /^\s*=>/, starts: { end: "$", contains: _ } + }, { + className: "meta", + begin: "^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", + starts: { end: "$", contains: _ } + }]; return t.unshift(b), { + name: "Ruby", + aliases: ["rb", "gemspec", "podspec", "thor", "irb"], keywords: s, illegal: /\/\*/, + contains: [n.SHEBANG({ binary: "ruby" })].concat(E).concat(t).concat(_) + } + } +})()); hljs.registerLanguage("swift", (() => { + "use strict"; function e(e) { + return e ? "string" == typeof e ? e : e.source : null + } function n(e) { return i("(?=", e, ")") } + function i(...n) { return n.map((n => e(n))).join("") } function a(...n) { + return "(" + n.map((n => e(n))).join("|") + ")" + } + const t = e => i(/\b/, e, /\w$/.test(e) ? /\b/ : /\B/), u = ["Protocol", "Type"].map(t), s = ["init", "self"].map(t), r = ["Any", "Self"], o = ["associatedtype", /as\?/, /as!/, "as", "break", "case", "catch", "class", "continue", "convenience", "default", "defer", "deinit", "didSet", "do", "dynamic", "else", "enum", "extension", "fallthrough", "fileprivate(set)", "fileprivate", "final", "for", "func", "get", "guard", "if", "import", "indirect", "infix", /init\?/, /init!/, "inout", "internal(set)", "internal", "in", "is", "lazy", "let", "mutating", "nonmutating", "open(set)", "open", "operator", "optional", "override", "postfix", "precedencegroup", "prefix", "private(set)", "private", "protocol", "public(set)", "public", "repeat", "required", "rethrows", "return", "set", "some", "static", "struct", "subscript", "super", "switch", "throws", "throw", /try\?/, /try!/, "try", "typealias", "unowned(safe)", "unowned(unsafe)", "unowned", "var", "weak", "where", "while", "willSet"], l = ["false", "nil", "true"], c = ["#colorLiteral", "#column", "#dsohandle", "#else", "#elseif", "#endif", "#error", "#file", "#fileID", "#fileLiteral", "#filePath", "#function", "#if", "#imageLiteral", "#keyPath", "#line", "#selector", "#sourceLocation", "#warn_unqualified_access", "#warning"], b = ["abs", "all", "any", "assert", "assertionFailure", "debugPrint", "dump", "fatalError", "getVaList", "isKnownUniquelyReferenced", "max", "min", "numericCast", "pointwiseMax", "pointwiseMin", "precondition", "preconditionFailure", "print", "readLine", "repeatElement", "sequence", "stride", "swap", "swift_unboxFromSwiftValueWithType", "transcode", "type", "unsafeBitCast", "unsafeDowncast", "withExtendedLifetime", "withUnsafeMutablePointer", "withUnsafePointer", "withVaList", "withoutActuallyEscaping", "zip"], p = a(/[/=\-+!*%<>&|^~?]/, /[\u00A1-\u00A7]/, /[\u00A9\u00AB]/, /[\u00AC\u00AE]/, /[\u00B0\u00B1]/, /[\u00B6\u00BB\u00BF\u00D7\u00F7]/, /[\u2016-\u2017]/, /[\u2020-\u2027]/, /[\u2030-\u203E]/, /[\u2041-\u2053]/, /[\u2055-\u205E]/, /[\u2190-\u23FF]/, /[\u2500-\u2775]/, /[\u2794-\u2BFF]/, /[\u2E00-\u2E7F]/, /[\u3001-\u3003]/, /[\u3008-\u3020]/, /[\u3030]/), F = a(p, /[\u0300-\u036F]/, /[\u1DC0-\u1DFF]/, /[\u20D0-\u20FF]/, /[\uFE00-\uFE0F]/, /[\uFE20-\uFE2F]/), d = i(p, F, "*"), g = a(/[a-zA-Z_]/, /[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/, /[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/, /[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/, /[\u1E00-\u1FFF]/, /[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/, /[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/, /[\u2C00-\u2DFF\u2E80-\u2FFF]/, /[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/, /[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/, /[\uFE47-\uFFFD]/), f = a(g, /\d/, /[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/), m = i(g, f, "*"), w = i(/[A-Z]/, f, "*"), E = ["autoclosure", i(/convention\(/, a("swift", "block", "c"), /\)/), "discardableResult", "dynamicCallable", "dynamicMemberLookup", "escaping", "frozen", "GKInspectable", "IBAction", "IBDesignable", "IBInspectable", "IBOutlet", "IBSegueAction", "inlinable", "main", "nonobjc", "NSApplicationMain", "NSCopying", "NSManaged", i(/objc\(/, m, /\)/), "objc", "objcMembers", "propertyWrapper", "requires_stored_property_inits", "testable", "UIApplicationMain", "unknown", "usableFromInline"], y = ["iOS", "iOSApplicationExtension", "macOS", "macOSApplicationExtension", "macCatalyst", "macCatalystApplicationExtension", "watchOS", "watchOSApplicationExtension", "tvOS", "tvOSApplicationExtension", "swift"] + ; return e => { + const p = e.COMMENT("/\\*", "\\*/", { contains: ["self"] }), g = { + className: "keyword", begin: i(/\./, n(a(...u, ...s))), end: a(...u, ...s), + excludeBegin: !0 + }, A = { + begin: i(/\./, a(...o)), relevance: 0 + }, C = o.filter((e => "string" == typeof e)).concat(["_|0"]), v = { + variants: [{ + className: "keyword", + begin: a(...o.filter((e => "string" != typeof e)).concat(r).map(t), ...s) + }] + }, _ = { + $pattern: a(/\b\w+(\(\w+\))?/, /#\w+/), keyword: C.concat(c).join(" "), + literal: l.join(" ") + }, N = [g, A, v], D = [{ begin: i(/\./, a(...b)), relevance: 0 }, { + className: "built_in", begin: i(/\b/, a(...b), /(?=\()/) + }], B = { + begin: /->/, relevance: 0 + }, M = [B, { + className: "operator", relevance: 0, variants: [{ begin: d }, { + begin: `\\.(\\.|${F})+` + }] + }], h = "([0-9a-fA-F]_*)+", S = { + className: "number", + relevance: 0, variants: [{ + begin: "\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b" + }, { + begin: `\\b0x(${h})(\\.(${h}))?([pP][+-]?(([0-9]_*)+))?\\b` + }, { + begin: /\b0o([0-7]_*)+\b/ + }, { begin: /\b0b([01]_*)+\b/ }] + }, O = (e = "") => ({ + className: "subst", variants: [{ begin: i(/\\/, e, /[0\\tnr"']/) }, { + begin: i(/\\/, e, /u\{[0-9a-fA-F]{1,8}\}/) + }] + }), x = (e = "") => ({ + className: "subst", + begin: i(/\\/, e, /[\t ]*(?:[\r\n]|\r\n)/) + }), k = (e = "") => ({ + className: "subst", + label: "interpol", begin: i(/\\/, e, /\(/), end: /\)/ + }), L = (e = "") => ({ + begin: i(e, /"""/), + end: i(/"""/, e), contains: [O(e), x(e), k(e)] + }), I = (e = "") => ({ + begin: i(e, /"/), + end: i(/"/, e), contains: [O(e), k(e)] + }), $ = { + className: "string", + variants: [L(), L("#"), L("##"), L("###"), I(), I("#"), I("##"), I("###")] + }, T = [{ + begin: i(/`/, m, /`/) + }, { className: "variable", begin: /\$\d+/ }, { + className: "variable", + begin: `\\$${f}+` + }], j = [{ + begin: /(@|#)available\(/, end: /\)/, keywords: { + $pattern: /[@#]?\w+/, keyword: y.concat(["@available", "#available"]).join(" ") + }, + contains: [...M, S, $] + }, { className: "keyword", begin: i(/@/, a(...E)) }, { + className: "meta", begin: i(/@/, m) + }], K = { + begin: n(/\b[A-Z]/), relevance: 0, contains: [{ + className: "type", + begin: i(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/, f, "+") + }, { className: "type", begin: w, relevance: 0 }, { begin: /[?!]+/, relevance: 0 }, { + begin: /\.\.\./, relevance: 0 + }, { begin: i(/\s+&\s+/, n(w)), relevance: 0 }] + }, P = { + begin: //, keywords: _, contains: [...N, ...j, B, K] + }; K.contains.push(P) + ; for (const e of $.variants) { + const n = e.contains.find((e => "interpol" === e.label)) + ; n.keywords = _; const i = [...N, ...D, ...M, S, $, ...T]; n.contains = [...i, { + begin: /\(/, + end: /\)/, contains: ["self", ...i] + }] + } return { + name: "Swift", keywords: _, + contains: [e.C_LINE_COMMENT_MODE, p, { + className: "function", beginKeywords: "func", + end: /\{/, excludeEnd: !0, contains: [e.inherit(e.TITLE_MODE, { + begin: /[A-Za-z$_][0-9A-Za-z$_]*/ + }), { begin: // }, { + className: "params", + begin: /\(/, end: /\)/, endsParent: !0, keywords: _, + contains: ["self", ...N, S, $, e.C_BLOCK_COMMENT_MODE, { begin: ":" }], illegal: /["']/ + }], + illegal: /\[|%/ + }, { + className: "class", + beginKeywords: "struct protocol class extension enum", end: "\\{", excludeEnd: !0, + keywords: _, contains: [e.inherit(e.TITLE_MODE, { + begin: /[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/ + }), ...N] + }, { + beginKeywords: "import", + end: /$/, contains: [e.C_LINE_COMMENT_MODE, p], relevance: 0 + }, ...N, ...D, ...M, S, $, ...T, ...j, K] + } + } +})()); hljs.registerLanguage("http", (() => { + "use strict"; function e(...e) { + return e.map((e => { + return (n = e) ? "string" == typeof n ? n : n.source : null; var n + })).join("") + } return n => { + const a = "HTTP/(2|1\\.[01])", s = [{ + className: "attribute", + begin: e("^", /[A-Za-z][A-Za-z0-9-]*/, "(?=\\:\\s)"), starts: { + contains: [{ + className: "punctuation", begin: /: /, relevance: 0, starts: { end: "$", relevance: 0 } + }] + } + }, { begin: "\\n\\n", starts: { subLanguage: [], endsWithParent: !0 } }]; return { + name: "HTTP", aliases: ["https"], illegal: /\S/, contains: [{ + begin: "^(?=" + a + " \\d{3})", + end: /$/, contains: [{ className: "meta", begin: a }, { + className: "number", + begin: "\\b\\d{3}\\b" + }], starts: { end: /\b\B/, illegal: /\S/, contains: s } + }, { + begin: "(?=^[A-Z]+ (.*?) " + a + "$)", end: /$/, contains: [{ + className: "string", + begin: " ", end: " ", excludeBegin: !0, excludeEnd: !0 + }, { className: "meta", begin: a }, { + className: "keyword", begin: "[A-Z]+" + }], starts: { end: /\b\B/, illegal: /\S/, contains: s } + }] + } + } +})()); hljs.registerLanguage("python", (() => { + "use strict"; return e => { + const n = { + keyword: "and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal|10 not or pass raise return try while with yield", + built_in: "__import__ abs all any ascii bin bool breakpoint bytearray bytes callable chr classmethod compile complex delattr dict dir divmod enumerate eval exec filter float format frozenset getattr globals hasattr hash help hex id input int isinstance issubclass iter len list locals map max memoryview min next object oct open ord pow print property range repr reversed round set setattr slice sorted staticmethod str sum super tuple type vars zip", + literal: "__debug__ Ellipsis False None NotImplemented True" + }, a = { + className: "meta", begin: /^(>>>|\.\.\.) / + }, s = { + className: "subst", begin: /\{/, + end: /\}/, keywords: n, illegal: /#/ + }, i = { begin: /\{\{/, relevance: 0 }, r = { + className: "string", contains: [e.BACKSLASH_ESCAPE], variants: [{ + begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/, end: /'''/, + contains: [e.BACKSLASH_ESCAPE, a], relevance: 10 + }, { + begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/, end: /"""/, + contains: [e.BACKSLASH_ESCAPE, a], relevance: 10 + }, { + begin: /([fF][rR]|[rR][fF]|[fF])'''/, end: /'''/, + contains: [e.BACKSLASH_ESCAPE, a, i, s] + }, { + begin: /([fF][rR]|[rR][fF]|[fF])"""/, + end: /"""/, contains: [e.BACKSLASH_ESCAPE, a, i, s] + }, { + begin: /([uU]|[rR])'/, end: /'/, + relevance: 10 + }, { begin: /([uU]|[rR])"/, end: /"/, relevance: 10 }, { + begin: /([bB]|[bB][rR]|[rR][bB])'/, end: /'/ + }, { + begin: /([bB]|[bB][rR]|[rR][bB])"/, + end: /"/ + }, { + begin: /([fF][rR]|[rR][fF]|[fF])'/, end: /'/, + contains: [e.BACKSLASH_ESCAPE, i, s] + }, { + begin: /([fF][rR]|[rR][fF]|[fF])"/, end: /"/, + contains: [e.BACKSLASH_ESCAPE, i, s] + }, e.APOS_STRING_MODE, e.QUOTE_STRING_MODE] + }, t = "[0-9](_?[0-9])*", l = `(\\b(${t}))?\\.(${t})|\\b(${t})\\.`, b = { + className: "number", relevance: 0, variants: [{ + begin: `(\\b(${t})|(${l}))[eE][+-]?(${t})[jJ]?\\b` + }, { begin: `(${l})[jJ]?` }, { + begin: "\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?\\b" + }, { + begin: "\\b0[bB](_?[01])+[lL]?\\b" + }, { begin: "\\b0[oO](_?[0-7])+[lL]?\\b" }, { + begin: "\\b0[xX](_?[0-9a-fA-F])+[lL]?\\b" + }, { begin: `\\b(${t})[jJ]\\b` }] + }, o = { + className: "params", variants: [{ begin: /\(\s*\)/, skip: !0, className: null }, { + begin: /\(/, end: /\)/, excludeBegin: !0, excludeEnd: !0, keywords: n, + contains: ["self", a, b, r, e.HASH_COMMENT_MODE] + }] + }; return s.contains = [r, b, a], { + name: "Python", aliases: ["py", "gyp", "ipython"], keywords: n, + illegal: /(<\/|->|\?)|=>/, contains: [a, b, { begin: /\bself\b/ }, { + beginKeywords: "if", + relevance: 0 + }, r, e.HASH_COMMENT_MODE, { + variants: [{ + className: "function", + beginKeywords: "def" + }, { className: "class", beginKeywords: "class" }], end: /:/, + illegal: /[${=;\n,]/, contains: [e.UNDERSCORE_TITLE_MODE, o, { + begin: /->/, + endsWithParent: !0, keywords: "None" + }] + }, { + className: "meta", begin: /^[\t ]*@/, + end: /(?=#)|$/, contains: [b, o, r] + }, { begin: /\b(print|exec)\(/ }] + } + } +})()); hljs.registerLanguage("python-repl", (() => { + "use strict"; return s => ({ + aliases: ["pycon"], contains: [{ + className: "meta", starts: { + end: / |$/, starts: { + end: "$", + subLanguage: "python" + } + }, variants: [{ begin: /^>>>(?=[ ]|$)/ }, { + begin: /^\.\.\.(?=[ ]|$)/ + }] + }] + }) +})()); hljs.registerLanguage("java", (() => { + "use strict" + ; var e = "\\.([0-9](_*[0-9])*)", n = "[0-9a-fA-F](_*[0-9a-fA-F])*", a = { + className: "number", variants: [{ + begin: `(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` + }, { begin: `\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)` }, { + begin: `(${e})[fFdD]?\\b` + }, { begin: "\\b([0-9](_*[0-9])*)[fFdD]\\b" }, { + begin: `\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` + }, { begin: "\\b(0|[1-9](_*[0-9])*)[lL]?\\b" }, { begin: `\\b0[xX](${n})[lL]?\\b` }, { + begin: "\\b0(_*[0-7])*[lL]?\\b" + }, { begin: "\\b0[bB][01](_*[01])*[lL]?\\b" }], + relevance: 0 + }; return e => { + var n = "false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do", s = { + className: "meta", begin: "@[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*", + contains: [{ begin: /\(/, end: /\)/, contains: ["self"] }] + }; const r = a; return { + name: "Java", aliases: ["jsp"], keywords: n, illegal: /<\/|#/, + contains: [e.COMMENT("/\\*\\*", "\\*/", { + relevance: 0, contains: [{ + begin: /\w+@/, + relevance: 0 + }, { className: "doctag", begin: "@[A-Za-z]+" }] + }), { + begin: /import java\.[a-z]+\./, keywords: "import", relevance: 2 + }, e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE, e.APOS_STRING_MODE, e.QUOTE_STRING_MODE, { + className: "class", beginKeywords: "class interface enum", end: /[{;=]/, + excludeEnd: !0, keywords: "class interface enum", illegal: /[:"\[\]]/, contains: [{ + beginKeywords: "extends implements" + }, e.UNDERSCORE_TITLE_MODE] + }, { + beginKeywords: "new throw return else", relevance: 0 + }, { + className: "class", + begin: "record\\s+" + e.UNDERSCORE_IDENT_RE + "\\s*\\(", returnBegin: !0, excludeEnd: !0, + end: /[{;=]/, keywords: n, contains: [{ beginKeywords: "record" }, { + begin: e.UNDERSCORE_IDENT_RE + "\\s*\\(", returnBegin: !0, relevance: 0, + contains: [e.UNDERSCORE_TITLE_MODE] + }, { + className: "params", begin: /\(/, end: /\)/, + keywords: n, relevance: 0, contains: [e.C_BLOCK_COMMENT_MODE] + }, e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE] + }, { + className: "function", + begin: "([\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(<[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(\\s*,\\s*[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*)*>)?\\s+)+" + e.UNDERSCORE_IDENT_RE + "\\s*\\(", + returnBegin: !0, end: /[{;=]/, excludeEnd: !0, keywords: n, contains: [{ + begin: e.UNDERSCORE_IDENT_RE + "\\s*\\(", returnBegin: !0, relevance: 0, + contains: [e.UNDERSCORE_TITLE_MODE] + }, { + className: "params", begin: /\(/, end: /\)/, + keywords: n, relevance: 0, + contains: [s, e.APOS_STRING_MODE, e.QUOTE_STRING_MODE, r, e.C_BLOCK_COMMENT_MODE] + }, e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE] + }, r, s] + } + } +})()); hljs.registerLanguage("nginx", (() => { + "use strict"; return e => { + const n = { + className: "variable", variants: [{ begin: /\$\d+/ }, { begin: /\$\{/, end: /\}/ }, { + begin: /[$@]/ + e.UNDERSCORE_IDENT_RE + }] + }, a = { + endsWithParent: !0, keywords: { + $pattern: "[a-z/_]+", + literal: "on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll" + }, relevance: 0, illegal: "=>", contains: [e.HASH_COMMENT_MODE, { + className: "string", + contains: [e.BACKSLASH_ESCAPE, n], variants: [{ begin: /"/, end: /"/ }, { + begin: /'/, end: /'/ + }] + }, { + begin: "([a-z]+):/", end: "\\s", endsWithParent: !0, excludeEnd: !0, contains: [n] + }, { + className: "regexp", contains: [e.BACKSLASH_ESCAPE, n], variants: [{ + begin: "\\s\\^", + end: "\\s|\\{|;", returnEnd: !0 + }, { begin: "~\\*?\\s+", end: "\\s|\\{|;", returnEnd: !0 }, { + begin: "\\*(\\.[a-z\\-]+)+" + }, { begin: "([a-z\\-]+\\.)+\\*" }] + }, { + className: "number", + begin: "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b" + }, { + className: "number", begin: "\\b\\d+[kKmMgGdshdwy]*\\b", relevance: 0 + }, n] + }; return { + name: "Nginx config", aliases: ["nginxconf"], contains: [e.HASH_COMMENT_MODE, { + begin: e.UNDERSCORE_IDENT_RE + "\\s+\\{", returnBegin: !0, end: /\{/, contains: [{ + className: "section", begin: e.UNDERSCORE_IDENT_RE + }], relevance: 0 + }, { + begin: e.UNDERSCORE_IDENT_RE + "\\s", end: ";|\\{", returnBegin: !0, contains: [{ + className: "attribute", begin: e.UNDERSCORE_IDENT_RE, starts: a + }], relevance: 0 + }], + illegal: "[^\\s\\}]" + } + } +})()); hljs.registerLanguage("xml", (() => { + "use strict"; function e(e) { + return e ? "string" == typeof e ? e : e.source : null + } function n(e) { return a("(?=", e, ")") } + function a(...n) { return n.map((n => e(n))).join("") } function s(...n) { + return "(" + n.map((n => e(n))).join("|") + ")" + } return e => { + const t = a(/[A-Z_]/, a("(", /[A-Z0-9_.-]+:/, ")?"), /[A-Z0-9_.-]*/), i = { + className: "symbol", begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/ + }, r = { + begin: /\s/, + contains: [{ className: "meta-keyword", begin: /#?[a-z_][a-z1-9_-]+/, illegal: /\n/ }] + }, c = e.inherit(r, { begin: /\(/, end: /\)/ }), l = e.inherit(e.APOS_STRING_MODE, { + className: "meta-string" + }), g = e.inherit(e.QUOTE_STRING_MODE, { + className: "meta-string" + }), m = { + endsWithParent: !0, illegal: /`]+/ }] + }] + }] + }; return { + name: "HTML, XML", + aliases: ["html", "xhtml", "rss", "atom", "xjb", "xsd", "xsl", "plist", "wsf", "svg"], + case_insensitive: !0, contains: [{ + className: "meta", begin: //, + relevance: 10, contains: [r, g, l, c, { + begin: /\[/, end: /\]/, contains: [{ + className: "meta", + begin: //, contains: [r, c, g, l] + }] + }] + }, e.COMMENT(//, { + relevance: 10 + }), { begin: //, relevance: 10 }, i, { + className: "meta", begin: /<\?xml/, end: /\?>/, relevance: 10 + }, { + className: "tag", + begin: /)/, end: />/, keywords: { name: "style" }, contains: [m], starts: { + end: /<\/style>/, returnEnd: !0, subLanguage: ["css", "xml"] + } + }, { + className: "tag", + begin: /)/, end: />/, keywords: { name: "script" }, contains: [m], starts: { + end: /<\/script>/, returnEnd: !0, subLanguage: ["javascript", "handlebars", "xml"] + } + }, { + className: "tag", begin: /<>|<\/>/ + }, { + className: "tag", + begin: a(//, />/, /\s/)))), end: /\/?>/, contains: [{ + className: "name", + begin: t, relevance: 0, starts: m + }] + }, { + className: "tag", begin: a(/<\//, n(a(t, />/))), + contains: [{ className: "name", begin: t, relevance: 0 }, { begin: />/, relevance: 0 }] + }] + } + } +})()); hljs.registerLanguage("markdown", (() => { + "use strict"; function n(...n) { + return n.map((n => { + return (e = n) ? "string" == typeof e ? e : e.source : null; var e + })).join("") + } return e => { + const a = { + begin: /<\/?[A-Za-z_]/, end: ">", + subLanguage: "xml", relevance: 0 + }, i = { + variants: [{ + begin: /\[.+?\]\[.*?\]/, relevance: 0 + }, { + begin: /\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, + relevance: 2 + }, { + begin: n(/\[.+?\]\(/, /[A-Za-z][A-Za-z0-9+.-]*/, /:\/\/.*?\)/), + relevance: 2 + }, { begin: /\[.+?\]\([./?&#].*?\)/, relevance: 1 }, { + begin: /\[.+?\]\(.*?\)/, relevance: 0 + }], returnBegin: !0, contains: [{ + className: "string", relevance: 0, begin: "\\[", end: "\\]", excludeBegin: !0, + returnEnd: !0 + }, { + className: "link", relevance: 0, begin: "\\]\\(", end: "\\)", + excludeBegin: !0, excludeEnd: !0 + }, { + className: "symbol", relevance: 0, begin: "\\]\\[", + end: "\\]", excludeBegin: !0, excludeEnd: !0 + }] + }, s = { + className: "strong", contains: [], + variants: [{ begin: /_{2}/, end: /_{2}/ }, { begin: /\*{2}/, end: /\*{2}/ }] + }, c = { + className: "emphasis", contains: [], variants: [{ begin: /\*(?!\*)/, end: /\*/ }, { + begin: /_(?!_)/, end: /_/, relevance: 0 + }] + }; s.contains.push(c), c.contains.push(s) + ; let t = [a, i] + ; return s.contains = s.contains.concat(t), c.contains = c.contains.concat(t), + t = t.concat(s, c), { + name: "Markdown", aliases: ["md", "mkdown", "mkd"], contains: [{ + className: "section", variants: [{ begin: "^#{1,6}", end: "$", contains: t }, { + begin: "(?=^.+?\\n[=-]{2,}$)", contains: [{ begin: "^[=-]*$" }, { + begin: "^", end: "\\n", + contains: t + }] + }] + }, a, { + className: "bullet", begin: "^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", + end: "\\s+", excludeEnd: !0 + }, s, c, { + className: "quote", begin: "^>\\s+", contains: t, + end: "$" + }, { + className: "code", variants: [{ begin: "(`{3,})[^`](.|\\n)*?\\1`*[ ]*" }, { + begin: "(~{3,})[^~](.|\\n)*?\\1~*[ ]*" + }, { begin: "```", end: "```+[ ]*$" }, { + begin: "~~~", end: "~~~+[ ]*$" + }, { begin: "`.+?`" }, { + begin: "(?=^( {4}|\\t))", + contains: [{ begin: "^( {4}|\\t)", end: "(\\n)$" }], relevance: 0 + }] + }, { + begin: "^[-\\*]{3,}", end: "$" + }, i, { + begin: /^\[[^\n]+\]:/, returnBegin: !0, contains: [{ + className: "symbol", begin: /\[/, end: /\]/, excludeBegin: !0, excludeEnd: !0 + }, { + className: "link", begin: /:\s*/, end: /$/, excludeBegin: !0 + }] + }] + } + } +})()); hljs.registerLanguage("yaml", (() => { + "use strict"; return e => { + var n = "true false yes no null", a = "[\\w#;/?:@&=+$,.~*'()[\\]]+", s = { + className: "string", relevance: 0, variants: [{ begin: /'/, end: /'/ }, { + begin: /"/, end: /"/ + }, { begin: /\S+/ }], contains: [e.BACKSLASH_ESCAPE, { + className: "template-variable", + variants: [{ begin: /\{\{/, end: /\}\}/ }, { begin: /%\{/, end: /\}/ }] + }] + }, i = e.inherit(s, { + variants: [{ begin: /'/, end: /'/ }, { begin: /"/, end: /"/ }, { begin: /[^\s,{}[\]]+/ }] + }), l = { + end: ",", endsWithParent: !0, excludeEnd: !0, contains: [], keywords: n, relevance: 0 + }, t = { + begin: /\{/, end: /\}/, contains: [l], illegal: "\\n", relevance: 0 + }, g = { + begin: "\\[", + end: "\\]", contains: [l], illegal: "\\n", relevance: 0 + }, b = [{ + className: "attr", + variants: [{ begin: "\\w[\\w :\\/.-]*:(?=[ \t]|$)" }, { + begin: '"\\w[\\w :\\/.-]*":(?=[ \t]|$)' + }, { + begin: "'\\w[\\w :\\/.-]*':(?=[ \t]|$)" + }] + }, { className: "meta", begin: "^---\\s*$", relevance: 10 }, { + className: "string", + begin: "[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*" + }, { + begin: "<%[%=-]?", end: "[%-]?%>", subLanguage: "ruby", excludeBegin: !0, excludeEnd: !0, + relevance: 0 + }, { className: "type", begin: "!\\w+!" + a }, { + className: "type", + begin: "!<" + a + ">" + }, { className: "type", begin: "!" + a }, { + className: "type", begin: "!!" + a + }, { className: "meta", begin: "&" + e.UNDERSCORE_IDENT_RE + "$" }, { + className: "meta", + begin: "\\*" + e.UNDERSCORE_IDENT_RE + "$" + }, { + className: "bullet", begin: "-(?=[ ]|$)", + relevance: 0 + }, e.HASH_COMMENT_MODE, { beginKeywords: n, keywords: { literal: n } }, { + className: "number", + begin: "\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" + }, { className: "number", begin: e.C_NUMBER_RE + "\\b", relevance: 0 }, t, g, s], r = [...b] + ; return r.pop(), r.push(i), l.contains = r, { + name: "YAML", case_insensitive: !0, + aliases: ["yml", "YAML"], contains: b + } + } +})()); hljs.registerLanguage("bash", (() => { + "use strict"; function e(...e) { + return e.map((e => { + return (s = e) ? "string" == typeof s ? s : s.source : null; var s + })).join("") + } return s => { + const n = {}, t = { + begin: /\$\{/, end: /\}/, contains: ["self", { + begin: /:-/, contains: [n] + }] + }; Object.assign(n, { + className: "variable", variants: [{ + begin: e(/\$[\w\d#@][\w\d_]*/, "(?![\\w\\d])(?![$])") + }, t] + }); const a = { + className: "subst", begin: /\$\(/, end: /\)/, contains: [s.BACKSLASH_ESCAPE] + }, i = { + begin: /<<-?\s*(?=\w+)/, starts: { + contains: [s.END_SAME_AS_BEGIN({ + begin: /(\w+)/, + end: /(\w+)/, className: "string" + })] + } + }, c = { + className: "string", begin: /"/, end: /"/, + contains: [s.BACKSLASH_ESCAPE, n, a] + }; a.contains.push(c); const o = { + begin: /\$\(\(/, + end: /\)\)/, contains: [{ begin: /\d+#[0-9a-f]+/, className: "number" }, s.NUMBER_MODE, n] + }, r = s.SHEBANG({ + binary: "(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)", relevance: 10 + }), l = { + className: "function", begin: /\w[\w\d_]*\s*\(\s*\)\s*\{/, returnBegin: !0, + contains: [s.inherit(s.TITLE_MODE, { begin: /\w[\w\d_]*/ })], relevance: 0 + }; return { + name: "Bash", aliases: ["sh", "zsh"], keywords: { + $pattern: /\b[a-z._-]+\b/, + keyword: "if then else elif fi for while in do done case esac function", + literal: "true false", + built_in: "break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp" + }, contains: [r, s.SHEBANG(), l, o, s.HASH_COMMENT_MODE, i, c, { + className: "", begin: /\\"/ + }, { className: "string", begin: /'/, end: /'/ }, n] + } + } +})()); hljs.registerLanguage("go", (() => { + "use strict"; return e => { + const n = { + keyword: "break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune", + literal: "true false iota nil", + built_in: "append cap close complex copy imag len make new panic print println real recover delete" + }; return { + name: "Go", aliases: ["golang"], keywords: n, illegal: " { + "use strict" + ; const e = ["as", "in", "of", "if", "for", "while", "finally", "var", "new", "function", "do", "return", "void", "else", "break", "catch", "instanceof", "with", "throw", "case", "default", "try", "switch", "continue", "typeof", "delete", "let", "yield", "const", "class", "debugger", "async", "await", "static", "import", "from", "export", "extends"], n = ["true", "false", "null", "undefined", "NaN", "Infinity"], a = [].concat(["setInterval", "setTimeout", "clearInterval", "clearTimeout", "require", "exports", "eval", "isFinite", "isNaN", "parseFloat", "parseInt", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "unescape"], ["arguments", "this", "super", "console", "window", "document", "localStorage", "module", "global"], ["Intl", "DataView", "Number", "Math", "Date", "String", "RegExp", "Object", "Function", "Boolean", "Error", "Symbol", "Set", "Map", "WeakSet", "WeakMap", "Proxy", "Reflect", "JSON", "Promise", "Float64Array", "Int16Array", "Int32Array", "Int8Array", "Uint16Array", "Uint32Array", "Float32Array", "Array", "Uint8Array", "Uint8ClampedArray", "ArrayBuffer"], ["EvalError", "InternalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"]) + ; return r => { + const t = { + keyword: e.concat(["then", "unless", "until", "loop", "by", "when", "and", "or", "is", "isnt", "not"]).filter((i = ["var", "const", "let", "function", "static"], + e => !i.includes(e))).join(" "), + literal: n.concat(["yes", "no", "on", "off"]).join(" "), + built_in: a.concat(["npm", "print"]).join(" ") + }; var i + ; const s = "[A-Za-z$_][0-9A-Za-z$_]*", o = { + className: "subst", begin: /#\{/, end: /\}/, + keywords: t + }, c = [r.BINARY_NUMBER_MODE, r.inherit(r.C_NUMBER_MODE, { + starts: { + end: "(\\s*/)?", relevance: 0 + } + }), { + className: "string", variants: [{ + begin: /'''/, + end: /'''/, contains: [r.BACKSLASH_ESCAPE] + }, { + begin: /'/, end: /'/, + contains: [r.BACKSLASH_ESCAPE] + }, { + begin: /"""/, end: /"""/, + contains: [r.BACKSLASH_ESCAPE, o] + }, { + begin: /"/, end: /"/, + contains: [r.BACKSLASH_ESCAPE, o] + }] + }, { + className: "regexp", variants: [{ + begin: "///", + end: "///", contains: [o, r.HASH_COMMENT_MODE] + }, { + begin: "//[gim]{0,3}(?=\\W)", + relevance: 0 + }, { begin: /\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/ }] + }, { + begin: "@" + s + }, { + subLanguage: "javascript", excludeBegin: !0, excludeEnd: !0, variants: [{ + begin: "```", end: "```" + }, { begin: "`", end: "`" }] + }]; o.contains = c + ; const l = r.inherit(r.TITLE_MODE, { begin: s }), d = "(\\(.*\\)\\s*)?\\B[-=]>", g = { + className: "params", begin: "\\([^\\(]", returnBegin: !0, contains: [{ + begin: /\(/, + end: /\)/, keywords: t, contains: ["self"].concat(c) + }] + }; return { + name: "CoffeeScript", + aliases: ["coffee", "cson", "iced"], keywords: t, illegal: /\/\*/, + contains: c.concat([r.COMMENT("###", "###"), r.HASH_COMMENT_MODE, { + className: "function", begin: "^\\s*" + s + "\\s*=\\s*" + d, end: "[-=]>", returnBegin: !0, + contains: [l, g] + }, { + begin: /[:\(,=]\s*/, relevance: 0, contains: [{ + className: "function", + begin: d, end: "[-=]>", returnBegin: !0, contains: [g] + }] + }, { + className: "class", + beginKeywords: "class", end: "$", illegal: /[:="\[\]]/, contains: [{ + beginKeywords: "extends", endsWithParent: !0, illegal: /[:="\[\]]/, contains: [l] + }, l] + }, { begin: s + ":", end: ":", returnBegin: !0, returnEnd: !0, relevance: 0 }]) + } + } +})()); hljs.registerLanguage("csharp", (() => { + "use strict"; return e => { + var n = { + keyword: ["abstract", "as", "base", "break", "case", "class", "const", "continue", "do", "else", "event", "explicit", "extern", "finally", "fixed", "for", "foreach", "goto", "if", "implicit", "in", "interface", "internal", "is", "lock", "namespace", "new", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "record", "ref", "return", "sealed", "sizeof", "stackalloc", "static", "struct", "switch", "this", "throw", "try", "typeof", "unchecked", "unsafe", "using", "virtual", "void", "volatile", "while"].concat(["add", "alias", "and", "ascending", "async", "await", "by", "descending", "equals", "from", "get", "global", "group", "init", "into", "join", "let", "nameof", "not", "notnull", "on", "or", "orderby", "partial", "remove", "select", "set", "unmanaged", "value|0", "var", "when", "where", "with", "yield"]).join(" "), + built_in: "bool byte char decimal delegate double dynamic enum float int long nint nuint object sbyte short string ulong unit ushort", + literal: "default false null true" + }, a = e.inherit(e.TITLE_MODE, { + begin: "[a-zA-Z](\\.?\\w)*" + }), i = { + className: "number", variants: [{ + begin: "\\b(0b[01']+)" + }, { + begin: "(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)" + }, { + begin: "(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" + }], relevance: 0 + }, s = { + className: "string", begin: '@"', end: '"', contains: [{ begin: '""' }] + }, t = e.inherit(s, { illegal: /\n/ }), r = { + className: "subst", begin: /\{/, end: /\}/, + keywords: n + }, l = e.inherit(r, { illegal: /\n/ }), c = { + className: "string", begin: /\$"/, + end: '"', illegal: /\n/, contains: [{ begin: /\{\{/ }, { + begin: /\}\}/ + }, e.BACKSLASH_ESCAPE, l] + }, o = { + className: "string", begin: /\$@"/, end: '"', contains: [{ + begin: /\{\{/ + }, { begin: /\}\}/ }, { begin: '""' }, r] + }, d = e.inherit(o, { + illegal: /\n/, + contains: [{ begin: /\{\{/ }, { begin: /\}\}/ }, { begin: '""' }, l] + }) + ; r.contains = [o, c, s, e.APOS_STRING_MODE, e.QUOTE_STRING_MODE, i, e.C_BLOCK_COMMENT_MODE], + l.contains = [d, c, t, e.APOS_STRING_MODE, e.QUOTE_STRING_MODE, i, e.inherit(e.C_BLOCK_COMMENT_MODE, { + illegal: /\n/ + })]; var g = { + variants: [o, c, s, e.APOS_STRING_MODE, e.QUOTE_STRING_MODE] + }, E = { + begin: "<", end: ">", contains: [{ beginKeywords: "in out" }, a] + }, _ = e.IDENT_RE + "(<" + e.IDENT_RE + "(\\s*,\\s*" + e.IDENT_RE + ")*>)?(\\[\\])?", b = { + begin: "@" + e.IDENT_RE, relevance: 0 + }; return { + name: "C#", aliases: ["cs", "c#"], + keywords: n, illegal: /::/, contains: [e.COMMENT("///", "$", { + returnBegin: !0, + contains: [{ + className: "doctag", variants: [{ begin: "///", relevance: 0 }, { + begin: "\x3c!--|--\x3e" + }, { begin: "" }] + }] + }), e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE, { + className: "meta", begin: "#", + end: "$", keywords: { + "meta-keyword": "if else elif endif define undef warning error line region endregion pragma checksum" + } + }, g, i, { + beginKeywords: "class interface", relevance: 0, end: /[{;=]/, + illegal: /[^\s:,]/, contains: [{ + beginKeywords: "where class" + }, a, E, e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE] + }, { + beginKeywords: "namespace", + relevance: 0, end: /[{;=]/, illegal: /[^\s:]/, + contains: [a, e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE] + }, { + beginKeywords: "record", relevance: 0, end: /[{;=]/, illegal: /[^\s:]/, + contains: [a, E, e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE] + }, { + className: "meta", + begin: "^\\s*\\[", excludeBegin: !0, end: "\\]", excludeEnd: !0, contains: [{ + className: "meta-string", begin: /"/, end: /"/ + }] + }, { + beginKeywords: "new return throw await else", relevance: 0 + }, { + className: "function", + begin: "(" + _ + "\\s+)+" + e.IDENT_RE + "\\s*(<.+>\\s*)?\\(", returnBegin: !0, + end: /\s*[{;=]/, excludeEnd: !0, keywords: n, contains: [{ + beginKeywords: "public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", + relevance: 0 + }, { + begin: e.IDENT_RE + "\\s*(<.+>\\s*)?\\(", returnBegin: !0, + contains: [e.TITLE_MODE, E], relevance: 0 + }, { + className: "params", begin: /\(/, end: /\)/, + excludeBegin: !0, excludeEnd: !0, keywords: n, relevance: 0, + contains: [g, i, e.C_BLOCK_COMMENT_MODE] + }, e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE] + }, b] + } + } +})()); hljs.registerLanguage("scss", (() => { + "use strict"; return e => { + var t = "@[a-z-]+", i = { + className: "variable", begin: "(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b" + }, r = { + className: "number", begin: "#[0-9A-Fa-f]+" + } + ; return e.CSS_NUMBER_MODE, e.QUOTE_STRING_MODE, + e.APOS_STRING_MODE, e.C_BLOCK_COMMENT_MODE, { + name: "SCSS", case_insensitive: !0, + illegal: "[=/|']", contains: [e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE, { + className: "selector-id", begin: "#[A-Za-z0-9_-]+", relevance: 0 + }, { + className: "selector-class", begin: "\\.[A-Za-z0-9_-]+", relevance: 0 + }, { + className: "selector-attr", begin: "\\[", end: "\\]", illegal: "$" + }, { + className: "selector-tag", + begin: "\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b", + relevance: 0 + }, { + className: "selector-pseudo", + begin: ":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)" + }, { + className: "selector-pseudo", + begin: "::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)" + }, i, { + className: "attribute", + begin: "\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b", + illegal: "[^\\s]" + }, { + begin: "\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" + }, { + begin: ":", end: ";", + contains: [i, r, e.CSS_NUMBER_MODE, e.QUOTE_STRING_MODE, e.APOS_STRING_MODE, { + className: "meta", begin: "!important" + }] + }, { + begin: "@(page|font-face)", lexemes: t, + keywords: "@page @font-face" + }, { + begin: "@", end: "[{;]", returnBegin: !0, + keywords: "and or not only", contains: [{ + begin: t, className: "keyword" + }, i, e.QUOTE_STRING_MODE, e.APOS_STRING_MODE, r, e.CSS_NUMBER_MODE] + }] + } + } +})()); hljs.registerLanguage("r", (() => { + "use strict"; function e(...e) { + return e.map((e => { + return (a = e) ? "string" == typeof a ? a : a.source : null; var a + })).join("") + } return a => { + const n = /(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/; return { + name: "R", + illegal: /->/, keywords: { + $pattern: n, + keyword: "function if in break next repeat else for while", + literal: "NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", + built_in: "LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" + }, compilerExtensions: [(a, n) => { + if (!a.beforeMatch) return + ; if (a.starts) throw Error("beforeMatch cannot be used with starts") + ; const i = Object.assign({}, a); Object.keys(a).forEach((e => { + delete a[e] + })), a.begin = e(i.beforeMatch, e("(?=", i.begin, ")")), a.starts = { + relevance: 0, + contains: [Object.assign(i, { endsParent: !0 })] + }, a.relevance = 0, delete i.beforeMatch + }], contains: [a.COMMENT(/#'/, /$/, { + contains: [{ + className: "doctag", + begin: "@examples", starts: { + contains: [{ begin: /\n/ }, { + begin: /#'\s*(?=@[a-zA-Z]+)/, + endsParent: !0 + }, { begin: /#'/, end: /$/, excludeBegin: !0 }] + } + }, { + className: "doctag", + begin: "@param", end: /$/, contains: [{ + className: "variable", variants: [{ begin: n }, { + begin: /`(?:\\.|[^`\\])+`/ + }], endsParent: !0 + }] + }, { + className: "doctag", + begin: /@[a-zA-Z]+/ + }, { className: "meta-keyword", begin: /\\[a-zA-Z]+/ }] + }), a.HASH_COMMENT_MODE, { + className: "string", contains: [a.BACKSLASH_ESCAPE], + variants: [a.END_SAME_AS_BEGIN({ + begin: /[rR]"(-*)\(/, end: /\)(-*)"/ + }), a.END_SAME_AS_BEGIN({ + begin: /[rR]"(-*)\{/, end: /\}(-*)"/ + }), a.END_SAME_AS_BEGIN({ + begin: /[rR]"(-*)\[/, end: /\](-*)"/ + }), a.END_SAME_AS_BEGIN({ + begin: /[rR]'(-*)\(/, end: /\)(-*)'/ + }), a.END_SAME_AS_BEGIN({ + begin: /[rR]'(-*)\{/, end: /\}(-*)'/ + }), a.END_SAME_AS_BEGIN({ begin: /[rR]'(-*)\[/, end: /\](-*)'/ }), { + begin: '"', end: '"', + relevance: 0 + }, { begin: "'", end: "'", relevance: 0 }] + }, { + className: "number", relevance: 0, + beforeMatch: /([^a-zA-Z0-9._])/, variants: [{ + match: /0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/ + }, { + match: /0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?/ + }, { + match: /(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?/ + }] + }, { begin: "%", end: "%" }, { + begin: e(/[a-zA-Z][a-zA-Z_0-9]*/, "\\s+<-\\s+") + }, { + begin: "`", end: "`", contains: [{ + begin: /\\./ + }] + }] + } + } +})()); hljs.registerLanguage("less", (() => { + "use strict"; return e => { + var n = "([\\w-]+|@\\{[\\w-]+\\})", a = [], s = [], t = e => ({ + className: "string", + begin: "~?" + e + ".*?" + e + }), r = (e, n, a) => ({ className: e, begin: n, relevance: a }), i = { + begin: "\\(", end: "\\)", contains: s, relevance: 0 + } + ; s.push(e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE, t("'"), t('"'), e.CSS_NUMBER_MODE, { + begin: "(url|data-uri)\\(", starts: { + className: "string", end: "[\\)\\n]", + excludeEnd: !0 + } + }, r("number", "#[0-9A-Fa-f]+\\b"), i, r("variable", "@@?[\\w-]+", 10), r("variable", "@\\{[\\w-]+\\}"), r("built_in", "~?`[^`]*?`"), { + className: "attribute", begin: "[\\w-]+\\s*:", end: ":", returnBegin: !0, excludeEnd: !0 + }, { className: "meta", begin: "!important" }); var c = s.concat({ + begin: /\{/, end: /\}/, + contains: a + }), l = { + beginKeywords: "when", endsWithParent: !0, contains: [{ + beginKeywords: "and not" + }].concat(s) + }, g = { + begin: n + "\\s*:", returnBegin: !0, + end: "[;}]", relevance: 0, contains: [{ + className: "attribute", begin: n, end: ":", + excludeEnd: !0, starts: { endsWithParent: !0, illegal: "[<=$]", relevance: 0, contains: s } + }] + }, d = { + className: "keyword", + begin: "@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", + starts: { end: "[;{}]", returnEnd: !0, contains: s, relevance: 0 } + }, o = { + className: "variable", variants: [{ begin: "@[\\w-]+\\s*:", relevance: 15 }, { + begin: "@[\\w-]+" + }], starts: { end: "[;}]", returnEnd: !0, contains: c } + }, b = { + variants: [{ + begin: "[\\.#:&\\[>]", end: "[;{}]" + }, { begin: n, end: /\{/ }], returnBegin: !0, + returnEnd: !0, illegal: "[<='$\"]", relevance: 0, + contains: [e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE, l, r("keyword", "all\\b"), r("variable", "@\\{[\\w-]+\\}"), r("selector-tag", n + "%?", 0), r("selector-id", "#" + n), r("selector-class", "\\." + n, 0), r("selector-tag", "&", 0), { + className: "selector-attr", begin: "\\[", end: "\\]" + }, { + className: "selector-pseudo", + begin: /:(:)?[a-zA-Z0-9_\-+()"'.]+/ + }, { begin: "\\(", end: "\\)", contains: c }, { + begin: "!important" + }] + } + ; return a.push(e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE, d, o, g, b), { + name: "Less", case_insensitive: !0, illegal: "[=>'/<($\"]", contains: a + } + } +})()); hljs.registerLanguage("objectivec", (() => { + "use strict"; return e => { + const n = /[a-zA-Z@][a-zA-Z0-9_]*/, _ = { + $pattern: n, + keyword: "@interface @class @protocol @implementation" + }; return { + name: "Objective-C", aliases: ["mm", "objc", "obj-c", "obj-c++", "objective-c++"], + keywords: { + $pattern: n, + keyword: "int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN", + literal: "false true FALSE TRUE nil YES NO NULL", + built_in: "BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once" + }, illegal: "/, end: /$/, + illegal: "\\n" + }, e.C_LINE_COMMENT_MODE, e.C_BLOCK_COMMENT_MODE] + }, { + className: "class", begin: "(" + _.keyword.split(" ").join("|") + ")\\b", end: /(\{|$)/, + excludeEnd: !0, keywords: _, contains: [e.UNDERSCORE_TITLE_MODE] + }, { + begin: "\\." + e.UNDERSCORE_IDENT_RE, relevance: 0 + }] + } + } +})()); hljs.registerLanguage("typescript", (() => { + "use strict" + ; const e = "[A-Za-z$_][0-9A-Za-z$_]*", n = ["as", "in", "of", "if", "for", "while", "finally", "var", "new", "function", "do", "return", "void", "else", "break", "catch", "instanceof", "with", "throw", "case", "default", "try", "switch", "continue", "typeof", "delete", "let", "yield", "const", "class", "debugger", "async", "await", "static", "import", "from", "export", "extends"], a = ["true", "false", "null", "undefined", "NaN", "Infinity"], s = [].concat(["setInterval", "setTimeout", "clearInterval", "clearTimeout", "require", "exports", "eval", "isFinite", "isNaN", "parseFloat", "parseInt", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "unescape"], ["arguments", "this", "super", "console", "window", "document", "localStorage", "module", "global"], ["Intl", "DataView", "Number", "Math", "Date", "String", "RegExp", "Object", "Function", "Boolean", "Error", "Symbol", "Set", "Map", "WeakSet", "WeakMap", "Proxy", "Reflect", "JSON", "Promise", "Float64Array", "Int16Array", "Int32Array", "Int8Array", "Uint16Array", "Uint32Array", "Float32Array", "Array", "Uint8Array", "Uint8ClampedArray", "ArrayBuffer"], ["EvalError", "InternalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"]) + ; function t(e) { return i("(?=", e, ")") } function i(...e) { + return e.map((e => { + return (n = e) ? "string" == typeof n ? n : n.source : null; var n + })).join("") + } return r => { + const c = { + $pattern: e, + keyword: n.concat(["type", "namespace", "typedef", "interface", "public", "private", "protected", "implements", "declare", "abstract", "readonly"]).join(" "), + literal: a.join(" "), + built_in: s.concat(["any", "void", "number", "boolean", "string", "object", "never", "enum"]).join(" ") + }, o = { className: "meta", begin: "@[A-Za-z$_][0-9A-Za-z$_]*" }, l = (e, n, a) => { + const s = e.contains.findIndex((e => e.label === n)) + ; if (-1 === s) throw Error("can not find mode to replace"); e.contains.splice(s, 1, a) + }, b = (r => { + const c = e, o = { + begin: /<[A-Za-z0-9\\._:-]+/, + end: /\/[A-Za-z0-9\\._:-]+>|\/>/, isTrulyOpeningTag: (e, n) => { + const a = e[0].length + e.index, s = e.input[a]; "<" !== s ? ">" === s && (((e, { after: n }) => { + const a = "", + returnBegin: !0, end: "\\s*=>", contains: [{ + className: "params", variants: [{ + begin: r.UNDERSCORE_IDENT_RE, relevance: 0 + }, { + className: null, begin: /\(\s*\)/, skip: !0 + }, { begin: /\(/, end: /\)/, excludeBegin: !0, excludeEnd: !0, keywords: l, contains: f }] + }] + }, { begin: /,/, relevance: 0 }, { className: "", begin: /\s/, end: /\s*/, skip: !0 }, { + variants: [{ begin: "<>", end: "" }, { + begin: o.begin, "on:begin": o.isTrulyOpeningTag, + end: o.end + }], subLanguage: "xml", contains: [{ + begin: o.begin, end: o.end, skip: !0, + contains: ["self"] + }] + }], relevance: 0 + }, { + className: "function", + beginKeywords: "function", end: /[{;]/, excludeEnd: !0, keywords: l, + contains: ["self", r.inherit(r.TITLE_MODE, { begin: c }), A], illegal: /%/ + }, { + beginKeywords: "while if switch catch for" + }, { + className: "function", + begin: r.UNDERSCORE_IDENT_RE + "\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", + returnBegin: !0, contains: [A, r.inherit(r.TITLE_MODE, { begin: c })] + }, { + variants: [{ + begin: "\\." + c + }, { begin: "\\$" + c }], relevance: 0 + }, { + className: "class", + beginKeywords: "class", end: /[{;=]/, excludeEnd: !0, illegal: /[:"[\]]/, contains: [{ + beginKeywords: "extends" + }, r.UNDERSCORE_TITLE_MODE] + }, { + begin: /\b(?=constructor)/, + end: /[{;]/, excludeEnd: !0, contains: [r.inherit(r.TITLE_MODE, { begin: c }), "self", A] + }, { + begin: "(get|set)\\s+(?=" + c + "\\()", end: /\{/, keywords: "get set", + contains: [r.inherit(r.TITLE_MODE, { begin: c }), { begin: /\(\)/ }, A] + }, { begin: /\$[(.]/ }] + } + })(r) + ; return Object.assign(b.keywords, c), b.exports.PARAMS_CONTAINS.push(o), b.contains = b.contains.concat([o, { + beginKeywords: "namespace", end: /\{/, excludeEnd: !0 + }, { + beginKeywords: "interface", + end: /\{/, excludeEnd: !0, keywords: "interface extends" + }]), l(b, "shebang", r.SHEBANG()), l(b, "use_strict", { + className: "meta", relevance: 10, + begin: /^\s*['"]use strict['"]/ + }), b.contains.find((e => "function" === e.className)).relevance = 0, Object.assign(b, { + name: "TypeScript", aliases: ["ts"] + }), b + } +})()); hljs.registerLanguage("plaintext", (() => { + "use strict"; return t => ({ + name: "Plain text", aliases: ["text", "txt"], disableAutodetect: !0 + }) +})()); hljs.registerLanguage("json", (() => { + "use strict"; return n => { + const e = { + literal: "true false null" + }, i = [n.C_LINE_COMMENT_MODE, n.C_BLOCK_COMMENT_MODE], a = [n.QUOTE_STRING_MODE, n.C_NUMBER_MODE], l = { + end: ",", endsWithParent: !0, excludeEnd: !0, contains: a, keywords: e + }, t = { + begin: /\{/, + end: /\}/, contains: [{ + className: "attr", begin: /"/, end: /"/, + contains: [n.BACKSLASH_ESCAPE], illegal: "\\n" + }, n.inherit(l, { + begin: /:/ + })].concat(i), illegal: "\\S" + }, s = { + begin: "\\[", end: "\\]", contains: [n.inherit(l)], + illegal: "\\S" + }; return a.push(t, s), i.forEach((n => { a.push(n) })), { + name: "JSON", + contains: a, keywords: e, illegal: "\\S" + } + } +})()); hljs.registerLanguage("perl", (() => { + "use strict"; function e(...e) { + return e.map((e => { + return (n = e) ? "string" == typeof n ? n : n.source : null; var n + })).join("") + } return n => { + const t = /[dualxmsipn]{0,12}/, s = { + $pattern: /[\w.]+/, + keyword: "getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when" + }, r = { className: "subst", begin: "[$@]\\{", end: "\\}", keywords: s }, i = { + begin: /->\{/, + end: /\}/ + }, a = { + variants: [{ begin: /\$\d/ }, { + begin: e(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/, "(?![A-Za-z])(?![@$%])") + }, { begin: /[$%@][^\s\w{]/, relevance: 0 }] + }, o = [n.BACKSLASH_ESCAPE, r, a], c = [a, n.HASH_COMMENT_MODE, n.COMMENT(/^=\w/, /=cut/, { + endsWithParent: !0 + }), i, { + className: "string", contains: o, variants: [{ + begin: "q[qwxr]?\\s*\\(", end: "\\)", relevance: 5 + }, { + begin: "q[qwxr]?\\s*\\[", + end: "\\]", relevance: 5 + }, { begin: "q[qwxr]?\\s*\\{", end: "\\}", relevance: 5 }, { + begin: "q[qwxr]?\\s*\\|", end: "\\|", relevance: 5 + }, { + begin: "q[qwxr]?\\s*<", end: ">", + relevance: 5 + }, { begin: "qw\\s+q", end: "q", relevance: 5 }, { + begin: "'", end: "'", + contains: [n.BACKSLASH_ESCAPE] + }, { begin: '"', end: '"' }, { + begin: "`", end: "`", + contains: [n.BACKSLASH_ESCAPE] + }, { begin: /\{\w+\}/, contains: [], relevance: 0 }, { + begin: "-?\\w+\\s*=>", contains: [], relevance: 0 + }] + }, { + className: "number", + begin: "(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", + relevance: 0 + }, { + begin: "(\\/\\/|" + n.RE_STARTERS_RE + "|\\b(split|return|print|reverse|grep)\\b)\\s*", + keywords: "split return print reverse grep", relevance: 0, + contains: [n.HASH_COMMENT_MODE, { + className: "regexp", + begin: e(/(s|tr|y)/, /\//, /(\\.|[^\\\/])*/, /\//, /(\\.|[^\\\/])*/, /\//, t), + relevance: 10 + }, { + className: "regexp", begin: /(m|qr)?\//, end: e(/\//, t), + contains: [n.BACKSLASH_ESCAPE], relevance: 0 + }] + }, { + className: "function", + beginKeywords: "sub", end: "(\\s*\\(.*?\\))?[;{]", excludeEnd: !0, relevance: 5, + contains: [n.TITLE_MODE] + }, { begin: "-\\w\\b", relevance: 0 }, { + begin: "^__DATA__$", + end: "^__END__$", subLanguage: "mojolicious", contains: [{ + begin: "^@@.*", end: "$", + className: "comment" + }] + }]; return r.contains = c, i.contains = c, { + name: "Perl", + aliases: ["pl", "pm"], keywords: s, contains: c + } + } +})()); hljs.registerLanguage("shell", (() => { + "use strict"; return s => ({ + name: "Shell Session", aliases: ["console"], contains: [{ + className: "meta", + begin: /^\s{0,3}[/~\w\d[\]()@-]*[>%$#]/, starts: { + end: /[^\\](?=\s*$)/, + subLanguage: "bash" + } + }] + }) +})()); hljs.registerLanguage("lua", (() => { + "use strict"; return e => { + const t = "\\[=*\\[", a = "\\]=*\\]", n = { + begin: t, end: a, contains: ["self"] + }, o = [e.COMMENT("--(?!\\[=*\\[)", "$"), e.COMMENT("--\\[=*\\[", a, { + contains: [n], + relevance: 10 + })]; return { + name: "Lua", keywords: { + $pattern: e.UNDERSCORE_IDENT_RE, + literal: "true false nil", + keyword: "and break do else elseif end for goto if in local not or repeat return then until while", + built_in: "_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" + }, contains: o.concat([{ + className: "function", beginKeywords: "function", end: "\\)", + contains: [e.inherit(e.TITLE_MODE, { + begin: "([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*" + }), { + className: "params", + begin: "\\(", endsWithParent: !0, contains: o + }].concat(o) + }, e.C_NUMBER_MODE, e.APOS_STRING_MODE, e.QUOTE_STRING_MODE, { + className: "string", + begin: t, end: a, contains: [n], relevance: 5 + }]) + } + } +})()); hljs.registerLanguage("makefile", (() => { + "use strict"; return e => { + const i = { + className: "variable", variants: [{ + begin: "\\$\\(" + e.UNDERSCORE_IDENT_RE + "\\)", + contains: [e.BACKSLASH_ESCAPE] + }, { begin: /\$[@% { + "use strict"; return e => { + const n = "([ui](8|16|32|64|128|size)|f(32|64))?", t = "drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!" + ; return { + name: "Rust", aliases: ["rs"], keywords: { + $pattern: e.IDENT_RE + "!?", + keyword: "abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield", + literal: "true false Some None Ok Err", built_in: t + }, illegal: "" }] + } + } +})()); hljs.registerLanguage("php", (() => { + "use strict"; return e => { + const r = { + className: "variable", + begin: "\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?![A-Za-z0-9])(?![$])" + }, t = { + className: "meta", variants: [{ begin: /<\?php/, relevance: 10 }, { begin: /<\?[=]?/ }, { + begin: /\?>/ + }] + }, a = { + className: "subst", variants: [{ begin: /\$\w+/ }, { + begin: /\{\$/, + end: /\}/ + }] + }, n = e.inherit(e.APOS_STRING_MODE, { + illegal: null + }), i = e.inherit(e.QUOTE_STRING_MODE, { + illegal: null, + contains: e.QUOTE_STRING_MODE.contains.concat(a) + }), o = e.END_SAME_AS_BEGIN({ + begin: /<<<[ \t]*(\w+)\n/, end: /[ \t]*(\w+)\b/, + contains: e.QUOTE_STRING_MODE.contains.concat(a) + }), l = { + className: "string", + contains: [e.BACKSLASH_ESCAPE, t], variants: [e.inherit(n, { + begin: "b'", end: "'" + }), e.inherit(i, { begin: 'b"', end: '"' }), i, n, o] + }, c = { + variants: [e.BINARY_NUMBER_MODE, e.C_NUMBER_MODE] + }, s = { + keyword: "__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list match|0 new object or private protected public real return string switch throw trait try unset use var void while xor yield", + literal: "false null true", + built_in: "Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass" + }; return { + aliases: ["php", "php3", "php4", "php5", "php6", "php7", "php8"], + case_insensitive: !0, keywords: s, + contains: [e.HASH_COMMENT_MODE, e.COMMENT("//", "$", { + contains: [t] + }), e.COMMENT("/\\*", "\\*/", { + contains: [{ className: "doctag", begin: "@[A-Za-z]+" }] + }), e.COMMENT("__halt_compiler.+?;", !1, { + endsWithParent: !0, + keywords: "__halt_compiler" + }), t, { className: "keyword", begin: /\$this\b/ }, r, { + begin: /(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/ + }, { + className: "function", + relevance: 0, beginKeywords: "fn function", end: /[;{]/, excludeEnd: !0, + illegal: "[$%\\[]", contains: [e.UNDERSCORE_TITLE_MODE, { begin: "=>" }, { + className: "params", begin: "\\(", end: "\\)", excludeBegin: !0, excludeEnd: !0, + keywords: s, contains: ["self", r, e.C_BLOCK_COMMENT_MODE, l, c] + }] + }, { + className: "class", + beginKeywords: "class interface", relevance: 0, end: /\{/, excludeEnd: !0, + illegal: /[:($"]/, contains: [{ + beginKeywords: "extends implements" + }, e.UNDERSCORE_TITLE_MODE] + }, { + beginKeywords: "namespace", relevance: 0, end: ";", + illegal: /[.']/, contains: [e.UNDERSCORE_TITLE_MODE] + }, { + beginKeywords: "use", + relevance: 0, end: ";", contains: [e.UNDERSCORE_TITLE_MODE] + }, l, c] + } + } +})()); hljs.registerLanguage("vbnet", (() => { + "use strict"; function e(e) { + return e ? "string" == typeof e ? e : e.source : null + } function n(...n) { + return n.map((n => e(n))).join("") + } function t(...n) { + return "(" + n.map((n => e(n))).join("|") + ")" + } return e => { + const a = /\d{1,2}\/\d{1,2}\/\d{4}/, i = /\d{4}-\d{1,2}-\d{1,2}/, s = /(\d|1[012])(:\d+){0,2} *(AM|PM)/, r = /\d{1,2}(:\d{1,2}){1,2}/, o = { + className: "literal", variants: [{ begin: n(/# */, t(i, a), / *#/) }, { + begin: n(/# */, r, / *#/) + }, { begin: n(/# */, s, / *#/) }, { + begin: n(/# */, t(i, a), / +/, t(s, r), / *#/) + }] + }, l = e.COMMENT(/'''/, /$/, { + contains: [{ + className: "doctag", begin: /<\/?/, end: />/ + }] + }), c = e.COMMENT(null, /$/, { + variants: [{ + begin: /'/ + }, { begin: /([\t ]|^)REM(?=\s)/ }] + }); return { + name: "Visual Basic .NET", + aliases: ["vb"], case_insensitive: !0, classNameAliases: { label: "symbol" }, keywords: { + keyword: "addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", + built_in: "addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", + type: "boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", + literal: "true false nothing" + }, + illegal: "//|\\{|\\}|endif|gosub|variant|wend|^\\$ ", contains: [{ + className: "string", begin: /"(""|[^/n])"C\b/ + }, { + className: "string", begin: /"/, + end: /"/, illegal: /\n/, contains: [{ begin: /""/ }] + }, o, { + className: "number", relevance: 0, + variants: [{ + begin: /\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ + }, { begin: /\b\d[\d_]*((U?[SIL])|[%&])?/ }, { begin: /&H[\dA-F_]+((U?[SIL])|[%&])?/ }, { + begin: /&O[0-7_]+((U?[SIL])|[%&])?/ + }, { begin: /&B[01_]+((U?[SIL])|[%&])?/ }] + }, { + className: "label", begin: /^\w+:/ + }, l, c, { + className: "meta", + begin: /[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, + end: /$/, keywords: { + "meta-keyword": "const disable else elseif enable end externalsource if region then" + }, contains: [c] + }] + } + } +})()); hljs.registerLanguage("c", (() => { + "use strict"; function e(e) { + return ((...e) => e.map((e => (e => e ? "string" == typeof e ? e : e.source : null)(e))).join(""))("(", e, ")?") + } return t => { + const n = (t => { + const n = t.COMMENT("//", "$", { + contains: [{ begin: /\\\n/ }] + }), r = "[a-zA-Z_]\\w*::", a = "(decltype\\(auto\\)|" + e(r) + "[a-zA-Z_]\\w*" + e("<[^<>]+>") + ")", s = { + className: "keyword", begin: "\\b[a-z\\d_]*_t\\b" + }, i = { + className: "string", + variants: [{ + begin: '(u8?|U|L)?"', end: '"', illegal: "\\n", + contains: [t.BACKSLASH_ESCAPE] + }, { + begin: "(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", + end: "'", illegal: "." + }, t.END_SAME_AS_BEGIN({ + begin: /(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/, end: /\)([^()\\ ]{0,16})"/ + })] + }, o = { + className: "number", variants: [{ begin: "\\b(0b[01']+)" }, { + begin: "(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" + }, { + begin: "(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" + }], relevance: 0 + }, c = { + className: "meta", begin: /#\s*[a-z]+\b/, end: /$/, keywords: { + "meta-keyword": "if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" + }, contains: [{ begin: /\\\n/, relevance: 0 }, t.inherit(i, { className: "meta-string" }), { + className: "meta-string", begin: /<.*?>/, end: /$/, illegal: "\\n" + }, n, t.C_BLOCK_COMMENT_MODE] + }, l = { + className: "title", begin: e(r) + t.IDENT_RE, + relevance: 0 + }, d = e(r) + t.IDENT_RE + "\\s*\\(", u = { + keyword: "int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq", + built_in: "std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary", + literal: "true false nullptr NULL" + }, m = [c, s, n, t.C_BLOCK_COMMENT_MODE, o, i], p = { + variants: [{ begin: /=/, end: /;/ }, { begin: /\(/, end: /\)/ }, { + beginKeywords: "new throw return else", end: /;/ + }], keywords: u, contains: m.concat([{ + begin: /\(/, end: /\)/, keywords: u, contains: m.concat(["self"]), relevance: 0 + }]), + relevance: 0 + }, _ = { + className: "function", begin: "(" + a + "[\\*&\\s]+)+" + d, + returnBegin: !0, end: /[{;=]/, excludeEnd: !0, keywords: u, illegal: /[^\w\s\*&:<>.]/, + contains: [{ begin: "decltype\\(auto\\)", keywords: u, relevance: 0 }, { + begin: d, + returnBegin: !0, contains: [l], relevance: 0 + }, { + className: "params", begin: /\(/, + end: /\)/, keywords: u, relevance: 0, contains: [n, t.C_BLOCK_COMMENT_MODE, i, o, s, { + begin: /\(/, end: /\)/, keywords: u, relevance: 0, + contains: ["self", n, t.C_BLOCK_COMMENT_MODE, i, o, s] + }] + }, s, n, t.C_BLOCK_COMMENT_MODE, c] + }; return { + aliases: ["c", "cc", "h", "c++", "h++", "hpp", "hh", "hxx", "cxx"], keywords: u, + disableAutodetect: !0, illegal: "", keywords: u, contains: ["self", s] + }, { begin: t.IDENT_RE + "::", keywords: u }, { + className: "class", beginKeywords: "enum class struct union", end: /[{;:<>=]/, + contains: [{ beginKeywords: "final class struct" }, t.TITLE_MODE] + }]), exports: { + preprocessor: c, strings: i, keywords: u + } + } + })(t) + ; return n.name = "C", n.aliases = ["c", "h"], n + } +})()); hljs.registerLanguage("css", (() => { + "use strict"; return e => { + var n = "[a-zA-Z-][a-zA-Z0-9_-]*", a = { + begin: /([*]\s?)?(?:[A-Z_.\-\\]+|--[a-zA-Z0-9_-]+)\s*(\/\*\*\/)?:/, + returnBegin: !0, end: ";", endsWithParent: !0, contains: [{ + className: "attribute", + begin: /\S/, end: ":", excludeEnd: !0, starts: { + endsWithParent: !0, excludeEnd: !0, + contains: [{ + begin: /[\w-]+\(/, returnBegin: !0, contains: [{ + className: "built_in", + begin: /[\w-]+/ + }, { + begin: /\(/, end: /\)/, + contains: [e.APOS_STRING_MODE, e.QUOTE_STRING_MODE, e.CSS_NUMBER_MODE] + }] + }, e.CSS_NUMBER_MODE, e.QUOTE_STRING_MODE, e.APOS_STRING_MODE, e.C_BLOCK_COMMENT_MODE, { + className: "number", begin: "#[0-9A-Fa-f]+" + }, { className: "meta", begin: "!important" }] + } + }] + }; return { + name: "CSS", case_insensitive: !0, illegal: /[=|'\$]/, + contains: [e.C_BLOCK_COMMENT_MODE, { + className: "selector-id", + begin: /#[A-Za-z0-9_-]+/ + }, { className: "selector-class", begin: "\\." + n }, { + className: "selector-attr", begin: /\[/, end: /\]/, illegal: "$", + contains: [e.APOS_STRING_MODE, e.QUOTE_STRING_MODE] + }, { + className: "selector-pseudo", + begin: /:(:)?[a-zA-Z0-9_+()"'.-]+/ + }, { + begin: "@(page|font-face)", + lexemes: "@[a-z-]+", keywords: "@page @font-face" + }, { + begin: "@", end: "[{;]", + illegal: /:/, returnBegin: !0, contains: [{ + className: "keyword", + begin: /@-?\w[\w]*(-\w+)*/ + }, { + begin: /\s/, endsWithParent: !0, excludeEnd: !0, + relevance: 0, keywords: "and or not only", contains: [{ + begin: /[a-z-]+:/, + className: "attribute" + }, e.APOS_STRING_MODE, e.QUOTE_STRING_MODE, e.CSS_NUMBER_MODE] + }] + }, { className: "selector-tag", begin: n, relevance: 0 }, { + begin: /\{/, end: /\}/, + illegal: /\S/, contains: [e.C_BLOCK_COMMENT_MODE, { begin: /;/ }, a] + }] + } + } +})()); hljs.registerLanguage("sql", (() => { + "use strict"; function e(e) { + return e ? "string" == typeof e ? e : e.source : null + } function r(...r) { + return r.map((r => e(r))).join("") + } function t(...r) { + return "(" + r.map((r => e(r))).join("|") + ")" + } return e => { + const n = e.COMMENT("--", "$"), a = ["true", "false", "unknown"], i = ["bigint", "binary", "blob", "boolean", "char", "character", "clob", "date", "dec", "decfloat", "decimal", "float", "int", "integer", "interval", "nchar", "nclob", "national", "numeric", "real", "row", "smallint", "time", "timestamp", "varchar", "varying", "varbinary"], s = ["abs", "acos", "array_agg", "asin", "atan", "avg", "cast", "ceil", "ceiling", "coalesce", "corr", "cos", "cosh", "count", "covar_pop", "covar_samp", "cume_dist", "dense_rank", "deref", "element", "exp", "extract", "first_value", "floor", "json_array", "json_arrayagg", "json_exists", "json_object", "json_objectagg", "json_query", "json_table", "json_table_primitive", "json_value", "lag", "last_value", "lead", "listagg", "ln", "log", "log10", "lower", "max", "min", "mod", "nth_value", "ntile", "nullif", "percent_rank", "percentile_cont", "percentile_disc", "position", "position_regex", "power", "rank", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2", "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "row_number", "sin", "sinh", "sqrt", "stddev_pop", "stddev_samp", "substring", "substring_regex", "sum", "tan", "tanh", "translate", "translate_regex", "treat", "trim", "trim_array", "unnest", "upper", "value_of", "var_pop", "var_samp", "width_bucket"], o = ["create table", "insert into", "primary key", "foreign key", "not null", "alter table", "add constraint", "grouping sets", "on overflow", "character set", "respect nulls", "ignore nulls", "nulls first", "nulls last", "depth first", "breadth first"], c = s, l = ["abs", "acos", "all", "allocate", "alter", "and", "any", "are", "array", "array_agg", "array_max_cardinality", "as", "asensitive", "asin", "asymmetric", "at", "atan", "atomic", "authorization", "avg", "begin", "begin_frame", "begin_partition", "between", "bigint", "binary", "blob", "boolean", "both", "by", "call", "called", "cardinality", "cascaded", "case", "cast", "ceil", "ceiling", "char", "char_length", "character", "character_length", "check", "classifier", "clob", "close", "coalesce", "collate", "collect", "column", "commit", "condition", "connect", "constraint", "contains", "convert", "copy", "corr", "corresponding", "cos", "cosh", "count", "covar_pop", "covar_samp", "create", "cross", "cube", "cume_dist", "current", "current_catalog", "current_date", "current_default_transform_group", "current_path", "current_role", "current_row", "current_schema", "current_time", "current_timestamp", "current_path", "current_role", "current_transform_group_for_type", "current_user", "cursor", "cycle", "date", "day", "deallocate", "dec", "decimal", "decfloat", "declare", "default", "define", "delete", "dense_rank", "deref", "describe", "deterministic", "disconnect", "distinct", "double", "drop", "dynamic", "each", "element", "else", "empty", "end", "end_frame", "end_partition", "end-exec", "equals", "escape", "every", "except", "exec", "execute", "exists", "exp", "external", "extract", "false", "fetch", "filter", "first_value", "float", "floor", "for", "foreign", "frame_row", "free", "from", "full", "function", "fusion", "get", "global", "grant", "group", "grouping", "groups", "having", "hold", "hour", "identity", "in", "indicator", "initial", "inner", "inout", "insensitive", "insert", "int", "integer", "intersect", "intersection", "interval", "into", "is", "join", "json_array", "json_arrayagg", "json_exists", "json_object", "json_objectagg", "json_query", "json_table", "json_table_primitive", "json_value", "lag", "language", "large", "last_value", "lateral", "lead", "leading", "left", "like", "like_regex", "listagg", "ln", "local", "localtime", "localtimestamp", "log", "log10", "lower", "match", "match_number", "match_recognize", "matches", "max", "member", "merge", "method", "min", "minute", "mod", "modifies", "module", "month", "multiset", "national", "natural", "nchar", "nclob", "new", "no", "none", "normalize", "not", "nth_value", "ntile", "null", "nullif", "numeric", "octet_length", "occurrences_regex", "of", "offset", "old", "omit", "on", "one", "only", "open", "or", "order", "out", "outer", "over", "overlaps", "overlay", "parameter", "partition", "pattern", "per", "percent", "percent_rank", "percentile_cont", "percentile_disc", "period", "portion", "position", "position_regex", "power", "precedes", "precision", "prepare", "primary", "procedure", "ptf", "range", "rank", "reads", "real", "recursive", "ref", "references", "referencing", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2", "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "release", "result", "return", "returns", "revoke", "right", "rollback", "rollup", "row", "row_number", "rows", "running", "savepoint", "scope", "scroll", "search", "second", "seek", "select", "sensitive", "session_user", "set", "show", "similar", "sin", "sinh", "skip", "smallint", "some", "specific", "specifictype", "sql", "sqlexception", "sqlstate", "sqlwarning", "sqrt", "start", "static", "stddev_pop", "stddev_samp", "submultiset", "subset", "substring", "substring_regex", "succeeds", "sum", "symmetric", "system", "system_time", "system_user", "table", "tablesample", "tan", "tanh", "then", "time", "timestamp", "timezone_hour", "timezone_minute", "to", "trailing", "translate", "translate_regex", "translation", "treat", "trigger", "trim", "trim_array", "true", "truncate", "uescape", "union", "unique", "unknown", "unnest", "update ", "upper", "user", "using", "value", "values", "value_of", "var_pop", "var_samp", "varbinary", "varchar", "varying", "versioning", "when", "whenever", "where", "width_bucket", "window", "with", "within", "without", "year", "add", "asc", "collation", "desc", "final", "first", "last", "view"].filter((e => !s.includes(e))), u = { + begin: r(/\b/, t(...c), /\s*\(/), keywords: { built_in: c.join(" ") } + }; return { + name: "SQL", case_insensitive: !0, illegal: /[{}]|<\//, keywords: { + $pattern: /\b[\w\.]+/, keyword: ((e, { exceptions: r, when: t } = {}) => { + const n = t + ; return r = r || [], e.map((e => e.match(/\|\d+$/) || r.includes(e) ? e : n(e) ? e + "|0" : e)) + })(l, { when: e => e.length < 3 }).join(" "), literal: a.join(" "), type: i.join(" "), + built_in: "current_catalog current_date current_default_transform_group current_path current_role current_schema current_transform_group_for_type current_user session_user system_time system_user current_time localtime current_timestamp localtimestamp" + }, contains: [{ + begin: t(...o), keywords: { + $pattern: /[\w\.]+/, + keyword: l.concat(o).join(" "), literal: a.join(" "), type: i.join(" ") + } + }, { + className: "type", + begin: t("double precision", "large object", "with timezone", "without timezone") + }, u, { className: "variable", begin: /@[a-z0-9]+/ }, { + className: "string", variants: [{ + begin: /'/, end: /'/, contains: [{ begin: /''/ }] + }] + }, { + begin: /"/, end: /"/, contains: [{ + begin: /""/ + }] + }, e.C_NUMBER_MODE, e.C_BLOCK_COMMENT_MODE, n, { + className: "operator", + begin: /[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, relevance: 0 + }] + } + } +})()); hljs.registerLanguage("ini", (() => { + "use strict"; function e(e) { + return e ? "string" == typeof e ? e : e.source : null + } function n(...n) { + return n.map((n => e(n))).join("") + } return s => { + const a = { + className: "number", + relevance: 0, variants: [{ begin: /([+-]+)?[\d]+_[\d_]+/ }, { begin: s.NUMBER_RE }] + }, i = s.COMMENT(); i.variants = [{ begin: /;/, end: /$/ }, { begin: /#/, end: /$/ }]; const t = { + className: "variable", variants: [{ begin: /\$[\w\d"][\w\d_]*/ }, { + begin: /\$\{(.*?)\}/ + }] + }, r = { className: "literal", begin: /\bon|off|true|false|yes|no\b/ }, l = { + className: "string", contains: [s.BACKSLASH_ESCAPE], variants: [{ + begin: "'''", + end: "'''", relevance: 10 + }, { begin: '"""', end: '"""', relevance: 10 }, { + begin: '"', end: '"' + }, { begin: "'", end: "'" }] + }, c = { + begin: /\[/, end: /\]/, contains: [i, r, t, l, a, "self"], + relevance: 0 + }, g = "(" + [/[A-Za-z0-9_-]+/, /"(\\"|[^"])*"/, /'[^']*'/].map((n => e(n))).join("|") + ")" + ; return { + name: "TOML, also INI", aliases: ["toml"], case_insensitive: !0, illegal: /\S/, + contains: [i, { className: "section", begin: /\[+/, end: /\]+/ }, { + begin: n(g, "(\\s*\\.\\s*", g, ")*", n("(?=", /\s*=\s*[^#\s]/, ")")), className: "attr", + starts: { end: /$/, contains: [i, c, r, t, l, a] } + }] + } + } +})()); hljs.registerLanguage("php-template", (() => { + "use strict"; return n => ({ + name: "PHP template", subLanguage: "xml", contains: [{ + begin: /<\?(php|=)?/, end: /\?>/, + subLanguage: "php", contains: [{ begin: "/\\*", end: "\\*/", skip: !0 }, { + begin: 'b"', + end: '"', skip: !0 + }, { begin: "b'", end: "'", skip: !0 }, n.inherit(n.APOS_STRING_MODE, { + illegal: null, className: null, contains: null, skip: !0 + }), n.inherit(n.QUOTE_STRING_MODE, { + illegal: null, className: null, contains: null, + skip: !0 + })] + }] + }) +})()); hljs.registerLanguage("javascript", (() => { + "use strict" + ; const e = "[A-Za-z$_][0-9A-Za-z$_]*", n = ["as", "in", "of", "if", "for", "while", "finally", "var", "new", "function", "do", "return", "void", "else", "break", "catch", "instanceof", "with", "throw", "case", "default", "try", "switch", "continue", "typeof", "delete", "let", "yield", "const", "class", "debugger", "async", "await", "static", "import", "from", "export", "extends"], a = ["true", "false", "null", "undefined", "NaN", "Infinity"], s = [].concat(["setInterval", "setTimeout", "clearInterval", "clearTimeout", "require", "exports", "eval", "isFinite", "isNaN", "parseFloat", "parseInt", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "unescape"], ["arguments", "this", "super", "console", "window", "document", "localStorage", "module", "global"], ["Intl", "DataView", "Number", "Math", "Date", "String", "RegExp", "Object", "Function", "Boolean", "Error", "Symbol", "Set", "Map", "WeakSet", "WeakMap", "Proxy", "Reflect", "JSON", "Promise", "Float64Array", "Int16Array", "Int32Array", "Int8Array", "Uint16Array", "Uint32Array", "Float32Array", "Array", "Uint8Array", "Uint8ClampedArray", "ArrayBuffer"], ["EvalError", "InternalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"]) + ; function r(e) { return i("(?=", e, ")") } function i(...e) { + return e.map((e => { + return (n = e) ? "string" == typeof n ? n : n.source : null; var n + })).join("") + } return t => { + const c = e, o = { + begin: /<[A-Za-z0-9\\._:-]+/, end: /\/[A-Za-z0-9\\._:-]+>|\/>/, + isTrulyOpeningTag: (e, n) => { + const a = e[0].length + e.index, s = e.input[a] + ; "<" !== s ? ">" === s && (((e, { after: n }) => { + const a = "", + returnBegin: !0, end: "\\s*=>", contains: [{ + className: "params", variants: [{ + begin: t.UNDERSCORE_IDENT_RE, relevance: 0 + }, { + className: null, begin: /\(\s*\)/, skip: !0 + }, { begin: /\(/, end: /\)/, excludeBegin: !0, excludeEnd: !0, keywords: l, contains: A }] + }] + }, { begin: /,/, relevance: 0 }, { className: "", begin: /\s/, end: /\s*/, skip: !0 }, { + variants: [{ begin: "<>", end: "" }, { + begin: o.begin, "on:begin": o.isTrulyOpeningTag, + end: o.end + }], subLanguage: "xml", contains: [{ + begin: o.begin, end: o.end, skip: !0, + contains: ["self"] + }] + }], relevance: 0 + }, { + className: "function", + beginKeywords: "function", end: /[{;]/, excludeEnd: !0, keywords: l, + contains: ["self", t.inherit(t.TITLE_MODE, { begin: c }), p], illegal: /%/ + }, { + beginKeywords: "while if switch catch for" + }, { + className: "function", + begin: t.UNDERSCORE_IDENT_RE + "\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", + returnBegin: !0, contains: [p, t.inherit(t.TITLE_MODE, { begin: c })] + }, { + variants: [{ + begin: "\\." + c + }, { begin: "\\$" + c }], relevance: 0 + }, { + className: "class", + beginKeywords: "class", end: /[{;=]/, excludeEnd: !0, illegal: /[:"[\]]/, contains: [{ + beginKeywords: "extends" + }, t.UNDERSCORE_TITLE_MODE] + }, { + begin: /\b(?=constructor)/, + end: /[{;]/, excludeEnd: !0, contains: [t.inherit(t.TITLE_MODE, { begin: c }), "self", p] + }, { + begin: "(get|set)\\s+(?=" + c + "\\()", end: /\{/, keywords: "get set", + contains: [t.inherit(t.TITLE_MODE, { begin: c }), { begin: /\(\)/ }, p] + }, { begin: /\$[(.]/ }] + } + } +})()); + + +// enable highlight +hljs.initHighlightingOnLoad(); diff --git a/src/Blogifier.Themes.Standard/assets/package-lock.json b/src/Blogifier.Themes.Standard/assets/package-lock.json new file mode 100644 index 000000000..2c1f33d81 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/package-lock.json @@ -0,0 +1,774 @@ +{ + "name": "blogifier-themes-standard", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "blogifier-themes-standard", + "version": "1.0.0", + "devDependencies": { + "@rollup/plugin-node-resolve": "^15.1.0", + "@rollup/plugin-terser": "^0.4.3", + "bootstrap": "~5.1.0", + "rollup": "^3.23.1", + "rtlcss": "^3.3.0", + "sass": "^1.32.12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://mirrors.cloud.tencent.com/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@popperjs/core": { + "version": "2.11.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/@popperjs/core/-/core-2.11.7.tgz", + "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", + "dev": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz", + "integrity": "sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/@rollup/plugin-terser/-/plugin-terser-0.4.3.tgz", + "integrity": "sha512-EF0oejTMtkyhrkwCdg0HJ0IpkcaVg1MMSf2olHb2Jp+1mnLM04OhjpJWGma4HobiDTF0WCyViWuvadyE9ch2XA==", + "dev": true, + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.x || ^3.x" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "dev": true + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bootstrap": { + "version": "5.1.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + }, + "peerDependencies": { + "@popperjs/core": "^2.10.2" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://mirrors.cloud.tencent.com/npm/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.23", + "resolved": "https://mirrors.cloud.tencent.com/npm/postcss/-/postcss-8.4.23.tgz", + "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.3", + "resolved": "https://mirrors.cloud.tencent.com/npm/resolve/-/resolve-1.22.3.tgz", + "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.12.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "3.23.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/rollup/-/rollup-3.23.1.tgz", + "integrity": "sha512-ybRdFVHOoljGEFILHLd2g/qateqUdjE6YS41WXq4p3C/WwD3xtWxV4FYWETA1u9TeXQc5K8L8zHE5d/scOvrOQ==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rtlcss": { + "version": "3.5.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/rtlcss/-/rtlcss-3.5.0.tgz", + "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.3.11", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sass": { + "version": "1.32.12", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", + "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/smob": { + "version": "1.4.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/smob/-/smob-1.4.0.tgz", + "integrity": "sha512-MqR3fVulhjWuRNSMydnTlweu38UhQ0HXM4buStD/S3mc/BzX3CuM9OmhyQpmtYCvoYdl5ris6TI0ZqH355Ymqg==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://mirrors.cloud.tencent.com/npm/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://mirrors.cloud.tencent.com/npm/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/terser": { + "version": "5.17.7", + "resolved": "https://mirrors.cloud.tencent.com/npm/terser/-/terser-5.17.7.tgz", + "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://mirrors.cloud.tencent.com/npm/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/src/Blogifier.Themes.Standard/assets/package.json b/src/Blogifier.Themes.Standard/assets/package.json new file mode 100644 index 000000000..c9abcfd93 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/package.json @@ -0,0 +1,24 @@ +{ + "name": "blogifier-themes-standard", + "version": "1.0.0", + "description": "only for compiling sass files", + "author": "Farzin", + "private": true, + "scripts": { + "start": "npm run rollup | npm run sass ", + "rtl": "rtlcss dist/css css/styles.rtl.css", + "sass": "sass --watch scss:dist/css --style=compressed", + "rollup": "rollup --config -w", + "build-sass": "sass scss:dist/css --style=compressed", + "build-rollup": "rollup --config", + "build": "npm run build-sass && npm run build-rollup" + }, + "devDependencies": { + "@rollup/plugin-node-resolve": "^15.1.0", + "@rollup/plugin-terser": "^0.4.3", + "bootstrap": "~5.1.0", + "rollup": "^3.23.1", + "rtlcss": "^3.3.0", + "sass": "^1.32.12" + } +} diff --git a/src/Blogifier.Themes.Standard/assets/rollup.config.mjs b/src/Blogifier.Themes.Standard/assets/rollup.config.mjs new file mode 100644 index 000000000..47e52f489 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/rollup.config.mjs @@ -0,0 +1,16 @@ +import resolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; + +export default { + input: 'js/index.js', + output: { + format: 'iife', + file: 'dist/js/index.min.js', + sourcemap: true, + minifyInternalExports: true, + plugins: [terser()] + }, + plugins: [ + resolve() + ] +}; diff --git a/src/Blogifier/wwwroot/themes/standard/screenshot.png b/src/Blogifier.Themes.Standard/assets/screenshot.png similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/screenshot.png rename to src/Blogifier.Themes.Standard/assets/screenshot.png diff --git a/src/Blogifier.Themes.Standard/assets/scss/account.scss b/src/Blogifier.Themes.Standard/assets/scss/account.scss new file mode 100644 index 000000000..40a2dc70b --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/scss/account.scss @@ -0,0 +1,153 @@ +@charset "utf-8"; + +@import "helpers/variables"; + +// contrast +$min-contrast-ratio: 2.5; + +// body +$body-bg: #f8f8f8; +$body-color: #1c1c1c; +$font-size-base: 0.875rem; + +// Link +$link-color: $blogifier; +$link-hover-color: $black; + +// shadow +$bf-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05); + +// input +$input-border-color: $default; +$input-focus-border-color: darken($default, 10%); +$input-box-shadow: 0; +$input-focus-box-shadow: 0; + +// check +$form-check-input-width: 1rem; +$form-check-input-bg: transparent; +$form-check-padding-start: 2rem; +$form-check-input-border-radius: 0.3rem; +$form-check-input-border: 1px solid $secondary; +$form-check-input-checked-color: $black; +$form-check-input-checked-border-color: $black; +$form-check-input-checked-bg-color: $body-bg; +$form-check-input-focus-border: $black; +$form-check-input-focus-box-shadow: 0; + +// switch +$form-check-label-color: $black; +$form-check-label-cursor: pointer; +$form-switch-color: #6c757d; +$form-switch-focus-color: $black; + +// active +$component-active-bg: $blogifier; + +// font and text +$link-decoration: none; + +// buttons +$input-btn-font-size: 0.875rem; +$input-btn-padding-y: 0.625rem; +$input-btn-padding-x: 1rem; +$input-btn-focus-width: 0; +$btn-border-radius: 0.325rem; + +// button link +$btn-link-color: #495057; +$btn-link-hover-color: $black; + +// accordion +$accordion-border-width: 0; +$accordion-body-padding-y: 1rem; +$accordion-body-padding-x: 0; +$accordion-button-focus-box-shadow: none; +$accordion-button-active-bg: none; +$accordion-button-active-color: $blogifier; +$accordion-button-padding-y: 0.5rem; +$accordion-button-padding-x: 0; + +// list +$list-height: 3rem; + +// headings +$h1-font-size: $font-size-base * 1.75; +$h2-font-size: $font-size-base * 1.5; +$h3-font-size: $font-size-base * 1.25; +$h4-font-size: $font-size-base * 1.125; +$h5-font-size: $font-size-base * 1; +$h6-font-size: $font-size-base; + +// tooltips +$tooltip-font-size: 0.625rem; +$tooltip-bg: #57606b; +$tooltip-border-radius: 5rem; +$tooltip-padding-y: 0.125rem; +$tooltip-arrow-color: transparent; +$tooltip-arrow-width: 0; +$tooltip-arrow-height: 0; +$tooltip-opacity: 1; + +// dropdown +$dropdown-link-hover-bg: #f8f9fa; + +// container +$container-max-widths: ( xl: 1024px, ); + +// floating input +$form-floating-height: 3.25rem; +$form-floating-padding-x: .875rem; +$form-floating-padding-y: .875rem; +$form-floating-label-opacity: .65; +$form-floating-label-transform: scale(.8) translateY(-.325rem) translateX(.15rem); + +@import "helpers/bootstrap"; +@import "layout/sidebar"; +@import "include/buttons"; +@import "include/forms"; +@import "page/account"; +@import "page/profile"; + +html { + height: 100%; +} +body { + cursor: default; + overflow-y: scroll; + height: 100%; + display: flex; + flex-direction: column; + + &::-webkit-scrollbar { + width: 0.75rem; + } + + &::-webkit-scrollbar-track { + background-color: #fff; + box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.3); + } + + &::-webkit-scrollbar-thumb { + background-color: $gray-500; + border-radius: 5rem; + &:hover { + background: $gray-600; + } + } +} + +// index.html +.blogifier { + height: 100%; + display: flex; + flex-direction: column; +} + +// shared/MainLayout.razor +.content { +} + +// Components/NavMenuComponent.razor +.menu { +} diff --git a/src/Blogifier/wwwroot/themes/standard/scss/components/_dropdowns.scss b/src/Blogifier.Themes.Standard/assets/scss/components/_dropdowns.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/components/_dropdowns.scss rename to src/Blogifier.Themes.Standard/assets/scss/components/_dropdowns.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/components/_highlight.scss b/src/Blogifier.Themes.Standard/assets/scss/components/_highlight.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/components/_highlight.scss rename to src/Blogifier.Themes.Standard/assets/scss/components/_highlight.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/components/_newsletter.scss b/src/Blogifier.Themes.Standard/assets/scss/components/_newsletter.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/components/_newsletter.scss rename to src/Blogifier.Themes.Standard/assets/scss/components/_newsletter.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/components/_pagination.scss b/src/Blogifier.Themes.Standard/assets/scss/components/_pagination.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/components/_pagination.scss rename to src/Blogifier.Themes.Standard/assets/scss/components/_pagination.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/components/_search.scss b/src/Blogifier.Themes.Standard/assets/scss/components/_search.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/components/_search.scss rename to src/Blogifier.Themes.Standard/assets/scss/components/_search.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/helpers/_base.scss b/src/Blogifier.Themes.Standard/assets/scss/helpers/_base.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/helpers/_base.scss rename to src/Blogifier.Themes.Standard/assets/scss/helpers/_base.scss diff --git a/src/Blogifier.Themes.Standard/assets/scss/helpers/_bootstrap.scss b/src/Blogifier.Themes.Standard/assets/scss/helpers/_bootstrap.scss new file mode 100644 index 000000000..36c956bda --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/scss/helpers/_bootstrap.scss @@ -0,0 +1,53 @@ +/* + * Bootstrap v5.0.0-beta3 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ + +// scss-docs-start import-stack +// Configuration +@import "../../node_modules/bootstrap/scss/functions"; +@import "../../node_modules/bootstrap/scss/variables"; +@import "../../node_modules/bootstrap/scss/mixins"; +@import "colors"; +@import "../../node_modules/bootstrap/scss/utilities"; + +// Layout & components +@import "../../node_modules/bootstrap/scss/root"; +@import "../../node_modules/bootstrap/scss/reboot"; +@import "../../node_modules/bootstrap/scss/type"; +@import "../../node_modules/bootstrap/scss/images"; +@import "../../node_modules/bootstrap/scss/containers"; +@import "../../node_modules/bootstrap/scss/grid"; +@import "../../node_modules/bootstrap/scss/tables"; +@import "../../node_modules/bootstrap/scss/forms"; +@import "../../node_modules/bootstrap/scss/buttons"; +@import "../../node_modules/bootstrap/scss/transitions"; +@import "../../node_modules/bootstrap/scss/dropdown"; +@import "../../node_modules/bootstrap/scss/button-group"; +// @import "../../node_modules/bootstrap/scss/nav"; +// @import "../../node_modules/bootstrap/scss/navbar"; +// @import "../../node_modules/bootstrap/scss/card"; +@import "../../node_modules/bootstrap/scss/accordion"; +// @import "../../node_modules/bootstrap/scss/breadcrumb"; +@import "../../node_modules/bootstrap/scss/pagination"; +@import "../../node_modules/bootstrap/scss/badge"; +@import "../../node_modules/bootstrap/scss/alert"; +// @import "../../node_modules/bootstrap/scss/progress"; +@import "../../node_modules/bootstrap/scss/list-group"; +@import "../../node_modules/bootstrap/scss/close"; +// @import "../../node_modules/bootstrap/scss/toasts"; +@import "../../node_modules/bootstrap/scss/modal"; +@import "../../node_modules/bootstrap/scss/tooltip"; +@import "../../node_modules/bootstrap/scss/popover"; +// @import "../../node_modules/bootstrap/scss/carousel"; +@import "../../node_modules/bootstrap/scss/spinners"; +// @import "../../node_modules/bootstrap/scss/offcanvas"; + +// Helpers +@import "../../node_modules/bootstrap/scss/helpers"; + +// Utilities +@import "../../node_modules/bootstrap/scss/utilities/api"; +// scss-docs-end import-stack diff --git a/src/Blogifier.Themes.Standard/assets/scss/helpers/_colors.scss b/src/Blogifier.Themes.Standard/assets/scss/helpers/_colors.scss new file mode 100644 index 000000000..411347140 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/scss/helpers/_colors.scss @@ -0,0 +1,8 @@ +// included in the _bootstrap.scss +$custom-colors: ( + "blogifier": $blogifier, + "default": $default, + "gold": $gold +); + +$theme-colors: map-merge($theme-colors, $custom-colors); diff --git a/src/Blogifier/wwwroot/themes/standard/scss/helpers/_mixins.scss b/src/Blogifier.Themes.Standard/assets/scss/helpers/_mixins.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/helpers/_mixins.scss rename to src/Blogifier.Themes.Standard/assets/scss/helpers/_mixins.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/helpers/_reset.scss b/src/Blogifier.Themes.Standard/assets/scss/helpers/_reset.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/helpers/_reset.scss rename to src/Blogifier.Themes.Standard/assets/scss/helpers/_reset.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/helpers/_variables.scss b/src/Blogifier.Themes.Standard/assets/scss/helpers/_variables.scss similarity index 84% rename from src/Blogifier/wwwroot/themes/standard/scss/helpers/_variables.scss rename to src/Blogifier.Themes.Standard/assets/scss/helpers/_variables.scss index 41c94306f..7064526c6 100644 --- a/src/Blogifier/wwwroot/themes/standard/scss/helpers/_variables.scss +++ b/src/Blogifier.Themes.Standard/assets/scss/helpers/_variables.scss @@ -1,3 +1,12 @@ +// colors +$blogifier: #7241fd; +$default: #e1e5e9; +$secondary: #8b949e; +$gold: #ff9f2e; +$black: #1c1c1c; +$success: #00b45a; +$danger: #ff4d58; + // colors $color: var(--bf-color); $color-hover: #888; @@ -27,7 +36,7 @@ $brands: ( "whatsapp": $color-whatsapp, ); -// highlighter +// syntax highlighter $hljs-foreground: #212529; $hljs-background: #f8f9fa; $hljs-comments: #90a4ae; diff --git a/src/Blogifier.Themes.Standard/assets/scss/include/_buttons.scss b/src/Blogifier.Themes.Standard/assets/scss/include/_buttons.scss new file mode 100644 index 000000000..0bf17992f --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/scss/include/_buttons.scss @@ -0,0 +1,36 @@ +.btn-rounded { + border-radius: 6rem; +} + +.btn { + text-transform: capitalize; + + &-link { + } + + &-outline { + &-default { + border-color: $secondary; + color: $gray-600; + + &:hover { + color: $gray-800; + border-color: $gray-500; + } + } + } + + &-xs { + padding: 0.125rem 0.5rem; + font-size: 0.75rem; + border-radius: 0.2rem; + } + &-block { + width: 100%; + } + + &-floating { + height: 3.125rem; + font-size: 0.9375rem; + } +} diff --git a/src/Blogifier.Themes.Standard/assets/scss/include/_forms.scss b/src/Blogifier.Themes.Standard/assets/scss/include/_forms.scss new file mode 100644 index 000000000..54a1c2443 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/scss/include/_forms.scss @@ -0,0 +1,103 @@ +.form-control, +.form-select { +} + +.form-switch .form-check-input { + border-color: $gray-600; + &:checked { + border-color: $success; + background-color: $success; + } +} + +.form-item { + margin-bottom: 1.375rem; + + .form-label { + margin-bottom: 0.25rem; + font-size: 0.875rem; + font-weight: 400; + color: $black; + } + + &-desc { + padding-left: 0.1rem; + font-size: 0.875rem; + color: $gray-600; + } + + &-error, + .validation-message { + font-size: 0.875rem; + font-weight: 500; + color: $danger; + margin-top: 0.125rem; + } +} + +.modal { + .form-select, + .form-control { + box-shadow: none; + border: 1px solid $gray-400; + } +} + +// dropdown +.dropdown-full { + .dropdown-toggle { + width: 100%; + text-align: left; + display: flex; + + &::after { + margin-left: auto; + margin-top: auto; + margin-bottom: auto; + } + } + + .dropdown-menu { + padding: 0; + width: 100%; + } + + .dropdown-item { + font-size: 0.875rem; + font-weight: 500; + padding: 0.5rem 1rem; + + &:not(:last-child) { + border-bottom: 1px solid $gray-200; + } + } +} + +.dropdown-flush { + .dropdown-menu { + padding: 0; + overflow: hidden; + border: 0; + margin-top: 0.5rem !important; + box-shadow: -1rem 1rem 5rem rgba(#000, 0.25); + } + .dropdown-item { + padding: 0.5rem 1rem; + font-weight: 500; + font-size: 0.875rem; + border-bottom: 1px solid $gray-100; + + &:active { + color: #fff !important; + .bi { + fill: #fff !important; + } + } + + .bi { + margin-right: 0.5rem; + margin-left: -0.25rem; + transform: translateY(-1px); + } + } +} diff --git a/src/Blogifier/wwwroot/themes/standard/scss/layout/_footer.scss b/src/Blogifier.Themes.Standard/assets/scss/layout/_footer.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/layout/_footer.scss rename to src/Blogifier.Themes.Standard/assets/scss/layout/_footer.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/layout/_header.scss b/src/Blogifier.Themes.Standard/assets/scss/layout/_header.scss similarity index 96% rename from src/Blogifier/wwwroot/themes/standard/scss/layout/_header.scss rename to src/Blogifier.Themes.Standard/assets/scss/layout/_header.scss index 8f5e89978..ad0245bd8 100644 --- a/src/Blogifier/wwwroot/themes/standard/scss/layout/_header.scss +++ b/src/Blogifier.Themes.Standard/assets/scss/layout/_header.scss @@ -18,6 +18,7 @@ &-desc { margin: 0; + margin-top: 0.432rem; font-size: 0.875rem; font-weight: 400; color: var(--bf-header-desc-color, #666); diff --git a/src/Blogifier/wwwroot/themes/standard/scss/layout/_nav.scss b/src/Blogifier.Themes.Standard/assets/scss/layout/_nav.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/layout/_nav.scss rename to src/Blogifier.Themes.Standard/assets/scss/layout/_nav.scss diff --git a/src/Blogifier.Themes.Standard/assets/scss/layout/_sidebar.scss b/src/Blogifier.Themes.Standard/assets/scss/layout/_sidebar.scss new file mode 100644 index 000000000..e3b0b0b14 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/scss/layout/_sidebar.scss @@ -0,0 +1,89 @@ +@mixin bf-shadow($rad: .25rem, $shad: $bf-shadow, $bg: #fff) { + background: $bg; + box-shadow: $shad; + border-radius: $rad; +} + + +.sidebar { + font-size: 1rem; + hr { + background: linear-gradient(90deg, $secondary, transparent); + width: 65%; + } + + &-toggle { + width: 100%; + display: block; + height: 3rem; + border: 0; + text-align: left; + padding: 0 1.5rem; + background: none; + border-top: 1px solid #f1f1f1; + box-shadow: $bf-shadow; + background-color: #fff; + font-size: 0.876rem; + font-weight: 500; + + .bi { + position: absolute; + top: 50%; + right: 1.5rem; + transform: translateY(-50%); + } + } + + &-link { + color: $gray-600; + display: block; + + &:hover { + color: $black; + } + + &.active { + color: $black; + // font-weight: 500; + } + } +} +@media screen and (min-width: 992px) { + .sidebar { + width: 12rem; + min-width: 12rem; + padding-right: 2rem; + + &-link { + line-height: 2rem; + } + } +} +@media screen and (max-width: 991px) { + .sidebar { + position: relative; + margin: -2rem -1.5rem 2rem; + + &-nav { + display: none; + width: 100%; + position: absolute; + z-index: 100; + top: 3.0625rem; + left: 0; + padding: 1rem 1.5rem; + @include bf-shadow(0); + &.active, + &.-active { + display: block; + } + } + hr { + margin: 0.75rem 0; + } + &-link { + padding: 0; + font-size: 0.875rem; + } + } +} diff --git a/src/Blogifier/wwwroot/themes/standard/scss/layout/_widgets.scss b/src/Blogifier.Themes.Standard/assets/scss/layout/_widgets.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/layout/_widgets.scss rename to src/Blogifier.Themes.Standard/assets/scss/layout/_widgets.scss diff --git a/src/Blogifier.Themes.Standard/assets/scss/page/_account.scss b/src/Blogifier.Themes.Standard/assets/scss/page/_account.scss new file mode 100644 index 000000000..fa1b01234 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/scss/page/_account.scss @@ -0,0 +1,77 @@ +.blogifier { + height: 100%; + display: flex; + flex-direction: column; +} + +.account { + width: 22rem; + max-width: 100%; + padding: 1rem 1.5rem; + margin-right: auto; + margin-left: auto; + + @media screen and (min-width: 992px) { + transform: translateY(-3rem); + margin: auto; + } + + // header + &-header { + margin-bottom: 2rem; + } + + &-title { + font-size: 1.25rem; + font-weight: 400; + + &:last-of-type { + margin: 0; + } + } + + &-desc { + font-size: 0.875rem; + } + + // message + &-message { + margin-bottom: 2rem; + font-size: 0.875rem; + padding: 0.5rem 1rem; + border-radius: 0.25rem; + + &.-error { + background-color: $danger; + color: #fff; + } + + &.-warning { + background-color: $warning; + color: #fff; + } + &.-success { + background-color: $success; + color: #fff; + } + } + + // logo + &-logo { + display: block; + width: 2.5rem; + height: 2.5rem; + margin: 0 auto 5rem; + } + + // back + &-back { + position: absolute; + top: 2rem; + left: 2rem; + width: 3rem; + height: 3rem; + text-align: center; + line-height: 3rem; + } +} diff --git a/src/Blogifier/wwwroot/themes/standard/scss/page/_home.scss b/src/Blogifier.Themes.Standard/assets/scss/page/_home.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/page/_home.scss rename to src/Blogifier.Themes.Standard/assets/scss/page/_home.scss diff --git a/src/Blogifier.Themes.Standard/assets/scss/page/_profile.scss b/src/Blogifier.Themes.Standard/assets/scss/page/_profile.scss new file mode 100644 index 000000000..1482ed813 --- /dev/null +++ b/src/Blogifier.Themes.Standard/assets/scss/page/_profile.scss @@ -0,0 +1,5 @@ + + +.profile { + margin-top: 8rem; +} diff --git a/src/Blogifier/wwwroot/themes/standard/scss/post/_featured.scss b/src/Blogifier.Themes.Standard/assets/scss/post/_featured.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/post/_featured.scss rename to src/Blogifier.Themes.Standard/assets/scss/post/_featured.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/post/_nav.scss b/src/Blogifier.Themes.Standard/assets/scss/post/_nav.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/post/_nav.scss rename to src/Blogifier.Themes.Standard/assets/scss/post/_nav.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/post/_post.scss b/src/Blogifier.Themes.Standard/assets/scss/post/_post.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/post/_post.scss rename to src/Blogifier.Themes.Standard/assets/scss/post/_post.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/post/_related.scss b/src/Blogifier.Themes.Standard/assets/scss/post/_related.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/post/_related.scss rename to src/Blogifier.Themes.Standard/assets/scss/post/_related.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/post/_share.scss b/src/Blogifier.Themes.Standard/assets/scss/post/_share.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/post/_share.scss rename to src/Blogifier.Themes.Standard/assets/scss/post/_share.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/post/_view-grid.scss b/src/Blogifier.Themes.Standard/assets/scss/post/_view-grid.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/post/_view-grid.scss rename to src/Blogifier.Themes.Standard/assets/scss/post/_view-grid.scss diff --git a/src/Blogifier/wwwroot/themes/standard/scss/post/_view-list.scss b/src/Blogifier.Themes.Standard/assets/scss/post/_view-list.scss similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/scss/post/_view-list.scss rename to src/Blogifier.Themes.Standard/assets/scss/post/_view-list.scss diff --git a/src/Blogifier/wwwroot/themes/test/scss/styles.scss b/src/Blogifier.Themes.Standard/assets/scss/styles.scss similarity index 95% rename from src/Blogifier/wwwroot/themes/test/scss/styles.scss rename to src/Blogifier.Themes.Standard/assets/scss/styles.scss index 8bddcbe98..f299d05da 100644 --- a/src/Blogifier/wwwroot/themes/test/scss/styles.scss +++ b/src/Blogifier.Themes.Standard/assets/scss/styles.scss @@ -6,6 +6,7 @@ // helpers @import "helpers/variables"; +@import "helpers/bootstrap"; @import "helpers/mixins"; @import "helpers/reset"; @import "helpers/base"; diff --git a/src/Blogifier/wwwroot/themes/standard/settings.json b/src/Blogifier.Themes.Standard/assets/settings.json similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/settings.json rename to src/Blogifier.Themes.Standard/assets/settings.json diff --git a/src/Blogifier/wwwroot/themes/standard/img/logo-black.png b/src/Blogifier.Themes.Standard/wwwroot/img/logo-black.png similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/img/logo-black.png rename to src/Blogifier.Themes.Standard/wwwroot/img/logo-black.png diff --git a/src/Blogifier/wwwroot/themes/standard/img/logo-white.png b/src/Blogifier.Themes.Standard/wwwroot/img/logo-white.png similarity index 100% rename from src/Blogifier/wwwroot/themes/standard/img/logo-white.png rename to src/Blogifier.Themes.Standard/wwwroot/img/logo-white.png diff --git a/src/Blogifier/Blog.db b/src/Blogifier/Blog.db deleted file mode 100644 index 1961d6869..000000000 Binary files a/src/Blogifier/Blog.db and /dev/null differ diff --git a/src/Blogifier/Blogifier.csproj b/src/Blogifier/Blogifier.csproj index e463cd6ba..229a9c24d 100644 --- a/src/Blogifier/Blogifier.csproj +++ b/src/Blogifier/Blogifier.csproj @@ -1,30 +1,43 @@ - + - - net6.0 - true - + + net7.0 + enable + false + - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + true + - - - - - + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + - - - - + + + + + - \ No newline at end of file + diff --git a/src/Blogifier/Blogs/AnalyticsProvider.cs b/src/Blogifier/Blogs/AnalyticsProvider.cs new file mode 100644 index 000000000..759a71e6f --- /dev/null +++ b/src/Blogifier/Blogs/AnalyticsProvider.cs @@ -0,0 +1,50 @@ +using Blogifier.Data; +using Blogifier.Shared; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Blogs; + +public class AnalyticsProvider +{ + private readonly AppDbContext _dbContext; + + public AnalyticsProvider(AppDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetPostSummaryAsync() + { + var currTime = DateTime.UtcNow; + var query = from post in _dbContext.Posts.AsNoTracking() + where post.State >= PostState.Release && post.PublishedAt >= currTime.AddDays(-7) + group post by new { Time = new { post.PublishedAt!.Value.Year, post.PublishedAt!.Value.Month, post.PublishedAt!.Value.Day } } into g + select new BlogSumDto + { + Time = g.Key.Time.Year + "-" + g.Key.Time.Month + "-" + g.Key.Time.Day, + Posts = g.Count(m => m.PostType == PostType.Post), + Pages = g.Count(m => m.PostType == PostType.Page), + Views = g.Sum(m => m.Views), + }; + return await query.ToListAsync(); + } + + //public async Task SaveDisplayType(int type) + //{ + // var blog = await _dbContext.Blogs.FirstAsync(); + // blog.AnalyticsListType = type; + // await _dbContext.SaveChangesAsync(); + //} + + //public async Task SaveDisplayPeriod(int period) + //{ + // var blog = await _dbContext.Blogs.OrderBy(b => b.Id).FirstAsync(); + // blog.AnalyticsPeriod = period; + // await _dbContext.SaveChangesAsync(); + //} + +} diff --git a/src/Blogifier/Blogs/BlogData.cs b/src/Blogifier/Blogs/BlogData.cs new file mode 100644 index 000000000..bf68678b0 --- /dev/null +++ b/src/Blogifier/Blogs/BlogData.cs @@ -0,0 +1,14 @@ +namespace Blogifier.Blogs; + +public class BlogData +{ + public string Title { get; set; } = default!; + public string Description { get; set; } = default!; + public string? Logo { get; set; } + public string Theme { get; set; } = default!; + public string? HeaderScript { get; set; } + public string? FooterScript { get; set; } + public string Version { get; set; } = default!; + public int ItemsPerPage { get; set; } + public bool IncludeFeatured { get; set; } +} diff --git a/src/Blogifier/Blogs/BlogManager.cs b/src/Blogifier/Blogs/BlogManager.cs new file mode 100644 index 000000000..65c7bb193 --- /dev/null +++ b/src/Blogifier/Blogs/BlogManager.cs @@ -0,0 +1,79 @@ +using Blogifier.Options; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Logging; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Blogifier.Blogs; + +public class BlogManager +{ + private readonly ILogger _logger; + private readonly IDistributedCache _distributedCache; + private readonly OptionProvider _optionProvider; + private BlogData? _blogData; + + public BlogManager( + ILogger logger, + IDistributedCache distributedCache, + OptionProvider optionProvider) + { + _logger = logger; + _distributedCache = distributedCache; + _optionProvider = optionProvider; + } + + public async Task GetAsync() + { + if (_blogData != null) return _blogData; + var key = BlogifierConstant.CacheKeys.BlogData; + _logger.LogDebug("get option {key}", key); + var cache = await _distributedCache.GetAsync(key); + if (cache != null) + { + var value = Encoding.UTF8.GetString(cache); + return Deserialize(value); + } + else + { + var value = await _optionProvider.GetByValueAsync(key); + if (value != null) + { + + var bytes = Encoding.UTF8.GetBytes(value); + await _distributedCache.SetAsync(key, bytes, new() { SlidingExpiration = TimeSpan.FromMinutes(15) }); + return Deserialize(value); + } + } + throw new BlogNotIitializeException(); + + BlogData Deserialize(string value) + { + _logger.LogDebug("return option {key}:{value}", key, value); + _blogData = JsonSerializer.Deserialize(value); + return _blogData!; + } + } + + public async Task AnyAsync() + { + var key = BlogifierConstant.CacheKeys.BlogData; + if (await _optionProvider.AnyKeyAsync(key)) + return true; + await _distributedCache.RemoveAsync(key); + return false; + } + + public async Task SetAsync(BlogData blogData) + { + var key = BlogifierConstant.CacheKeys.BlogData; + var value = JsonSerializer.Serialize(blogData); + _logger.LogCritical("blog set {value}", value); + var bytes = Encoding.UTF8.GetBytes(value); + await _distributedCache.SetAsync(key, bytes, new() { SlidingExpiration = TimeSpan.FromMinutes(15) }); + await _optionProvider.SetValue(key, value); + } + +} diff --git a/src/Blogifier/Blogs/BlogNotIitializeException.cs b/src/Blogifier/Blogs/BlogNotIitializeException.cs new file mode 100644 index 000000000..f9a84efa6 --- /dev/null +++ b/src/Blogifier/Blogs/BlogNotIitializeException.cs @@ -0,0 +1,20 @@ +using System; + +namespace Blogifier.Blogs; + +public class BlogNotIitializeException : Exception +{ + public BlogNotIitializeException() + { + } + + public BlogNotIitializeException(string message) + : base(message) + { + } + + public BlogNotIitializeException(string message, Exception inner) + : base(message, inner) + { + } +} diff --git a/src/Blogifier/Blogs/MainMamager.cs b/src/Blogifier/Blogs/MainMamager.cs new file mode 100644 index 000000000..33c79c2a6 --- /dev/null +++ b/src/Blogifier/Blogs/MainMamager.cs @@ -0,0 +1,70 @@ +using AutoMapper; +using Blogifier.Identity; +using Blogifier.Posts; +using Blogifier.Shared; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Caching.Distributed; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Blogifier.Blogs; + +public class MainMamager +{ + private readonly IMapper _mapper; + private readonly IDistributedCache _distributedCache; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly BlogManager _blogManager; + private readonly CategoryProvider _categoryProvider; + public MainMamager( + IMapper mapper, + IDistributedCache distributedCache, + IHttpContextAccessor httpContextAccessor, + BlogManager blogManager, + CategoryProvider categoryProvider) + { + _mapper = mapper; + _distributedCache = distributedCache; + _httpContextAccessor = httpContextAccessor; + _blogManager = blogManager; + _categoryProvider = categoryProvider; + } + + public async Task GetAsync() + { + var blog = await _blogManager.GetAsync(); + var main = _mapper.Map(blog); + main.Categories = await GetCategoryItemesCacheAsync(); + var httpContext = _httpContextAccessor.HttpContext; + if (httpContext != null) + { + var request = httpContext.Request; + main.AbsoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; + main.PathUrl = request.Path; + main.Claims = BlogifierClaims.Analysis(httpContext.User); + } + return main; + } + + public async Task> GetCategoryItemesCacheAsync() + { + var key = BlogifierConstant.CacheKeys.CategoryItemes; + var cache = await _distributedCache.GetAsync(key); + if (cache != null) + { + var value = Encoding.UTF8.GetString(cache); + return JsonSerializer.Deserialize>(value)!; + } + else + { + var data = await _categoryProvider.GetItemsExistPostAsync(); + var value = JsonSerializer.Serialize(data); + var bytes = Encoding.UTF8.GetBytes(value); + await _distributedCache.SetAsync(key, bytes, new() { SlidingExpiration = TimeSpan.FromMinutes(15) }); + return data; + } + } +} diff --git a/src/Blogifier/Controllers/AboutController.cs b/src/Blogifier/Controllers/AboutController.cs deleted file mode 100644 index a0c5aa40b..000000000 --- a/src/Blogifier/Controllers/AboutController.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Mvc; -using System.Threading.Tasks; - -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class AboutController : ControllerBase - { - private readonly IAboutProvider _aboutProvider; - - public AboutController(IAboutProvider aboutProvider) - { - _aboutProvider = aboutProvider; - } - - [HttpGet] - public async Task GetAbout() - { - return await _aboutProvider.GetAboutModel(); - } - } -} diff --git a/src/Blogifier/Controllers/AccountController.cs b/src/Blogifier/Controllers/AccountController.cs new file mode 100644 index 000000000..1d047b68a --- /dev/null +++ b/src/Blogifier/Controllers/AccountController.cs @@ -0,0 +1,227 @@ +using Blogifier.Blogs; +using Blogifier.Identity; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Controllers; + +[Route("account")] +public class AccountController : Controller +{ + protected readonly ILogger _logger; + protected readonly UserManager _userManager; + protected readonly SignInManager _signInManager; + protected readonly BlogManager _blogManager; + + public AccountController( + ILogger logger, + UserManager userManager, + SignInManager signInManager, + BlogManager blogManager) + { + _logger = logger; + _userManager = userManager; + _signInManager = signInManager; + _blogManager = blogManager; + } + + [HttpGet] + [HttpPost] + public IActionResult Index([FromQuery] AccountModel parameter) + => RedirectToAction("login", routeValues: parameter); + + [HttpGet("login")] + public async Task Login([FromQuery] AccountModel parameter) + { + var data = await _blogManager.GetAsync(); + var model = new AccountLoginModel { RedirectUri = parameter.RedirectUri }; + return View($"~/Views/Themes/{data.Theme}/login.cshtml", model); + } + + [HttpPost("login")] + public async Task LoginForm([FromForm] AccountLoginModel model) + { + if (ModelState.IsValid) + { + var user = await _userManager.FindByEmailAsync(model.Email); + if (user != null) + { + var result = await _signInManager.PasswordSignInAsync(user, model.Password, true, lockoutOnFailure: true); + if (result.Succeeded) + { + _logger.LogInformation("User logged in."); + if (string.IsNullOrEmpty(model.RedirectUri)) return LocalRedirect("~/"); + return Redirect(model.RedirectUri); + } + } + } + model.ShowError = true; + var data = await _blogManager.GetAsync(); + return View($"~/Views/Themes/{data.Theme}/login.cshtml", model); + } + + [HttpGet("register")] + public async Task Register([FromQuery] AccountModel parameter) + { + var model = new AccountRegisterModel { RedirectUri = parameter.RedirectUri }; + var data = await _blogManager.GetAsync(); + return View($"~/Views/Themes/{data.Theme}/register.cshtml", model); + } + + [HttpPost("register")] + public async Task RegisterForm([FromForm] AccountRegisterModel model) + { + if (ModelState.IsValid) + { + var user = new UserInfo(model.UserName) + { + NickName = model.NickName, + Email = model.Email + }; + var result = await _userManager.CreateAsync(user, model.Password); + if (result.Succeeded) + { + return RedirectToAction("login", routeValues: new AccountModel { RedirectUri = model.RedirectUri }); + } + } + model.ShowError = true; + var data = await _blogManager.GetAsync(); + return View($"~/Views/Themes/{data.Theme}/register.cshtml", model); + } + + [HttpGet("logout")] + public async Task Logout() + { + await _signInManager.SignOutAsync(); + return Redirect("~/"); + } + + [HttpGet("initialize")] + public async Task Initialize([FromQuery] AccountModel parameter) + { + if (await _blogManager.AnyAsync()) + return RedirectToAction("login", routeValues: parameter); + + var model = new AccountInitializeModel { RedirectUri = parameter.RedirectUri }; + return View($"~/Views/Themes/{BlogifierConstant.DefaultTheme}/initialize.cshtml", model); + } + + [HttpPost("initialize")] + public async Task InitializeForm([FromForm] AccountInitializeModel model) + { + if (await _blogManager.AnyAsync()) + return RedirectToAction("login", routeValues: new AccountModel { RedirectUri = model.RedirectUri }); + + if (ModelState.IsValid) + { + var user = new UserInfo(model.UserName) + { + NickName = model.NickName, + Email = model.Email, + Type = UserType.Administrator, + }; + var result = await _userManager.CreateAsync(user, model.Password); + if (result.Succeeded) + { + var blogData = new BlogData + { + Title = model.Title, + Description = model.Description, + Theme = BlogifierConstant.DefaultTheme, + ItemsPerPage = BlogifierConstant.DefaultItemsPerPage, + Version = BlogifierConstant.DefaultVersion, + Logo = BlogifierConstant.DefaultLogo + }; + await _blogManager.SetAsync(blogData); + return Redirect("~/"); + } + } + model.ShowError = true; + return View($"~/Views/Themes/{BlogifierConstant.DefaultTheme}/initialize.cshtml", model); + } + + [Authorize] + [HttpGet("profile")] + public async Task Profile([FromQuery] AccountModel parameter) + { + var userId = User.FirstUserId(); + var user = (await _userManager.FindByIdAsync(userId))!; + var model = new AccountProfileEditModel + { + RedirectUri = parameter.RedirectUri, + IsProfile = true, + Email = user.Email, + NickName = user.NickName, + Avatar = user.Avatar, + Bio = user.Bio, + }; + var data = await _blogManager.GetAsync(); + return View($"~/Views/Themes/{data.Theme}/profile.cshtml", model); + } + + [Authorize] + [HttpPost("profile")] + public async Task ProfileForm([FromForm] AccountProfileEditModel model) + { + if (ModelState.IsValid) + { + var userId = User.FirstUserId(); + var user = (await _userManager.FindByIdAsync(userId))!; + user.Email = model.Email; + user.NickName = model.NickName; + user.Avatar = model.Avatar; + user.Bio = model.Bio; + var result = await _userManager.UpdateAsync(user); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: true); + } + else + { + model.Error = result.Errors.FirstOrDefault()?.Description; + } + } + var data = await _blogManager.GetAsync(); + return View($"~/Views/Themes/{data.Theme}/profile.cshtml", model); + } + + [Authorize] + [HttpGet("password")] + public async Task Password([FromQuery] AccountModel parameter) + { + var model = new AccountProfilePasswordModel + { + RedirectUri = parameter.RedirectUri, + IsPassword = true, + }; + var data = await _blogManager.GetAsync(); + return View($"~/Views/Themes/{data.Theme}/password.cshtml", model); + } + + [Authorize] + [HttpPost("password")] + public async Task Password([FromForm] AccountProfilePasswordModel model) + { + if (ModelState.IsValid) + { + var userId = User.FirstUserId(); + var user = (await _userManager.FindByIdAsync(userId))!; + var token = await _userManager.GeneratePasswordResetTokenAsync(user); + var result = await _userManager.ResetPasswordAsync(user, token, model.Password); + if (result.Succeeded) + { + return await Logout(); + } + else + { + model.Error = result.Errors.FirstOrDefault()?.Description; + } + } + var data = await _blogManager.GetAsync(); + return View($"~/Views/Themes/{data.Theme}/password.cshtml", model); + } +} diff --git a/src/Blogifier/Controllers/AdminController.cs b/src/Blogifier/Controllers/AdminController.cs new file mode 100644 index 000000000..a22f771bc --- /dev/null +++ b/src/Blogifier/Controllers/AdminController.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; + +namespace Blogifier.Controllers; + +public class AdminController : Controller +{ + [HttpGet("/admin")] + [Authorize] + public Task Admin() => Task.FromResult(File("~/index.html", "text/html")); +} diff --git a/src/Blogifier/Controllers/AnalyticsController.cs b/src/Blogifier/Controllers/AnalyticsController.cs deleted file mode 100644 index ff720d577..000000000 --- a/src/Blogifier/Controllers/AnalyticsController.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.Threading.Tasks; - -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class AnalyticsController : ControllerBase - { - private readonly IAnalyticsProvider _analyticsProvider; - - public AnalyticsController(IAnalyticsProvider analyticsProvider) - { - _analyticsProvider = analyticsProvider; - } - - [Authorize] - [HttpGet] - public async Task GetAnalytics() - { - return await _analyticsProvider.GetAnalytics(); - } - - [Authorize] - [HttpPut("displayType/{typeId:int}")] - public async Task> SaveDisplayType(int typeId) - { - return await _analyticsProvider.SaveDisplayType(typeId); - } - - [Authorize] - [HttpPut("displayPeriod/{typeId:int}")] - public async Task> SaveDisplayPeriod(int typeId) - { - return await _analyticsProvider.SaveDisplayPeriod(typeId); - } - } -} diff --git a/src/Blogifier/Controllers/AuthorController.cs b/src/Blogifier/Controllers/AuthorController.cs deleted file mode 100644 index 9bfd41fba..000000000 --- a/src/Blogifier/Controllers/AuthorController.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class AuthorController : ControllerBase - { - private readonly IAuthorProvider _authorProvider; - - public AuthorController(IAuthorProvider authorProvider) - { - _authorProvider = authorProvider; - } - - [Authorize] - [HttpGet("all")] - public async Task> All() - { - return await _authorProvider.GetAuthors(); - } - - [Authorize] - [HttpGet("email/{email}")] - public async Task> FindByEmail(string email) - { - return await _authorProvider.FindByEmail(email); - } - - [HttpGet("getcurrent")] - public async Task> GetCurrentAuthor() - { - if (User.Identity.IsAuthenticated) - return await FindByEmail(User.FindFirstValue(ClaimTypes.Name)); - return new Author(); - } - - [Authorize] - [HttpDelete("{id:int}")] - public async Task> RemoveAuthor(int id) - { - return await _authorProvider.Remove(id); - } - - [Authorize] - [HttpPost("add")] - public async Task> Add(Author author) - { - var success = await _authorProvider.Add(author); - return success ? Ok() : BadRequest(); - } - - [Authorize] - [HttpPut("update")] - public async Task> Update(Author author) - { - var success = await _authorProvider.Update(author); - return success ? Ok() : BadRequest(); - } - - [HttpPost("register")] - public async Task> Register(RegisterModel model) - { - var success = await _authorProvider.Register(model); - return success ? Ok() : BadRequest(); - } - - [HttpPost("login")] - public async Task Login(LoginModel model) - { - if (await _authorProvider.Verify(model) == false) - return BadRequest(); - - var claim = new Claim(ClaimTypes.Name, model.Email); - var claimsIdentity = new ClaimsIdentity(new[] { claim }, "serverAuth"); - var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); - - await HttpContext.SignInAsync(claimsPrincipal); - return Ok(); - } - - [HttpGet("logout")] - public async Task> LogOutUser() - { - await HttpContext.SignOutAsync(); - return await Task.FromResult(true); - } - - [Authorize] - [HttpPut("changepassword")] - public async Task> ChangePassword(RegisterModel model) - { - var success = await _authorProvider.ChangePassword(model); - return success ? Ok() : BadRequest(); - } - } -} diff --git a/src/Blogifier/Controllers/BlogController.cs b/src/Blogifier/Controllers/BlogController.cs deleted file mode 100644 index 41e9193ba..000000000 --- a/src/Blogifier/Controllers/BlogController.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class BlogController : ControllerBase - { - private readonly IBlogProvider _blogProvider; - - public BlogController(IBlogProvider blogProvider) - { - _blogProvider = blogProvider; - } - - [HttpGet] - public async Task GetBlog() - { - return await _blogProvider.GetBlog(); - } - - [HttpGet("categories")] - public async Task> GetBlogCategories() - { - return await _blogProvider.GetBlogCategories(); - } - - [Authorize] - [HttpPut] - public async Task> ChangeTheme([FromBody] Blog blog) - { - return await _blogProvider.Update(blog); - } - } -} diff --git a/src/Blogifier/Controllers/CategoryController.cs b/src/Blogifier/Controllers/CategoryController.cs index d4504da9c..9b05b7d7b 100644 --- a/src/Blogifier/Controllers/CategoryController.cs +++ b/src/Blogifier/Controllers/CategoryController.cs @@ -1,73 +1,32 @@ -using Blogifier.Core.Providers; +using Blogifier.Blogs; +using Blogifier.Posts; using Blogifier.Shared; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; using System.Threading.Tasks; -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class CategoryController : ControllerBase - { - private readonly ICategoryProvider _categoryProvider; - - public CategoryController(ICategoryProvider categoryProvider) - { - _categoryProvider = categoryProvider; - } - - [HttpGet("{postId:int}")] - public async Task> GetPostCategories(int postId) - { - return await _categoryProvider.GetPostCategories(postId); - } - - [HttpGet("byId/{categoryId:int}")] - public async Task GetCategory(int categoryId) - { - return await _categoryProvider.GetCategory(categoryId); - } - - [HttpGet] - public async Task> GetCategories() - { - return await _categoryProvider.Categories(); - } +namespace Blogifier.Controllers; - [HttpGet("{term}")] - public async Task> SearchCategories(string term = "*") - { - return await _categoryProvider.SearchCategories(term); - } - - [Authorize] - [HttpPost("{postId:int}/{tag}")] - public async Task> AddPostCategory(int postId, string tag) - { - return await _categoryProvider.AddPostCategory(postId, tag); - } - - [Authorize] - [HttpPut] - public async Task> SaveCategory(Category category) - { - return await _categoryProvider.SaveCategory(category); - } - - [Authorize] - [HttpPut("{postId:int}")] - public async Task> SavePostCategories(int postId, List categories) - { - return await _categoryProvider.SavePostCategories(postId, categories); - } - - [Authorize] - [HttpDelete("{categoryId:int}")] - public async Task> RemoveCategory(int categoryId) - { - return await _categoryProvider.RemoveCategory(categoryId); - } - } +[Route("category")] +public class CategoryController : Controller +{ + private readonly MainMamager _mainMamager; + private readonly PostProvider _postProvider; + + public CategoryController( + MainMamager mainMamager, + PostProvider postProvider) + { + _mainMamager = mainMamager; + _postProvider = postProvider; + } + + [HttpGet("{category}")] + public async Task Category([FromRoute] string category, [FromQuery] int page = 1) + { + var main = await _mainMamager.GetAsync(); + var pager = await _postProvider.GetByCategoryAsync(category, page, main.ItemsPerPage); + pager.Configure(main.PathUrl, "page"); + var model = new CategoryModel(category, pager, main); + return View($"~/Views/Themes/{main.Theme}/category.cshtml", model); + } } diff --git a/src/Blogifier/Controllers/ErrorController.cs b/src/Blogifier/Controllers/ErrorController.cs new file mode 100644 index 000000000..fc2346599 --- /dev/null +++ b/src/Blogifier/Controllers/ErrorController.cs @@ -0,0 +1,42 @@ +using AutoMapper; +using Blogifier.Blogs; +using Blogifier.Shared; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace Blogifier.Controllers; + +public class ErrorController : Controller +{ + protected readonly ILogger _logger; + protected readonly IMapper _mapper; + protected readonly MainMamager _mainMamager; + + public ErrorController( + ILogger logger, + IMapper mapper, + MainMamager mainMamager) + { + _logger = logger; + _mapper = mapper; + _mainMamager = mainMamager; + } + + [Route("404")] + public async Task Error404() + { + try + { + var data = await _mainMamager.GetAsync(); + var model = new MainModel(data); + return View($"~/Views/Themes/{data.Theme}/404.cshtml", model); + } + catch (Exception ex) + { + _logger.LogError(ex, "error page exception"); + return View($"~/Views/404.cshtml"); + } + } +} diff --git a/src/Blogifier/Controllers/FeedController.cs b/src/Blogifier/Controllers/FeedController.cs new file mode 100644 index 000000000..ec3041af4 --- /dev/null +++ b/src/Blogifier/Controllers/FeedController.cs @@ -0,0 +1,74 @@ +using Blogifier.Blogs; +using Blogifier.Posts; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.ServiceModel.Syndication; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace Blogifier.Controllers; + +public class FeedController : Controller +{ + private readonly ILogger _logger; + private readonly BlogManager _blogManager; + private readonly PostProvider _postProvider; + private readonly MarkdigProvider _markdigProvider; + + public FeedController( + ILogger logger, + BlogManager blogManager, + PostProvider postProvider, + MarkdigProvider markdigProvider) + { + _logger = logger; + _blogManager = blogManager; + _postProvider = postProvider; + _markdigProvider = markdigProvider; + } + + [ResponseCache(Duration = 1200)] + [HttpGet("feed")] + public async Task Rss() + { + var host = Request.Scheme + "://" + Request.Host; + var data = await _blogManager.GetAsync(); + var posts = await _postProvider.GetAsync(); + var items = new List(); + + var publishedAt = DateTime.UtcNow; + if (posts != null) foreach (var post in posts) + { + var url = $"{host}/posts/{post.Slug}"; + var description = _markdigProvider.ToHtml(post.Content); + var item = new SyndicationItem(post.Title, description, new Uri(url), url, publishedAt) + { + PublishDate = publishedAt + }; + items.Add(item); + } + var feed = new SyndicationFeed(data.Title, data.Description, new Uri(host), host, publishedAt) + { + Items = items + }; + var settings = new XmlWriterSettings + { + Encoding = Encoding.UTF8, + NewLineHandling = NewLineHandling.Entitize, + NewLineOnAttributes = true, + Indent = true + }; + using var stream = new MemoryStream(); + using (var xmlWriter = XmlWriter.Create(stream, settings)) + { + var rssFormatter = new Rss20FeedFormatter(feed, false); + rssFormatter.WriteTo(xmlWriter); + xmlWriter.Flush(); + } + return File(stream.ToArray(), "application/xml; charset=utf-8"); + } +} diff --git a/src/Blogifier/Controllers/HomeController.cs b/src/Blogifier/Controllers/HomeController.cs index 42cd41320..1b1318c82 100644 --- a/src/Blogifier/Controllers/HomeController.cs +++ b/src/Blogifier/Controllers/HomeController.cs @@ -1,262 +1,45 @@ -using Blogifier.Core.Extensions; -using Blogifier.Core.Providers; +using Blogifier.Blogs; +using Blogifier.Models; +using Blogifier.Posts; using Blogifier.Shared; using Microsoft.AspNetCore.Mvc; -using System; +using Microsoft.Extensions.Logging; using System.Threading.Tasks; -using System.ServiceModel.Syndication; -using System.Text; -using System.Xml; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Mvc.ViewEngines; -namespace Blogifier.Controllers -{ - public class HomeController : Controller - { - protected readonly IBlogProvider _blogProvider; - protected readonly IPostProvider _postProvider; - protected readonly IFeedProvider _feedProvider; - protected readonly IAuthorProvider _authorProvider; - protected readonly IThemeProvider _themeProvider; - protected readonly IStorageProvider _storageProvider; - protected readonly ICompositeViewEngine _compositeViewEngine; - - public HomeController(IBlogProvider blogProvider, - IPostProvider postProvider, IFeedProvider feedProvider, IAuthorProvider authorProvider, IThemeProvider themeProvider, - IStorageProvider storageProvider, ICompositeViewEngine compositeViewEngine) - { - _blogProvider = blogProvider; - _postProvider = postProvider; - _feedProvider = feedProvider; - _authorProvider = authorProvider; - _themeProvider = themeProvider; - _storageProvider = storageProvider; - _compositeViewEngine = compositeViewEngine; - } - - public async Task Index(int page = 1) - { - - var model = await getBlogPosts(pager: page); - - //If no blogs are setup redirect to first time registration - if(model == null){ - return Redirect("~/admin/register"); - } - - return View($"~/Views/Themes/{model.Blog.Theme}/Index.cshtml", model); - } - - [HttpGet("/{slug}")] - public async Task Index(string slug) - { - if (!string.IsNullOrEmpty(slug)) - { - return await getSingleBlogPost(slug); - } - return Redirect("~/"); - } - - [HttpGet("/admin")] - public async Task Admin() - { - return await Task.FromResult(File("~/index.html", "text/html")); - } - - [HttpPost] - public async Task Search(string term, int page = 1) - { - - if (!string.IsNullOrEmpty(term)) - { - var model = await getBlogPosts(term, page); - string viewPath = $"~/Views/Themes/{model.Blog.Theme}/Search.cshtml"; - if (IsViewExists(viewPath)) - return View(viewPath, model); - else - return Redirect("~/home"); - } - else{ - return Redirect("~/home"); - } - } - - [HttpGet("categories/{category}")] - public async Task Categories(string category, int page = 1) - { - var model = await getBlogPosts("", page, category); - string viewPath = $"~/Views/Themes/{model.Blog.Theme}/Category.cshtml"; - - ViewBag.Category = category; - - if (IsViewExists(viewPath)) - return View(viewPath, model); - - return View($"~/Views/Themes/{model.Blog.Theme}/Index.cshtml", model); - } - - [HttpGet("posts/{slug}")] - public async Task Single(string slug) - { - return await getSingleBlogPost(slug); - } - - [HttpGet("error")] - public async Task Error() - { - try - { - PostModel model = new PostModel(); - model.Blog = await _blogProvider.GetBlogItem(); - string viewPath = $"~/Views/Themes/{model.Blog.Theme}/404.cshtml"; - if (IsViewExists(viewPath)) - return View(viewPath, model); - return View($"~/Views/Error.cshtml"); - } - catch - { - return View($"~/Views/Error.cshtml"); - } - } - - [ResponseCache(Duration = 1200)] - [HttpGet("feed/{type}")] - public async Task Rss(string type) - { - string host = Request.Scheme + "://" + Request.Host; - var blog = await _blogProvider.GetBlog(); - - var posts = await _feedProvider.GetEntries(type, host); - var items = new List(); +namespace Blogifier.Controllers; - var feed = new SyndicationFeed( - blog.Title, - blog.Description, - new Uri(host), - host, - posts.FirstOrDefault().Published - ); - - if (posts != null && posts.Count() > 0) - { - foreach (var post in posts) - { - var item = new SyndicationItem( - post.Title, - post.Description.MdToHtml(), - new Uri(post.Id), - post.Id, - post.Published - ); - item.PublishDate = post.Published; - items.Add(item); - } - } - feed.Items = items; - - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - NewLineHandling = NewLineHandling.Entitize, - NewLineOnAttributes = true, - Indent = true - }; - - using (var stream = new MemoryStream()) - { - using (var xmlWriter = XmlWriter.Create(stream, settings)) - { - var rssFormatter = new Rss20FeedFormatter(feed, false); - rssFormatter.WriteTo(xmlWriter); - xmlWriter.Flush(); - } - return File(stream.ToArray(), "application/xml; charset=utf-8"); - } - } - - private bool IsViewExists(string viewPath) - { - var result = _compositeViewEngine.GetView("", viewPath, false); - return result.Success; - } - - - public async Task getSingleBlogPost(string slug){ - try - { - ViewBag.Slug = slug; - PostModel model = await _postProvider.GetPostModel(slug); - - // If unpublished and unauthorised redirect to error / 404. - if (model.Post.Published == DateTime.MinValue && !User.Identity.IsAuthenticated) - { - return Redirect("~/error"); - } - - model.Blog = await _blogProvider.GetBlogItem(); - model.Post.Description = model.Post.Description.MdToHtml(); - model.Post.Content = model.Post.Content.MdToHtml(); - - if (!model.Post.Author.Avatar.StartsWith("data:")) - model.Post.Author.Avatar = Url.Content($"~/{model.Post.Author.Avatar}"); - - if (model.Post.PostType == PostType.Page) - { - string viewPath = $"~/Views/Themes/{model.Blog.Theme}/Page.cshtml"; - if (IsViewExists(viewPath)) - return View(viewPath, model); - } - - return View($"~/Views/Themes/{model.Blog.Theme}/Post.cshtml", model); - } - catch - { - return Redirect("~/error"); - } - } - public async Task getBlogPosts(string term ="", int pager = 1, string category = "", string slug = ""){ - - var model = new ListModel{}; - - try - { - model.Blog = await _blogProvider.GetBlogItem(); - } - catch - { - return null; - } - - model.Pager = new Pager(pager, model.Blog.ItemsPerPage); - - if(!string.IsNullOrEmpty(category)) - { - model.PostListType = PostListType.Category; - model.Posts = await _postProvider.GetList(model.Pager, 0, category, "PF"); - } - else if (string.IsNullOrEmpty(term)) - { - model.PostListType = PostListType.Blog; - if (model.Blog.IncludeFeatured) - model.Posts = await _postProvider.GetList(model.Pager, 0, "", "FP"); - else - model.Posts = await _postProvider.GetList(model.Pager, 0, "", "P"); - } - else - { - model.PostListType = PostListType.Search; - model.Blog.Title = term; - model.Blog.Description = ""; - model.Posts = await _postProvider.Search(model.Pager, term, 0, "FP"); - } - - if (model.Pager.ShowOlder) model.Pager.LinkToOlder = $"?page={model.Pager.Older}"; - if (model.Pager.ShowNewer) model.Pager.LinkToNewer = $"?page={model.Pager.Newer}"; - - return model; - } - } +public class HomeController : Controller +{ + private readonly ILogger _logger; + private readonly MainMamager _mainMamager; + private readonly PostProvider _postProvider; + + public HomeController( + ILogger logger, + MainMamager mainMamager, + PostProvider postProvider) + { + _logger = logger; + _mainMamager = mainMamager; + _postProvider = postProvider; + } + + [HttpGet] + public async Task Index([FromQuery] int page = 1) + { + MainDto main; + try + { + main = await _mainMamager.GetAsync(); + } + catch (BlogNotIitializeException ex) + { + _logger.LogError(ex, "blgo not iitialize redirect"); + return Redirect("~/account/initialize"); + } + var pager = await _postProvider.GetPostsAsync(page, main.ItemsPerPage); + pager.Configure(main.PathUrl, "page"); + var model = new IndexModel(pager, main); + return View($"~/Views/Themes/{main.Theme}/index.cshtml", model); + } } diff --git a/src/Blogifier/Controllers/NewsletterController.cs b/src/Blogifier/Controllers/NewsletterController.cs deleted file mode 100644 index e82977b62..000000000 --- a/src/Blogifier/Controllers/NewsletterController.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class NewsletterController : ControllerBase - { - protected readonly INewsletterProvider _newsletterProvider; - - public NewsletterController(INewsletterProvider newsletterProvider) - { - _newsletterProvider = newsletterProvider; - } - - [HttpPost("subscribe")] - public async Task> Subscribe([FromBody] Subscriber subscriber) - { - return await _newsletterProvider.AddSubscriber(subscriber); - } - - [Authorize] - [HttpGet("subscribers")] - public async Task> GetSubscribers() - { - return await _newsletterProvider.GetSubscribers(); - } - - [HttpDelete("unsubscribe/{id:int}")] - public async Task> RemoveSubscriber(int id) - { - return await _newsletterProvider.RemoveSubscriber(id); - } - - [Authorize] - [HttpGet("newsletters")] - public async Task> GetNewsletters() - { - return await _newsletterProvider.GetNewsletters(); - } - - [Authorize] - [HttpGet("send/{postId:int}")] - public async Task SendNewsletter(int postId) - { - return await _newsletterProvider.SendNewsletter(postId); - } - - [Authorize] - [HttpDelete("remove/{id:int}")] - public async Task> RemoveNewsletter(int id) - { - return await _newsletterProvider.RemoveNewsletter(id); - } - - [Authorize] - [HttpGet("mailsettings")] - public async Task GetMailSettings() - { - return await _newsletterProvider.GetMailSettings(); - } - - [Authorize] - [HttpPut("mailsettings")] - public async Task> SaveMailSettings([FromBody] MailSetting mailSettings) - { - return await _newsletterProvider.SaveMailSettings(mailSettings); - } - } -} diff --git a/src/Blogifier/Controllers/PageController.cs b/src/Blogifier/Controllers/PageController.cs new file mode 100644 index 000000000..38e66f293 --- /dev/null +++ b/src/Blogifier/Controllers/PageController.cs @@ -0,0 +1,48 @@ +using AutoMapper; +using Blogifier.Blogs; +using Blogifier.Posts; +using Blogifier.Shared; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +namespace Blogifier.Controllers; + +[Route("page")] +public class PageController : Controller +{ + protected readonly ILogger _logger; + protected readonly IMapper _mapper; + protected readonly MainMamager _mainMamager; + protected readonly PostManager _postManager; + public PageController( + ILogger logger, + IMapper mapper, + MainMamager mainMamager, + PostManager postManager) + { + _logger = logger; + _mapper = mapper; + _mainMamager = mainMamager; + _postManager = postManager; + } + + [HttpGet("{slug}")] + public async Task GetAsync([FromRoute] string slug) + { + var main = await _mainMamager.GetAsync(); + var postSlug = await _postManager.GetToHtmlAsync(slug); + if (postSlug.Post.State == PostState.Draft) + { + if (User.Identity == null || User.FirstUserId() != postSlug.Post.User.Id) + return Redirect("~/404"); + } + else if (postSlug.Post.PostType == PostType.Page) + { + return Redirect($"~/page/{postSlug.Post.Slug}"); + } + var categoriesUrl = Url.Content("~/category"); + var model = new PostModel(postSlug, categoriesUrl, main); + return View($"~/Views/Themes/{main.Theme}/post.cshtml", model); + } +} diff --git a/src/Blogifier/Controllers/PostController.cs b/src/Blogifier/Controllers/PostController.cs index f3df1f9e2..c5f202135 100644 --- a/src/Blogifier/Controllers/PostController.cs +++ b/src/Blogifier/Controllers/PostController.cs @@ -1,80 +1,48 @@ -using Blogifier.Core.Providers; +using AutoMapper; +using Blogifier.Blogs; +using Blogifier.Posts; using Blogifier.Shared; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; +using Microsoft.Extensions.Logging; using System.Threading.Tasks; -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class PostController : ControllerBase - { - private readonly IPostProvider _postProvider; - - public PostController(IPostProvider postProvider) - { - _postProvider = postProvider; - } - - [HttpGet("list/{filter}/{postType}")] - public async Task>> GetPosts(PublishedStatus filter, PostType postType) - { - return await _postProvider.GetPosts(filter, postType); - } - - [HttpGet("list/search/{term}")] - public async Task>> SearchPosts(string term) - { - return await _postProvider.SearchPosts(term); - } - - [HttpGet("byslug/{slug}")] - public async Task> GetPostBySlug(string slug) - { - return await _postProvider.GetPostBySlug(slug); - } - - [HttpGet("getslug/{title}")] - public async Task> GetSlug(string title) - { - return await _postProvider.GetSlugFromTitle(title); - } +namespace Blogifier.Controllers; - [Authorize] - [HttpPost("add")] - public async Task> AddPost(Post post) - { - return await _postProvider.Add(post); - } - - [Authorize] - [HttpPut("update")] - public async Task> UpdatePost(Post post) - { - return await _postProvider.Update(post); - } - - [Authorize] - [HttpPut("publish/{id:int}")] - public async Task> PublishPost(int id, [FromBody] bool publish) - { - return await _postProvider.Publish(id, publish); - } - - [Authorize] - [HttpPut("featured/{id:int}")] - public async Task> FeaturedPost(int id, [FromBody] bool featured) - { - return await _postProvider.Featured(id, featured); - } - - [Authorize] - [HttpDelete("{id:int}")] - public async Task> RemovePost(int id) - { - return await _postProvider.Remove(id); - } - } +[Route("post")] +public class PostController : Controller +{ + protected readonly ILogger _logger; + protected readonly IMapper _mapper; + protected readonly MainMamager _mainMamager; + protected readonly PostManager _postManager; + public PostController( + ILogger logger, + IMapper mapper, + MainMamager mainMamager, + PostManager postManager) + { + _logger = logger; + _mapper = mapper; + _mainMamager = mainMamager; + _postManager = postManager; + } + + [HttpGet("{slug}")] + public async Task GetAsync([FromRoute] string slug) + { + var main = await _mainMamager.GetAsync(); + var postSlug = await _postManager.GetToHtmlAsync(slug); + if (postSlug.Post.State == PostState.Draft) + { + if (User.Identity == null || User.FirstUserId() != postSlug.Post.User.Id) + return Redirect("~/404"); + } + else if (postSlug.Post.PostType == PostType.Page) + { + return Redirect($"~/page/{postSlug.Post.Slug}"); + } + var categoriesUrl = Url.Content("~/category"); + var model = new PostModel(postSlug, categoriesUrl, main); + return View($"~/Views/Themes/{main.Theme}/post.cshtml", model); + } } diff --git a/src/Blogifier/Controllers/SearchController.cs b/src/Blogifier/Controllers/SearchController.cs new file mode 100644 index 000000000..50a0c4042 --- /dev/null +++ b/src/Blogifier/Controllers/SearchController.cs @@ -0,0 +1,39 @@ +using Blogifier.Blogs; +using Blogifier.Posts; +using Blogifier.Shared; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; + +namespace Blogifier.Controllers; + +[Route("search")] +public class SearchController : Controller +{ + private readonly MainMamager _mainMamager; + private readonly PostProvider _postProvider; + + public SearchController( + MainMamager mainMamager, + PostProvider postProvider) + { + _mainMamager = mainMamager; + _postProvider = postProvider; + } + + [HttpPost] + public async Task Post([FromQuery] string term, [FromQuery] int page = 1) + { + if (!string.IsNullOrEmpty(term)) + { + var main = await _mainMamager.GetAsync(); + var pager = await _postProvider.GetSearchAsync(term, page, main.ItemsPerPage); + pager.Configure(main.PathUrl, "page"); + var model = new SearchModel(pager, main); + return View($"~/Views/Themes/{main.Theme}/search.cshtml", model); + } + else + { + return Redirect("~/"); + } + } +} diff --git a/src/Blogifier/Controllers/SitemapController.cs b/src/Blogifier/Controllers/SitemapController.cs index 5832069a2..b6124d2a4 100644 --- a/src/Blogifier/Controllers/SitemapController.cs +++ b/src/Blogifier/Controllers/SitemapController.cs @@ -1,4 +1,4 @@ -using Blogifier.Core.Providers; +using Blogifier.Posts; using Blogifier.Shared; using Microsoft.AspNetCore.Mvc; using System; @@ -6,52 +6,46 @@ using System.Threading.Tasks; using System.Xml.Linq; -namespace Blogifier.Controllers -{ - public class SitemapController : ControllerBase - { - private readonly IPostProvider _postProvider; - - public SitemapController(IPostProvider postProvider) - { - _postProvider = postProvider; - } - - [Route("sitemap")] - [Produces("text/xml")] - public async Task Sitemap() - { - var sitemapNamespace = XNamespace.Get("http://www.sitemaps.org/schemas/sitemap/0.9"); - - var posts = await _postProvider.GetPosts(PublishedStatus.Published, PostType.Post); - - var doc = new XDocument( - new XDeclaration("1.0", "utf-8", null), - new XElement(sitemapNamespace + "urlset", - from post in posts - select new XElement(sitemapNamespace + "url", - new XElement(sitemapNamespace + "loc", GetPostUrl(post)), - new XElement(sitemapNamespace + "lastmod", GetPostDate(post)), - new XElement(sitemapNamespace + "changefreq", "monthly") - ) - ) - ); +namespace Blogifier.Controllers; - return Content(doc.Declaration + Environment.NewLine + doc, "text/xml"); - } - - public string GetPostUrl(Post post) - { - string webRoot = Url.Content("~/"); - - var sitemapBaseUri = $"{Request.Scheme}://{Request.Host}{webRoot}"; - - return $"{sitemapBaseUri}posts/{post.Slug}"; - } - - public string GetPostDate(Post post) - { - return post.Published.ToString("yyyy-MM-ddTHH:mm:sszzz"); - } - } +public class SitemapController : ControllerBase +{ + private readonly PostProvider _postProvider; + + public SitemapController(PostProvider postProvider) + { + _postProvider = postProvider; + } + + [Route("sitemap")] + [Produces("text/xml")] + public async Task Sitemap() + { + var sitemapNamespace = XNamespace.Get("http://www.sitemaps.org/schemas/sitemap/0.9"); + var posts = await _postProvider.GetAsync(); + var doc = new XDocument( + new XDeclaration("1.0", "utf-8", null), + new XElement(sitemapNamespace + "urlset", + from post in posts + select new XElement(sitemapNamespace + "url", + new XElement(sitemapNamespace + "loc", GetPostUrl(post)), + new XElement(sitemapNamespace + "lastmod", GetPostDate(post)), + new XElement(sitemapNamespace + "changefreq", "monthly") + ) + ) + ); + return Content(doc.Declaration + Environment.NewLine + doc, "text/xml"); + } + + private string GetPostUrl(PostDto post) + { + string webRoot = Url.Content("~/"); + var sitemapBaseUri = $"{Request.Scheme}://{Request.Host}{webRoot}"; + return $"{sitemapBaseUri}posts/{post.Slug}"; + } + + private string GetPostDate(PostDto post) + { + return post.PublishedAt!.Value.ToString("yyyy-MM-ddTHH:mm:sszzz"); + } } diff --git a/src/Blogifier/Controllers/StorageController.cs b/src/Blogifier/Controllers/StorageController.cs index 5a0b60dac..b6dfcceac 100644 --- a/src/Blogifier/Controllers/StorageController.cs +++ b/src/Blogifier/Controllers/StorageController.cs @@ -1,85 +1,31 @@ -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; +using Blogifier.Storages; using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; +using Microsoft.AspNetCore.OutputCaching; +using System.IO; using System.Threading.Tasks; -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class StorageController : ControllerBase - { - private readonly IStorageProvider _storageProvider; - private readonly IAuthorProvider _authorProvider; - private readonly IBlogProvider _blogProvider; - private readonly IPostProvider _postProvider; - - public StorageController(IStorageProvider storageProvider, IAuthorProvider authorProvider, IBlogProvider blogProvider, IPostProvider postProvider) - { - _storageProvider = storageProvider; - _authorProvider = authorProvider; - _blogProvider = blogProvider; - _postProvider = postProvider; - } - - [Authorize] - [HttpGet("themes")] - public async Task> GetThemes() - { - return await _storageProvider.GetThemes(); - } - - [Authorize] - [HttpPut("exists")] - public async Task FileExists([FromBody] string path) - { - return (await Task.FromResult(_storageProvider.FileExists(path))) ? Ok() : BadRequest(); - } - - [Authorize] - [HttpPost("upload/{uploadType}")] - public async Task Upload(IFormFile file, UploadType uploadType, int postId = 0) - { - var author = await _authorProvider.FindByEmail(User.Identity.Name); - var post = postId == 0 ? new Post() : await _postProvider.GetPostById(postId); +namespace Blogifier.Controllers; - var path = $"{author.Id}/{DateTime.Now.Year}/{DateTime.Now.Month}"; - var fileName = $"data/{path}/{file.FileName}"; - - if (uploadType == UploadType.PostImage) - fileName = Url.Content("~/") + fileName; - - if (await _storageProvider.UploadFormFile(file, path)) - { - var blog = await _blogProvider.GetBlog(); - - switch (uploadType) - { - case UploadType.Avatar: - author.Avatar = fileName; - return (await _authorProvider.Update(author)) ? new JsonResult(fileName) : BadRequest(); - case UploadType.AppLogo: - blog.Logo = fileName; - return (await _blogProvider.Update(blog)) ? new JsonResult(fileName) : BadRequest(); - case UploadType.AppCover: - blog.Cover = fileName; - return (await _blogProvider.Update(blog)) ? new JsonResult(fileName) : BadRequest(); - case UploadType.PostCover: - post.Cover = fileName; - return new JsonResult(fileName); - case UploadType.PostImage: - return new JsonResult(fileName); - } - return Ok(); - } - else - { - return BadRequest(); - } - } - } +public class StorageController : ControllerBase +{ + private readonly StorageProvider _storageManager; + + public StorageController( + StorageProvider storageManager) + { + _storageManager = storageManager; + } + + [HttpGet($"{BlogifierConstant.StorageObjectUrl}/{{**storageUrl}}")] + [ResponseCache(VaryByHeader = "User-Agent", Duration = 3600)] + [OutputCache(PolicyName = BlogifierConstant.OutputCacheExpire1)] + public async Task ObjectAsync([FromRoute] string storageUrl) + { + var memoryStream = new MemoryStream(); + var storage = await _storageManager.GetAsync(storageUrl, + (stream, cancellationToken) => stream.CopyToAsync(memoryStream, cancellationToken)); + if (storage == null) return NotFound(); + memoryStream.Position = 0; + return File(memoryStream, storage.ContentType); + } } diff --git a/src/Blogifier/Controllers/SyndicationController.cs b/src/Blogifier/Controllers/SyndicationController.cs deleted file mode 100644 index f6b6ebbb2..000000000 --- a/src/Blogifier/Controllers/SyndicationController.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Blogifier.Core.Data; -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class SyndicationController : ControllerBase - { - private readonly AppDbContext _dbContext; - private readonly ISyndicationProvider _syndicationProvider; - - public SyndicationController(AppDbContext dbContext, ISyndicationProvider syndicationProvider) - { - _dbContext = dbContext; - _syndicationProvider = syndicationProvider; - } - - [Authorize] - [HttpGet("getitems")] - public async Task> GetItems(string feedUrl, string baseUrl) - { - Author author = await _dbContext.Authors - .Where(a => a.Email == User.Identity.Name) - .FirstOrDefaultAsync(); - - string webRoot = Url.Content("~/"); - - return await _syndicationProvider.GetPosts(feedUrl, author.Id, new System.Uri(baseUrl), webRoot); - } - - [Authorize] - [HttpPost("import")] - public async Task> Import(Post post) - { - var success = await _syndicationProvider.ImportPost(post); - return success ? Ok() : BadRequest(); - - //Random rnd = new Random(); - //var ok = rnd.Next(1, 10) >= 2; - - //System.Threading.Thread.Sleep(1000); - - //if(ok) - // return await Task.FromResult(Ok()); - //else - // return await Task.FromResult(BadRequest()); - } - } -} diff --git a/src/Blogifier/Controllers/ThemeController.cs b/src/Blogifier/Controllers/ThemeController.cs deleted file mode 100644 index 518819b95..000000000 --- a/src/Blogifier/Controllers/ThemeController.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.Threading.Tasks; - -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class ThemeController : ControllerBase - { - private readonly IThemeProvider _themeProvider; - private readonly IStorageProvider _storageProvider; - - public ThemeController(IThemeProvider themeProvider, IStorageProvider storageProvider) - { - _themeProvider = themeProvider; - _storageProvider = storageProvider; - } - - [Authorize] - [HttpGet("{theme}")] - public async Task GetThemeSettings(string theme) - { - return await _storageProvider.GetThemeSettings(theme); - } - - [Authorize] - [HttpPost("{theme}")] - public async Task SaveThemeSettings(string theme, ThemeSettings settings) - { - return await _storageProvider.SaveThemeSettings(theme, settings); - } - } -} diff --git a/src/Blogifier/Controllers/UploadController.cs b/src/Blogifier/Controllers/UploadController.cs deleted file mode 100644 index a5c891bbf..000000000 --- a/src/Blogifier/Controllers/UploadController.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Blogifier.Core.Providers; -using Blogifier.Shared; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace Blogifier.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class UploadController : ControllerBase - { - - } -} diff --git a/src/Blogifier/Data/AppDbContext.cs b/src/Blogifier/Data/AppDbContext.cs new file mode 100644 index 000000000..5dbd45791 --- /dev/null +++ b/src/Blogifier/Data/AppDbContext.cs @@ -0,0 +1,91 @@ +using Blogifier.Identity; +using Blogifier.Newsletters; +using Blogifier.Options; +using Blogifier.Shared; +using Blogifier.Storages; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace Blogifier.Data; + +public class AppDbContext : IdentityUserContext +{ + protected readonly DbContextOptions _options; + + public AppDbContext(DbContextOptions options) : base(options) + { + _options = options; + } + + public DbSet Options { get; set; } = default!; + public DbSet Posts { get; set; } = default!; + public DbSet Storages { get; set; } = default!; + public DbSet StorageReferences { get; set; } = default!; + public DbSet Categories { get; set; } = default!; + public DbSet PostCategories { get; set; } = default!; + public DbSet Subscribers { get; set; } = default!; + public DbSet Newsletters { get; set; } = default!; + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(e => + { + e.ToTable("User"); + e.Property(p => p.Id).HasMaxLength(128); + e.Property(p => p.CreatedAt).HasColumnOrder(0); + e.Property(p => p.PasswordHash).HasMaxLength(256); + e.Property(p => p.SecurityStamp).HasMaxLength(32); + e.Property(p => p.ConcurrencyStamp).HasMaxLength(64); + e.Property(p => p.PhoneNumber).HasMaxLength(32); + }); + + modelBuilder.Entity>(e => + { + e.ToTable("UserClaim"); + e.Property(p => p.ClaimType).HasMaxLength(16); + e.Property(p => p.ClaimValue).HasMaxLength(256); + }); + modelBuilder.Entity>(e => + { + e.ToTable("UserLogin"); + e.Property(p => p.ProviderDisplayName).HasMaxLength(128); + }); + modelBuilder.Entity>(e => + { + e.ToTable("UserToken"); + e.Property(p => p.Value).HasMaxLength(1024); + }); + + modelBuilder.Entity(e => + { + e.ToTable("Options"); + e.HasIndex(b => b.Key).IsUnique(); + }); + + modelBuilder.Entity(e => + { + e.ToTable("StorageReferences"); + e.HasKey(t => new { t.StorageId, t.EntityId, t.Type }); + }); + + modelBuilder.Entity(e => + { + e.ToTable("PostCategories"); + e.HasKey(t => new { t.PostId, t.CategoryId }); + }); + + modelBuilder.Entity(e => + { + e.ToTable("Post"); + e.HasIndex(b => b.Slug).IsUnique(); + + e.HasMany(e => e.StorageReferences) + .WithOne(e => e.Post) + .HasForeignKey(e => e.EntityId) + .IsRequired(); + }); + } +} diff --git a/src/Blogifier/Data/AppEntity.cs b/src/Blogifier/Data/AppEntity.cs new file mode 100644 index 000000000..0a906f614 --- /dev/null +++ b/src/Blogifier/Data/AppEntity.cs @@ -0,0 +1,10 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Blogifier.Data; + +public abstract class AppEntity where TKey : IEquatable +{ + [Key] + public virtual TKey Id { get; set; } = default!; +} diff --git a/src/Blogifier/Data/AppProvider.cs b/src/Blogifier/Data/AppProvider.cs new file mode 100644 index 000000000..2ed1f11fc --- /dev/null +++ b/src/Blogifier/Data/AppProvider.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Data; + +public class AppProvider where T : AppEntity where TKey : IEquatable +{ + protected readonly AppDbContext _dbContext; + + protected AppProvider(AppDbContext dbContext) + { + _dbContext = dbContext; + } + + public Task DeleteAsync(TKey id) + { + var query = _dbContext.Set() + .Where(m => id.Equals(m.Id)); + return DeleteInternalAsync(query); + } + + public Task DeleteAsync(IEnumerable? ids) + { + if (ids != null && ids.Any()) + { + var query = _dbContext.Set() + .Where(m => ids.Contains(m.Id)); + return DeleteInternalAsync(query); + } + return Task.CompletedTask; + } + + protected static async Task DeleteInternalAsync(IQueryable query) + { + await query.ExecuteDeleteAsync(); + } +} diff --git a/src/Blogifier/Data/Migrations/20230602104208_Init.Designer.cs b/src/Blogifier/Data/Migrations/20230602104208_Init.Designer.cs new file mode 100644 index 000000000..46e58dc6f --- /dev/null +++ b/src/Blogifier/Data/Migrations/20230602104208_Init.Designer.cs @@ -0,0 +1,509 @@ +// +using System; +using Blogifier.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Blogifier.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20230602104208_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Blogifier.Identity.UserInfo", b => + { + b.Property("Id") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Avatar") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("Bio") + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnOrder(0); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("Gender") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NickName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PhoneNumber") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Newsletter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("Success") + .HasColumnType("tinyint(1)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.ToTable("Newsletters"); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Subscriber", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Country") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("Ip") + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("Region") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Subscribers"); + }); + + modelBuilder.Entity("Blogifier.Options.OptionInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Options", (string)null); + }); + + modelBuilder.Entity("Blogifier.Shared.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Cover") + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("varchar(450)"); + + b.Property("PostType") + .HasColumnType("int"); + + b.Property("PublishedAt") + .HasColumnType("datetime(6)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Views") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("Blogifier.Shared.PostCategory", b => + { + b.Property("PostId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.HasKey("PostId", "CategoryId"); + + b.HasIndex("CategoryId"); + + b.ToTable("PostCategories", (string)null); + }); + + modelBuilder.Entity("Blogifier.Shared.Storage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuthorId") + .HasColumnType("int"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("Length") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.Property("StorageType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Storages"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasMaxLength(16) + .HasColumnType("varchar(16)"); + + b.Property("ClaimValue") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(128)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("UserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(128)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserToken", (string)null); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Newsletter", b => + { + b.HasOne("Blogifier.Shared.Post", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.HasOne("Blogifier.Identity.UserInfo", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blogifier.Shared.PostCategory", b => + { + b.HasOne("Blogifier.Shared.Category", "Category") + .WithMany("PostCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blogifier.Shared.Post", "Post") + .WithMany("PostCategories") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Blogifier.Shared.Category", b => + { + b.Navigation("PostCategories"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.Navigation("PostCategories"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Blogifier/Data/Migrations/20230602104208_Init.cs b/src/Blogifier/Data/Migrations/20230602104208_Init.cs new file mode 100644 index 000000000..e280ed4da --- /dev/null +++ b/src/Blogifier/Data/Migrations/20230602104208_Init.cs @@ -0,0 +1,400 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +#nullable disable + +namespace Blogifier.Data.Migrations +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Categories", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Content = table.Column(type: "varchar(120)", maxLength: 120, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Description = table.Column(type: "varchar(255)", maxLength: 255, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Categories", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Options", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), + Key = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Options", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Storages", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + AuthorId = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + IsDeleted = table.Column(type: "tinyint(1)", nullable: false), + DeletedAt = table.Column(type: "datetime(6)", nullable: true), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Path = table.Column(type: "varchar(2048)", maxLength: 2048, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Length = table.Column(type: "bigint", nullable: false), + ContentType = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + StorageType = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Storages", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Subscribers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), + Email = table.Column(type: "varchar(160)", maxLength: 160, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Ip = table.Column(type: "varchar(80)", maxLength: 80, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Country = table.Column(type: "varchar(120)", maxLength: 120, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Region = table.Column(type: "varchar(120)", maxLength: 120, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Subscribers", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "User", + columns: table => new + { + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Id = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + NickName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Avatar = table.Column(type: "varchar(1024)", maxLength: 1024, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Bio = table.Column(type: "varchar(2048)", maxLength: 2048, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Gender = table.Column(type: "varchar(32)", maxLength: 32, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Type = table.Column(type: "int", nullable: false), + State = table.Column(type: "int", nullable: false), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "varchar(32)", maxLength: 32, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "varchar(64)", maxLength: 64, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(32)", maxLength: 32, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Posts", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), + UserId = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Title = table.Column(type: "varchar(160)", maxLength: 160, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Slug = table.Column(type: "varchar(160)", maxLength: 160, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Description = table.Column(type: "varchar(450)", maxLength: 450, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Content = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Cover = table.Column(type: "varchar(160)", maxLength: 160, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Views = table.Column(type: "int", nullable: false), + PublishedAt = table.Column(type: "datetime(6)", nullable: true), + PostType = table.Column(type: "int", nullable: false), + State = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Posts", x => x.Id); + table.ForeignKey( + name: "FK_Posts_User_UserId", + column: x => x.UserId, + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "UserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "varchar(128)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimType = table.Column(type: "varchar(16)", maxLength: 16, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_UserClaim", x => x.Id); + table.ForeignKey( + name: "FK_UserClaim_User_UserId", + column: x => x.UserId, + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "UserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "varchar(128)", maxLength: 128, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "varchar(128)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_UserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_UserLogin_User_UserId", + column: x => x.UserId, + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "UserToken", + columns: table => new + { + UserId = table.Column(type: "varchar(128)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(1024)", maxLength: 1024, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_UserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_UserToken_User_UserId", + column: x => x.UserId, + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Newsletters", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), + PostId = table.Column(type: "int", nullable: false), + Success = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Newsletters", x => x.Id); + table.ForeignKey( + name: "FK_Newsletters_Posts_PostId", + column: x => x.PostId, + principalTable: "Posts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "PostCategories", + columns: table => new + { + PostId = table.Column(type: "int", nullable: false), + CategoryId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PostCategories", x => new { x.PostId, x.CategoryId }); + table.ForeignKey( + name: "FK_PostCategories_Categories_CategoryId", + column: x => x.CategoryId, + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_PostCategories_Posts_PostId", + column: x => x.PostId, + principalTable: "Posts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Newsletters_PostId", + table: "Newsletters", + column: "PostId"); + + migrationBuilder.CreateIndex( + name: "IX_Options_Key", + table: "Options", + column: "Key", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_PostCategories_CategoryId", + table: "PostCategories", + column: "CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_Posts_UserId", + table: "Posts", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "User", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "User", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_UserClaim_UserId", + table: "UserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_UserLogin_UserId", + table: "UserLogin", + column: "UserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Newsletters"); + + migrationBuilder.DropTable( + name: "Options"); + + migrationBuilder.DropTable( + name: "PostCategories"); + + migrationBuilder.DropTable( + name: "Storages"); + + migrationBuilder.DropTable( + name: "Subscribers"); + + migrationBuilder.DropTable( + name: "UserClaim"); + + migrationBuilder.DropTable( + name: "UserLogin"); + + migrationBuilder.DropTable( + name: "UserToken"); + + migrationBuilder.DropTable( + name: "Categories"); + + migrationBuilder.DropTable( + name: "Posts"); + + migrationBuilder.DropTable( + name: "User"); + } + } +} diff --git a/src/Blogifier/Data/Migrations/20230606095435_Storage.Designer.cs b/src/Blogifier/Data/Migrations/20230606095435_Storage.Designer.cs new file mode 100644 index 000000000..26459d812 --- /dev/null +++ b/src/Blogifier/Data/Migrations/20230606095435_Storage.Designer.cs @@ -0,0 +1,579 @@ +// +using System; +using Blogifier.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Blogifier.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20230606095435_Storage")] + partial class Storage + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Blogifier.Identity.UserInfo", b => + { + b.Property("Id") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Avatar") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("Bio") + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnOrder(0); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("Gender") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NickName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PhoneNumber") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Newsletter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("Success") + .HasColumnType("tinyint(1)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.ToTable("Newsletters"); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Subscriber", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Country") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("Ip") + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("Region") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Subscribers"); + }); + + modelBuilder.Entity("Blogifier.Options.OptionInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Options", (string)null); + }); + + modelBuilder.Entity("Blogifier.Shared.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Cover") + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("varchar(450)"); + + b.Property("PostType") + .HasColumnType("int"); + + b.Property("PublishedAt") + .HasColumnType("datetime(6)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Views") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("Post", (string)null); + }); + + modelBuilder.Entity("Blogifier.Shared.PostCategory", b => + { + b.Property("PostId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.HasKey("PostId", "CategoryId"); + + b.HasIndex("CategoryId"); + + b.ToTable("PostCategories", (string)null); + }); + + modelBuilder.Entity("Blogifier.Storages.Storage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("Length") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Storages"); + }); + + modelBuilder.Entity("Blogifier.Storages.StorageReference", b => + { + b.Property("StorageId") + .HasColumnType("int"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.HasKey("StorageId", "EntityId", "Type"); + + b.HasIndex("EntityId"); + + b.ToTable("StorageReferences", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasMaxLength(16) + .HasColumnType("varchar(16)"); + + b.Property("ClaimValue") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(128)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("UserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(128)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserToken", (string)null); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Newsletter", b => + { + b.HasOne("Blogifier.Shared.Post", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.HasOne("Blogifier.Identity.UserInfo", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blogifier.Shared.PostCategory", b => + { + b.HasOne("Blogifier.Shared.Category", "Category") + .WithMany("PostCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blogifier.Shared.Post", "Post") + .WithMany("PostCategories") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Blogifier.Storages.Storage", b => + { + b.HasOne("Blogifier.Identity.UserInfo", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blogifier.Storages.StorageReference", b => + { + b.HasOne("Blogifier.Shared.Post", "Post") + .WithMany("StorageReferences") + .HasForeignKey("EntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blogifier.Storages.Storage", "Storage") + .WithMany("StorageReferences") + .HasForeignKey("StorageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("Storage"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Blogifier.Shared.Category", b => + { + b.Navigation("PostCategories"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.Navigation("PostCategories"); + + b.Navigation("StorageReferences"); + }); + + modelBuilder.Entity("Blogifier.Storages.Storage", b => + { + b.Navigation("StorageReferences"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Blogifier/Data/Migrations/20230606095435_Storage.cs b/src/Blogifier/Data/Migrations/20230606095435_Storage.cs new file mode 100644 index 000000000..8b561c8cc --- /dev/null +++ b/src/Blogifier/Data/Migrations/20230606095435_Storage.cs @@ -0,0 +1,241 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +#nullable disable + +namespace Blogifier.Data.Migrations +{ + /// + public partial class Storage : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Newsletters_Posts_PostId", + table: "Newsletters"); + + migrationBuilder.DropForeignKey( + name: "FK_PostCategories_Posts_PostId", + table: "PostCategories"); + + migrationBuilder.DropForeignKey( + name: "FK_Posts_User_UserId", + table: "Posts"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Posts", + table: "Posts"); + + migrationBuilder.DropColumn( + name: "AuthorId", + table: "Storages"); + + migrationBuilder.RenameTable( + name: "Posts", + newName: "Post"); + + migrationBuilder.RenameColumn( + name: "StorageType", + table: "Storages", + newName: "Type"); + + migrationBuilder.RenameIndex( + name: "IX_Posts_UserId", + table: "Post", + newName: "IX_Post_UserId"); + + migrationBuilder.AddColumn( + name: "Slug", + table: "Storages", + type: "varchar(2048)", + maxLength: 2048, + nullable: false, + defaultValue: "") + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "UserId", + table: "Storages", + type: "varchar(128)", + nullable: false, + defaultValue: "") + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Post", + table: "Post", + column: "Id"); + + migrationBuilder.CreateTable( + name: "StorageReferences", + columns: table => new + { + StorageId = table.Column(type: "int", nullable: false), + EntityId = table.Column(type: "int", nullable: false), + Type = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_StorageReferences", x => new { x.StorageId, x.EntityId, x.Type }); + table.ForeignKey( + name: "FK_StorageReferences_Post_EntityId", + column: x => x.EntityId, + principalTable: "Post", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_StorageReferences_Storages_StorageId", + column: x => x.StorageId, + principalTable: "Storages", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Storages_UserId", + table: "Storages", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Post_Slug", + table: "Post", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_StorageReferences_EntityId", + table: "StorageReferences", + column: "EntityId"); + + migrationBuilder.AddForeignKey( + name: "FK_Newsletters_Post_PostId", + table: "Newsletters", + column: "PostId", + principalTable: "Post", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Post_User_UserId", + table: "Post", + column: "UserId", + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_PostCategories_Post_PostId", + table: "PostCategories", + column: "PostId", + principalTable: "Post", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Storages_User_UserId", + table: "Storages", + column: "UserId", + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Newsletters_Post_PostId", + table: "Newsletters"); + + migrationBuilder.DropForeignKey( + name: "FK_Post_User_UserId", + table: "Post"); + + migrationBuilder.DropForeignKey( + name: "FK_PostCategories_Post_PostId", + table: "PostCategories"); + + migrationBuilder.DropForeignKey( + name: "FK_Storages_User_UserId", + table: "Storages"); + + migrationBuilder.DropTable( + name: "StorageReferences"); + + migrationBuilder.DropIndex( + name: "IX_Storages_UserId", + table: "Storages"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Post", + table: "Post"); + + migrationBuilder.DropIndex( + name: "IX_Post_Slug", + table: "Post"); + + migrationBuilder.DropColumn( + name: "Slug", + table: "Storages"); + + migrationBuilder.DropColumn( + name: "UserId", + table: "Storages"); + + migrationBuilder.RenameTable( + name: "Post", + newName: "Posts"); + + migrationBuilder.RenameColumn( + name: "Type", + table: "Storages", + newName: "StorageType"); + + migrationBuilder.RenameIndex( + name: "IX_Post_UserId", + table: "Posts", + newName: "IX_Posts_UserId"); + + migrationBuilder.AddColumn( + name: "AuthorId", + table: "Storages", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddPrimaryKey( + name: "PK_Posts", + table: "Posts", + column: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Newsletters_Posts_PostId", + table: "Newsletters", + column: "PostId", + principalTable: "Posts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_PostCategories_Posts_PostId", + table: "PostCategories", + column: "PostId", + principalTable: "Posts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Posts_User_UserId", + table: "Posts", + column: "UserId", + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/Blogifier/Data/Migrations/AppDbContextModelSnapshot.cs b/src/Blogifier/Data/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 000000000..d8aeb9266 --- /dev/null +++ b/src/Blogifier/Data/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,576 @@ +// +using System; +using Blogifier.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Blogifier.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Blogifier.Identity.UserInfo", b => + { + b.Property("Id") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Avatar") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("Bio") + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnOrder(0); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("Gender") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NickName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PhoneNumber") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Newsletter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("Success") + .HasColumnType("tinyint(1)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.ToTable("Newsletters"); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Subscriber", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Country") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("Ip") + .HasMaxLength(80) + .HasColumnType("varchar(80)"); + + b.Property("Region") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Subscribers"); + }); + + modelBuilder.Entity("Blogifier.Options.OptionInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Options", (string)null); + }); + + modelBuilder.Entity("Blogifier.Shared.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Cover") + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("varchar(450)"); + + b.Property("PostType") + .HasColumnType("int"); + + b.Property("PublishedAt") + .HasColumnType("datetime(6)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("varchar(160)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Views") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("Post", (string)null); + }); + + modelBuilder.Entity("Blogifier.Shared.PostCategory", b => + { + b.Property("PostId") + .HasColumnType("int"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.HasKey("PostId", "CategoryId"); + + b.HasIndex("CategoryId"); + + b.ToTable("PostCategories", (string)null); + }); + + modelBuilder.Entity("Blogifier.Storages.Storage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.Property("DeletedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("Length") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("varchar(2048)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Storages"); + }); + + modelBuilder.Entity("Blogifier.Storages.StorageReference", b => + { + b.Property("StorageId") + .HasColumnType("int"); + + b.Property("EntityId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + b.HasKey("StorageId", "EntityId", "Type"); + + b.HasIndex("EntityId"); + + b.ToTable("StorageReferences", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasMaxLength(16) + .HasColumnType("varchar(16)"); + + b.Property("ClaimValue") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(128)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("UserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(128)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserToken", (string)null); + }); + + modelBuilder.Entity("Blogifier.Newsletters.Newsletter", b => + { + b.HasOne("Blogifier.Shared.Post", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.HasOne("Blogifier.Identity.UserInfo", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blogifier.Shared.PostCategory", b => + { + b.HasOne("Blogifier.Shared.Category", "Category") + .WithMany("PostCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blogifier.Shared.Post", "Post") + .WithMany("PostCategories") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Blogifier.Storages.Storage", b => + { + b.HasOne("Blogifier.Identity.UserInfo", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blogifier.Storages.StorageReference", b => + { + b.HasOne("Blogifier.Shared.Post", "Post") + .WithMany("StorageReferences") + .HasForeignKey("EntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blogifier.Storages.Storage", "Storage") + .WithMany("StorageReferences") + .HasForeignKey("StorageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("Storage"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Blogifier.Identity.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Blogifier.Shared.Category", b => + { + b.Navigation("PostCategories"); + }); + + modelBuilder.Entity("Blogifier.Shared.Post", b => + { + b.Navigation("PostCategories"); + + b.Navigation("StorageReferences"); + }); + + modelBuilder.Entity("Blogifier.Storages.Storage", b => + { + b.Navigation("StorageReferences"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Blogifier/Extensions/PrincipalExtensions.cs b/src/Blogifier/Extensions/PrincipalExtensions.cs new file mode 100644 index 000000000..b004fcc4f --- /dev/null +++ b/src/Blogifier/Extensions/PrincipalExtensions.cs @@ -0,0 +1,17 @@ +using System; +using System.Security.Claims; + +namespace Blogifier.Extensions; + +public static class PrincipalExtensions +{ + public static string FirstValue(this ClaimsPrincipal principal, string claimType) + { + var value = FirstOrDefault(principal, claimType); + if (value == null) throw new NullReferenceException(nameof(value)); + return value; + } + + public static string? FirstOrDefault(this ClaimsPrincipal principal, string claimType) + => principal.FindFirstValue(claimType); +} diff --git a/src/Blogifier/Extensions/StringExtensions.cs b/src/Blogifier/Extensions/StringExtensions.cs new file mode 100644 index 000000000..a74974165 --- /dev/null +++ b/src/Blogifier/Extensions/StringExtensions.cs @@ -0,0 +1,212 @@ +using Markdig; +using Microsoft.AspNetCore.Cryptography.KeyDerivation; +using System; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Blogifier.Extensions; + +public static class StringExtensions +{ + public static string ToThumb(this string img) + { + if (img.IndexOf('/') < 1) return img; + + var first = img.Substring(0, img.LastIndexOf('/')); + var second = img.Substring(img.LastIndexOf('/')); + + return $"{first}/thumbs{second}"; + } + + public static string Capitalize(this string str) + { + if (string.IsNullOrEmpty(str)) + return string.Empty; + char[] a = str.ToCharArray(); + a[0] = char.ToUpper(a[0]); + return new string(a); + } + + public static string MdToHtml(this string str) + { + var mpl = new MarkdownPipelineBuilder() + .UsePipeTables() + .UseAdvancedExtensions() + .Build(); + + return Markdown.ToHtml(str, mpl); + } + + public static bool Contains(this string source, string toCheck, StringComparison comp) + { + return source.IndexOf(toCheck, comp) >= 0; + } + + private static readonly Regex RegexStripHtml = new Regex("<[^>]*>", RegexOptions.Compiled); + + public static string StripHtml(this string str) + { + return string.IsNullOrWhiteSpace(str) ? string.Empty : RegexStripHtml.Replace(str, string.Empty).Trim(); + } + + /// + /// Should extract title (file name) from file path or Url + /// + /// c:\foo\test.png + /// test.png + public static string ExtractTitle(this string str) + { + if (str.Contains("\\")) + { + return string.IsNullOrWhiteSpace(str) ? string.Empty : str.Substring(str.LastIndexOf("\\")).Replace("\\", ""); + } + else if (str.Contains("/")) + { + return string.IsNullOrWhiteSpace(str) ? string.Empty : str.Substring(str.LastIndexOf("/")).Replace("/", ""); + } + else + { + return str; + } + } + + /// + /// Converts title to valid URL slug + /// + /// Slug + public static string ToSlug(this string title) + { + var str = title.ToLowerInvariant(); + str = str.Trim('-', '_'); + + if (string.IsNullOrEmpty(str)) + return string.Empty; + + var bytes = Encoding.GetEncoding("utf-8").GetBytes(str); + str = Encoding.UTF8.GetString(bytes); + + str = Regex.Replace(str, @"\s", "-", RegexOptions.Compiled); + + str = Regex.Replace(str, @"([-_]){2,}", "$1", RegexOptions.Compiled); + str = RemoveIllegalCharacters(str); + return str; + } + + public static string Hash(this string source, string salt) + { + var bytes = KeyDerivation.Pbkdf2( + password: source, + salt: Encoding.UTF8.GetBytes(salt), + prf: KeyDerivationPrf.HMACSHA512, + iterationCount: 10000, + numBytesRequested: 256 / 8); + + return Convert.ToBase64String(bytes); + } + + public static string ReplaceIgnoreCase(this string str, string search, string replacement) + { + string result = Regex.Replace( + str, + Regex.Escape(search), + replacement.Replace("$", "$$"), + RegexOptions.IgnoreCase + ); + return result; + } + + #region Helper Methods + + static string RemoveIllegalCharacters(string text) + { + if (string.IsNullOrEmpty(text)) + { + return text; + } + + string[] chars = new string[] { + ":", "/", "?", "!", "#", "[", "]", "{", "}", "@", "*", ".", ",", + "\"","&", "'", "~", "$" + }; + + foreach (var ch in chars) + { + text = text.Replace(ch, string.Empty); + } + + text = text.Replace("–", "-"); + text = text.Replace(" ", "-"); + + text = RemoveUnicodePunctuation(text); + text = RemoveDiacritics(text); + text = RemoveExtraHyphen(text); + + return System.Web.HttpUtility.HtmlEncode(text).Replace("%", string.Empty); + } + + static string RemoveUnicodePunctuation(string text) + { + var normalized = text.Normalize(NormalizationForm.FormD); + var sb = new StringBuilder(); + + foreach (var c in + normalized.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.InitialQuotePunctuation && + CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.FinalQuotePunctuation)) + { + sb.Append(c); + } + + return sb.ToString(); + } + + static string RemoveDiacritics(string text) + { + var normalized = text.Normalize(NormalizationForm.FormD); + var sb = new StringBuilder(); + + foreach (var c in + normalized.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)) + { + sb.Append(c); + } + + return sb.ToString(); + } + + static string RemoveExtraHyphen(string text) + { + if (text.Contains("--")) + { + text = text.Replace("--", "-"); + return RemoveExtraHyphen(text); + } + + return text; + } + + public static string SanitizePath(this string str) + { + if (string.IsNullOrWhiteSpace(str)) + return string.Empty; + + str = str.Replace("%2E", ".").Replace("%2F", "/"); + + if (str.Contains("..") || str.Contains("//")) + throw new ApplicationException("Invalid directory path"); + + return str; + } + + public static string SanitizeFileName(this string str) + { + str = str.SanitizePath(); + + //TODO: add filename specific validation here + + return str; + } + + #endregion +} diff --git a/src/Blogifier/Identity/SignInManager.cs b/src/Blogifier/Identity/SignInManager.cs new file mode 100644 index 000000000..71626202e --- /dev/null +++ b/src/Blogifier/Identity/SignInManager.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Blogifier.Identity; + +public class SignInManager : SignInManager +{ + public SignInManager( + UserManager userManager, + IHttpContextAccessor contextAccessor, + IUserClaimsPrincipalFactory claimsFactory, + IOptions optionsAccessor, + ILogger> logger, + IAuthenticationSchemeProvider schemes, + IUserConfirmation confirmation) + : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation) + { + } +} diff --git a/src/Blogifier/Identity/UserClaimsPrincipalFactory.cs b/src/Blogifier/Identity/UserClaimsPrincipalFactory.cs new file mode 100644 index 000000000..a2fb0100e --- /dev/null +++ b/src/Blogifier/Identity/UserClaimsPrincipalFactory.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Blogifier.Identity; + +public class UserClaimsPrincipalFactory : UserClaimsPrincipalFactory +{ + public UserClaimsPrincipalFactory( + UserManager userManager, + IOptions options) + : base(userManager, options) + { + } + + public override async Task CreateAsync(UserInfo user) + { + var claimsPrincipal = await base.CreateAsync(user); + var id = new ClaimsIdentity("Application"); + id.AddClaim(new Claim(BlogifierClaimTypes.NickName, user.NickName)); + id.AddClaim(new Claim(BlogifierClaimTypes.Type, ((int)user.Type).ToString())); + if (!string.IsNullOrEmpty(user.Avatar)) + id.AddClaim(new Claim(BlogifierClaimTypes.Avatar, user.Avatar)); + claimsPrincipal.AddIdentity(id); + return claimsPrincipal; + } +} diff --git a/src/Blogifier/Identity/UserInfo.cs b/src/Blogifier/Identity/UserInfo.cs new file mode 100644 index 000000000..083533263 --- /dev/null +++ b/src/Blogifier/Identity/UserInfo.cs @@ -0,0 +1,34 @@ +using Blogifier.Shared; +using Microsoft.AspNetCore.Identity; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blogifier.Identity; + +public class UserInfo : IdentityUser +{ + public UserInfo() : base() + { + + } + + public UserInfo(string userName) : base() + { + Id = Guid.NewGuid().ToString(); + UserName = userName; + } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public DateTime CreatedAt { get; set; } + [StringLength(256)] + public string NickName { get; set; } = default!; + [StringLength(1024)] + public string? Avatar { get; set; } + [StringLength(2048)] + public string? Bio { get; set; } + [StringLength(32)] + public string? Gender { get; set; } + public UserType Type { get; set; } + public UserState State { get; set; } +} diff --git a/src/Blogifier/Identity/UserManager.cs b/src/Blogifier/Identity/UserManager.cs new file mode 100644 index 000000000..bda607a69 --- /dev/null +++ b/src/Blogifier/Identity/UserManager.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; + +namespace Blogifier.Identity; + +public class UserManager : UserManager +{ + public UserManager( + IUserStore store, + IOptions optionsAccessor, + IPasswordHasher passwordHasher, + IEnumerable> userValidators, + IEnumerable> passwordValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + IServiceProvider services, + ILogger> logger) + : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) + { + + } +} diff --git a/src/Blogifier/Identity/UserProvider.cs b/src/Blogifier/Identity/UserProvider.cs new file mode 100644 index 000000000..87df2bce0 --- /dev/null +++ b/src/Blogifier/Identity/UserProvider.cs @@ -0,0 +1,44 @@ +using AutoMapper; +using Blogifier.Data; +using Blogifier.Shared; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Identity; + +public class UserProvider +{ + private readonly IMapper _mapper; + private readonly AppDbContext _dbContext; + + public UserProvider(IMapper mapper, AppDbContext dbContext) + { + _mapper = mapper; + _dbContext = dbContext; + } + + public async Task FindByIdAsync(string id) + { + var query = _dbContext.Users + .AsNoTracking() + .Where(m => m.Id == id); + return await _mapper.ProjectTo(query).FirstAsync(); + } + + public async Task> GetAsync() + { + var query = _dbContext.Users + .AsNoTracking(); + return await _mapper.ProjectTo(query).ToListAsync(); + } + + public async Task GetAsync(string id) + { + var query = _dbContext.Users + .AsNoTracking() + .Where(m => m.Id == id); + return await _mapper.ProjectTo(query).FirstOrDefaultAsync(); + } +} diff --git a/src/Blogifier/Interfaces/AnalyticsController.cs b/src/Blogifier/Interfaces/AnalyticsController.cs new file mode 100644 index 000000000..c3d543c39 --- /dev/null +++ b/src/Blogifier/Interfaces/AnalyticsController.cs @@ -0,0 +1,38 @@ +using Blogifier.Blogs; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[Route("api/analytics")] +[ApiController] +[Authorize] +public class AnalyticsController : ControllerBase +{ + private readonly AnalyticsProvider _analyticsProvider; + public AnalyticsController(AnalyticsProvider analyticsProvider) + { + _analyticsProvider = analyticsProvider; + } + + [HttpGet] + public async Task GetAnalytics() + { + var blogs = await _analyticsProvider.GetPostSummaryAsync(); + return new AnalyticsDto { Blogs = blogs }; + } + + //[HttpPut("displayType/{typeId:int}")] + //public async Task SaveDisplayType(int typeId) + //{ + // await _analyticsProvider.SaveDisplayType(typeId); + //} + + //[HttpPut("displayPeriod/{typeId:int}")] + //public async Task SaveDisplayPeriod(int typeId) + //{ + // await _analyticsProvider.SaveDisplayPeriod(typeId); + //} +} diff --git a/src/Blogifier/Interfaces/BlogController.cs b/src/Blogifier/Interfaces/BlogController.cs new file mode 100644 index 000000000..05f12dd3e --- /dev/null +++ b/src/Blogifier/Interfaces/BlogController.cs @@ -0,0 +1,63 @@ +using AutoMapper; +using Blogifier.Blogs; +using Blogifier.Data; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[ApiController] +[Authorize] +[Route("api/blog")] +public class BlogController : ControllerBase +{ + private readonly IMapper _mapper; + private readonly BlogManager _blogManager; + + public BlogController(IMapper mapper, BlogManager blogManager) + { + _mapper = mapper; + _blogManager = blogManager; + } + + [HttpGet] + public async Task GetAsync() + { + var data = await _blogManager.GetAsync(); + var dataDto = _mapper.Map(data); + return dataDto; + } + + [HttpPut] + public async Task PutAsync([FromBody] BlogEitorDto blog) + { + var data = await _blogManager.GetAsync(); + data.Title = blog.Title; + data.Description = blog.Description; + data.HeaderScript = blog.HeaderScript; + data.FooterScript = blog.FooterScript; + data.IncludeFeatured = blog.IncludeFeatured; + data.ItemsPerPage = blog.ItemsPerPage; + await _blogManager.SetAsync(data); + } + + [HttpGet("about")] + public AboutDto GetAboutAsync([FromServices] AppDbContext dbContext) + { + var result = new AboutDto + { + Version = typeof(BlogController)? + .GetTypeInfo()? + .Assembly? + .GetCustomAttribute()? + .InformationalVersion, + DatabaseProvider = dbContext.Database.ProviderName, + OperatingSystem = RuntimeInformation.OSDescription + }; + return result; + } +} diff --git a/src/Blogifier/Interfaces/CategoryController.cs b/src/Blogifier/Interfaces/CategoryController.cs new file mode 100644 index 000000000..405238fe8 --- /dev/null +++ b/src/Blogifier/Interfaces/CategoryController.cs @@ -0,0 +1,65 @@ +using Blogifier.Posts; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[Route("api/category")] +[Authorize] +[ApiController] +public class CategoryController : ControllerBase +{ + private readonly CategoryProvider _categoryProvider; + + public CategoryController(CategoryProvider categoryProvider) + { + _categoryProvider = categoryProvider; + } + + [HttpGet("items")] + public async Task> GetItemsAsync() + { + return await _categoryProvider.GetItemsAsync(); + } + + [HttpDelete("{id:int}")] + public async Task DeleteAsync([FromRoute] int id) + { + await _categoryProvider.DeleteAsync(id); + } + + [HttpDelete("{idsString}")] + public async Task DeleteAsync([FromRoute] string idsString) + { + var ids = idsString.Split(',').Select(int.Parse); + await _categoryProvider.DeleteAsync(ids); + } + + [HttpGet("{postId:int}")] + public async Task> GetPostCategories(int postId) + { + return await _categoryProvider.GetPostCategories(postId); + } + + [HttpGet("byId/{id:int}")] + public async Task GetCategory(int id) + { + return await _categoryProvider.GetCategory(id); + } + + [HttpGet("{term}")] + public async Task> SearchCategories(string term = "*") + { + return await _categoryProvider.SearchCategories(term); + } + + [HttpPut] + public async Task> SaveCategory(Category category) + { + return await _categoryProvider.SaveCategory(category); + } +} diff --git a/src/Blogifier/Interfaces/ImportController.cs b/src/Blogifier/Interfaces/ImportController.cs new file mode 100644 index 000000000..5fb7df97d --- /dev/null +++ b/src/Blogifier/Interfaces/ImportController.cs @@ -0,0 +1,35 @@ +using Blogifier.Posts; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[Route("api/import")] +[Authorize] +[ApiController] +public class ImportController : ControllerBase +{ + private readonly ImportManager _importManager; + + public ImportController(ImportManager importManager) + { + _importManager = importManager; + } + + [HttpGet("rss")] + public ImportDto Rss([FromQuery] ImportRssDto request, [FromServices] ImportRssProvider importRssProvider) + { + return importRssProvider.Analysis(request.FeedUrl); + } + + [HttpPost("write")] + public async Task> Write([FromBody] ImportDto request) + { + var userId = User.FirstUserId(); + var webRoot = Url.Content("~/"); + return await _importManager.WriteAsync(request, webRoot, userId); + } +} diff --git a/src/Blogifier/Interfaces/MailController.cs b/src/Blogifier/Interfaces/MailController.cs new file mode 100644 index 000000000..04c34bd37 --- /dev/null +++ b/src/Blogifier/Interfaces/MailController.cs @@ -0,0 +1,31 @@ +using Blogifier.Newsletters; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[Route("api/mail")] +[ApiController] +[Authorize] +public class MailController : ControllerBase +{ + private readonly EmailManager _emailManager; + public MailController(EmailManager emailManager) + { + _emailManager = emailManager; + } + + [HttpGet("settings")] + public async Task GetSettingsAsync() + { + return await _emailManager.GetSettingsAsync(); + } + + [HttpPut("settings")] + public async Task PutSettingsAsync([FromBody] MailSettingDto input) + { + await _emailManager.PutSettingsAsync(input); + } +} diff --git a/src/Blogifier/Interfaces/NewsletterController.cs b/src/Blogifier/Interfaces/NewsletterController.cs new file mode 100644 index 000000000..55d5730a3 --- /dev/null +++ b/src/Blogifier/Interfaces/NewsletterController.cs @@ -0,0 +1,39 @@ +using Blogifier.Newsletters; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[Route("api/newsletter")] +[ApiController] +[Authorize] +public class NewsletterController : ControllerBase +{ + private readonly NewsletterProvider _newsletterProvider; + + public NewsletterController(NewsletterProvider newsletterProvider) + { + _newsletterProvider = newsletterProvider; + } + + [HttpGet("items")] + public async Task> GetItemsAsync() + { + return await _newsletterProvider.GetItemsAsync(); + } + + [HttpDelete("{id:int}")] + public async Task DeleteAsync([FromRoute] int id) + { + await _newsletterProvider.DeleteAsync(id); + } + + [HttpGet("send/{postId:int}")] + public async Task SendNewsletter([FromRoute] int postId, [FromServices] EmailManager emailManager) + { + await emailManager.SendNewsletter(postId); + } +} diff --git a/src/Blogifier/Interfaces/PostController.cs b/src/Blogifier/Interfaces/PostController.cs new file mode 100644 index 000000000..d1e2adcbd --- /dev/null +++ b/src/Blogifier/Interfaces/PostController.cs @@ -0,0 +1,80 @@ +using Blogifier.Posts; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[ApiController] +[Authorize] +[Route("api/post")] +public class PostController : ControllerBase +{ + private readonly PostProvider _postProvider; + + public PostController(PostProvider postProvider) + { + _postProvider = postProvider; + } + + [HttpGet("items/{filter}/{postType}")] + public async Task> GetItemsAsync([FromRoute] PublishedStatus filter, [FromRoute] PostType postType) + { + return await _postProvider.GetAsync(filter, postType); + } + + [HttpGet("items/search/{term}")] + public async Task> GetSearchAsync([FromRoute] string term) + { + return await _postProvider.GetSearchAsync(term); + } + + [HttpGet("byslug/{slug}")] + public async Task GetPostBySlug(string slug) + { + return await _postProvider.GetEditorAsync(slug); + } + + [HttpPost("add")] + public async Task AddPostAsync([FromBody] PostEditorDto post) + { + var userId = User.FirstUserId(); + return await _postProvider.AddAsync(post, userId); + } + + [HttpPut("update")] + public async Task> UpdateAsync(PostEditorDto post) + { + var userId = User.FirstUserId(); + return await _postProvider.UpdateAsync(post, userId); + } + + [HttpPut("state/{id:int}")] + public async Task StateAsynct([FromRoute] int id, [FromBody] PostState state) + { + await _postProvider.StateAsynct(id, state); + } + + [HttpPut("state/{idsString}")] + public async Task StateAsynct([FromRoute] string idsString, [FromBody] PostState state) + { + var ids = idsString.Split(',').Select(int.Parse); + await _postProvider.StateAsynct(ids, state); + } + + [HttpDelete("{id:int}")] + public async Task DeleteAsync([FromRoute] int id) + { + await _postProvider.DeleteAsync(id); + } + + [HttpDelete("{idsString}")] + public async Task DeleteAsync([FromRoute] string idsString) + { + var ids = idsString.Split(',').Select(int.Parse); + await _postProvider.DeleteAsync(ids); + } +} diff --git a/src/Blogifier/Interfaces/StorageController.cs b/src/Blogifier/Interfaces/StorageController.cs new file mode 100644 index 000000000..70122ed8e --- /dev/null +++ b/src/Blogifier/Interfaces/StorageController.cs @@ -0,0 +1,79 @@ +using Blogifier.Blogs; +using Blogifier.Posts; +using Blogifier.Shared; +using Blogifier.Storages; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[Route("api/storage")] +[ApiController] +[Authorize] +public class StorageController : ControllerBase +{ + private readonly StorageProvider _storageProvider; + private readonly BlogManager _blogManager; + private readonly PostProvider _postProvider; + + public StorageController( + StorageProvider storageProvider, + BlogManager blogManager, + PostProvider postProvider) + { + _storageProvider = storageProvider; + _blogManager = blogManager; + _postProvider = postProvider; + } + + [HttpPut("exists")] + public async Task FileExists([FromBody] string path) + { + return (await Task.FromResult(_storageProvider.FileExistsAsync(path))) ? Ok() : BadRequest(); + } + + [HttpPost("upload/{uploadType}")] + public async Task Upload(IFormFile file, UploadType uploadType, int postId = 0) + { + var userId = User.FirstUserId(); + + + var path = $"{userId}/{DateTime.Now.Year}/{DateTime.Now.Month}"; + var fileName = $"data/{path}/{file.FileName}"; + + if (uploadType == UploadType.PostImage) + fileName = Url.Content("~/") + fileName; + + if (await _storageProvider.UploadFormFileAsync(file, path)) + { + var blog = await _blogManager.GetAsync(); + + switch (uploadType) + { + //case UploadType.Avatar: + // author.Avatar = fileName; + // return (await _authorProvider.Update(author)) ? new JsonResult(fileName) : BadRequest(); + case UploadType.AppLogo: + blog.Logo = fileName; + await _blogManager.SetAsync(blog); + return new JsonResult(fileName); + case UploadType.PostCover: + { + var post = await _postProvider.FirstAsync(postId); + post.Cover = fileName; + return new JsonResult(fileName); + } + case UploadType.PostImage: + return new JsonResult(fileName); + } + return Ok(); + } + else + { + return BadRequest(); + } + } +} diff --git a/src/Blogifier/Interfaces/SubscriberController.cs b/src/Blogifier/Interfaces/SubscriberController.cs new file mode 100644 index 000000000..921218722 --- /dev/null +++ b/src/Blogifier/Interfaces/SubscriberController.cs @@ -0,0 +1,40 @@ +using Blogifier.Newsletters; +using Blogifier.Shared; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[Route("api/subscriber")] +[ApiController] +public class SubscriberController : ControllerBase +{ + private readonly SubscriberProvider _subscriberProvider; + + public SubscriberController(SubscriberProvider subscriberProvider) + { + _subscriberProvider = subscriberProvider; + } + + [HttpGet("items")] + [Authorize] + public async Task> GetItemsAsync() + { + return await _subscriberProvider.GetItemsAsync(); + } + + [HttpDelete("{id:int}")] + [Authorize] + public async Task DeleteAsync([FromRoute] int id) + { + await _subscriberProvider.DeleteAsync(id); + } + + [HttpPost("apply")] + public async Task Apply([FromForm] SubscriberApplyDto subscriber) + { + await _subscriberProvider.ApplyAsync(subscriber); + } +} diff --git a/src/Blogifier/Interfaces/TokenController.cs b/src/Blogifier/Interfaces/TokenController.cs new file mode 100644 index 000000000..488c70ef5 --- /dev/null +++ b/src/Blogifier/Interfaces/TokenController.cs @@ -0,0 +1,16 @@ +using Blogifier.Identity; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Blogifier.Interfaces; + + +[Route("api/token")] +[ApiController] +public class TokenController : ControllerBase +{ + + [HttpGet("userinfo")] + [Authorize] + public BlogifierClaims? Get() => BlogifierClaims.Analysis(User); +} diff --git a/src/Blogifier/Interfaces/UserController.cs b/src/Blogifier/Interfaces/UserController.cs new file mode 100644 index 000000000..639bb5825 --- /dev/null +++ b/src/Blogifier/Interfaces/UserController.cs @@ -0,0 +1,78 @@ +using Blogifier.Identity; +using Blogifier.Shared; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Interfaces; + +[Route("api/user")] +[ApiController] +public class UserController : ControllerBase +{ + private readonly UserProvider _userProvider; + + public UserController(UserProvider userProvider) + { + _userProvider = userProvider; + } + + [HttpGet("items")] + public async Task> GetItemsAsync() + { + return await _userProvider.GetAsync(); + } + + [HttpGet("{id}")] + public async Task GetAsync([FromRoute] string id) + { + return await _userProvider.GetAsync(id); + } + + [HttpPut("{id?}")] + public async Task EditorAsync([FromRoute] string? id, [FromBody] UserEditorDto input, [FromServices] UserManager userManager) + { + if (string.IsNullOrEmpty(id)) + { + var user = new UserInfo(input.UserName) + { + NickName = input.NickName, + Email = input.Email, + Avatar = input.Avatar, + Bio = input.Bio, + Type = input.Type, + }; + var result = await userManager.CreateAsync(user, input.Password!); + if (!result.Succeeded) + { + var error = result.Errors.First(); + return Problem(detail: error.Description, title: error.Code); + } + } + else + { + var user = (await userManager.FindByIdAsync(id))!; + user.NickName = input.NickName; + user.Avatar = input.Avatar; + user.Bio = input.Bio; + user.Type = input.Type; + var result = await userManager.UpdateAsync(user); + if (result.Succeeded) + { + if (!string.IsNullOrEmpty(input.Password)) + { + var token = await userManager.GeneratePasswordResetTokenAsync(user); + result = await userManager.ResetPasswordAsync(user, token, input.Password); + if (result.Succeeded) return Ok(); + } + return Ok(); + } + var error = result.Errors.First(); + return Problem(detail: error.Description, title: error.Code); + } + + return Ok(); + } + +} diff --git a/src/Blogifier/Newsletters/EmailManager.cs b/src/Blogifier/Newsletters/EmailManager.cs new file mode 100644 index 000000000..6ec2a317e --- /dev/null +++ b/src/Blogifier/Newsletters/EmailManager.cs @@ -0,0 +1,147 @@ +using AutoMapper; +using Blogifier.Options; +using Blogifier.Posts; +using Blogifier.Shared; +using MailKit.Net.Smtp; +using MailKit.Security; +using Microsoft.Extensions.Logging; +using MimeKit; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Blogifier.Newsletters; + +public class EmailManager +{ + private readonly ILogger _logger; + private readonly IMapper _mapper; + private readonly MarkdigProvider _markdigProvider; + private readonly OptionProvider _optionProvider; + private readonly PostProvider _postProvider; + private readonly NewsletterProvider _newsletterProvider; + private readonly SubscriberProvider _subscriberProvider; + + public EmailManager( + ILogger logger, + IMapper mapper, + MarkdigProvider markdigProvider, + OptionProvider optionProvider, + PostProvider postProvider, + NewsletterProvider newsletterProvider, + SubscriberProvider subscriberProvider) + { + _logger = logger; + _mapper = mapper; + _markdigProvider = markdigProvider; + _optionProvider = optionProvider; + _postProvider = postProvider; + _newsletterProvider = newsletterProvider; + _subscriberProvider = subscriberProvider; + } + + public async Task SendNewsletter(int postId) + { + var newsletter = await _newsletterProvider.FirstOrDefaultByPostIdAsync(postId); + if (newsletter != null && newsletter.Success) return SendNewsletterState.NewsletterSuccess; + + var post = await _postProvider.GetAsync(postId); + if (post == null) return SendNewsletterState.NotPost; + + var subscribers = await _subscriberProvider.GetItemsAsync(); + if (!subscribers.Any()) return SendNewsletterState.NotSubscriber; + + var settings = await GetSettingsAsync(); + if (settings == null || settings.Enabled == false) return SendNewsletterState.NotMailEnabled; + + var subject = post.Title; + var content = _markdigProvider.ToHtml(post.Content); + + var sent = await Send(settings, subscribers, subject, content); + if (newsletter == null) + { + await _newsletterProvider.AddAsync(postId, sent); + } + else + { + await _newsletterProvider.UpdateAsync(newsletter.Id, sent); + } + return sent ? SendNewsletterState.OK : SendNewsletterState.SentError; + } + + public async Task GetSettingsAsync() + { + var key = BlogifierConstant.CacheKeys.BlogMailData; + var value = await _optionProvider.GetByValueAsync(key); + if (value != null) + { + var data = JsonSerializer.Deserialize(value); + return _mapper.Map(data); + } + return null; + } + + public async Task PutSettingsAsync(MailSettingDto input) + { + var key = BlogifierConstant.CacheKeys.BlogMailData; + var data = _mapper.Map(input); + var value = JsonSerializer.Serialize(data); + await _optionProvider.SetValue(key, value); + } + + + private async Task Send(MailSettingDto settings, IEnumerable subscribers, string subject, string content) + { + var client = GetClient(settings); + if (client == null) + return false; + + var bodyBuilder = new BodyBuilder + { + HtmlBody = content + }; + + foreach (var subscriber in subscribers) + { + try + { + var message = new MimeMessage + { + Subject = subject, + Body = bodyBuilder.ToMessageBody() + }; + message.From.Add(new MailboxAddress(settings.FromName, settings.FromEmail)); + message.To.Add(new MailboxAddress(settings.ToName, subscriber.Email)); + client.Send(message); + } + catch (Exception ex) + { + _logger.LogWarning("Error sending email to {Email}: {Message}", subscriber.Email, ex.Message); + } + } + client.Disconnect(true); + return await Task.FromResult(true); + } + + private SmtpClient GetClient(MailSettingDto settings) + { + try + { + var client = new SmtpClient + { + ServerCertificateValidationCallback = (s, c, h, e) => true + }; + client.Connect(settings.Host, settings.Port, SecureSocketOptions.Auto); + client.Authenticate(settings.UserEmail, settings.UserPassword); + return client; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error connecting to SMTP client"); + throw; + } + } + +} diff --git a/src/Blogifier/Newsletters/MailSettingData.cs b/src/Blogifier/Newsletters/MailSettingData.cs new file mode 100644 index 000000000..2815f2f24 --- /dev/null +++ b/src/Blogifier/Newsletters/MailSettingData.cs @@ -0,0 +1,13 @@ +namespace Blogifier.Newsletters; + +public class MailSettingData +{ + public bool Enabled { get; set; } + public string Host { get; set; } = default!; + public int Port { get; set; } + public string UserEmail { get; set; } = default!; + public string UserPassword { get; set; } = default!; + public string FromName { get; set; } = default!; + public string FromEmail { get; set; } = default!; + public string ToName { get; set; } = default!; +} diff --git a/src/Blogifier/Newsletters/Newsletter.cs b/src/Blogifier/Newsletters/Newsletter.cs new file mode 100644 index 000000000..6bf25cfa1 --- /dev/null +++ b/src/Blogifier/Newsletters/Newsletter.cs @@ -0,0 +1,17 @@ +using Blogifier.Data; +using Blogifier.Shared; +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blogifier.Newsletters; + +public class Newsletter : AppEntity +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public DateTime CreatedAt { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public DateTime UpdatedAt { get; set; } + public int PostId { get; set; } + public bool Success { get; set; } + public Post Post { get; set; } = default!; +} diff --git a/src/Blogifier/Newsletters/NewsletterProvider.cs b/src/Blogifier/Newsletters/NewsletterProvider.cs new file mode 100644 index 000000000..f450075fc --- /dev/null +++ b/src/Blogifier/Newsletters/NewsletterProvider.cs @@ -0,0 +1,54 @@ +using AutoMapper; +using Blogifier.Data; +using Blogifier.Shared; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Newsletters; + +public class NewsletterProvider : AppProvider +{ + private readonly IMapper _mapper; + + public NewsletterProvider(IMapper mapper, AppDbContext dbContext) : base(dbContext) + { + _mapper = mapper; + } + + public async Task> GetItemsAsync() + { + var query = _dbContext.Newsletters + .AsNoTracking() + .Include(n => n.Post) + .OrderByDescending(n => n.CreatedAt); + return await _mapper.ProjectTo(query).ToListAsync(); + } + + public async Task FirstOrDefaultByPostIdAsync(int postId) + { + var query = _dbContext.Newsletters + .Where(m => m.PostId == postId); + return await _mapper.ProjectTo(query).FirstOrDefaultAsync(); + } + + public async Task AddAsync(int postId, bool success) + { + var entry = new Newsletter + { + PostId = postId, + Success = success, + }; + _dbContext.Newsletters.Add(entry); + await _dbContext.SaveChangesAsync(); + } + + public async Task UpdateAsync(int id, bool success) + { + await _dbContext.Newsletters + .Where(m => m.Id == id) + .ExecuteUpdateAsync(setters => + setters.SetProperty(b => b.Success, success)); + } +} diff --git a/src/Blogifier/Newsletters/Subscriber.cs b/src/Blogifier/Newsletters/Subscriber.cs new file mode 100644 index 000000000..849e10a05 --- /dev/null +++ b/src/Blogifier/Newsletters/Subscriber.cs @@ -0,0 +1,23 @@ +using Blogifier.Data; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blogifier.Newsletters; + +public class Subscriber : AppEntity +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public DateTime CreatedAt { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public DateTime UpdatedAt { get; set; } + [EmailAddress] + [StringLength(160)] + public string Email { get; set; } = default!; + [StringLength(80)] + public string? Ip { get; set; } + [StringLength(120)] + public string? Country { get; set; } + [StringLength(120)] + public string? Region { get; set; } +} diff --git a/src/Blogifier/Newsletters/SubscriberProvider.cs b/src/Blogifier/Newsletters/SubscriberProvider.cs new file mode 100644 index 000000000..a7845300e --- /dev/null +++ b/src/Blogifier/Newsletters/SubscriberProvider.cs @@ -0,0 +1,40 @@ +using AutoMapper; +using Blogifier.Data; +using Blogifier.Shared; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Newsletters; + +public class SubscriberProvider : AppProvider +{ + private readonly IMapper _mapper; + public SubscriberProvider( + IMapper mapper, + AppDbContext dbContext) + : base(dbContext) + { + _mapper = mapper; + } + + public async Task> GetItemsAsync() + { + var query = _dbContext.Subscribers + .AsNoTracking() + .OrderByDescending(n => n.CreatedAt); + return await _mapper.ProjectTo(query).ToListAsync(); + } + + public async Task ApplyAsync(SubscriberApplyDto input) + { + if (await _dbContext.Subscribers.AnyAsync(m => m.Email == input.Email)) + { + var data = _mapper.Map(input); + _dbContext.Subscribers.Add(data); + await _dbContext.SaveChangesAsync(); + } + } + +} diff --git a/src/Blogifier/Options/OptionInfo.cs b/src/Blogifier/Options/OptionInfo.cs new file mode 100644 index 000000000..9b01294f8 --- /dev/null +++ b/src/Blogifier/Options/OptionInfo.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blogifier.Options; + +public class OptionInfo +{ + [Key] + public int Id { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public DateTime CreatedAt { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public DateTime UpdatedAt { get; set; } + [StringLength(256)] + public string Key { get; set; } = default!; + public string Value { get; set; } = default!; +} diff --git a/src/Blogifier/Options/OptionProvider.cs b/src/Blogifier/Options/OptionProvider.cs new file mode 100644 index 000000000..f4947ca2d --- /dev/null +++ b/src/Blogifier/Options/OptionProvider.cs @@ -0,0 +1,53 @@ +using Blogifier.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Logging; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Options; + +public class OptionProvider +{ + private readonly ILogger _logger; + private readonly AppDbContext _dbContext; + + public OptionProvider( + ILogger logger, + IDistributedCache distributedCache, + AppDbContext dbContext) + { + _logger = logger; + _dbContext = dbContext; + } + + public async Task AnyKeyAsync(string key) + { + return await _dbContext.Options.AnyAsync(m => m.Key == key); + } + + public async Task GetByValueAsync(string key) + { + return await _dbContext.Options + .AsNoTracking() + .Where(m => m.Key == key) + .Select(m => m.Value) + .FirstOrDefaultAsync(); + } + + public async Task SetValue(string key, string value) + { + var option = await _dbContext.Options + .Where(m => m.Key == key) + .FirstOrDefaultAsync(); + if (option == null) + { + _dbContext.Options.Add(new OptionInfo { Key = key, Value = value }); + } + else + { + option.Value = value; + } + await _dbContext.SaveChangesAsync(); + } +} diff --git a/src/Blogifier/Pages/Error.cshtml b/src/Blogifier/Pages/Error.cshtml deleted file mode 100644 index de6d71d2c..000000000 --- a/src/Blogifier/Pages/Error.cshtml +++ /dev/null @@ -1,42 +0,0 @@ -@page -@model Blogifier.Pages.ErrorModel - - - - - - - - Error - - - - - -
-
-

Error.

-

An error occurred while processing your request.

- - @if (Model.ShowRequestId) - { -

- Request ID: @Model.RequestId -

- } - -

Development Mode

-

- Swapping to the Development environment displays detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

-
-
- - - diff --git a/src/Blogifier/Pages/Error.cshtml.cs b/src/Blogifier/Pages/Error.cshtml.cs deleted file mode 100644 index de8091fd7..000000000 --- a/src/Blogifier/Pages/Error.cshtml.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace Blogifier.Pages -{ - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - [IgnoreAntiforgeryToken] - public class ErrorModel : PageModel - { - public string RequestId { get; set; } - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - private readonly ILogger _logger; - - public ErrorModel(ILogger logger) - { - _logger = logger; - } - - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } - } -} diff --git a/src/Blogifier/Posts/Category.cs b/src/Blogifier/Posts/Category.cs new file mode 100644 index 000000000..22561545e --- /dev/null +++ b/src/Blogifier/Posts/Category.cs @@ -0,0 +1,18 @@ +using Blogifier.Data; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blogifier.Shared; + +public class Category : AppEntity +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public DateTime CreatedAt { get; set; } + [StringLength(120)] + public string Content { get; set; } = default!; + [StringLength(255)] + public string? Description { get; set; } = default!; + public List? PostCategories { get; set; } +} diff --git a/src/Blogifier/Posts/CategoryProvider.cs b/src/Blogifier/Posts/CategoryProvider.cs new file mode 100644 index 000000000..195427656 --- /dev/null +++ b/src/Blogifier/Posts/CategoryProvider.cs @@ -0,0 +1,107 @@ +using Blogifier.Data; +using Blogifier.Shared; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Posts; + +public class CategoryProvider : AppProvider +{ + public CategoryProvider(AppDbContext dbContext) : base(dbContext) + { + } + + public async Task> GetItemsAsync() + { + return await _dbContext.Categories + .Include(pc => pc.PostCategories) + .GroupBy(m => new { m.Id, m.Content, m.Description }) + .Select(m => new CategoryItemDto + { + Id = m.Key.Id, + Category = m.Key.Content, + Description = m.Key.Description, + PostCount = m.Sum(p => p.PostCategories!.Count()) + }) + .AsNoTracking() + .ToListAsync(); + } + + public async Task> GetItemsExistPostAsync() + { + return await _dbContext.PostCategories + .Include(pc => pc.Category) + .GroupBy(m => new { m.Category.Id, m.Category.Content, m.Category.Description }) + .Select(m => new CategoryItemDto + { + Id = m.Key.Id, + Category = m.Key.Content, + Description = m.Key.Description, + PostCount = m.Count() + }) + .AsNoTracking() + .ToListAsync(); + } + + public async Task> SearchCategories(string term) + { + var cats = await GetItemsAsync(); + + if (term == "*") + return cats; + + return cats.Where(c => c.Category.ToLower().Contains(term.ToLower())).ToList(); + } + + public async Task GetCategory(int categoryId) + { + return await _dbContext.Categories + .AsNoTracking() + .Where(c => c.Id == categoryId) + .FirstAsync(); + } + + public async Task> GetPostCategories(int postId) + { + return await _dbContext.Posts + .AsNoTracking() + .Where(pc => pc.Id == postId) + .SelectMany(pc => pc.PostCategories!, (parent, child) => child.Category) + .ToListAsync(); + } + + public async Task SaveCategory(Category category) + { + var dbCategory = await _dbContext.Categories.Where(c => c.Id == category.Id).FirstOrDefaultAsync(); + if (dbCategory == null) + return false; + + dbCategory.Content = category.Content; + dbCategory.Description = category.Description; + return await _dbContext.SaveChangesAsync() > 0; + } + + public async Task SaveCategory(string tag) + { + var category = await _dbContext.Categories + .AsNoTracking() + .Where(c => c.Content == tag) + .FirstOrDefaultAsync(); + + if (category != null) + return category; + + category = new Category() + { + Content = tag, + CreatedAt = DateTime.UtcNow + }; + _dbContext.Categories.Add(category); + await _dbContext.SaveChangesAsync(); + + return category; + } +} diff --git a/src/Blogifier/Posts/ImportManager.cs b/src/Blogifier/Posts/ImportManager.cs new file mode 100644 index 000000000..19c6c5c26 --- /dev/null +++ b/src/Blogifier/Posts/ImportManager.cs @@ -0,0 +1,74 @@ +using Blogifier.Extensions; +using Blogifier.Identity; +using Blogifier.Shared; +using Blogifier.Storages; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Posts; + +public class ImportManager +{ + private readonly ILogger _logger; + private readonly UserProvider _userProvider; + private readonly MarkdigProvider _markdigProvider; + private readonly PostProvider _postProvider; + private readonly StorageProvider _storageProvider; + + public ImportManager( + ILogger logger, + UserProvider userProvider, + MarkdigProvider markdigProvider, + PostProvider postProvider, + StorageProvider storageProvider) + { + _logger = logger; + _userProvider = userProvider; + _markdigProvider = markdigProvider; + _postProvider = postProvider; + _storageProvider = storageProvider; + } + + public async Task> WriteAsync(ImportDto request, string webRoot, string userId) + { + var user = await _userProvider.FindByIdAsync(userId); + var titles = request.Posts.Select(m => m.Title); + var matchPosts = await _postProvider.MatchTitleAsync(titles); + + var posts = new List(); + + foreach (var post in request.Posts) + { + var postDb = matchPosts.FirstOrDefault(m => m.Title.Equals(post.Title, StringComparison.Ordinal)); + if (postDb != null) + { + posts.Add(postDb); + continue; + } + + var publishedAt = post.PublishedAt!.Value; + + if (post.Cover != null && !post.Cover.Equals(BlogifierConstant.DefaultCover, StringComparison.Ordinal)) + { + await _storageProvider.UploadFromWeb(webRoot, user.Id, post.Slug!, post.Cover, publishedAt); + } + + var importImagesContent = await _storageProvider.UploadImagesFoHtml(webRoot, user.Id, post.Slug!, publishedAt, post.Content); + var importFilesContent = await _storageProvider.UploadFilesFoHtml(webRoot, user.Id, post.Slug!, publishedAt, post.Content); + + var markdownContent = _markdigProvider.ToMarkdown(importFilesContent); + post.Content = markdownContent; + + var markdownDescription = _markdigProvider.ToMarkdown(post.Description); + post.Description = markdownDescription; + + post.State = PostState.Release; + posts.Add(post); + } + + return await _postProvider.AddAsync(posts, userId); + } +} diff --git a/src/Blogifier/Posts/ImportRssProvider.cs b/src/Blogifier/Posts/ImportRssProvider.cs new file mode 100644 index 000000000..e06f1d066 --- /dev/null +++ b/src/Blogifier/Posts/ImportRssProvider.cs @@ -0,0 +1,71 @@ +using Blogifier.Extensions; +using Blogifier.Shared; +using System.Collections.Generic; +using System.ServiceModel.Syndication; +using System.Xml; +using System.Xml.Linq; + +namespace Blogifier.Posts; + +public class ImportRssProvider +{ + public ImportDto Analysis(string feedUrl) + { + var xml = XmlReader.Create(feedUrl); + var feed = SyndicationFeed.Load(xml); + + var result = new ImportDto + { + BaseUrl = feed.Id, + Posts = new List(), + }; + + foreach (var item in feed.Items) + { + var content = ((TextSyndicationContent)item.Content).Text; + var post = new PostEditorDto + { + Slug = item.Id, + Title = item.Title.Text, + Description = GetDescription(item.Summary.Text), + Content = content, + Cover = BlogifierConstant.DefaultCover, + PublishedAt = item.PublishDate.DateTime, + PostType = PostType.Post, + }; + + if (item.ElementExtensions != null) + { + foreach (SyndicationElementExtension ext in item.ElementExtensions) + { + if (ext.GetObject().Name.LocalName == "summary") + post.Description = GetDescription(ext.GetObject().Value); + + if (ext.GetObject().Name.LocalName == "cover") + post.Cover = ext.GetObject().Value; + } + } + + if (item.Categories != null) + { + post.Categories ??= new List(); + foreach (var category in item.Categories) + { + post.Categories.Add(new CategoryDto + { + Content = category.Name + }); + } + }; + result.Posts.Add(post); + } + return result; + } + + static string GetDescription(string description) + { + description = description.StripHtml(); + if (description.Length > 450) description = description.Substring(0, 446) + "..."; + return description; + } +} diff --git a/src/Blogifier/Posts/MarkdigProvider.cs b/src/Blogifier/Posts/MarkdigProvider.cs new file mode 100644 index 000000000..a722d2a9c --- /dev/null +++ b/src/Blogifier/Posts/MarkdigProvider.cs @@ -0,0 +1,48 @@ +using Markdig; +using Microsoft.Extensions.Logging; +using ReverseMarkdown; + +namespace Blogifier.Posts; + +public class MarkdigProvider +{ + private readonly ILogger _logger; + private readonly MarkdownPipeline _markdownPipeline; + private readonly Converter _converter; + + public MarkdigProvider(ILogger logger) + { + _logger = logger; + _markdownPipeline = new MarkdownPipelineBuilder() + .UsePipeTables() + .UseAdvancedExtensions() + .Build(); + + var config = new Config + { + // generate GitHub flavoured markdown, supported for BR, PRE and table tags + GithubFlavored = true, + // will ignore all comments + RemoveComments = true, + // Include the unknown tag completely in the result (default as well) + UnknownTags = Config.UnknownTagsOption.Bypass, + // remove markdown output for links where appropriate + SmartHrefHandling = false + }; + _converter = new Converter(config); + } + + public string ToHtml(string markdown) + { + var html = Markdown.ToHtml(markdown, _markdownPipeline); + //_logger.LogDebug("ToHtml markdown:{markdown}, html:{html}", markdown, html); + return html; + } + + public string ToMarkdown(string html) + { + var markdown = _converter.Convert(html); + //_logger.LogDebug("ToMarkdown html:{html}, markdown:{markdown}", html, markdown); + return markdown; + } +} diff --git a/src/Blogifier/Posts/Post.cs b/src/Blogifier/Posts/Post.cs new file mode 100644 index 000000000..ac6560142 --- /dev/null +++ b/src/Blogifier/Posts/Post.cs @@ -0,0 +1,38 @@ +using Blogifier.Data; +using Blogifier.Identity; +using Blogifier.Storages; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blogifier.Shared; + +public class Post : AppEntity +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public DateTime CreatedAt { get; set; } + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public DateTime UpdatedAt { get; set; } + [StringLength(128)] + public string UserId { get; set; } = default!; + public UserInfo User { get; set; } = default!; + [Required] + [StringLength(160)] + public string Title { get; set; } = default!; + [Required] + [StringLength(160)] + public string Slug { get; set; } = default!; + [Required] + [StringLength(450)] + public string Description { get; set; } = default!; + public string Content { get; set; } = default!; + [StringLength(160)] + public string? Cover { get; set; } + public int Views { get; set; } + public DateTime? PublishedAt { get; set; } + public PostType PostType { get; set; } + public PostState State { get; set; } + public List? PostCategories { get; set; } + public List? StorageReferences { get; set; } +} diff --git a/src/Blogifier/Posts/PostCategory.cs b/src/Blogifier/Posts/PostCategory.cs new file mode 100644 index 000000000..9f28a9dc7 --- /dev/null +++ b/src/Blogifier/Posts/PostCategory.cs @@ -0,0 +1,9 @@ +namespace Blogifier.Shared; + +public class PostCategory +{ + public int PostId { get; set; } + public Post Post { get; set; } = default!; + public int CategoryId { get; set; } + public Category Category { get; set; } = default!; +} diff --git a/src/Blogifier/Posts/PostManager.cs b/src/Blogifier/Posts/PostManager.cs new file mode 100644 index 000000000..347acb21a --- /dev/null +++ b/src/Blogifier/Posts/PostManager.cs @@ -0,0 +1,34 @@ +using Blogifier.Shared; +using System.Linq; +using System.Threading.Tasks; + +namespace Blogifier.Posts; + +public class PostManager +{ + private readonly PostProvider _postProvider; + private readonly MarkdigProvider _markdigProvider; + + public PostManager( + PostProvider postProvider, + MarkdigProvider markdigProvider) + { + _postProvider = postProvider; + _markdigProvider = markdigProvider; + } + + public async Task GetToHtmlAsync(string slug) + { + var postSlug = await _postProvider.GetAsync(slug); + postSlug.Post.ContentHtml = _markdigProvider.ToHtml(postSlug.Post.Content); + postSlug.Post.DescriptionHtml = _markdigProvider.ToHtml(postSlug.Post.Description); + + foreach (var related in postSlug.Related) + { + var relatedDto = postSlug.Related.First(m => m.Id == related.Id); + relatedDto.DescriptionHtml = _markdigProvider.ToHtml(related.Description); + } + return postSlug; + } +} + diff --git a/src/Blogifier/Posts/PostProvider.cs b/src/Blogifier/Posts/PostProvider.cs new file mode 100644 index 000000000..9713c24d0 --- /dev/null +++ b/src/Blogifier/Posts/PostProvider.cs @@ -0,0 +1,385 @@ +using AutoMapper; +using Blogifier.Blogs; +using Blogifier.Data; +using Blogifier.Extensions; +using Blogifier.Helper; +using Blogifier.Shared; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Blogifier.Posts; + +public class PostProvider : AppProvider +{ + private readonly IMapper _mapper; + + public PostProvider(IMapper mapper, AppDbContext dbContext) : base(dbContext) + { + _mapper = mapper; + } + + public async Task FirstAsync(int id) + { + var query = _dbContext.Posts + .Where(p => p.Id == id); + return await _mapper.ProjectTo(query).FirstAsync(); + } + + public async Task GetAsync(int id) + { + var query = _dbContext.Posts + .AsNoTracking() + .Where(p => p.Id == id); + return await _mapper.ProjectTo(query).FirstAsync(); + } + + public async Task> GetAsync() + { + var query = _dbContext.Posts + .AsNoTracking() + .Include(pc => pc.User) + .OrderByDescending(m => m.CreatedAt); + return await _mapper.ProjectTo(query).ToListAsync(); + } + + public async Task GetAsync(string slug) + { + var postQuery = _dbContext.Posts + .Include(m => m.User) + .Where(p => p.Slug == slug); + var post = await _mapper.ProjectTo(postQuery).FirstAsync(); + post.Views++; + await _dbContext.SaveChangesAsync(); + + var olderQuery = _dbContext.Posts + .AsNoTracking() + .Where(m => m.State >= PostState.Release && m.PublishedAt < post.PublishedAt) + .OrderByDescending(p => p.PublishedAt); + + var older = await _mapper.ProjectTo(olderQuery).FirstOrDefaultAsync(); + + var newerQuery = _dbContext.Posts + .AsNoTracking() + .Where(m => m.State >= PostState.Release && m.PublishedAt > post.PublishedAt) + .OrderBy(p => p.PublishedAt); + + var newer = await _mapper.ProjectTo(newerQuery).FirstOrDefaultAsync(); + + var relatedQuery = _dbContext.Posts + .AsNoTracking() + .Include(m => m.User) + .Where(m => m.State == PostState.Featured && m.Id != post.Id); + + if (older != null) relatedQuery = relatedQuery.Where(m => m.Id != older.Id); + if (newer != null) relatedQuery = relatedQuery.Where(m => m.Id != newer.Id); + relatedQuery = relatedQuery.OrderByDescending(p => p.PublishedAt).Take(3); + var related = await _mapper.ProjectTo(relatedQuery).ToListAsync(); + + return new PostSlugDto { Post = post, Older = older, Newer = newer, Related = related }; + } + + public async Task GetPostsAsync(int page, int pageSize) + { + var skip = (page - 1) * pageSize; + var query = _dbContext.Posts + .AsNoTracking() + .Include(pc => pc.User) + .Where(m => m.PostType == PostType.Post && m.State >= PostState.Release); + + var total = await query.CountAsync(); + query = query.OrderByDescending(m => m.CreatedAt).Skip(skip).Take(pageSize); + var items = await _mapper.ProjectTo(query).ToListAsync(); + return new PostPagerDto(items, total, page, pageSize); + } + + public async Task GetEditorAsync(string slug) + { + var query = _dbContext.Posts + .AsNoTracking() + .Include(m => m.PostCategories)! + .ThenInclude(m => m.Category) + .Include(m => m.StorageReferences!.Where(s => s.Type == StorageReferenceType.Post)) + .AsSingleQuery() + .Where(p => p.Slug == slug); + return await _mapper.ProjectTo(query).FirstAsync(); + } + + public async Task> MatchTitleAsync(IEnumerable titles) + { + var query = _dbContext.Posts + .AsNoTracking() + .Include(m => m.PostCategories)! + .ThenInclude(m => m.Category) + .Where(p => titles.Contains(p.Title)); + return await _mapper.ProjectTo(query).ToListAsync(); + } + + public async Task> GetAsync(PublishedStatus filter, PostType postType) + { + var query = _dbContext.Posts + .AsNoTracking() + .Where(p => p.PostType == postType); + + query = filter switch + { + PublishedStatus.Featured | + PublishedStatus.Published => query.Where(p => p.State >= PostState.Release).OrderByDescending(p => p.PublishedAt), + PublishedStatus.Drafts => query.Where(p => p.State == PostState.Draft).OrderByDescending(p => p.Id), + _ => query.OrderByDescending(p => p.Id), + }; + + return await _mapper.ProjectTo(query).ToListAsync(); + } + + public async Task GetSearchAsync(string term, int page, int pageSize) + { + var query = _dbContext.Posts + .AsNoTracking() + .Include(pc => pc.User) + .Include(pc => pc.PostCategories) + .Where(m => m.Title.Contains(term) || m.Description.Contains(term) || m.Content.Contains(term)); + + + var posts = await _mapper.ProjectTo(query).ToListAsync(); + + var postsSearch = new List(); + var termList = term.ToLower().Split(' ').ToList(); + + foreach (var post in posts) + { + var rank = 0; + var hits = 0; + + foreach (var termItem in termList) + { + if (termItem.Length < 4 && rank > 0) continue; + + if (post.Categories != null) + { + foreach (var category in post.Categories) + { + if (category.Content.ToLower() == termItem) rank += 10; + } + } + if (post.Title.ToLower().Contains(termItem)) + { + hits = Regex.Matches(post.Title.ToLower(), termItem).Count; + rank += hits * 10; + } + if (post.Description.ToLower().Contains(termItem)) + { + hits = Regex.Matches(post.Description.ToLower(), termItem).Count; + rank += hits * 3; + } + //if (post.Content.ToLower().Contains(termItem)) + //{ + // rank += Regex.Matches(post.Content.ToLower(), termItem).Count; + //} + } + + if (rank > 0) + { + postsSearch.Add(new PostSearch(post, rank)); + } + } + + var total = postsSearch.Count; + var skip = page * pageSize - pageSize; + var items = postsSearch + .OrderByDescending(r => r.Rank) + .Skip(skip) + .Take(pageSize) + .Select(m => m.Post) + .ToList(); + + return new PostPagerDto(items, total, page, pageSize); + } + + public async Task GetByCategoryAsync(string category, int page, int pageSize) + { + var skip = (page - 1) * pageSize; + var query = _dbContext.Categories + .AsNoTracking() + .Include(pc => pc.PostCategories)! + .ThenInclude(m => m.Post) + .ThenInclude(m => m.User) + .Where(m => m.Content.Contains(category)) + .SelectMany(pc => pc.PostCategories!, (parent, child) => child.Post); + var total = await query.CountAsync(); + query = query.Skip(skip).Take(pageSize); + var items = await _mapper.ProjectTo(query).ToListAsync(); + return new PostPagerDto(items, total, page, pageSize); + } + + public async Task> GetSearchAsync(string term) + { + var query = _dbContext.Posts.AsNoTracking(); + if ("*".Equals(term, StringComparison.Ordinal)) + query = query.Where(p => p.Title.ToLower().Contains(term.ToLower())); + return await _mapper.ProjectTo(query).ToListAsync(); + } + + public Task StateAsynct(int id, PostState state) + { + var query = _dbContext.Posts + .Where(p => p.Id == id); + return StateInternalAsynct(query, state); + } + + public Task StateAsynct(IEnumerable ids, PostState state) + { + var query = _dbContext.Posts + .Where(p => ids.Contains(p.Id)); + return StateInternalAsynct(query, state); + } + + public async Task StateInternalAsynct(IQueryable query, PostState state) + { + await query.ExecuteUpdateAsync(setters => + setters.SetProperty(b => b.State, state) + .SetProperty(b => b.PublishedAt, b => GetPublishedAt(b.PublishedAt, state))); + } + + public async Task AddAsync(PostEditorDto postInput, string userId) + { + var post = await AddInternalAsync(postInput, userId); + await _dbContext.SaveChangesAsync(); + return _mapper.Map(post); + } + + private async Task AddInternalAsync(PostEditorDto postInput, string userId) + { + var slug = await GetSlugFromTitle(postInput.Title); + var postCategories = await CheckPostCategories(postInput.Categories); + var contentFiltr = StringHelper.RemoveImgTags(StringHelper.RemoveScriptTags(postInput.Content)); + var descriptionFiltr = StringHelper.RemoveImgTags(StringHelper.RemoveScriptTags(postInput.Description)); + var publishedAt = GetPublishedAt(postInput.PublishedAt, postInput.State); + var post = new Post + { + UserId = userId, + Title = postInput.Title, + Slug = slug, + Content = contentFiltr, + Description = descriptionFiltr, + Cover = postInput.Cover, + PostType = postInput.PostType, + State = postInput.State, + PublishedAt = publishedAt, + PostCategories = postCategories, + }; + _dbContext.Posts.Add(post); + return post; + } + + private static DateTime? GetPublishedAt(DateTime? inputPublishedAt, PostState inputState) + { + if (inputState >= PostState.Release) + { + if (!inputPublishedAt.HasValue) + { + return DateTime.UtcNow; + } + return inputPublishedAt; + } + else + { + return null; + } + } + + public async Task> AddAsync(IEnumerable posts, string userId) + { + var postsInput = new List(); + foreach (var post in posts) + { + var postInput = await AddInternalAsync(post, userId); + postsInput.Add(postInput); + } + await _dbContext.SaveChangesAsync(); + return _mapper.Map>(postsInput); + } + + public async Task UpdateAsync(PostEditorDto postInput, string userId) + { + var post = await _dbContext.Posts + .Include(m => m.PostCategories)! + .ThenInclude(m => m.Category) + .FirstAsync(m => m.Id == postInput.Id); + + if (post.UserId != userId) throw new BlogNotIitializeException(); + var postCategories = await CheckPostCategories(postInput.Categories, post.PostCategories); + + post.Slug = postInput.Slug!; + post.Title = postInput.Title; + var contentFiltr = StringHelper.RemoveImgTags(StringHelper.RemoveScriptTags(postInput.Content)); + var descriptionFiltr = StringHelper.RemoveImgTags(StringHelper.RemoveScriptTags(postInput.Description)); + post.Description = descriptionFiltr; + post.Content = contentFiltr; + post.Cover = postInput.Cover; + post.PostCategories = postCategories; + post.PublishedAt = GetPublishedAt(postInput.PublishedAt, postInput.State); + post.State = postInput.State; + _dbContext.Update(post); + await _dbContext.SaveChangesAsync(); + return _mapper.Map(post); + } + + private async Task GetSlugFromTitle(string title) + { + var slug = title.ToSlug(); + var i = 1; + var slugOriginal = slug; + while (true) + { + if (!await _dbContext.Posts.Where(p => p.Slug == slug).AnyAsync()) return slug; + i++; + if (i >= 100) throw new BlogNotIitializeException(); + slug = $"{slugOriginal}{i}"; + } + } + + private async Task?> CheckPostCategories(List? input, List? original = null) + { + if (input == null || !input.Any()) return null; + + // 去重 + var categories = input.GroupBy(d => new { d.Content }).Select(m => m.Key.Content).ToList(); + + if (original == null) + { + original = new List(); + } + else + { + original = original.Where(p => + { + var item = categories.FirstOrDefault(m => p.Category.Content.Equals(m, StringComparison.Ordinal)); + if (item != null) + { + categories.Remove(item); + return true; + } + return false; + }).ToList(); + } + + if (categories.Any()) + { + var categoriesDb = await _dbContext.Categories + .Where(m => categories.Contains(m.Content)) + .ToListAsync(); + + foreach (var item in categories) + { + var categoryDb = categoriesDb.FirstOrDefault(m => item.Equals(m.Content, StringComparison.Ordinal)); + original.Add(new PostCategory { Category = categoryDb ?? new Category { Content = item } }); + } + } + return original; + } + +} diff --git a/src/Blogifier/Posts/PostSearch.cs b/src/Blogifier/Posts/PostSearch.cs new file mode 100644 index 000000000..e4e605150 --- /dev/null +++ b/src/Blogifier/Posts/PostSearch.cs @@ -0,0 +1,14 @@ +using Blogifier.Shared; + +namespace Blogifier.Posts; + +public class PostSearch +{ + public PostSearch(PostItemDto post, int rank) + { + Post = post; + Rank = rank; + } + public PostItemDto Post { get; set; } + public int Rank { get; set; } +} diff --git a/src/Blogifier/Profiles/BlogProfile.cs b/src/Blogifier/Profiles/BlogProfile.cs new file mode 100644 index 000000000..3239ef3e6 --- /dev/null +++ b/src/Blogifier/Profiles/BlogProfile.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using Blogifier.Blogs; +using Blogifier.Shared; + +namespace Blogifier.Profiles; + +public class BlogProfile : Profile +{ + public BlogProfile() + { + CreateMap(); + CreateMap().ReverseMap(); + } +} diff --git a/src/Blogifier/Profiles/CategoryProfile.cs b/src/Blogifier/Profiles/CategoryProfile.cs new file mode 100644 index 000000000..64fa25586 --- /dev/null +++ b/src/Blogifier/Profiles/CategoryProfile.cs @@ -0,0 +1,15 @@ +using AutoMapper; +using Blogifier.Shared; + +namespace Blogifier.Profiles; + +public class CategoryProfile : Profile +{ + public CategoryProfile() + { + CreateMap().ReverseMap(); + CreateMap() + .IncludeMembers(m => m.Category) + .ReverseMap(); + } +} diff --git a/src/Blogifier/Profiles/MailSettingProfile.cs b/src/Blogifier/Profiles/MailSettingProfile.cs new file mode 100644 index 000000000..d97a827b9 --- /dev/null +++ b/src/Blogifier/Profiles/MailSettingProfile.cs @@ -0,0 +1,13 @@ +using AutoMapper; +using Blogifier.Newsletters; +using Blogifier.Shared; + +namespace Blogifier.Profiles; + +public class MailSettingProfile : Profile +{ + public MailSettingProfile() + { + CreateMap().ReverseMap(); + } +} diff --git a/src/Blogifier/Profiles/NewsletterProfile.cs b/src/Blogifier/Profiles/NewsletterProfile.cs new file mode 100644 index 000000000..29f62fbbd --- /dev/null +++ b/src/Blogifier/Profiles/NewsletterProfile.cs @@ -0,0 +1,13 @@ +using AutoMapper; +using Blogifier.Newsletters; +using Blogifier.Shared; + +namespace Blogifier.Profiles; + +public class NewsletterProfile : Profile +{ + public NewsletterProfile() + { + CreateMap(); + } +} diff --git a/src/Blogifier/Profiles/PostProfile.cs b/src/Blogifier/Profiles/PostProfile.cs new file mode 100644 index 000000000..58bac84da --- /dev/null +++ b/src/Blogifier/Profiles/PostProfile.cs @@ -0,0 +1,21 @@ +using AutoMapper; +using Blogifier.Shared; + +namespace Blogifier.Profiles; + +public class PostProfile : Profile +{ + public PostProfile() + { + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap() + .ForMember(d => d.Categories, opt => opt.MapFrom(src => src.PostCategories)) + .ForMember(d => d.Storages, opt => opt.MapFrom(src => src.StorageReferences)) + .ReverseMap() + .ForMember(d => d.PostCategories, opt => opt.MapFrom(src => src.Categories)) + .ForMember(d => d.StorageReferences, opt => opt.MapFrom(src => src.Storages)); + } +} diff --git a/src/Blogifier/Profiles/StorageProfile.cs b/src/Blogifier/Profiles/StorageProfile.cs new file mode 100644 index 000000000..aded18903 --- /dev/null +++ b/src/Blogifier/Profiles/StorageProfile.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using Blogifier.Shared; +using Blogifier.Storages; + +namespace Blogifier.Profiles; + +public class StorageProfile : Profile +{ + public StorageProfile() + { + CreateMap().ReverseMap(); + CreateMap() + .IncludeMembers(m => m.Storage) + .ReverseMap(); + } +} diff --git a/src/Blogifier/Profiles/SubscriberProfile.cs b/src/Blogifier/Profiles/SubscriberProfile.cs new file mode 100644 index 000000000..40cb10e72 --- /dev/null +++ b/src/Blogifier/Profiles/SubscriberProfile.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using Blogifier.Newsletters; +using Blogifier.Shared; + +namespace Blogifier.Profiles; + +public class SubscriberProfile : Profile +{ + public SubscriberProfile() + { + CreateMap(); + CreateMap(); + } +} diff --git a/src/Blogifier/Profiles/UserProfile.cs b/src/Blogifier/Profiles/UserProfile.cs new file mode 100644 index 000000000..ab9a2893a --- /dev/null +++ b/src/Blogifier/Profiles/UserProfile.cs @@ -0,0 +1,15 @@ +using AutoMapper; +using Blogifier.Identity; +using Blogifier.Shared; + +namespace Blogifier.Profiles; + +public class UserProfile : Profile +{ + public UserProfile() + { + CreateMap(); + CreateMap().ReverseMap(); + CreateMap(); + } +} diff --git a/src/Blogifier/Program.cs b/src/Blogifier/Program.cs index fdd7af2a4..07a22d1e5 100644 --- a/src/Blogifier/Program.cs +++ b/src/Blogifier/Program.cs @@ -1,43 +1,174 @@ -using Blogifier.Core.Data; -using Microsoft.AspNetCore.Hosting; +using Blogifier; +using Blogifier.Blogs; +using Blogifier.Data; +using Blogifier.Identity; +using Blogifier.Newsletters; +using Blogifier.Options; +using Blogifier.Posts; +using Blogifier.Shared.Resources; +using Blogifier.Storages; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; +using Serilog; +using System; using System.IO; using System.Linq; -namespace Blogifier -{ - public class Program - { - public static void Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); - - using (var scope = host.Services.CreateScope()) - { - var services = scope.ServiceProvider; - var dbContext = services.GetRequiredService(); - - try - { - if (dbContext.Database.GetPendingMigrations().Any()) - dbContext.Database.Migrate(); - } - catch { } - } - - host.Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseStartup(); - }); - } +var builder = WebApplication.CreateBuilder(args); +builder.Host.UseSerilog((context, builder) => builder.ReadFrom.Configuration(context.Configuration).Enrich.FromLogContext()); +var redis = builder.Configuration.GetSection("Blogifier:Redis").Value; +if (redis == null) builder.Services.AddDistributedMemoryCache(); +else builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = redis; options.InstanceName = "blogifier:"; }); + +builder.Services.AddHttpClient(); +builder.Services.AddLocalization(); +builder.Services.AddScoped(); +builder.Services.AddIdentityCore(options => +{ + options.User.RequireUniqueEmail = true; + options.Password.RequireUppercase = false; + options.Password.RequireNonAlphanumeric = false; + options.ClaimsIdentity.UserIdClaimType = BlogifierClaimTypes.UserId; + options.ClaimsIdentity.UserNameClaimType = BlogifierClaimTypes.UserName; + options.ClaimsIdentity.EmailClaimType = BlogifierClaimTypes.Email; + options.ClaimsIdentity.SecurityStampClaimType = BlogifierClaimTypes.SecurityStamp; +}).AddUserManager() + .AddSignInManager() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders() + .AddClaimsPrincipalFactory(); + +builder.Services.ConfigureApplicationCookie(options => +{ + options.AccessDeniedPath = "/account/accessdenied"; + options.LoginPath = "/account/login"; +}); + +builder.Services.AddAuthentication(IdentityConstants.ApplicationScheme).AddIdentityCookies(); + +builder.Services.AddAuthorization(); +builder.Services.AddCors(o => o.AddPolicy(BlogifierConstant.PolicyCorsName, + builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader())); + +var section = builder.Configuration.GetSection("Blogifier"); +var conn = section.GetValue("ConnString"); +if (section.GetValue("DbProvider") == "SQLite") + builder.Services.AddDbContext(o => o.UseSqlite(conn)); +else if (section.GetValue("DbProvider") == "SqlServer") + builder.Services.AddDbContext(o => o.UseSqlServer(conn)); +else if (section.GetValue("DbProvider") == "Postgres") + builder.Services.AddDbContext(o => o.UseNpgsql(conn)); +else if (section.GetValue("DbProvider") == "MySql") + builder.Services.AddDbContext(o => o.UseMySql(conn, ServerVersion.AutoDetect(conn))); + +if (builder.Environment.IsDevelopment()) + builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +builder.Services.Configure(options => +{ + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + options.KnownNetworks.Clear(); + options.KnownProxies.Clear(); +}); + +builder.Services.AddRouting(options => options.LowercaseUrls = true); + +builder.Services.AddResponseCaching(); +builder.Services.AddOutputCache(options => +{ + options.AddPolicy(BlogifierConstant.OutputCacheExpire1, builder => builder.Expire(TimeSpan.FromMinutes(15))); +}); + +builder.Services.AddControllersWithViews() + .AddDataAnnotationsLocalization(options => options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(Resource))); + +builder.Services.AddRazorPages().AddViewLocalization(); + +builder.Services.AddAutoMapper(typeof(Program)); + +var app = builder.Build(); + +using (var scope = app.Services.CreateScope()) +{ + var dbContext = scope.ServiceProvider.GetRequiredService(); + if (dbContext.Database.GetPendingMigrations().Any()) + { + await dbContext.Database.MigrateAsync(); + } +} + +app.UseSerilogRequestLogging(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); + app.UseWebAssemblyDebugging(); +} +else +{ + app.UseExceptionHandler("/404"); } + +app.UseForwardedHeaders(); +app.UseBlazorFrameworkFiles(); +app.UseStaticFiles(); +var fileProviderRoot = Path.Combine(app.Environment.ContentRootPath, "App_Data/public"); +if (!Directory.Exists(fileProviderRoot)) Directory.CreateDirectory(fileProviderRoot); +app.UseStaticFiles(new StaticFileOptions +{ + FileProvider = new PhysicalFileProvider(fileProviderRoot), + RequestPath = "/data" +}); +app.UseCookiePolicy(); +app.UseRouting(); + +var supportedCultures = new[] { + "zh-CN", + "zh-TW", + "el-GR", + "es", + "fa", + "pt-BR", + "ru", + "sv-SE", + "ur-PK" +}; +app.UseRequestLocalization(new RequestLocalizationOptions() + .AddSupportedCultures(supportedCultures) + .AddSupportedUICultures(supportedCultures)); + +app.UseCors(BlogifierConstant.PolicyCorsName); +app.UseAuthentication(); +app.UseAuthorization(); +app.UseResponseCaching(); +app.UseOutputCache(); +app.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); +app.MapRazorPages(); +app.MapFallbackToFile("admin/{*path:nonfile}", "index.html"); + +await app.RunAsync(); diff --git a/src/Blogifier/Properties/launchSettings.json b/src/Blogifier/Properties/launchSettings.json index b7852472e..94ad82cb7 100644 --- a/src/Blogifier/Properties/launchSettings.json +++ b/src/Blogifier/Properties/launchSettings.json @@ -1,30 +1,29 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:3169", - "sslPort": 0 - } + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "Blogifier": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" }, - "Blogifier": { - "commandName": "Project", - "dotnetRunMessages": "true", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } + "dotnetRunMessages": "true", + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5000" + } + }, + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:3169", + "sslPort": 0 } } +} diff --git a/src/Blogifier/Startup.cs b/src/Blogifier/Startup.cs deleted file mode 100644 index 9ed638358..000000000 --- a/src/Blogifier/Startup.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Blogifier.Core.Extensions; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Serilog; - -namespace Blogifier -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - Log.Logger = new LoggerConfiguration() - .Enrich.FromLogContext() - .WriteTo.File("Logs/log-.txt", rollingInterval: RollingInterval.Day) - .CreateLogger(); - - Log.Warning("Application start"); - } - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - Log.Warning("Start configure services"); - - services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; }); - - services.AddAuthentication(options => - { - options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; - }).AddCookie(); - - services.AddCors(o => o.AddPolicy("BlogifierPolicy", builder => - { - builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); - })); - - services.AddBlogDatabase(Configuration); - - services.AddBlogProviders(); - - - services.AddControllersWithViews(); - services.AddRazorPages(); - - Log.Warning("Done configure services"); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseWebAssemblyDebugging(); - } - else - { - app.UseExceptionHandler("/Error"); - } - - app.UseBlazorFrameworkFiles(); - app.UseStaticFiles(); - app.UseCookiePolicy(); - - app.UseRouting(); - app.UseCors("BlogifierPolicy"); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}" - ); - endpoints.MapRazorPages(); - endpoints.MapFallbackToFile("admin/{*path:nonfile}", "index.html"); - endpoints.MapFallbackToFile("account/{*path:nonfile}", "index.html"); - }); - } - } -} diff --git a/src/Blogifier/Storages/MinioProvider.cs b/src/Blogifier/Storages/MinioProvider.cs new file mode 100644 index 000000000..b1ecb97a6 --- /dev/null +++ b/src/Blogifier/Storages/MinioProvider.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Minio; +using Minio.DataModel; +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Blogifier.Storages; + +public class MinioProvider : IDisposable +{ + private readonly ILogger _logger; + private readonly string _bucketName = default!; + private readonly MinioClient _minioClient = default!; + + public MinioProvider(ILogger logger, IHttpClientFactory httpClientFactory, IConfiguration configuration) + { + _logger = logger; + var section = configuration.GetSection("Blogifier:Minio"); + + if (section == null) + { + _logger.LogWarning("Minio 配置信息不存在未初始化 MinioProvider."); + return; + } + _bucketName = section.GetValue("BucketName")!; + _minioClient = new MinioClient() + .WithEndpoint(section.GetValue("Endpoint")!, section.GetValue("Port")) + .WithRegion(section.GetValue("Region")!) + .WithCredentials(section.GetValue("AccessKey")!, section.GetValue("SecretKey")!) + .WithHttpClient(httpClientFactory.CreateClient()) + .Build(); + } + + public async Task GetObjectAsync(string objectName, Func callback) + { + var args = new GetObjectArgs().WithBucket(_bucketName).WithObject(objectName).WithCallbackStream(callback); + return await _minioClient.GetObjectAsync(args).ConfigureAwait(false); + } + + private bool _disposedValue; + + ~MinioProvider() => Dispose(false); + + // Public implementation of Dispose pattern callable by consumers. + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + // Protected implementation of Dispose pattern. + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _minioClient.Dispose(); + } + _disposedValue = true; + } + } +} diff --git a/src/Blogifier/Storages/Storage.cs b/src/Blogifier/Storages/Storage.cs new file mode 100644 index 000000000..e36f8a76d --- /dev/null +++ b/src/Blogifier/Storages/Storage.cs @@ -0,0 +1,31 @@ +using Blogifier.Identity; +using Blogifier.Shared; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blogifier.Storages; + +public class Storage +{ + [Key] + public int Id { get; set; } + public string UserId { get; set; } = default!; + public UserInfo User { get; set; } = default!; + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public DateTime CreatedAt { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletedAt { get; set; } + [StringLength(2048)] + public string Slug { get; set; } = default!; + [StringLength(256)] + public string Name { get; set; } = default!; + [StringLength(2048)] + public string Path { get; set; } = default!; + public long Length { get; set; } + [StringLength(128)] + public string ContentType { get; set; } = default!; + public StorageType Type { get; set; } + public List? StorageReferences { get; set; } +} diff --git a/src/Blogifier/Storages/StorageProvider.cs b/src/Blogifier/Storages/StorageProvider.cs new file mode 100644 index 000000000..fd3afe94a --- /dev/null +++ b/src/Blogifier/Storages/StorageProvider.cs @@ -0,0 +1,397 @@ +using Blogifier.Data; +using Blogifier.Extensions; +using Blogifier.Helper; +using Blogifier.Shared; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Blogifier.Storages; + +public class StorageProvider +{ + private readonly ILogger _logger; + private readonly AppDbContext _dbContext; + private readonly IHttpClientFactory _httpClientFactory; + private readonly MinioProvider _minioProvider; + private readonly string _publicStorageRoot; + private readonly string _slash = Path.DirectorySeparatorChar.ToString(); + private readonly IConfiguration _configuration; + + public StorageProvider( + ILogger logger, + AppDbContext dbContext, + IHttpClientFactory httpClientFactory, + MinioProvider minioProvider, + IConfiguration configuration) + { + _logger = logger; + _dbContext = dbContext; + _httpClientFactory = httpClientFactory; + _minioProvider = minioProvider; + _configuration = configuration; + _publicStorageRoot = Path.Combine(ContentRoot, "App_Data", "public"); + } + + /// + /// 根据存储路径获取文件 + /// + public async Task GetAsync(string storagePath, Func callback) + { + _logger.LogInformation("Storage Url:{storagePath}", storagePath); + var storage = await _dbContext.Storages.FirstOrDefaultAsync(m => m.Path == storagePath); + if (storage == null) return null; + var objectStat = await _minioProvider.GetObjectAsync(storagePath, callback); + if (objectStat == null) return null; + storage.ContentType = objectStat.ContentType; + storage.Length = objectStat.Size; + return storage; + } + + public bool FileExistsAsync(string path) + { + var absolutePath = Path.Combine(ContentRoot, path); + _logger.LogInformation("File exists: {absolutePath}", absolutePath); + return File.Exists(absolutePath); + } + + public async Task> GetThemesAsync() + { + var themes = new List(); + var themesDirectory = Path.Combine(ContentRoot, $"Views{_slash}Themes"); + foreach (string dir in Directory.GetDirectories(themesDirectory)) + { + themes.Add(Path.GetFileName(dir)); + } + return await Task.FromResult(themes); + } + + public async Task GetThemeSettingsAsync(string theme) + { + var settings = new ThemeSettings(); + var fileName = Path.Combine(ContentRoot, $"wwwroot{_slash}themes{_slash}{theme.ToLower()}{_slash}settings.json"); + if (File.Exists(fileName)) + { + try + { + string jsonString = File.ReadAllText(fileName); + settings = JsonSerializer.Deserialize(jsonString); + } + catch (Exception ex) + { + _logger.LogError("Error reading theme settings: {Message}", ex.Message); + return null; + } + } + + return await Task.FromResult(settings); + } + + public async Task SaveThemeSettingsAsync(string theme, ThemeSettings settings) + { + var fileName = Path.Combine(ContentRoot, $"wwwroot{_slash}themes{_slash}{theme.ToLower()}{_slash}settings.json"); + try + { + if (File.Exists(fileName)) + File.Delete(fileName); + + var options = new JsonSerializerOptions { WriteIndented = true, PropertyNameCaseInsensitive = true }; + + string jsonString = JsonSerializer.Serialize(settings, options); + + using FileStream createStream = File.Create(fileName); + await JsonSerializer.SerializeAsync(createStream, settings, options); + } + catch (Exception ex) + { + _logger.LogError("Error writing theme settings: {Message}", ex.Message); + return false; + } + return true; + } + + public async Task UploadFormFileAsync(IFormFile file, string path = "") + { + path = path.Replace("/", _slash); + VerifyPath(_publicStorageRoot, path); + + var fileName = GetFileName(file.FileName); + + if (InvalidFileName(fileName)) + { + _logger.LogError("Invalid file name: {fileName}", fileName); + return false; + } + + var filePath = string.IsNullOrEmpty(path) ? + Path.Combine(_publicStorageRoot, fileName) : + Path.Combine(_publicStorageRoot, path + _slash + fileName); + + _logger.LogInformation("Storage root: {_publicStorageRoot}", _publicStorageRoot); + _logger.LogInformation("Uploading file: {filePath}", filePath); + try + { + using var fileStream = new FileStream(filePath, FileMode.Create); + await file.CopyToAsync(fileStream); + _logger.LogInformation("Uploaded file: {filePath}", filePath); + } + catch (Exception ex) + { + _logger.LogInformation("Error uploading file: {Message}", ex.Message); + } + + return true; + } + + public async Task UploadFromWeb(string root, string userid, string baseUrl, string requestUri, DateTime createdAt) + { + var path = $"{userid}/{createdAt.Year}/{createdAt.Month}"; + var slash = Path.DirectorySeparatorChar.ToString(); + path = path.Replace("/", slash); + VerifyPath(_publicStorageRoot, path); + var fileName = TitleFromUri(requestUri); + var filePath = Path.Combine(_publicStorageRoot, path + _slash + fileName); + if (!File.Exists(filePath)) + { + var client = _httpClientFactory.CreateClient(); + client.BaseAddress = new Uri(baseUrl); + var response = await client.GetAsync(requestUri); + using var fileStream = new FileStream(filePath, FileMode.CreateNew); + await response.Content.CopyToAsync(fileStream); + } + return $"![{fileName}]({root}{PathToUrl(filePath)})"; + } + + public async Task UploadImagesFoHtml(string webRoot, string userid, string baseUrl, DateTime createdAt, string input) + { + var matches = StringHelper.MatchesImgTags(input); + if (matches.Any()) + { + var contentBuilder = new StringBuilder(input); + foreach (Match match in matches.Cast()) + { + var tag = match.Value; + var matchUrl = StringHelper.MatchImgSrc(tag); + var urlString = matchUrl.Groups[1].Value; + var uploadTag = await UploadFromWeb(webRoot, userid, baseUrl, urlString, createdAt); + contentBuilder.Replace(tag, uploadTag); + } + input = contentBuilder.ToString(); + } + return input; + } + + private static readonly string[] _arrayfileExts = new string[] { "zip", "7z", "xml", "pdf", "doc", "docx", "xls", "xlsx", "mp3", "mp4", "avi" }; + public async Task UploadFilesFoHtml(string webRoot, string userid, string baseUrl, DateTime createdAt, string input) + { + var matches = StringHelper.MatchesFile(input); + if (matches.Any()) + { + var contentBuilder = new StringBuilder(input); + foreach (Match match in matches.Cast()) + { + var tag = match.Value; + var urlString = XElement.Parse(tag).Attribute("href")!.Value; + if (_arrayfileExts.Any(m => urlString.ToLower().EndsWith($".{m}"))) + { + var uploadTag = await UploadFromWeb(webRoot, userid, baseUrl, urlString, createdAt); + contentBuilder.Replace(tag, uploadTag); + } + + + } + input = contentBuilder.ToString(); + } + return input; + } + + public async Task UploadBase64Image(string baseImg, string root, string path = "") + { + path = path.Replace("/", _slash); + var fileName = ""; + + VerifyPath(_publicStorageRoot, path); + string imgSrc = GetImgSrcValue(baseImg); + + var rnd = new Random(); + if (imgSrc.StartsWith("data:image/png;base64,")) + { + fileName = string.Format("{0}.png", rnd.Next(1000, 9999)); + imgSrc = imgSrc.Replace("data:image/png;base64,", ""); + } + if (imgSrc.StartsWith("data:image/jpeg;base64,")) + { + fileName = string.Format("{0}.jpeg", rnd.Next(1000, 9999)); + imgSrc = imgSrc.Replace("data:image/jpeg;base64,", ""); + } + if (imgSrc.StartsWith("data:image/gif;base64,")) + { + fileName = string.Format("{0}.gif", rnd.Next(1000, 9999)); + imgSrc = imgSrc.Replace("data:image/gif;base64,", ""); + } + + var filePath = string.IsNullOrEmpty(path) ? + Path.Combine(_publicStorageRoot, fileName) : + Path.Combine(_publicStorageRoot, path + _slash + fileName); + + await File.WriteAllBytesAsync(filePath, Convert.FromBase64String(imgSrc)); + + return $"![{fileName}]({root}{PathToUrl(filePath)})"; + } + + #region Private members + + private string ContentRoot + { + get + { + string path = Directory.GetCurrentDirectory(); + string testsDirectory = $"tests{_slash}Blogifier.Tests"; + string appDirectory = $"src{_slash}Blogifier"; + + _logger.LogInformation("Current directory path: {path}", path); + + // development unit test run + if (path.LastIndexOf(testsDirectory) > 0) + { + path = path[..path.LastIndexOf(testsDirectory)]; + var rootPath = $"{path}src{_slash}Blogifier"; + _logger.LogInformation("Unit test path: {rootPath}", rootPath); + return rootPath; + } + + // this needed to make sure we have correct data directory + // when running in debug mode in Visual Studio + // so instead of debug (src/Blogifier/bin/Debug..) + // will be used src/Blogifier/wwwroot/data + // !! this can mess up installs that have "src/Blogifier" in the path !! + if (path.LastIndexOf(appDirectory) > 0) + { + path = path[..path.LastIndexOf(appDirectory)]; + var rootPath = $"{path}src{_slash}Blogifier"; + _logger.LogInformation("Development debug path: {rootPath}", rootPath); + return rootPath; + } + _logger.LogInformation($"Final path: {path}"); + return path; + } + } + string GetFileName(string fileName) + { + // some browsers pass uploaded file name as short file name + // and others include the path; remove path part if needed + if (fileName.Contains(_slash)) + { + fileName = fileName.Substring(fileName.LastIndexOf(_slash)); + fileName = fileName.Replace(_slash, ""); + } + // when drag-and-drop or copy image to TinyMce editor + // it uses "mceclip0" as file name; randomize it for multiple uploads + if (fileName.StartsWith("mceclip0")) + { + var rnd = new Random(); + fileName = fileName.Replace("mceclip0", rnd.Next(100000, 999999).ToString()); + } + return fileName.SanitizePath(); + } + static void VerifyPath(string basePath, string path) + { + path = path.SanitizePath(); + + if (!string.IsNullOrEmpty(path)) + { + var dir = Path.Combine(basePath, path); + + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + } + } + static string TitleFromUri(string uri) + { + var title = uri.ToLower(); + title = title.Replace("%2f", "/"); + + if (title.EndsWith(".axdx")) + { + title = title.Replace(".axdx", ""); + } + if (title.Contains("image.axd?picture=")) + { + title = title[(title.IndexOf("image.axd?picture=") + 18)..]; + } + if (title.Contains("file.axd?file=")) + { + title = title[(title.IndexOf("file.axd?file=") + 14)..]; + } + if (title.Contains("encrypted-tbn") || title.Contains("base64,")) + { + var rnd = new Random(); + title = string.Format("{0}.png", rnd.Next(1000, 9999)); + } + if (title.Contains('/')) + { + title = title[title.LastIndexOf("/")..]; + } + title = title.Replace(" ", "-"); + return title.Replace("/", "").SanitizeFileName(); + } + string PathToUrl(string path) + { + string url = path.ReplaceIgnoreCase(_publicStorageRoot, "").Replace(_slash, "/"); + return $"data/{url}"; + } + static string GetImgSrcValue(string imgTag) + { + if (!(imgTag.Contains("data:image") && imgTag.Contains("src="))) + return imgTag; + + int start = imgTag.IndexOf("src="); + int srcStart = imgTag.IndexOf("\"", start) + 1; + + if (srcStart < 2) + return imgTag; + + int srcEnd = imgTag.IndexOf("\"", srcStart); + + if (srcEnd < 1 || srcEnd <= srcStart) + return imgTag; + + return imgTag.Substring(srcStart, srcEnd - srcStart); + } + bool InvalidFileName(string fileName) + { + List fileExtensions = new List() { "png", "gif", "jpeg", "jpg", "zip", "7z", "pdf", "doc", "docx", "xls", "xlsx", "mp3", "mp4", "avi" }; + var configFileExtensions = _configuration.GetSection("Blogifier").GetValue("FileExtensions"); + + if (!string.IsNullOrEmpty(configFileExtensions)) + { + fileExtensions = new List(configFileExtensions.Split(',')); + } + + foreach (string ext in fileExtensions) + { + if (fileName.EndsWith(ext)) + { + return false; + } + } + + return true; + } + + #endregion +} diff --git a/src/Blogifier/Storages/StorageReference.cs b/src/Blogifier/Storages/StorageReference.cs new file mode 100644 index 000000000..b488fa2e1 --- /dev/null +++ b/src/Blogifier/Storages/StorageReference.cs @@ -0,0 +1,16 @@ +using Blogifier.Shared; +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blogifier.Storages; + +public class StorageReference +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public DateTime CreatedAt { get; set; } + public int StorageId { get; set; } + public Storage Storage { get; set; } = default!; + public int EntityId { get; set; } + public StorageReferenceType Type { get; set; } + public Post? Post { get; set; } +} diff --git a/src/Blogifier/Views/404.cshtml b/src/Blogifier/Views/404.cshtml new file mode 100644 index 000000000..e45e4b31c --- /dev/null +++ b/src/Blogifier/Views/404.cshtml @@ -0,0 +1,117 @@ +@inject IStringLocalizer _localizer + + + + + + + + + @_localizer["page-not-found"] + + + + +
+

@_localizer["page-not-found"]

+

@_localizer["page-not-found-message"]

+

+ + + + + @_localizer["home"] + +

+
+ + + diff --git a/src/Blogifier/Views/Error.cshtml b/src/Blogifier/Views/Error.cshtml deleted file mode 100644 index 6cf157e0c..000000000 --- a/src/Blogifier/Views/Error.cshtml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - Page Not Found - - - - -
-

Not Found!

-

The Page you are looking for doesn't exist, or error occurred.

-

- - - - - Home Page - -

-
- - - diff --git a/src/Blogifier/Views/Index.cshtml b/src/Blogifier/Views/Index.cshtml index fdd307839..c7fdea5e3 100644 --- a/src/Blogifier/Views/Index.cshtml +++ b/src/Blogifier/Views/Index.cshtml @@ -1,6 +1,2 @@ -@* - For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 -*@ -@{ -} +@* For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 *@

Welcome to the blog!

diff --git a/src/Blogifier/Views/Shared/FooterScript.cshtml b/src/Blogifier/Views/Shared/FooterScript.cshtml deleted file mode 100644 index 0aa9af4a1..000000000 --- a/src/Blogifier/Views/Shared/FooterScript.cshtml +++ /dev/null @@ -1 +0,0 @@ -@Html.Raw(Model.Blog.FooterScript) \ No newline at end of file diff --git a/src/Blogifier/Views/Shared/HeaderScript.cshtml b/src/Blogifier/Views/Shared/HeaderScript.cshtml deleted file mode 100644 index 441fca2ff..000000000 --- a/src/Blogifier/Views/Shared/HeaderScript.cshtml +++ /dev/null @@ -1,27 +0,0 @@ -@using Blogifier.Core -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - - if (Model.ToString().EndsWith("PostModel")) - { - PostModel postModel = (PostModel)Model; - @postModel.Post.Title & @postModel.Blog.Title - - - if(postModel.Post.PostType == PostType.Page){ - - } - else{ - - } - } - else - { - ListModel listModel = (ListModel)Model; - @listModel.Blog.Title - - } - -} -@Html.Raw(Model.Blog.HeaderScript) diff --git a/src/Blogifier/Views/Themes/standard/404.cshtml b/src/Blogifier/Views/Themes/standard/404.cshtml deleted file mode 100644 index 3fce723dd..000000000 --- a/src/Blogifier/Views/Themes/standard/404.cshtml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - Error 404 - - - - - - - - - - - - -
-

Page Not Found

-
- - - - - - - - - - - diff --git a/src/Blogifier/Views/Themes/standard/Category.cshtml b/src/Blogifier/Views/Themes/standard/Category.cshtml deleted file mode 100644 index afe01deb2..000000000 --- a/src/Blogifier/Views/Themes/standard/Category.cshtml +++ /dev/null @@ -1,12 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - Layout = "layouts/_main.cshtml"; -} - -
-

@_localizer["categories"]: @ViewBag.Category

- -
diff --git a/src/Blogifier/Views/Themes/standard/Index.cshtml b/src/Blogifier/Views/Themes/standard/Index.cshtml deleted file mode 100644 index ff960887a..000000000 --- a/src/Blogifier/Views/Themes/standard/Index.cshtml +++ /dev/null @@ -1,19 +0,0 @@ -@using Blogifier.Shared -@{ - Layout = "layouts/_main.cshtml"; -} - -
- - @* - TODO: - Custom fields: - - Post list style options: - 1. view-grid (default) - 2. view-list - *@ - - @* *@ - - -
diff --git a/src/Blogifier/Views/Themes/standard/Page.cshtml b/src/Blogifier/Views/Themes/standard/Page.cshtml deleted file mode 100644 index ffd4e052c..000000000 --- a/src/Blogifier/Views/Themes/standard/Page.cshtml +++ /dev/null @@ -1,52 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - Layout = "layouts/_main.cshtml"; - PostModel postModel = (PostModel)Model; - var post = postModel.Post; - string cover = $"{Url.Content("~/")}{postModel.Post.Cover}"; -} - -
-
- -
- @post.Title -
- -
- -

@post.Title

- - -
- -
- @Html.Raw(post.Content) -
- -
- - - -
diff --git a/src/Blogifier/Views/Themes/standard/Post.cshtml b/src/Blogifier/Views/Themes/standard/Post.cshtml deleted file mode 100644 index c701183b7..000000000 --- a/src/Blogifier/Views/Themes/standard/Post.cshtml +++ /dev/null @@ -1,96 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - Layout = "layouts/_main.cshtml"; - PostModel postModel = (PostModel)Model; - var post = postModel.Post; - string cover = postModel.Post.Cover; - var catUrl = Url.Content("~/categories"); -} - -
-
- -
- -
- -
- -

@post.Title

- - -
- -
- @Html.Raw(post.Content) -
- -
- - - -
- -
- -
- - - - -
- -
diff --git a/src/Blogifier/Views/Themes/standard/Search.cshtml b/src/Blogifier/Views/Themes/standard/Search.cshtml deleted file mode 100644 index 87f4172e8..000000000 --- a/src/Blogifier/Views/Themes/standard/Search.cshtml +++ /dev/null @@ -1,33 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - Layout = "layouts/_main.cshtml"; - ListModel listModel = (ListModel)Model; -} - -
- -
- -
-

Search Results

- - @if (listModel.Posts.Count() > 0) - { - @foreach (var post in listModel.Posts) - { -
-
- @post.Title -
-
- @post.Title -

@post.Description

-
-
- } - } - -
diff --git a/src/Blogifier/Views/Themes/standard/components/footer.cshtml b/src/Blogifier/Views/Themes/standard/components/footer.cshtml deleted file mode 100644 index cb5b0b628..000000000 --- a/src/Blogifier/Views/Themes/standard/components/footer.cshtml +++ /dev/null @@ -1,28 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - var siteTitle = Model.Blog.Title; - var version = "3.0"; -} - - - - diff --git a/src/Blogifier/Views/Themes/standard/components/header.cshtml b/src/Blogifier/Views/Themes/standard/components/header.cshtml deleted file mode 100644 index 97ee14b4b..000000000 --- a/src/Blogifier/Views/Themes/standard/components/header.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -@using Blogifier.Shared -@using Blogifier.Shared.Resources -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - - var siteTitle = Model.Blog.Title; - var siteDesc = Model.Blog.Description; - - @* TODO: Customfield *@ - var siteMark = "titleDesc"; - var siteLogo = "themes/standard/img/logo-black.png"; -} - -
-
-
- @if (siteMark == "logo") - { - - } - else if (siteMark == "title") - { - @siteTitle - } - else if (siteMark == "titleDesc") - { - @siteTitle -

@siteDesc

- } -
- - -
-
diff --git a/src/Blogifier/Views/Themes/standard/components/metadata.cshtml b/src/Blogifier/Views/Themes/standard/components/metadata.cshtml deleted file mode 100644 index 3ccb0833f..000000000 --- a/src/Blogifier/Views/Themes/standard/components/metadata.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@if (Model.ToString().EndsWith("PostModel")) -{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - PostModel postModel = (PostModel)Model; - var postTitle = postModel.Post.Title; - var postDesc = postModel.Post.Description.StripHtml(); - var postUrl = postModel.Post.PostType == PostType.Post ? absoluteUrl + "/posts/" + postModel.Post.Slug : absoluteUrl + "/" + postModel.Post.Slug; - var postCover = absoluteUrl + "/" + postModel.Post.Cover; - var postPublished = postModel.Post.Published.ToString("s"); - var postAuthor = postModel.Post.Author.DisplayName; - - -} diff --git a/src/Blogifier/Views/Themes/standard/components/nav.cshtml b/src/Blogifier/Views/Themes/standard/components/nav.cshtml deleted file mode 100644 index e94cc16a1..000000000 --- a/src/Blogifier/Views/Themes/standard/components/nav.cshtml +++ /dev/null @@ -1,116 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@inject ICategoryProvider _categoryProvider -@inject IAuthorProvider _authorProvider - -@{ - var categories = await _categoryProvider.Categories(); - var catUrl = Url.Content("~/categories"); - var currentUserAuthor = await _authorProvider.FindByEmail(User.Identity.Name); -} - - diff --git a/src/Blogifier/Views/Themes/standard/components/newsletter.cshtml b/src/Blogifier/Views/Themes/standard/components/newsletter.cshtml deleted file mode 100644 index a3aa2b7c1..000000000 --- a/src/Blogifier/Views/Themes/standard/components/newsletter.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - var newsletterAction = @absoluteUrl + "/api/newsletter/subscribe"; -} - - diff --git a/src/Blogifier/Views/Themes/standard/components/pagination.cshtml b/src/Blogifier/Views/Themes/standard/components/pagination.cshtml deleted file mode 100644 index bed79a37d..000000000 --- a/src/Blogifier/Views/Themes/standard/components/pagination.cshtml +++ /dev/null @@ -1,35 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - ListModel _model = (ListModel)Model; -} - -@if(_model.Pager.ShowNewer || _model.Pager.ShowOlder) -{ - -} diff --git a/src/Blogifier/Views/Themes/standard/layouts/_main.cshtml b/src/Blogifier/Views/Themes/standard/layouts/_main.cshtml deleted file mode 100644 index 552893d46..000000000 --- a/src/Blogifier/Views/Themes/standard/layouts/_main.cshtml +++ /dev/null @@ -1,74 +0,0 @@ -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - - var siteTitle = Model.Blog.Title; - var siteDesc = Model.Blog.Description; - var siteFeed = absoluteUrl + "/feed/rss"; - - var pageTitle = siteTitle + " | " + siteDesc; - var pageDesc = siteDesc; - var pageCanonical = absoluteUrl; - - if (Model.ToString().EndsWith("PostModel")) - { - PostModel postModel = (PostModel)Model; - pageTitle = postModel.Post.Title + " | " + siteTitle; - pageDesc = postModel.Post.Description.StripHtml(); - pageCanonical = postModel.Post.PostType == PostType.Post ? absoluteUrl + "/posts/" + postModel.Post.Slug : absoluteUrl + "/" + postModel.Post.Slug; - } -} - - - - - - - - @pageTitle - - - - - - - - - - - @Html.Raw(Model.Blog.HeaderScript) - - - - - - @RenderBody() - - - - - - - - @Html.Raw(Model.Blog.FooterScript) - - - - diff --git a/src/Blogifier/Views/Themes/standard/post/author.cshtml b/src/Blogifier/Views/Themes/standard/post/author.cshtml deleted file mode 100644 index b36139761..000000000 --- a/src/Blogifier/Views/Themes/standard/post/author.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@using Blogifier.Shared -@{ - PostModel postModel = (PostModel)Model; - var author = postModel.Post.Author; -} - diff --git a/src/Blogifier/Views/Themes/standard/post/featured.cshtml b/src/Blogifier/Views/Themes/standard/post/featured.cshtml deleted file mode 100644 index d70656ea4..000000000 --- a/src/Blogifier/Views/Themes/standard/post/featured.cshtml +++ /dev/null @@ -1,77 +0,0 @@ -@{ - ListModel _model = (ListModel)Model; - List _featured = _model.Posts.Where(p => p.Featured).Take(3).ToList(); -} - -@if(_featured != null) -{ - -} diff --git a/src/Blogifier/Views/Themes/standard/post/nav.cshtml b/src/Blogifier/Views/Themes/standard/post/nav.cshtml deleted file mode 100644 index 0ce4a18d8..000000000 --- a/src/Blogifier/Views/Themes/standard/post/nav.cshtml +++ /dev/null @@ -1,30 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer - -@{ - PostModel _model = (PostModel)Model; -} - diff --git a/src/Blogifier/Views/Themes/standard/post/related.cshtml b/src/Blogifier/Views/Themes/standard/post/related.cshtml deleted file mode 100644 index 3d614400d..000000000 --- a/src/Blogifier/Views/Themes/standard/post/related.cshtml +++ /dev/null @@ -1,79 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - PostModel _model = (PostModel)Model; - List _related = _model.Related.Take(3).ToList(); -} - - -@if (_related.Any()) -{ - -} diff --git a/src/Blogifier/Views/Themes/standard/post/share.cshtml b/src/Blogifier/Views/Themes/standard/post/share.cshtml deleted file mode 100644 index 79b0cee1d..000000000 --- a/src/Blogifier/Views/Themes/standard/post/share.cshtml +++ /dev/null @@ -1,55 +0,0 @@ -@using Blogifier.Shared -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - - PostModel postModel = (PostModel)Model; - var post = postModel.Post; - - var postTitle = post.Title; - var postSlug = absoluteUrl + "/posts/" + post.Slug; - - var share_facebook = "https://www.facebook.com/sharer/sharer.php?u=" + postSlug; - var share_twitter = "https://twitter.com/intent/tweet?text=" + postTitle + "&url=" + postSlug; - var share_email = "mailto:?&subject=" + postTitle + "&cc=&bcc=&body=" + postTitle + "%0" + postSlug; -} - - diff --git a/src/Blogifier/Views/Themes/standard/post/view-grid.cshtml b/src/Blogifier/Views/Themes/standard/post/view-grid.cshtml deleted file mode 100644 index f70b80045..000000000 --- a/src/Blogifier/Views/Themes/standard/post/view-grid.cshtml +++ /dev/null @@ -1,62 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer - -@{ - ListModel listModel = (ListModel)Model; - string root = Url.Content("~/"); -} - -@if (listModel.Posts.Count() > 0) -{ -
- @foreach (var post in listModel.Posts) - { -
-
-
- @post.Title -
- @if(post.Categories != null) - { -
- @foreach (var cat in post.Categories) - { - @cat.Content - } -
- } -

- @post.Title -

-

@Html.Raw(post.Description)

-
-
- @post.Author.DisplayName - @post.Author.DisplayName -
-
- - - - - -
- - Read - - - - -
- -
-
- } -
-} -else -{ -
@_localizer["empty"]!
-} diff --git a/src/Blogifier/Views/Themes/standard/post/view-list.cshtml b/src/Blogifier/Views/Themes/standard/post/view-list.cshtml deleted file mode 100644 index 5381dfae7..000000000 --- a/src/Blogifier/Views/Themes/standard/post/view-list.cshtml +++ /dev/null @@ -1,62 +0,0 @@ -@using Microsoft.Extensions.Localization -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@inject IStringLocalizer _localizer - -@{ - ListModel listModel = (ListModel)Model; - string root = Url.Content("~/"); -} - -@if (listModel.Posts.Count() > 0) -{ - @foreach (var post in listModel.Posts) - { -
-
- @post.Title -
-
-

- @post.Title -

-
-
- @post.Author.DisplayName - @post.Author.DisplayName -
-
- - - - - -
- @if(post.Categories != null) - { -
- - - - @foreach (var cat in post.Categories) - { - @cat.Content - } -
- } -
-

@Html.Raw(post.Description)

- - Read More - - - - -
-
- } -} -else -{ -
@_localizer["empty"]!
-} diff --git a/src/Blogifier/Views/Themes/test/404.cshtml b/src/Blogifier/Views/Themes/test/404.cshtml deleted file mode 100644 index 3fce723dd..000000000 --- a/src/Blogifier/Views/Themes/test/404.cshtml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - Error 404 - - - - - - - - - - - - -
-

Page Not Found

-
- - - - - - - - - - - diff --git a/src/Blogifier/Views/Themes/test/Category.cshtml b/src/Blogifier/Views/Themes/test/Category.cshtml deleted file mode 100644 index 5366792ab..000000000 --- a/src/Blogifier/Views/Themes/test/Category.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@{ - Layout = "layouts/_main.cshtml"; -} - -
-

CATEGORY_TITLE

- -
diff --git a/src/Blogifier/Views/Themes/test/Index.cshtml b/src/Blogifier/Views/Themes/test/Index.cshtml deleted file mode 100644 index ff960887a..000000000 --- a/src/Blogifier/Views/Themes/test/Index.cshtml +++ /dev/null @@ -1,19 +0,0 @@ -@using Blogifier.Shared -@{ - Layout = "layouts/_main.cshtml"; -} - -
- - @* - TODO: - Custom fields: - - Post list style options: - 1. view-grid (default) - 2. view-list - *@ - - @* *@ - - -
diff --git a/src/Blogifier/Views/Themes/test/Page.cshtml b/src/Blogifier/Views/Themes/test/Page.cshtml deleted file mode 100644 index 940aa5bbb..000000000 --- a/src/Blogifier/Views/Themes/test/Page.cshtml +++ /dev/null @@ -1,52 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - Layout = "layouts/_main.cshtml"; - PostModel postModel = (PostModel)Model; - var post = postModel.Post; - string cover = $"{Url.Content("~/")}{postModel.Post.Cover}"; -} - -
-
- -
- @post.Title -
- -
- -

@post.Title

- - -
- -
- @Html.Raw(post.Content) -
- -
- - - -
diff --git a/src/Blogifier/Views/Themes/test/Post.cshtml b/src/Blogifier/Views/Themes/test/Post.cshtml deleted file mode 100644 index 264528c61..000000000 --- a/src/Blogifier/Views/Themes/test/Post.cshtml +++ /dev/null @@ -1,96 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - Layout = "layouts/_main.cshtml"; - PostModel postModel = (PostModel)Model; - var post = postModel.Post; - string cover = postModel.Post.Cover; - var catUrl = Url.Content("~/categories"); -} - -
-
- -
- -
- -
- -

@post.Title

- - -
- -
- @Html.Raw(post.Content) -
- -
- - - -
- -
- -
- - - - -
- -
diff --git a/src/Blogifier/Views/Themes/test/Search.cshtml b/src/Blogifier/Views/Themes/test/Search.cshtml deleted file mode 100644 index 48bedae5a..000000000 --- a/src/Blogifier/Views/Themes/test/Search.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@{ - Layout = "layouts/_main.cshtml"; -} - -
- -
- -
-

Search Results

- -
-
- TITLE -
-
- TITLE -

DESCRIPTION

-
-
- -
diff --git a/src/Blogifier/Views/Themes/test/components/footer.cshtml b/src/Blogifier/Views/Themes/test/components/footer.cshtml deleted file mode 100644 index cb5b0b628..000000000 --- a/src/Blogifier/Views/Themes/test/components/footer.cshtml +++ /dev/null @@ -1,28 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - var siteTitle = Model.Blog.Title; - var version = "3.0"; -} - - - - diff --git a/src/Blogifier/Views/Themes/test/components/header.cshtml b/src/Blogifier/Views/Themes/test/components/header.cshtml deleted file mode 100644 index dee20bd7e..000000000 --- a/src/Blogifier/Views/Themes/test/components/header.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -@using Blogifier.Shared -@using Blogifier.Shared.Resources -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - - var siteTitle = Model.Blog.Title; - var siteDesc = Model.Blog.Description; - - @* TODO: Customfield *@ - var siteMark = "titleDesc"; - var siteLogo = "/themes/standard/img/logo-black.png"; -} - -
-
-
- @if (siteMark == "logo") - { - - } - else if (siteMark == "title") - { - @siteTitle - } - else if (siteMark == "titleDesc") - { - @siteTitle -

@siteDesc

- } -
- - -
-
diff --git a/src/Blogifier/Views/Themes/test/components/metadata.cshtml b/src/Blogifier/Views/Themes/test/components/metadata.cshtml deleted file mode 100644 index 9b39427c5..000000000 --- a/src/Blogifier/Views/Themes/test/components/metadata.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@if (Model.ToString().EndsWith("PostModel")) -{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - PostModel postModel = (PostModel)Model; - var postTitle = postModel.Post.Title; - var postDesc = postModel.Post.Description.StripHtml(); - var postUrl = absoluteUrl + "/posts/" + postModel.Post.Slug; - var postCover = absoluteUrl + "/" + postModel.Post.Cover; - var postPublished = postModel.Post.Published.ToString("s"); - var postAuthor = postModel.Post.Author.DisplayName; - - -} diff --git a/src/Blogifier/Views/Themes/test/components/nav.cshtml b/src/Blogifier/Views/Themes/test/components/nav.cshtml deleted file mode 100644 index b26efe49d..000000000 --- a/src/Blogifier/Views/Themes/test/components/nav.cshtml +++ /dev/null @@ -1,114 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@inject ICategoryProvider _categoryProvider - -@{ - var categories = await _categoryProvider.Categories(); - var catUrl = Url.Content("~/categories"); -} - - diff --git a/src/Blogifier/Views/Themes/test/components/newsletter.cshtml b/src/Blogifier/Views/Themes/test/components/newsletter.cshtml deleted file mode 100644 index a3aa2b7c1..000000000 --- a/src/Blogifier/Views/Themes/test/components/newsletter.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - var newsletterAction = @absoluteUrl + "/api/newsletter/subscribe"; -} - - diff --git a/src/Blogifier/Views/Themes/test/components/pagination.cshtml b/src/Blogifier/Views/Themes/test/components/pagination.cshtml deleted file mode 100644 index bed79a37d..000000000 --- a/src/Blogifier/Views/Themes/test/components/pagination.cshtml +++ /dev/null @@ -1,35 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - ListModel _model = (ListModel)Model; -} - -@if(_model.Pager.ShowNewer || _model.Pager.ShowOlder) -{ - -} diff --git a/src/Blogifier/Views/Themes/test/layouts/_main.cshtml b/src/Blogifier/Views/Themes/test/layouts/_main.cshtml deleted file mode 100644 index 2fcf74e23..000000000 --- a/src/Blogifier/Views/Themes/test/layouts/_main.cshtml +++ /dev/null @@ -1,74 +0,0 @@ -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - - var siteTitle = Model.Blog.Title; - var siteDesc = Model.Blog.Description; - var siteFeed = absoluteUrl + "/feed/rss"; - - var pageTitle = siteTitle + " | " + siteDesc; - var pageDesc = siteDesc; - var pageCanonical = absoluteUrl; - - if (Model.ToString().EndsWith("PostModel")) - { - PostModel postModel = (PostModel)Model; - pageTitle = postModel.Post.Title + " | " + siteTitle; - pageDesc = postModel.Post.Description.StripHtml(); - pageCanonical = absoluteUrl + "/posts/" + postModel.Post.Slug; - } -} - - - - - - - - @pageTitle - - - - - - - - - - - @Html.Raw(Model.Blog.HeaderScript) - - - - - - @RenderBody() - - - - - - - - @Html.Raw(Model.Blog.FooterScript) - - - - diff --git a/src/Blogifier/Views/Themes/test/post/author.cshtml b/src/Blogifier/Views/Themes/test/post/author.cshtml deleted file mode 100644 index b36139761..000000000 --- a/src/Blogifier/Views/Themes/test/post/author.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@using Blogifier.Shared -@{ - PostModel postModel = (PostModel)Model; - var author = postModel.Post.Author; -} - diff --git a/src/Blogifier/Views/Themes/test/post/comments.cshtml b/src/Blogifier/Views/Themes/test/post/comments.cshtml deleted file mode 100644 index 651b46b49..000000000 --- a/src/Blogifier/Views/Themes/test/post/comments.cshtml +++ /dev/null @@ -1,2 +0,0 @@ - -
diff --git a/src/Blogifier/Views/Themes/test/post/featured.cshtml b/src/Blogifier/Views/Themes/test/post/featured.cshtml deleted file mode 100644 index dcdd9e42f..000000000 --- a/src/Blogifier/Views/Themes/test/post/featured.cshtml +++ /dev/null @@ -1,77 +0,0 @@ -@{ - ListModel _model = (ListModel)Model; - List _featured = _model.Posts.Where(p => p.Featured).Take(3).ToList(); -} - -@if(_featured != null) -{ - -} diff --git a/src/Blogifier/Views/Themes/test/post/nav.cshtml b/src/Blogifier/Views/Themes/test/post/nav.cshtml deleted file mode 100644 index 0ce4a18d8..000000000 --- a/src/Blogifier/Views/Themes/test/post/nav.cshtml +++ /dev/null @@ -1,30 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer - -@{ - PostModel _model = (PostModel)Model; -} - diff --git a/src/Blogifier/Views/Themes/test/post/related.cshtml b/src/Blogifier/Views/Themes/test/post/related.cshtml deleted file mode 100644 index 3f2270035..000000000 --- a/src/Blogifier/Views/Themes/test/post/related.cshtml +++ /dev/null @@ -1,79 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer -@{ - PostModel _model = (PostModel)Model; - List _related = _model.Related.Take(3).ToList(); -} - - -@if (_related.Any()) -{ - -} diff --git a/src/Blogifier/Views/Themes/test/post/share.cshtml b/src/Blogifier/Views/Themes/test/post/share.cshtml deleted file mode 100644 index 79b0cee1d..000000000 --- a/src/Blogifier/Views/Themes/test/post/share.cshtml +++ /dev/null @@ -1,55 +0,0 @@ -@using Blogifier.Shared -@{ - var request = Url.ActionContext.HttpContext.Request; - var absoluteUrl = $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}"; - - PostModel postModel = (PostModel)Model; - var post = postModel.Post; - - var postTitle = post.Title; - var postSlug = absoluteUrl + "/posts/" + post.Slug; - - var share_facebook = "https://www.facebook.com/sharer/sharer.php?u=" + postSlug; - var share_twitter = "https://twitter.com/intent/tweet?text=" + postTitle + "&url=" + postSlug; - var share_email = "mailto:?&subject=" + postTitle + "&cc=&bcc=&body=" + postTitle + "%0" + postSlug; -} - - diff --git a/src/Blogifier/Views/Themes/test/post/view-grid.cshtml b/src/Blogifier/Views/Themes/test/post/view-grid.cshtml deleted file mode 100644 index b368dfad9..000000000 --- a/src/Blogifier/Views/Themes/test/post/view-grid.cshtml +++ /dev/null @@ -1,62 +0,0 @@ -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@using Microsoft.Extensions.Localization -@inject IStringLocalizer _localizer - -@{ - ListModel listModel = (ListModel)Model; - string root = Url.Content("~/"); -} - -@if (listModel.Posts.Count() > 0) -{ -
- @foreach (var post in listModel.Posts) - { -
-
-
- @post.Title -
- @if(post.Categories != null) - { -
- @foreach (var cat in post.Categories) - { - @cat.Content - } -
- } -

- @post.Title -

-

@Html.Raw(post.Description)

-
-
- @post.Author.DisplayName - @post.Author.DisplayName -
-
- - - - - -
- - Read - - - - -
- -
-
- } -
-} -else -{ -
@_localizer["empty"]!
-} diff --git a/src/Blogifier/Views/Themes/test/post/view-list.cshtml b/src/Blogifier/Views/Themes/test/post/view-list.cshtml deleted file mode 100644 index e41351a06..000000000 --- a/src/Blogifier/Views/Themes/test/post/view-list.cshtml +++ /dev/null @@ -1,62 +0,0 @@ -@using Microsoft.Extensions.Localization -@using Blogifier.Shared.Resources -@using Blogifier.Shared -@inject IStringLocalizer _localizer - -@{ - ListModel listModel = (ListModel)Model; - string root = Url.Content("~/"); -} - -@if (listModel.Posts.Count() > 0) -{ - @foreach (var post in listModel.Posts) - { -
-
- @post.Title -
-
-

- @post.Title -

-
-
- @post.Author.DisplayName - @post.Author.DisplayName -
-
- - - - - -
- @if(post.Categories != null) - { -
- - - - @foreach (var cat in post.Categories) - { - @cat.Content - } -
- } -
-

@Html.Raw(post.Description)

- - Read More - - - - -
-
- } -} -else -{ -
@_localizer["empty"]!
-} diff --git a/src/Blogifier/Views/_ViewImports.cshtml b/src/Blogifier/Views/_ViewImports.cshtml index 820926592..c9a8abece 100644 --- a/src/Blogifier/Views/_ViewImports.cshtml +++ b/src/Blogifier/Views/_ViewImports.cshtml @@ -1,4 +1,8 @@ -@using Blogifier.Shared -@using Blogifier.Core.Providers -@using Blogifier.Core.Extensions +@using Microsoft.Extensions.Localization +@using Blogifier.Shared +@using Blogifier.Shared.Resources +@using Blogifier.Extensions; +@using Blogifier.Models; +@using Blogifier.Helper; +@using Blogifier; @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/Blogifier/appsettings.Development.json b/src/Blogifier/appsettings.Development.json deleted file mode 100644 index 8983e0fc1..000000000 --- a/src/Blogifier/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/src/Blogifier/appsettings.json b/src/Blogifier/appsettings.json index 3ff95ca99..6428eb5c2 100644 --- a/src/Blogifier/appsettings.json +++ b/src/Blogifier/appsettings.json @@ -1,17 +1,56 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*", - "Blogifier": { - "DbProvider": "SQLite", - "ConnString": "Data Source=Blog.db", - "Salt": "SECRET-CHANGE-ME!", - "DemoMode": false, - "FileExtensions": "png,gif,jpeg,jpg,zip,7z,pdf,doc,docx,xls,xlsx,mp3,mp4,avi" - } + + "Blogifier": { + "DbProvider": "MySql", + "ConnString": "server=mysql;user=root;password=root;database=blogifier;", + //"Redis": "redis:6379,password=root,defaultDatabase=0", + "Salt": "SECRET-CHANGE-ME!", + "DemoMode": false, + "FileExtensions": "png,gif,jpeg,jpg,zip,7z,pdf,doc,docx,xls,xlsx,mp3,mp4,avi", + "Minio": { + "Endpoint": "minio", + "Port": 9000, + "Region": "root", + "BucketName": "blogifier", + "AccessKey": "root", + "SecretKey": "root" + } + }, + "AllowedHosts": "*", + "Serilog": { + "MinimumLevel": { + "Default": "Warning", + "Override": { + "Default": "Warning", + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console", + "outputTemplate": "{Timestamp:HH:mm:ss}|{RequestId}|{SourceContext}|{Level:u3}|{Message:lj}{NewLine}{Exception}", + "restrictedToMinimumLevel": "Information" + } + }, + { + "Name": "File", + "Args": { + "path": "App_Data/logs/orchard-log.txt", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.ffff}|{RequestId}|{SourceContext}|{Level:u3}|{Message:lj}{NewLine}{Exception}", + "restrictedToMinimumLevel": "Warning" + } + } + ] + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } } diff --git a/src/Blogifier/wwwroot/img/avatar.webp b/src/Blogifier/wwwroot/img/avatar.webp new file mode 100644 index 000000000..5e2b44e1b Binary files /dev/null and b/src/Blogifier/wwwroot/img/avatar.webp differ diff --git a/src/Blogifier/wwwroot/themes/standard/css/styles.css b/src/Blogifier/wwwroot/themes/standard/css/styles.css deleted file mode 100644 index 386025104..000000000 --- a/src/Blogifier/wwwroot/themes/standard/css/styles.css +++ /dev/null @@ -1,3 +0,0 @@ -/*! - Author: https://farzin.dev -*/img{max-width:100%}iframe{width:100% !important;margin-top:1rem;margin-bottom:1.5rem}figure,p,ul,ol{margin-bottom:1.5rem}table{width:100%}blockquote{font-weight:300;position:relative;padding:1rem 3rem;margin-bottom:1.5rem}blockquote::before,blockquote::after{position:absolute;content:"";font-size:5rem;width:2.5rem;height:2.5rem;background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4IiB3aWR0aD0iNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDM0aDZsNC04di0xMmgtMTJ2MTJoNnptMTYgMGg2bDQtOHYtMTJoLTEydjEyaDZ6Ii8+PHBhdGggZD0iTTAgMGg0OHY0OGgtNDh6IiBmaWxsPSJub25lIi8+PC9zdmc+");display:block;background-size:2.5rem;background-repeat:no-repeat;opacity:.1}blockquote::before{top:.75rem;left:0;transform:rotate(180deg)}blockquote::after{bottom:.75rem;right:0}blockquote p:last-child{margin-bottom:0}.container{width:61.5rem;max-width:100%}@media(max-width: 991px){:root{--bs-gutter-x: 1.5rem}}.header{padding-top:4rem;padding-bottom:4rem}.header-title{display:inline-block;margin:0;font-size:var(--bf-header-title-size);font-weight:var(--bf-header-title-weight);color:var(--bf-header-title-color);line-height:1.2;text-decoration:none}.header-title:hover,.header-title:focus{color:var(--bf-header-title-hover)}.header-desc{margin:0;font-size:.875rem;font-weight:400;color:var(--bf-header-desc-color, #666)}.header-logo{display:block}.header-logo-img{width:var(--bf-header-logo-width);height:var(--bf-header-logo-height)}@media(max-width: 767px){.header{padding:0;margin-bottom:2rem;background-color:var(--bs-light)}}.header-nav ul{list-style:none;margin:0;padding:0}.header-nav-link,.header-nav-button{color:#444;height:2.5rem;line-height:2.5rem;border:0;background:none;display:inline-block;border-radius:.125rem;transition:all ease-in-out .15s;text-align:center;border-radius:.25rem;font-size:1rem}.header-nav-link:hover,.header-nav-link:focus,.header-nav-button:hover,.header-nav-button:focus{color:#000;background-color:var(--bs-light)}.header-nav-link>.bi,.header-nav-link>img,.header-nav-button>.bi,.header-nav-button>img{transform:translateY(-0.1rem)}.header-nav-link{padding:0 1rem;font-weight:500}.header-nav-link .bi-chevron-down{width:.75rem;height:.75rem}.header-nav-button{padding:0;width:2.5rem}.header-nav .-login{position:relative}.header-nav .-login::after{content:"";width:1px;height:1rem;position:absolute;top:50%;transform:translateY(-50%);left:-0.75rem;background-color:rgba(0,0,0,.1);display:block}.header-nav .-login .bi-box-arrow-in-right{transform:translateX(-0.125rem)}@media(max-width: 767px){.header-nav{border-bottom:1px solid rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.03);padding:.25rem .75rem;margin:0 -1.5rem 1rem}}.social-link-blogifier:hover{color:#622aff}.social-link-facebook:hover{color:#1678f2}.social-link-twitter:hover{color:#1da1f2}.social-link-github:hover{color:#171617}.social-link-pinterest:hover{color:#e60023}.social-link-linkedin:hover{color:#0a66c2}.social-link-instagram:hover{color:#b900b4}.social-link-telegram:hover{color:#08c}.social-link-youtube:hover{color:#ff0300}.social-link-whatsapp:hover{color:#25d366}.bft-widget-title{font-weight:300;letter-spacing:.1em;color:#aaa;font-size:1rem;text-transform:uppercase;display:block;margin-bottom:2rem}.bft-widget-content ul{list-style:none;padding:0;margin:0}.footer{border-top:1px solid rgba(0,0,0,.05);margin-top:4rem;color:#444;font-size:.75rem}.footer a{color:#444;text-decoration:none}.footer a:hover{color:var(--bf-color)}.footer p{margin:0}.pagination{margin:4rem auto 4rem;height:3rem;align-items:center;justify-content:center}.pagination-item{margin:0 .5rem;z-index:5}.pagination-link{display:block;color:#888;font-weight:500;font-size:.875rem;height:3rem;line-height:3rem;text-decoration:none;text-transform:uppercase;transition:opacity ease .2s;opacity:.5}.pagination-link:hover{opacity:1;color:var(--bf-color)}.pagination-link:hover .bi{width:1.375rem;height:1.375rem}.pagination-link .bi{width:1.25rem;height:1.25rem;transition:all ease .2s;margin:0 .25rem;transform:translateY(-0.0625rem)}.newsletter{background-color:#000;color:#aaa;border-radius:var(--bf-radius, 0.125rem);margin-top:4rem;position:relative}.newsletter-body{padding:3rem}@media screen and (min-width: 991px){.newsletter-body{padding:4rem}}.newsletter-title{font-size:1.75rem;font-weight:600;color:#fff}.newsletter-desc{margin:0;font-size:1.125rem;font-weight:300}.newsletter-form{border:2px solid #222;border-radius:var(--bf-radius, 0.125rem)}.newsletter-input{background:none;border:0;flex-grow:1;width:100%;padding:.5rem 1rem;color:#fff;outline:none !important}.newsletter-btn{background:none;border:0;padding:.5rem 1rem;color:#fff;display:block;border-left:1px solid #222}.newsletter-btn:focus,.newsletter-btn:hover{outline:none;background:#222}.newsletter-msg{padding:2rem;text-align:center;background-color:#000;border-radius:var(--bf-radius, 0.125rem);position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;margin:0;color:#fff;cursor:pointer}.newsletter-msg .spinner-border{color:#fff}.dropdown-menu{border-radius:.125rem;box-shadow:0 0 3rem rgba(0,0,0,.2);border:none}.dropdown-item{font-size:.875rem;font-weight:500;text-transform:capitalize;padding:.5rem 1rem;color:#555;transition:all ease-in-out .2s}.dropdown-item:active,.dropdown-item:hover{color:#000;background-color:var(--bs-light)}.dropdown-divider{background:none;border-top:1px solid rgba(0,0,0,.3);margin:0}pre>code{display:block;overflow-x:auto;padding:2rem;color:#212529;background:#f8f9fa;margin:0;font-family:var(--bs-font-monospace);font-size:.875rem;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.4;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}.hljs-comment{color:#90a4ae}.hljs-keyword,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-section,.hljs-selector-class,.hljs-meta,.hljs-selector-pseudo,.hljs-attr{color:#006ee0}.hljs-attribute{color:#803378}.hljs-name{color:#168174}.hljs-type,.hljs-number,.hljs-selector-id,.hljs-quote,.hljs-template-tag,.hljs-literal{color:#168174}.hljs-title,.hljs-string,.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-meta-string{color:#c30}.hljs-bullet,.hljs-code{color:#ccc}.hljs-deletion{color:#de7176}.hljs-addition{color:#76c490}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}.featured{margin-bottom:5rem}.featured-cover{margin:0;max-height:100%;min-height:100%;overflow:hidden;position:relative;border-radius:var(--bf-radius, 0.125rem);box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}@media screen and (max-width: 991px){.featured-cover{height:20rem}}.featured-cover-link{display:block;position:absolute;top:0;left:0;width:100%;height:100%}.featured-cover-img{border-radius:var(--bf-radius, 0.125rem);position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;object-position:center}.featured-content{padding-top:2rem;padding-bottom:2rem;padding-left:2rem}.featured-title{font-size:1.75rem;font-weight:700;margin-bottom:1.5rem;line-height:1.3}.featured-link{text-decoration:none;color:#000}.featured-link:hover{color:var(--bf-color)}.featured-meta{font-size:.875rem;color:#777;margin-bottom:1.5rem;opacity:.7}.featured-meta a{color:#777}.featured-meta a:hover{color:#000}.featured-author{display:flex;align-items:center;margin-right:1rem}.featured-author-img{border-radius:50rem;margin-right:.25rem;filter:grayscale(100%)}.featured-author-name{text-transform:capitalize}.featured-date{display:flex;align-items:center;margin-right:.875rem}.featured-date-icon{margin-right:.375rem}.featured-cat{display:flex;align-items:center}.featured-cat-icon{margin-right:0rem}.featured-cat-title{text-decoration:none}.featured-desc{color:#444;line-height:1.8;margin-bottom:1.5rem}.featured-more{font-weight:500;font-size:.875rem;color:var(--bf-color);text-decoration:none}.featured-more:hover{color:#000}.featured-prev,.featured-next{width:3rem;opacity:0}.featured-prev{left:-4rem}.featured-next{right:-3.5rem}.featured:hover .featured-next,.featured:hover .featured-prev{opacity:.2}@media screen and (max-width: 767px){.featured{border-bottom:1px solid rgba(0,0,0,.03);margin:-2rem -1.5rem 2rem;background-color:var(--bs-light)}.featured-item{padding:1.5rem}.featured-content{padding:0;padding:1rem 1rem 0}.featured-title{font-size:1.25rem;margin-bottom:.5rem;font-weight:500}.featured-desc{font-size:.875rem;margin-bottom:.5rem}}.post-grid{height:100%;position:relative;transition:box-shadow ease-in-out .3s;border-radius:var(--bf-radius, 0.125rem);box-shadow:0 0 0rem 1px rgba(0,0,0,.05);overflow:hidden}.post-grid-cover{display:block;margin:0 0 1.5rem;position:relative;border-bottom:1px solid rgba(0,0,0,.05)}.post-grid-img{min-width:100%;height:11rem;object-fit:cover;object-position:center;border-top-left-radius:var(--bf-radius, 0.125rem);border-top-right-radius:var(--bf-radius, 0.125rem)}.post-grid-cats{padding:0 1rem;line-height:1.4;display:flex;flex-wrap:wrap}.post-grid-cats-link{text-decoration:none;font-size:.75rem;margin-right:.5rem;color:#999}.post-grid-cats-link::before{content:"#";display:inline-block;margin-right:.1rem}.post-grid-cats-link:hover{color:var(--bf-color)}.post-grid-title{font-size:1.125rem;font-weight:600;padding:1rem 1rem 0}.post-grid-link{color:#000;transition:color ease-in-out .2s;text-decoration:none}.post-grid-link:hover{color:var(--bf-color)}.post-grid-link::before{content:"";display:block;width:100%;height:11rem;background-color:transparent;top:0;left:0;position:absolute}.post-grid-desc{font-size:.875rem;color:#777;margin:0;line-height:1.75;transition:color ease-in-out .2s;padding:.5rem 1rem 1rem}.post-grid-meta{font-size:.75rem;color:#555;border-top:1px solid rgba(0,0,0,.05);opacity:.7;padding:1rem;margin-top:auto;position:relative}.post-grid-meta a{text-decoration:none;color:#555}.post-grid-meta a:hover,.post-grid-meta a:focus{color:var(--bf-color)}.post-grid-author{display:flex;align-items:center;margin-right:.875rem}.post-grid-author-img{border-radius:50rem;margin-right:.25rem;filter:grayscale(100%)}.post-grid-author-name{text-transform:capitalize}.post-grid-date{display:flex;align-items:center;margin-right:.875rem}.post-grid-date-icon{margin-right:.375rem}.post-grid-more{font-weight:500;position:absolute;top:0;right:0;padding-right:2.5rem;height:100%;line-height:3.125rem;font-weight:500}.post-grid-more .bi{margin-left:.1rem;width:1.5rem;height:1.5rem;position:absolute;right:1rem;top:50%;transform:translateY(-50%)}.post-grid:hover{box-shadow:0 0 10rem rgba(0,0,0,.1);border-color:#fff}.post-grid:hover .post-grid-link{color:var(--bf-color)}.post-grid:hover .post-grid-desc{color:#000}.post-list{align-items:center;height:100%;padding-left:20rem;margin-bottom:2rem;position:relative}.post-list-cover{position:absolute;top:0;left:0;margin:0;display:block;width:20rem;height:100%;border-radius:var(--bf-radius, 0.125rem);overflow:hidden}.post-list-cover::after{content:"";width:100%;height:100%;position:absolute;top:0;left:0;display:block;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);border-radius:var(--bf-radius, 0.125rem)}.post-list-img{width:100%;height:100%;object-fit:cover;object-position:center}.post-list-details{padding:1.5rem 0 1.5rem 2rem;flex-grow:1}.post-list-meta{font-size:.75rem;color:#555;margin-top:1rem;margin-bottom:1rem;opacity:.7}.post-list-meta a{color:#555;text-decoration:none}.post-list-meta-item{display:flex;align-items:center;margin-right:.875rem}.post-list-author-img{border-radius:5rem;margin-right:.25rem;filter:grayscale(100%)}.post-list-author-name{text-transform:capitalize}.post-list-date-icon{margin-right:.375rem}.post-list-cat-icon{margin-right:0rem}.post-list-title{font-size:1.5rem;font-weight:500;margin-bottom:1rem}.post-list-link{color:#000;transition:color ease-in-out .2s;text-decoration:none}.post-list-link:hover{color:var(--bf-color)}.post-list-link::before{content:"";display:block;width:20rem;height:100%;top:0;left:0;position:absolute}.post-list-desc{margin-bottom:.5rem;font-size:.875rem;color:#666;line-height:1.75;transition:color ease-in-out .2s;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.post-list-more{display:inline-block;font-weight:500;font-size:.875rem;color:#555;text-decoration:none}.post-list-more:hover{color:var(--bf-color)}.post-list:hover .post-list-desc{color:#000}.post-cover{width:100%;height:20rem;margin:0;position:relative;background-color:#000}.post-cover-img{height:100%;width:100%;object-fit:cover;object-position:center;opacity:.9}.post-cover-caption{display:block;max-width:100%;width:calc(var(--bf-post-width) + 8rem);position:relative;bottom:calc(8rem / 2);margin:-1.5rem auto 0;color:#fff;font-size:.625rem;text-align:right;line-height:1rem}.post-cover-caption a,.post-cover-caption span{border-radius:.125rem;display:inline-block;padding:0 .25rem;opacity:.7;color:#fff;background-color:rgba(0,0,0,.3)}.post-cover-caption a:hover,.post-cover-caption span:hover{opacity:1;background-color:#000}.post-cover::after{content:"";display:block;height:calc(8rem / 2);width:calc(var(--bf-post-width) + 8rem);max-width:100%;position:absolute;left:50%;bottom:0;transform:translateX(-50%);background-color:#fff;border-top-left-radius:var(--bf-radius, 0.125rem);border-top-right-radius:var(--bf-radius, 0.125rem)}.post-container{max-width:calc( var(--bf-post-width) + 3rem );width:100%;margin-right:auto;margin-left:auto;padding-right:1.5rem;padding-left:1.5rem}.post-title{margin-bottom:2.5rem;line-height:1.1;letter-spacing:-1px;font-weight:700;font-size:2.75rem;color:#000}.post-meta{margin-bottom:2.5rem;line-height:1.4;font-size:.875rem;font-weight:300;color:#666;white-space:nowrap;cursor:default}.post-meta-item:not(:last-of-type){margin-right:1.75rem}.post-meta-label{display:block;font-size:.75rem;color:#999}.post-meta a{color:#666;text-decoration:none}.post-meta a:hover{color:var(--bf-color)}.post-meta-btn{height:2rem;width:2rem;border:0;margin:0;padding:0;background:none;text-align:center}.post-meta-btn .bi{width:1.25rem;height:100%;fill:#888}.post-meta-btn:hover .bi{fill:var(--bf-color)}.post-meta-author{display:flex;align-items:center}.post-meta-author-img{margin-right:.5rem;border-radius:100%}.post-meta-author-name{text-transform:capitalize}.post-meta-cats{word-break:keep-all}.post-meta-cats-list{margin:0;padding:0;list-style:none}.post-meta-cats-item:not(:last-child){margin-right:.125rem}.post-meta-cats-item:not(:last-child) .post-meta-cats-link::after{content:","}.post-content{line-height:1.8;margin-bottom:4rem;hyphens:auto}.post-content a{overflow-wrap:break-word;word-break:break-all}.post-content iframe,.post-content video,.post-content img{border-radius:.125rem}.post-content audio,.post-content video{margin-bottom:1rem;width:100%;max-width:100%}.post-footer{margin-bottom:4rem}.post-author{margin-bottom:4rem;padding:2rem;border-radius:var(--bf-radius);background-color:var(--bs-light)}.post-author-name{margin:0 0 .25rem;font-size:1.125rem;text-transform:capitalize}.post-author-bio{margin-bottom:0;font-size:.875rem;font-weight:400;color:#000}.post-author-cover{margin:0 1.5rem 0 0;min-width:5rem}.post-author-img{width:5rem;height:5rem;border-radius:100%}.post #disqus_thread:not(:empty){padding:2rem;background-color:var(--bs-light);margin-bottom:4rem}.post #disqus_thread:not(:empty)>iframe{margin:0 !important}@media screen and (max-width: 767px){.post{margin-top:-2rem}.post-cover{margin-bottom:2rem;height:14rem}.post-cover-caption{display:none}.post-cover::after{border-radius:0;display:none}.post-title{font-size:1.5rem;letter-spacing:0}.post-meta{font-size:.75rem}.post-meta-item:not(:last-of-type){margin-right:1rem}.post-meta-cats-link::after{display:none}.post-meta-cats-item{display:none}.post-meta-cats-item:nth-child(1){display:block}}.post-nav{margin-bottom:4rem}.post-nav-item{position:relative;color:#000;text-decoration:none;display:block}.post-nav-item:hover{color:var(--bf-color)}.post-nav-title{z-index:2;font-size:.875rem;font-weight:600;margin:0}.post-nav-text{opacity:.5;text-transform:uppercase;font-size:.75rem}.share-modal .modal-dialog{max-width:25rem}.share-modal .modal-title{font-size:.875rem}.share-modal .btn-close{background-size:.75rem}.share-modal-list{list-style:none;padding:0;margin-bottom:2rem}.share-modal-link{padding:1.25rem 0 1.25rem;display:block;text-align:center;color:#666;background-color:rgba(68,68,68,.03);border-radius:.25rem;transition:all ease-in-out .2s;text-decoration:none}.share-modal-link-label{display:block;font-size:.75rem}.share-modal-link-icon{width:1.5rem;height:1.5rem;margin-bottom:.5rem;transition:fill ease .1s}.share-modal-link:hover{color:#fff;background-color:#444}.share-modal-link.-blogifier{background-color:rgba(98,42,255,.07);color:#622aff}.share-modal-link.-blogifier:hover{background-color:#622aff;color:#fff}.share-modal-link.-facebook{background-color:rgba(22,120,242,.07);color:#1678f2}.share-modal-link.-facebook:hover{background-color:#1678f2;color:#fff}.share-modal-link.-twitter{background-color:rgba(29,161,242,.07);color:#1da1f2}.share-modal-link.-twitter:hover{background-color:#1da1f2;color:#fff}.share-modal-link.-github{background-color:rgba(23,22,23,.07);color:#171617}.share-modal-link.-github:hover{background-color:#171617;color:#fff}.share-modal-link.-pinterest{background-color:rgba(230,0,35,.07);color:#e60023}.share-modal-link.-pinterest:hover{background-color:#e60023;color:#fff}.share-modal-link.-linkedin{background-color:rgba(10,102,194,.07);color:#0a66c2}.share-modal-link.-linkedin:hover{background-color:#0a66c2;color:#fff}.share-modal-link.-instagram{background-color:rgba(185,0,180,.07);color:#b900b4}.share-modal-link.-instagram:hover{background-color:#b900b4;color:#fff}.share-modal-link.-telegram{background-color:rgba(0,136,204,.07);color:#08c}.share-modal-link.-telegram:hover{background-color:#08c;color:#fff}.share-modal-link.-youtube{background-color:rgba(255,3,0,.07);color:#ff0300}.share-modal-link.-youtube:hover{background-color:#ff0300;color:#fff}.share-modal-link.-whatsapp{background-color:rgba(37,211,102,.07);color:#25d366}.share-modal-link.-whatsapp:hover{background-color:#25d366;color:#fff}.share-modal-input{border:0;background-color:rgba(68,68,68,.04);width:100%;padding:.75rem 1rem;outline:none !important;font-size:.875rem;border-radius:.25rem;cursor:pointer;color:#666;transition:all ease-in-out .3s}.share-modal-input:hover{color:#111;background-color:rgba(68,68,68,.07)}.share-modal-input.copied{background-color:#000;color:#fff;text-align:center}.related{margin-top:-1px;margin-bottom:4rem}.related-header{border-top:1px solid var(--bs-light);padding-top:3rem;margin-bottom:3rem;align-items:center;display:flex}.related-header-title{margin-bottom:0;font-size:1rem}.related-header-link{margin-bottom:0;color:rgba(0,0,0,.5);font-size:.875rem;font-weight:500;text-decoration:none}.related-header-link:hover{color:var(--bf-color)}/*# sourceMappingURL=styles.css.map */ diff --git a/src/Blogifier/wwwroot/themes/standard/css/styles.css.map b/src/Blogifier/wwwroot/themes/standard/css/styles.css.map deleted file mode 100644 index 22a3b3e48..000000000 --- a/src/Blogifier/wwwroot/themes/standard/css/styles.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":["../scss/styles.scss","../scss/helpers/_reset.scss","../scss/helpers/_base.scss","../scss/layout/_header.scss","../scss/layout/_nav.scss","../scss/helpers/_variables.scss","../scss/helpers/_mixins.scss","../scss/layout/_widgets.scss","../scss/layout/_footer.scss","../scss/components/_pagination.scss","../scss/components/_newsletter.scss","../scss/components/_dropdowns.scss","../scss/components/_highlight.scss","../scss/post/_featured.scss","../scss/post/_view-grid.scss","../scss/post/_view-list.scss","../scss/post/_post.scss","../scss/post/_nav.scss","../scss/post/_share.scss","../scss/post/_related.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA,MCCE,eAGF,OACE,sBACA,gBACA,qBAGF,eAIE,qBAGF,MACE,WAGF,WACE,gBACA,kBACA,kBACA,qBACA,qCAEE,kBAEA,WACA,eACA,aACA,cACA,+UACA,cACA,uBACA,4BACA,WAGF,mBACE,WACA,OACA,yBAGF,kBACE,cACA,QAIA,wBACE,gBCtDN,WACE,cACA,eAGF,yBACE,MACE,uBCPJ,QACE,iBACA,oBAEA,cACE,qBACA,SACA,sCACA,0CACA,mCACA,gBACA,qBACA,wCAEE,mCAIJ,aACE,SACA,kBACA,gBACA,wCAGF,aACE,cACA,iBACE,kCACA,oCAIJ,yBAjCF,QAkCI,UACA,mBACA,kCCnCF,eACE,gBACA,SACA,UAGF,oCAEE,WACA,cACA,mBACA,SACA,gBACA,qBACA,cCwBK,QDvBL,gCACA,kBACA,qBACA,eAEA,gGAEE,WACA,iCAEF,wFAEE,8BAIJ,iBACE,eACA,gBACA,kCACE,aACA,cAGJ,mBACE,UACA,aAGF,oBACE,kBACA,2BACE,WACA,UACA,YACA,kBACA,QACA,2BACA,cACA,gCACA,cAEF,2CACE,gCAGJ,yBA9DF,YA+DI,wCACA,qCACA,sBACA,uBE1CA,6BACE,MDTG,QCQL,4BACE,MDTG,QCQL,2BACE,MDTG,QCQL,0BACE,MDTG,QCQL,6BACE,MDTG,QCQL,4BACE,MDTG,QCQL,6BACE,MDTG,QCQL,4BACE,MDTG,KCQL,2BACE,MDTG,QCQL,4BACE,MDTG,QEfP,kBACE,gBACA,oBACA,WACA,eACA,yBACA,cACA,mBAGA,uBACE,gBACA,UACA,SCdN,QACE,qCACA,gBACA,WACA,iBACA,UACE,WACA,qBACA,gBACE,MHRE,gBGWN,UACE,SCbJ,YACE,sBACA,YACA,mBACA,uBAEA,iBACE,eACA,UAEF,iBACE,cACA,WACA,gBACA,kBACA,YACA,iBACA,qBACA,yBACA,4BACA,WAEA,uBACE,UACA,MJvBE,gBIwBF,2BACE,eACA,gBAGJ,qBACE,cACA,eACA,wBACA,gBACA,iCCnCN,YACE,sBACA,WACA,cLqCW,2BKpCX,gBACA,kBAEA,iBACE,aAEA,qCAHF,iBAII,cAIJ,kBACE,kBACA,gBACA,WAGF,iBACE,SACA,mBACA,gBAGF,iBACE,sBACA,cLWS,2BKRX,kBACE,gBACA,SACA,YACA,WACA,mBACA,WACA,wBAEF,gBACE,gBACA,SACA,mBACA,WACA,cACA,2BACA,4CAEE,aACA,gBAIJ,gBACE,aACA,kBACA,sBACA,cLnBS,2BKoBT,kBACA,MACA,OACA,QACA,SACA,aACA,sBACA,SACA,WACA,eAEA,gCACE,WCvEN,eACE,cNqCO,QMpCP,mCACA,YAGF,eACE,kBACA,gBACA,0BACA,mBACA,WACA,+BACA,2CAEE,WACA,iCAGJ,kBACE,gBACA,oCACA,SCvBF,SACE,cACA,gBACA,aACA,MP0BgB,QOzBhB,WP0BgB,QOzBhB,SACA,qCACA,kBACA,gBACA,gBACA,oBACA,kBACA,iBACA,gBACA,gBACA,cACA,WACA,qBACA,kBACA,iBACA,aAGF,cACE,MPOc,QOJhB,gJASE,MPJU,QOOZ,gBACE,cAEF,WACE,cAEF,uFAOE,cAGF,2IASE,MP/BS,KOkCX,wBAEE,WAGF,eACE,cAGF,eACE,cAGF,eACE,kBAGF,aACE,iBCtFF,UACE,mBAGA,gBACE,SACA,gBACA,gBACA,gBACA,kBACA,cR8BS,2BQ7BT,2CAEA,qCATF,gBAUI,cAEF,qBACE,cACA,kBACA,MACA,OACA,WACA,YAEF,oBACE,cReO,2BQdP,kBACA,MACA,OACA,WACA,YACA,iBACA,uBAKJ,kBACE,iBACA,oBACA,kBAIF,gBACE,kBACA,gBACA,qBACA,gBAEF,eACE,qBACA,WACA,qBACE,MRrDE,gBQ0DN,eACE,kBACA,WACA,qBACA,WACA,iBACE,WACA,uBACE,WAIN,iBACE,aACA,mBACA,kBACA,qBACE,oBACA,oBACA,uBAEF,sBACE,0BAGJ,eACE,aACA,mBACA,qBAEA,oBACE,qBAKJ,cACE,aACA,mBACA,mBACE,kBAEF,oBACE,qBAKJ,eACE,WACA,gBACA,qBAEF,eACE,gBACA,kBACA,MRlHI,gBQmHJ,qBACA,qBACE,WAKJ,8BAEE,WACA,UAEF,eACE,WAEF,eACE,cAKA,8DAEE,WAIJ,qCA/IF,UAgJI,wCACA,0BACA,iCAEA,eACE,eAGF,kBACE,UACA,oBAGF,gBACE,kBACA,oBACA,gBAGF,eACE,kBACA,qBCrKN,WACE,YACA,kBACA,sCACA,cToCW,2BSnCX,wCACA,gBAGA,iBACE,cACA,kBACA,kBACA,wCAEF,eACE,eACA,aACA,iBACA,uBACA,uBToBS,2BSnBT,wBTmBS,2BShBX,gBACE,eACA,gBACA,aACA,eAEA,qBACE,qBACA,iBACA,mBACA,WACA,6BACE,YACA,qBACA,mBAGF,2BACE,MTzCA,gBS+CN,iBACE,mBACA,gBACA,oBAGF,gBACE,WACA,iCACA,qBACA,sBACE,MT1DE,gBS4DJ,wBACE,WACA,cACA,WACA,aACA,6BACA,MACA,OACA,kBAKJ,gBACE,kBACA,WACA,SACA,iBACA,iCACA,wBAIF,gBACE,iBACA,WACA,qCAEA,WACA,aACA,gBACA,kBACA,kBACE,qBACA,WACA,gDAEE,MTjGA,gBSqGN,kBACE,aACA,mBACA,qBACA,sBACE,oBACA,oBACA,uBAEF,uBACE,0BAGJ,gBACE,aACA,mBACA,qBACA,qBACE,qBAIJ,gBACE,gBACA,kBACA,MACA,QACA,qBACA,YACA,qBACA,gBAEA,oBACE,kBACA,aACA,cACA,kBACA,WACA,QACA,2BAKJ,iBACE,oCACA,kBACA,iCACE,MTrJE,gBSuJJ,iCACE,WCzJN,WACE,mBACA,YACA,mBACA,mBACA,kBAGA,iBACE,kBACA,MACA,OACA,SACA,cACA,YACA,YACA,cVwBS,2BUvBT,gBAEA,wBACE,WACA,WACA,YACA,kBACA,MACA,OACA,cACA,0CACA,cVYO,2BUTX,eACE,WACA,YACA,iBACA,uBAIF,mBACE,6BACA,YAIF,gBACE,iBACA,WACA,gBACA,mBACA,WACA,kBACE,WACA,qBAEF,qBACE,aACA,mBACA,qBAIF,sBACE,mBACA,oBACA,uBAEF,uBACE,0BAIF,qBACE,qBAIF,oBACE,kBAKJ,iBACE,iBACA,gBACA,mBAGF,gBACE,WACA,iCACA,qBACA,sBACE,MV7FE,gBUgGJ,wBACE,WACA,cACA,YACA,YACA,MACA,OACA,kBAKJ,gBACE,oBACA,kBACA,WACA,iBACA,iCAEA,oBACA,qBACA,4BACA,gBAIF,gBACE,qBACA,gBACA,kBACA,WACA,qBACA,sBACE,MVjIE,gBUuIJ,iCACE,WCxIJ,YACE,WACA,aACA,SACA,kBACA,sBACA,gBACE,YACA,WACA,iBACA,uBACA,WAEF,oBACE,cACA,eACA,wCACA,kBACA,sBACA,sBACA,WACA,kBACA,iBACA,iBACA,+CAEE,cXYC,QWXD,qBACA,iBACA,WACA,WACA,gCACA,2DACE,UACA,sBAMN,mBACE,WACA,cACA,sBACA,wCACA,eACA,kBACA,SACA,SACA,2BACA,sBACA,uBXZO,2BWaP,wBXbO,2BWiBX,gBACE,8CAGA,WACA,kBACA,iBACA,qBACA,oBAIF,YACE,qBACA,gBACA,oBACA,gBACA,kBACA,WAGF,WACE,qBACA,gBACA,kBACA,gBACA,WACA,mBACA,eAGE,mCACE,qBAIJ,iBACE,cACA,iBACA,WAGF,aACE,WACA,qBACA,mBACE,MXtGA,gBW0GJ,eACE,YACA,WACA,SACA,SACA,UACA,gBACA,kBAEA,mBACE,cACA,YACA,UAIA,yBACE,KX3HF,gBWgIJ,kBACE,aACA,mBACA,sBACE,mBACA,mBAEF,uBACE,0BAIJ,gBACE,oBACA,qBACE,SACA,UACA,gBAGA,sCACE,qBAEE,kEACE,YASZ,cACE,gBACA,mBACA,aACA,gBACE,yBACA,qBAEF,2DAGE,sBAGF,wCAEE,mBACA,WACA,eAKJ,aACE,mBAIF,aACE,mBACA,aACA,+BACA,iCACA,kBACE,kBACA,mBACA,0BAEF,iBACE,gBACA,kBACA,gBACA,WAEF,mBACE,oBACA,eAEF,iBACE,WACA,YACA,mBAMF,iCACE,aACA,iCACA,mBAEA,wCACE,oBAKN,qCAtOF,MAuOI,iBAEA,YACE,mBACA,aACA,oBACE,aAEF,mBACE,gBACA,aAIJ,YACE,iBACA,iBAGF,WACE,iBAEE,mCACE,kBAKA,4BACE,aAGJ,qBACE,aAEA,kCACE,eC3QZ,UACE,mBAGA,eACE,kBACA,WACA,qBACA,cACA,qBACE,MZTE,gBYcN,gBACE,UACA,kBACA,gBACA,SAIF,eACE,WACA,yBACA,iBCxBA,2BACE,gBAEF,0BACE,kBAEF,wBACE,uBAKF,kBACE,gBACA,UACA,mBAGF,kBACE,0BACA,cACA,kBACA,WACA,oCACA,qBACA,+BACA,qBACA,wBACE,cACA,iBAEF,uBACE,aACA,cACA,oBACA,yBAGF,wBACE,WACA,sBZnCN,6BACE,iBYwCM,oBZvCN,MDOK,QCLL,mCACE,iBDIG,QCHH,MYsCI,KZ5CR,4BACE,iBYwCM,qBZvCN,MDOK,QCLL,kCACE,iBDIG,QCHH,MYsCI,KZ5CR,2BACE,iBYwCM,qBZvCN,MDOK,QCLL,iCACE,iBDIG,QCHH,MYsCI,KZ5CR,0BACE,iBYwCM,mBZvCN,MDOK,QCLL,gCACE,iBDIG,QCHH,MYsCI,KZ5CR,6BACE,iBYwCM,mBZvCN,MDOK,QCLL,mCACE,iBDIG,QCHH,MYsCI,KZ5CR,4BACE,iBYwCM,qBZvCN,MDOK,QCLL,kCACE,iBDIG,QCHH,MYsCI,KZ5CR,6BACE,iBYwCM,oBZvCN,MDOK,QCLL,mCACE,iBDIG,QCHH,MYsCI,KZ5CR,4BACE,iBYwCM,oBZvCN,MDOK,KCLL,kCACE,iBDIG,KCHH,MYsCI,KZ5CR,2BACE,iBYwCM,kBZvCN,MDOK,QCLL,iCACE,iBDIG,QCHH,MYsCI,KZ5CR,4BACE,iBYwCM,qBZvCN,MDOK,QCLL,kCACE,iBDIG,QCHH,MYsCI,KAKN,mBACE,SACA,oCACA,WACA,oBACA,wBACA,kBACA,qBACA,eACA,WACA,+BACA,yBACE,WACA,oCAEF,0BACE,sBACA,WACA,kBC1ER,SACE,gBACA,mBAEA,gBACE,qCACA,iBACA,mBACA,mBACA,aACA,sBACE,gBACA,eAEF,qBACE,gBACA,qBACA,kBACA,gBACA,qBACA,2BACE,MdpBA","file":"styles.css"} \ No newline at end of file diff --git a/src/Blogifier/wwwroot/themes/standard/js/highlight.js b/src/Blogifier/wwwroot/themes/standard/js/highlight.js deleted file mode 100644 index 07ead4002..000000000 --- a/src/Blogifier/wwwroot/themes/standard/js/highlight.js +++ /dev/null @@ -1,1257 +0,0 @@ -/* - Highlight.js 10.5.0 (af20048d) - License: BSD-3-Clause - Copyright (c) 2006-2020, Ivan Sagalaev -*/ -var hljs=function(){"use strict";function e(t){ -return t instanceof Map?t.clear=t.delete=t.set=()=>{ -throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ -throw Error("set is read-only") -}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{var s=t[n] -;"object"!=typeof s||Object.isFrozen(s)||e(s)})),t}var t=e,n=e;t.default=n -;class s{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data} -ignoreMatch(){this.ignore=!0}}function r(e){ -return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") -}function a(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] -;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const i=e=>!!e.kind -;class o{constructor(e,t){ -this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ -this.buffer+=r(e)}openNode(e){if(!i(e))return;let t=e.kind -;e.sublanguage||(t=`${this.classPrefix}${t}`),this.span(t)}closeNode(e){ -i(e)&&(this.buffer+="
")}value(){return this.buffer}span(e){ -this.buffer+=``}}class l{constructor(){this.rootNode={ -children:[]},this.stack=[this.rootNode]}get top(){ -return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ -this.top.children.push(e)}openNode(e){const t={kind:e,children:[]} -;this.add(t),this.stack.push(t)}closeNode(){ -if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ -for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} -walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ -return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), -t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ -"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ -l._collapse(e)})))}}class c extends l{constructor(e){super(),this.options=e} -addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())} -addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root -;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){ -return new o(this,this.options).value()}finalize(){return!0}}function u(e){ -return e?"string"==typeof e?e:e.source:null} -const g="[a-zA-Z]\\w*",d="[a-zA-Z_]\\w*",h="\\b\\d+(\\.\\d+)?",f="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",p="\\b(0b[01]+)",m={ -begin:"\\\\[\\s\\S]",relevance:0},b={className:"string",begin:"'",end:"'", -illegal:"\\n",contains:[m]},x={className:"string",begin:'"',end:'"', -illegal:"\\n",contains:[m]},E={ -begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ -},v=(e,t,n={})=>{const s=a({className:"comment",begin:e,end:t,contains:[]},n) -;return s.contains.push(E),s.contains.push({className:"doctag", -begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),s -},N=v("//","$"),w=v("/\\*","\\*/"),R=v("#","$");var y=Object.freeze({ -__proto__:null,IDENT_RE:g,UNDERSCORE_IDENT_RE:d,NUMBER_RE:h,C_NUMBER_RE:f, -BINARY_NUMBER_RE:p, -RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", -SHEBANG:(e={})=>{const t=/^#![ ]*\// -;return e.binary&&(e.begin=((...e)=>e.map((e=>u(e))).join(""))(t,/.*\b/,e.binary,/\b.*/)), -a({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{ -0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:m,APOS_STRING_MODE:b, -QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:E,COMMENT:v,C_LINE_COMMENT_MODE:N, -C_BLOCK_COMMENT_MODE:w,HASH_COMMENT_MODE:R,NUMBER_MODE:{className:"number", -begin:h,relevance:0},C_NUMBER_MODE:{className:"number",begin:f,relevance:0}, -BINARY_NUMBER_MODE:{className:"number",begin:p,relevance:0},CSS_NUMBER_MODE:{ -className:"number", -begin:h+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", -relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp", -begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[m,{begin:/\[/,end:/\]/, -relevance:0,contains:[m]}]}]},TITLE_MODE:{className:"title",begin:g,relevance:0 -},UNDERSCORE_TITLE_MODE:{className:"title",begin:d,relevance:0},METHOD_GUARD:{ -begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{ -"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ -t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function _(e,t){ -"."===e.input[e.index-1]&&t.ignoreMatch()}function k(e,t){ -t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", -e.__beforeBegin=_,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords) -}function M(e,t){ -Array.isArray(e.illegal)&&(e.illegal=((...e)=>"("+e.map((e=>u(e))).join("|")+")")(...e.illegal)) -}function O(e,t){if(e.match){ -if(e.begin||e.end)throw Error("begin & end are not supported with match") -;e.begin=e.match,delete e.match}}function A(e,t){ -void 0===e.relevance&&(e.relevance=1)} -const L=["of","and","for","in","not","or","if","then","parent","list","value"] -;function B(e,t){return t?Number(t):(e=>L.includes(e.toLowerCase()))(e)?0:1} -function I(e,{plugins:t}){function n(t,n){ -return RegExp(u(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class s{ -constructor(){ -this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} -addRule(e,t){ -t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), -this.matchAt+=(e=>RegExp(e.toString()+"|").exec("").length-1)(e)+1}compile(){ -0===this.regexes.length&&(this.exec=()=>null) -;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(((e,t="|")=>{ -const n=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;let s=0,r="" -;for(let a=0;a0&&(r+=t),r+="(";o.length>0;){const e=n.exec(o);if(null==e){r+=o;break} -r+=o.substring(0,e.index), -o=o.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+(Number(e[1])+i):(r+=e[0], -"("===e[0]&&s++)}r+=")"}return r})(e),!0),this.lastIndex=0}exec(e){ -this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e) -;if(!t)return null -;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),s=this.matchIndexes[n] -;return t.splice(0,n),Object.assign(t,s)}}class r{constructor(){ -this.rules=[],this.multiRegexes=[], -this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ -if(this.multiRegexes[e])return this.multiRegexes[e];const t=new s -;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), -t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ -return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ -this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ -const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex -;let n=t.exec(e) -;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ -const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} -return n&&(this.regexIndex+=n.position+1, -this.regexIndex===this.count&&this.considerAll()),n}} -if(e.compilerExtensions||(e.compilerExtensions=[]), -e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") -;return e.classNameAliases=a(e.classNameAliases||{}),function t(s,i){const o=s -;if(s.compiled)return o -;[O].forEach((e=>e(s,i))),e.compilerExtensions.forEach((e=>e(s,i))), -s.__beforeBegin=null,[k,M,A].forEach((e=>e(s,i))),s.compiled=!0;let l=null -;if("object"==typeof s.keywords&&(l=s.keywords.$pattern, -delete s.keywords.$pattern),s.keywords&&(s.keywords=((e,t)=>{const n={} -;return"string"==typeof e?s("keyword",e):Object.keys(e).forEach((t=>{s(t,e[t]) -})),n;function s(e,s){t&&(s=s.toLowerCase()),s.split(" ").forEach((t=>{ -const s=t.split("|");n[s[0]]=[e,B(s[0],s[1])]}))} -})(s.keywords,e.case_insensitive)), -s.lexemes&&l)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ") -;return l=l||s.lexemes||/\w+/, -o.keywordPatternRe=n(l,!0),i&&(s.begin||(s.begin=/\B|\b/), -o.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin), -s.end||s.endsWithParent||(s.end=/\B|\b/), -s.end&&(o.endRe=n(s.end)),o.terminatorEnd=u(s.end)||"", -s.endsWithParent&&i.terminatorEnd&&(o.terminatorEnd+=(s.end?"|":"")+i.terminatorEnd)), -s.illegal&&(o.illegalRe=n(s.illegal)), -s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>a(e,{ -variants:null},t)))),e.cachedVariants?e.cachedVariants:T(e)?a(e,{ -starts:e.starts?a(e.starts):null -}):Object.isFrozen(e)?a(e):e))("self"===e?s:e)))),s.contains.forEach((e=>{t(e,o) -})),s.starts&&t(s.starts,i),o.matcher=(e=>{const t=new r -;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" -}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" -}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(o),o}(e)}function T(e){ -return!!e&&(e.endsWithParent||T(e.starts))}function j(e){const t={ -props:["language","code","autodetect"],data:()=>({detectedLanguage:"", -unknownLanguage:!1}),computed:{className(){ -return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){ -if(!this.autoDetect&&!e.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`), -this.unknownLanguage=!0,r(this.code);let t={} -;return this.autoDetect?(t=e.highlightAuto(this.code), -this.detectedLanguage=t.language):(t=e.highlight(this.language,this.code,this.ignoreIllegals), -this.detectedLanguage=this.language),t.value},autoDetect(){ -return!(this.language&&(e=this.autodetect,!e&&""!==e));var e}, -ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{ -class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{ -Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}const S={ -"after:highlightBlock":({block:e,result:t,text:n})=>{const s=D(e) -;if(!s.length)return;const a=document.createElement("div") -;a.innerHTML=t.value,t.value=((e,t,n)=>{let s=0,a="";const i=[];function o(){ -return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset"}function c(e){ -a+=""}function u(e){("start"===e.event?l:c)(e.node)} -for(;e.length||t.length;){let t=o() -;if(a+=r(n.substring(s,t[0].offset)),s=t[0].offset,t===e){i.reverse().forEach(c) -;do{u(t.splice(0,1)[0]),t=o()}while(t===e&&t.length&&t[0].offset===s) -;i.reverse().forEach(l) -}else"start"===t[0].event?i.push(t[0].node):i.pop(),u(t.splice(0,1)[0])} -return a+r(n.substr(s))})(s,D(a),n)}};function P(e){ -return e.nodeName.toLowerCase()}function D(e){const t=[];return function e(n,s){ -for(let r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?s+=r.nodeValue.length:1===r.nodeType&&(t.push({ -event:"start",offset:s,node:r}),s=e(r,s),P(r).match(/br|hr|img|input/)||t.push({ -event:"stop",offset:s,node:r}));return s}(e,0),t}const C=e=>{console.error(e) -},H=(e,...t)=>{console.log("WARN: "+e,...t)},$=(e,t)=>{ -console.log(`Deprecated as of ${e}. ${t}`)},U=r,z=a,K=Symbol("nomatch") -;return(e=>{const n=Object.create(null),r=Object.create(null),a=[];let i=!0 -;const o=/(^(<[^>]+>|\t|)+|\n)/gm,l="Could not find the language '{}', did you forget to load/include a language module?",u={ -disableAutodetect:!0,name:"Plain text",contains:[]};let g={ -noHighlightRe:/^(no-?highlight)$/i, -languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", -tabReplace:null,useBR:!1,languages:null,__emitter:c};function d(e){ -return g.noHighlightRe.test(e)}function h(e,t,n,s){const r={code:t,language:e} -;_("before:highlight",r);const a=r.result?r.result:f(r.language,r.code,n,s) -;return a.code=r.code,_("after:highlight",a),a}function f(e,t,r,o){const c=t -;function u(e,t){const n=w.case_insensitive?t[0].toLowerCase():t[0] -;return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]} -function d(){null!=_.subLanguage?(()=>{if(""===O)return;let e=null -;if("string"==typeof _.subLanguage){ -if(!n[_.subLanguage])return void M.addText(O) -;e=f(_.subLanguage,O,!0,k[_.subLanguage]),k[_.subLanguage]=e.top -}else e=p(O,_.subLanguage.length?_.subLanguage:null) -;_.relevance>0&&(A+=e.relevance),M.addSublanguage(e.emitter,e.language) -})():(()=>{if(!_.keywords)return void M.addText(O);let e=0 -;_.keywordPatternRe.lastIndex=0;let t=_.keywordPatternRe.exec(O),n="";for(;t;){ -n+=O.substring(e,t.index);const s=u(_,t);if(s){const[e,r]=s -;M.addText(n),n="",A+=r;const a=w.classNameAliases[e]||e;M.addKeyword(t[0],a) -}else n+=t[0];e=_.keywordPatternRe.lastIndex,t=_.keywordPatternRe.exec(O)} -n+=O.substr(e),M.addText(n)})(),O=""}function h(e){ -return e.className&&M.openNode(w.classNameAliases[e.className]||e.className), -_=Object.create(e,{parent:{value:_}}),_}function m(e,t,n){let r=((e,t)=>{ -const n=e&&e.exec(t);return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){ -const n=new s(e);e["on:end"](t,n),n.ignore&&(r=!1)}if(r){ -for(;e.endsParent&&e.parent;)e=e.parent;return e}} -if(e.endsWithParent)return m(e.parent,t,n)}function b(e){ -return 0===_.matcher.regexIndex?(O+=e[0],1):(T=!0,0)}function x(e){ -const t=e[0],n=c.substr(e.index),s=m(_,e,n);if(!s)return K;const r=_ -;r.skip?O+=t:(r.returnEnd||r.excludeEnd||(O+=t),d(),r.excludeEnd&&(O=t));do{ -_.className&&M.closeNode(),_.skip||_.subLanguage||(A+=_.relevance),_=_.parent -}while(_!==s.parent) -;return s.starts&&(s.endSameAsBegin&&(s.starts.endRe=s.endRe), -h(s.starts)),r.returnEnd?0:t.length}let E={};function v(t,n){const a=n&&n[0] -;if(O+=t,null==a)return d(),0 -;if("begin"===E.type&&"end"===n.type&&E.index===n.index&&""===a){ -if(O+=c.slice(n.index,n.index+1),!i){const t=Error("0 width match regex") -;throw t.languageName=e,t.badRule=E.rule,t}return 1} -if(E=n,"begin"===n.type)return function(e){ -const t=e[0],n=e.rule,r=new s(n),a=[n.__beforeBegin,n["on:begin"]] -;for(const n of a)if(n&&(n(e,r),r.ignore))return b(t) -;return n&&n.endSameAsBegin&&(n.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")), -n.skip?O+=t:(n.excludeBegin&&(O+=t), -d(),n.returnBegin||n.excludeBegin||(O=t)),h(n),n.returnBegin?0:t.length}(n) -;if("illegal"===n.type&&!r){ -const e=Error('Illegal lexeme "'+a+'" for mode "'+(_.className||"")+'"') -;throw e.mode=_,e}if("end"===n.type){const e=x(n);if(e!==K)return e} -if("illegal"===n.type&&""===a)return 1 -;if(B>1e5&&B>3*n.index)throw Error("potential infinite loop, way more iterations than matches") -;return O+=a,a.length}const w=N(e) -;if(!w)throw C(l.replace("{}",e)),Error('Unknown language: "'+e+'"') -;const R=I(w,{plugins:a});let y="",_=o||R;const k={},M=new g.__emitter(g);(()=>{ -const e=[];for(let t=_;t!==w;t=t.parent)t.className&&e.unshift(t.className) -;e.forEach((e=>M.openNode(e)))})();let O="",A=0,L=0,B=0,T=!1;try{ -for(_.matcher.considerAll();;){ -B++,T?T=!1:_.matcher.considerAll(),_.matcher.lastIndex=L -;const e=_.matcher.exec(c);if(!e)break;const t=v(c.substring(L,e.index),e) -;L=e.index+t}return v(c.substr(L)),M.closeAllNodes(),M.finalize(),y=M.toHTML(),{ -relevance:A,value:y,language:e,illegal:!1,emitter:M,top:_}}catch(t){ -if(t.message&&t.message.includes("Illegal"))return{illegal:!0,illegalBy:{ -msg:t.message,context:c.slice(L-100,L+100),mode:t.mode},sofar:y,relevance:0, -value:U(c),emitter:M};if(i)return{illegal:!1,relevance:0,value:U(c),emitter:M, -language:e,top:_,errorRaised:t};throw t}}function p(e,t){ -t=t||g.languages||Object.keys(n);const s=(e=>{const t={relevance:0, -emitter:new g.__emitter(g),value:U(e),illegal:!1,top:u} -;return t.emitter.addText(e),t})(e),r=t.filter(N).filter(R).map((t=>f(t,e,!1))) -;r.unshift(s);const a=r.sort(((e,t)=>{ -if(e.relevance!==t.relevance)return t.relevance-e.relevance -;if(e.language&&t.language){if(N(e.language).supersetOf===t.language)return 1 -;if(N(t.language).supersetOf===e.language)return-1}return 0})),[i,o]=a,l=i -;return l.second_best=o,l}const m={"before:highlightBlock":({block:e})=>{ -g.useBR&&(e.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")) -},"after:highlightBlock":({result:e})=>{ -g.useBR&&(e.value=e.value.replace(/\n/g,"
"))}},b=/^(<[^>]+>|\t)+/gm,x={ -"after:highlightBlock":({result:e})=>{ -g.tabReplace&&(e.value=e.value.replace(b,(e=>e.replace(/\t/g,g.tabReplace))))}} -;function E(e){let t=null;const n=(e=>{let t=e.className+" " -;t+=e.parentNode?e.parentNode.className:"";const n=g.languageDetectRe.exec(t) -;if(n){const t=N(n[1]) -;return t||(H(l.replace("{}",n[1])),H("Falling back to no-highlight mode for this block.",e)), -t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>d(e)||N(e)))})(e) -;if(d(n))return;_("before:highlightBlock",{block:e,language:n}),t=e -;const s=t.textContent,a=n?h(n,s,!0):p(s);_("after:highlightBlock",{block:e, -result:a,text:s}),e.innerHTML=a.value,((e,t,n)=>{const s=t?r[t]:n -;e.classList.add("hljs"),s&&e.classList.add(s)})(e,n,a.language),e.result={ -language:a.language,re:a.relevance,relavance:a.relevance -},a.second_best&&(e.second_best={language:a.second_best.language, -re:a.second_best.relevance,relavance:a.second_best.relevance})}const v=()=>{ -v.called||(v.called=!0,document.querySelectorAll("pre code").forEach(E))} -;function N(e){return e=(e||"").toLowerCase(),n[e]||n[r[e]]} -function w(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{r[e]=t -}))}function R(e){const t=N(e);return t&&!t.disableAutodetect}function _(e,t){ -const n=e;a.forEach((e=>{e[n]&&e[n](t)}))}Object.assign(e,{highlight:h, -highlightAuto:p,fixMarkup:e=>{ -return $("10.2.0","fixMarkup will be removed entirely in v11.0"), -$("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"), -t=e, -g.tabReplace||g.useBR?t.replace(o,(e=>"\n"===e?g.useBR?"
":e:g.tabReplace?e.replace(/\t/g,g.tabReplace):e)):t -;var t},highlightBlock:E,configure:e=>{ -e.useBR&&($("10.3.0","'useBR' will be removed entirely in v11.0"), -$("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")), -g=z(g,e)},initHighlighting:v,initHighlightingOnLoad:()=>{ -window.addEventListener("DOMContentLoaded",v,!1)},registerLanguage:(t,s)=>{ -let r=null;try{r=s(e)}catch(e){ -if(C("Language definition for '{}' could not be registered.".replace("{}",t)), -!i)throw e;C(e),r=u} -r.name||(r.name=t),n[t]=r,r.rawDefinition=s.bind(null,e),r.aliases&&w(r.aliases,{ -languageName:t})},listLanguages:()=>Object.keys(n),getLanguage:N, -registerAliases:w,requireLanguage:e=>{ -$("10.4.0","requireLanguage will be removed entirely in v11."), -$("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844") -;const t=N(e);if(t)return t -;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))}, -autoDetection:R,inherit:z,addPlugin:e=>{a.push(e)},vuePlugin:j(e).VuePlugin -}),e.debugMode=()=>{i=!1},e.safeMode=()=>{i=!0},e.versionString="10.5.0" -;for(const e in y)"object"==typeof y[e]&&t(y[e]) -;return Object.assign(e,y),e.addPlugin(m),e.addPlugin(S),e.addPlugin(x),e})({}) -}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("apache",(()=>{"use strict";return e=>{const n={ -className:"number",begin:/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?/} -;return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0, -contains:[e.HASH_COMMENT_MODE,{className:"section",begin:/<\/?/,end:/>/, -contains:[n,{className:"number",begin:/:\d{1,5}/ -},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute", -begin:/\w+/,relevance:0,keywords:{ -nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername" -},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"}, -contains:[{className:"meta",begin:/\s\[/,end:/\]$/},{className:"variable", -begin:/[\$%]\{/,end:/\}/,contains:["self",{className:"number",begin:/[$%]\d+/}] -},n,{className:"number",begin:/\d+/},e.QUOTE_STRING_MODE]}}],illegal:/\S/}} -})());hljs.registerLanguage("properties",(()=>{"use strict";return e=>{ -var n="[ \\t\\f]*",a=n+"[:=]"+n,t="("+a+"|[ \\t\\f]+)",r="([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",s="([^\\\\:= \\t\\f\\n]|\\\\.)+",i={ -end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{ -begin:"\\\\\\\\"},{begin:"\\\\\\n"}]}};return{name:".properties", -case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{ -returnBegin:!0,variants:[{begin:r+a,relevance:1},{begin:r+"[ \\t\\f]+", -relevance:0}],contains:[{className:"attr",begin:r,endsParent:!0,relevance:0}], -starts:i},{begin:s+t,returnBegin:!0,relevance:0,contains:[{className:"meta", -begin:s,endsParent:!0,relevance:0}],starts:i},{className:"attr",relevance:0, -begin:s+n+"$"}]}}})());hljs.registerLanguage("diff",(()=>{"use strict";return e=>({name:"Diff", -aliases:["patch"],contains:[{className:"meta",relevance:10,variants:[{ -begin:/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/},{begin:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{ -begin:/^--- +\d+,\d+ +----$/}]},{className:"comment",variants:[{begin:/Index: /, -end:/$/},{begin:/^index/,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^-{3}/,end:/$/ -},{begin:/^\*{3} /,end:/$/},{begin:/^\+{3}/,end:/$/},{begin:/^\*{15}$/},{ -begin:/^diff --git/,end:/$/}]},{className:"addition",begin:/^\+/,end:/$/},{ -className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, -end:/$/}]})})());hljs.registerLanguage("cpp",(()=>{"use strict";function e(e){ -return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?") -}return t=>{const n=(t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}] -}),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",s={ -className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},i={className:"string", -variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n", -contains:[t.BACKSLASH_ESCAPE]},{ -begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", -end:"'",illegal:"."},t.END_SAME_AS_BEGIN({ -begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},c={ -className:"number",variants:[{begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" -},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},o={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ -"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" -},contains:[{begin:/\\\n/,relevance:0},t.inherit(i,{className:"meta-string"}),{ -className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n" -},n,t.C_BLOCK_COMMENT_MODE]},l={className:"title",begin:e(r)+t.IDENT_RE, -relevance:0},d=e(r)+t.IDENT_RE+"\\s*\\(",u={ -keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq", -built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary", -literal:"true false nullptr NULL"},p=[o,s,n,t.C_BLOCK_COMMENT_MODE,c,i],m={ -variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{ -beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:p.concat([{ -begin:/\(/,end:/\)/,keywords:u,contains:p.concat(["self"]),relevance:0}]), -relevance:0},_={className:"function",begin:"("+a+"[\\*&\\s]+)+"+d, -returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:u,illegal:/[^\w\s\*&:<>.]/, -contains:[{begin:"decltype\\(auto\\)",keywords:u,relevance:0},{begin:d, -returnBegin:!0,contains:[l],relevance:0},{className:"params",begin:/\(/, -end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,i,c,s,{ -begin:/\(/,end:/\)/,keywords:u,relevance:0, -contains:["self",n,t.C_BLOCK_COMMENT_MODE,i,c,s]}] -},s,n,t.C_BLOCK_COMMENT_MODE,o]};return{ -aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:u, -disableAutodetect:!0,illegal:"",keywords:u,contains:["self",s]},{begin:t.IDENT_RE+"::",keywords:u},{ -className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/, -contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{ -preprocessor:o,strings:i,keywords:u}}})(t) -;return n.disableAutodetect=!1,n.name="C++", -n.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],n}})());hljs.registerLanguage("kotlin",(()=>{"use strict" -;var e="\\.([0-9](_*[0-9])*)",n="[0-9a-fA-F](_*[0-9a-fA-F])*",a={ -className:"number",variants:[{ -begin:`(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` -},{begin:`\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ -begin:`(${e})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ -begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` -},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ -begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], -relevance:0};return e=>{const n={ -keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", -built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", -literal:"true false null"},i={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" -},s={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},t={ -className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", -variants:[{begin:'"""',end:'"""(?=[^"])',contains:[t,s]},{begin:"'",end:"'", -illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, -contains:[e.BACKSLASH_ESCAPE,t,s]}]};s.contains.push(r);const l={ -className:"meta", -begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" -},c={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, -end:/\)/,contains:[e.inherit(r,{className:"meta-string"})]}] -},o=a,b=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),E={ -variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, -contains:[]}]},d=E;return d.variants[1].contains=[E],E.variants[1].contains=[d], -{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{ -relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}] -}),e.C_LINE_COMMENT_MODE,b,{className:"keyword", -begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", -begin:/@\w+/}]}},i,l,c,{className:"function",beginKeywords:"fun",end:"[(]|$", -returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ -begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, -contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, -keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, -endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, -endsWithParent:!0,contains:[E,e.C_LINE_COMMENT_MODE,b],relevance:0 -},e.C_LINE_COMMENT_MODE,b,l,c,r,e.C_NUMBER_MODE]},b]},{className:"class", -beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, -illegal:"extends implements",contains:[{ -beginKeywords:"public protected internal private constructor" -},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, -excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/, -excludeBegin:!0,returnEnd:!0},l,c]},r,{className:"meta",begin:"^#!/usr/bin/env", -end:"$",illegal:"\n"},o]}}})());hljs.registerLanguage("ruby",(()=>{"use strict";function e(...e){ -return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n -})).join("")}return n=>{ -var a,i="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",s={ -keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor __FILE__", -built_in:"proc lambda",literal:"true false nil"},r={className:"doctag", -begin:"@[A-Za-z]+"},b={begin:"#<",end:">"},t=[n.COMMENT("#","$",{contains:[r] -}),n.COMMENT("^=begin","^=end",{contains:[r],relevance:10 -}),n.COMMENT("^__END__","\\n$")],c={className:"subst",begin:/#\{/,end:/\}/, -keywords:s},d={className:"string",contains:[n.BACKSLASH_ESCAPE,c],variants:[{ -begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/, -end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{ -begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/, -end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{ -begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{ -begin:/<<[-~]?'?(\w+)\n(?:[^\n]*\n)*?\s*\1\b/,returnBegin:!0,contains:[{ -begin:/<<[-~]?'?/},n.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, -contains:[n.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",l={className:"number", -relevance:0,variants:[{ -begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ -begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" -},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ -begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ -begin:"\\b0(_?[0-7])+r?i?\\b"}]},o={className:"params",begin:"\\(",end:"\\)", -endsParent:!0,keywords:s},_=[d,{className:"class",beginKeywords:"class module", -end:"$|;",illegal:/=/,contains:[n.inherit(n.TITLE_MODE,{ -begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|!)?"}),{begin:"<\\s*",contains:[{ -begin:"("+n.IDENT_RE+"::)?"+n.IDENT_RE}]}].concat(t)},{className:"function", -begin:e(/def\s*/,(a=i+"\\s*(\\(|;|$)",e("(?=",a,")"))),keywords:"def",end:"$|;", -contains:[n.inherit(n.TITLE_MODE,{begin:i}),o].concat(t)},{begin:n.IDENT_RE+"::" -},{className:"symbol",begin:n.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{ -className:"symbol",begin:":(?!\\s)",contains:[d,{begin:i}],relevance:0},l,{ -className:"variable", -begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ -className:"params",begin:/\|/,end:/\|/,relevance:0,keywords:s},{ -begin:"("+n.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{ -className:"regexp",contains:[n.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{ -begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(", -end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}] -}].concat(b,t),relevance:0}].concat(b,t);c.contains=_,o.contains=_;var E=[{ -begin:/^\s*=>/,starts:{end:"$",contains:_}},{className:"meta", -begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", -starts:{end:"$",contains:_}}];return t.unshift(b),{name:"Ruby", -aliases:["rb","gemspec","podspec","thor","irb"],keywords:s,illegal:/\/\*/, -contains:[n.SHEBANG({binary:"ruby"})].concat(E).concat(t).concat(_)}}})());hljs.registerLanguage("swift",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function n(e){return i("(?=",e,")")} -function i(...n){return n.map((n=>e(n))).join("")}function a(...n){ -return"("+n.map((n=>e(n))).join("|")+")"} -const t=e=>i(/\b/,e,/\w$/.test(e)?/\b/:/\B/),u=["Protocol","Type"].map(t),s=["init","self"].map(t),r=["Any","Self"],o=["associatedtype",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough","fileprivate(set)","fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout","internal(set)","internal","in","is","lazy","let","mutating","nonmutating","open(set)","open","operator","optional","override","postfix","precedencegroup","prefix","private(set)","private","protocol","public(set)","public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias","unowned(safe)","unowned(unsafe)","unowned","var","weak","where","while","willSet"],l=["false","nil","true"],c=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],b=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],p=a(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),F=a(p,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),d=i(p,F,"*"),g=a(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFFFD]/),f=a(g,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),m=i(g,f,"*"),w=i(/[A-Z]/,f,"*"),E=["autoclosure",i(/convention\(/,a("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",i(/objc\(/,m,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","testable","UIApplicationMain","unknown","usableFromInline"],y=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] -;return e=>{const p=e.COMMENT("/\\*","\\*/",{contains:["self"]}),g={ -className:"keyword",begin:i(/\./,n(a(...u,...s))),end:a(...u,...s), -excludeBegin:!0},A={begin:i(/\./,a(...o)),relevance:0 -},C=o.filter((e=>"string"==typeof e)).concat(["_|0"]),v={variants:[{ -className:"keyword", -begin:a(...o.filter((e=>"string"!=typeof e)).concat(r).map(t),...s)}]},_={ -$pattern:a(/\b\w+(\(\w+\))?/,/#\w+/),keyword:C.concat(c).join(" "), -literal:l.join(" ")},N=[g,A,v],D=[{begin:i(/\./,a(...b)),relevance:0},{ -className:"built_in",begin:i(/\b/,a(...b),/(?=\()/)}],B={begin:/->/,relevance:0 -},M=[B,{className:"operator",relevance:0,variants:[{begin:d},{ -begin:`\\.(\\.|${F})+`}]}],h="([0-9a-fA-F]_*)+",S={className:"number", -relevance:0,variants:[{ -begin:"\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b"},{ -begin:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(([0-9]_*)+))?\\b`},{ -begin:/\b0o([0-7]_*)+\b/},{begin:/\b0b([01]_*)+\b/}]},O=(e="")=>({ -className:"subst",variants:[{begin:i(/\\/,e,/[0\\tnr"']/)},{ -begin:i(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),x=(e="")=>({className:"subst", -begin:i(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),k=(e="")=>({className:"subst", -label:"interpol",begin:i(/\\/,e,/\(/),end:/\)/}),L=(e="")=>({begin:i(e,/"""/), -end:i(/"""/,e),contains:[O(e),x(e),k(e)]}),I=(e="")=>({begin:i(e,/"/), -end:i(/"/,e),contains:[O(e),k(e)]}),$={className:"string", -variants:[L(),L("#"),L("##"),L("###"),I(),I("#"),I("##"),I("###")]},T=[{ -begin:i(/`/,m,/`/)},{className:"variable",begin:/\$\d+/},{className:"variable", -begin:`\\$${f}+`}],j=[{begin:/(@|#)available\(/,end:/\)/,keywords:{ -$pattern:/[@#]?\w+/,keyword:y.concat(["@available","#available"]).join(" ")}, -contains:[...M,S,$]},{className:"keyword",begin:i(/@/,a(...E))},{ -className:"meta",begin:i(/@/,m)}],K={begin:n(/\b[A-Z]/),relevance:0,contains:[{ -className:"type", -begin:i(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,f,"+") -},{className:"type",begin:w,relevance:0},{begin:/[?!]+/,relevance:0},{ -begin:/\.\.\./,relevance:0},{begin:i(/\s+&\s+/,n(w)),relevance:0}]},P={ -begin://,keywords:_,contains:[...N,...j,B,K]};K.contains.push(P) -;for(const e of $.variants){const n=e.contains.find((e=>"interpol"===e.label)) -;n.keywords=_;const i=[...N,...D,...M,S,$,...T];n.contains=[...i,{begin:/\(/, -end:/\)/,contains:["self",...i]}]}return{name:"Swift",keywords:_, -contains:[e.C_LINE_COMMENT_MODE,p,{className:"function",beginKeywords:"func", -end:/\{/,excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{ -begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params", -begin:/\(/,end:/\)/,endsParent:!0,keywords:_, -contains:["self",...N,S,$,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}], -illegal:/\[|%/},{className:"class", -beginKeywords:"struct protocol class extension enum",end:"\\{",excludeEnd:!0, -keywords:_,contains:[e.inherit(e.TITLE_MODE,{ -begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...N]},{beginKeywords:"import", -end:/$/,contains:[e.C_LINE_COMMENT_MODE,p],relevance:0 -},...N,...D,...M,S,$,...T,...j,K]}}})());hljs.registerLanguage("http",(()=>{"use strict";function e(...e){ -return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n -})).join("")}return n=>{const a="HTTP/(2|1\\.[01])",s=[{className:"attribute", -begin:e("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{ -className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]} -},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{ -name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+a+" \\d{3})", -end:/$/,contains:[{className:"meta",begin:a},{className:"number", -begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:s}},{ -begin:"(?=^[A-Z]+ (.*?) "+a+"$)",end:/$/,contains:[{className:"string", -begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:a},{ -className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:s} -}]}}})());hljs.registerLanguage("python",(()=>{"use strict";return e=>{const n={ -keyword:"and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal|10 not or pass raise return try while with yield", -built_in:"__import__ abs all any ascii bin bool breakpoint bytearray bytes callable chr classmethod compile complex delattr dict dir divmod enumerate eval exec filter float format frozenset getattr globals hasattr hash help hex id input int isinstance issubclass iter len list locals map max memoryview min next object oct open ord pow print property range repr reversed round set setattr slice sorted staticmethod str sum super tuple type vars zip", -literal:"__debug__ Ellipsis False None NotImplemented True"},a={ -className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, -end:/\}/,keywords:n,illegal:/#/},i={begin:/\{\{/,relevance:0},r={ -className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ -begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, -contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{ -begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, -contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{ -begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, -contains:[e.BACKSLASH_ESCAPE,a,i,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, -end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,i,s]},{begin:/([uU]|[rR])'/,end:/'/, -relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ -begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, -end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, -contains:[e.BACKSLASH_ESCAPE,i,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, -contains:[e.BACKSLASH_ESCAPE,i,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] -},t="[0-9](_?[0-9])*",l=`(\\b(${t}))?\\.(${t})|\\b(${t})\\.`,b={ -className:"number",relevance:0,variants:[{ -begin:`(\\b(${t})|(${l}))[eE][+-]?(${t})[jJ]?\\b`},{begin:`(${l})[jJ]?`},{ -begin:"\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?\\b"},{ -begin:"\\b0[bB](_?[01])+[lL]?\\b"},{begin:"\\b0[oO](_?[0-7])+[lL]?\\b"},{ -begin:"\\b0[xX](_?[0-9a-fA-F])+[lL]?\\b"},{begin:`\\b(${t})[jJ]\\b`}]},o={ -className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{ -begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n, -contains:["self",a,b,r,e.HASH_COMMENT_MODE]}]};return s.contains=[r,b,a],{ -name:"Python",aliases:["py","gyp","ipython"],keywords:n, -illegal:/(<\/|->|\?)|=>/,contains:[a,b,{begin:/\bself\b/},{beginKeywords:"if", -relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function", -beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/, -illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,o,{begin:/->/, -endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/, -end:/(?=#)|$/,contains:[b,o,r]},{begin:/\b(print|exec)\(/}]}}})());hljs.registerLanguage("python-repl",(()=>{"use strict";return s=>({ -aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$", -subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{ -begin:/^\.\.\.(?=[ ]|$)/}]}]})})());hljs.registerLanguage("java",(()=>{"use strict" -;var e="\\.([0-9](_*[0-9])*)",n="[0-9a-fA-F](_*[0-9a-fA-F])*",a={ -className:"number",variants:[{ -begin:`(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` -},{begin:`\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ -begin:`(${e})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ -begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` -},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ -begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], -relevance:0};return e=>{ -var n="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",s={ -className:"meta",begin:"@[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*", -contains:[{begin:/\(/,end:/\)/,contains:["self"]}]};const r=a;return{ -name:"Java",aliases:["jsp"],keywords:n,illegal:/<\/|#/, -contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, -relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ -begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ -className:"class",beginKeywords:"class interface enum",end:/[{;=]/, -excludeEnd:!0,keywords:"class interface enum",illegal:/[:"\[\]]/,contains:[{ -beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ -beginKeywords:"new throw return else",relevance:0},{className:"class", -begin:"record\\s+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,excludeEnd:!0, -end:/[{;=]/,keywords:n,contains:[{beginKeywords:"record"},{ -begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, -contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/, -keywords:n,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE] -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"function", -begin:"([\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(<[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(\\s*,\\s*[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(", -returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:n,contains:[{ -begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, -contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/, -keywords:n,relevance:0, -contains:[s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,r,e.C_BLOCK_COMMENT_MODE] -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},r,s]}}})());hljs.registerLanguage("nginx",(()=>{"use strict";return e=>{const n={ -className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/\}/},{ -begin:/[$@]/+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{ -$pattern:"[a-z/_]+", -literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll" -},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string", -contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/ -}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n] -},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^", -end:"\\s|\\{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|\\{|;",returnEnd:!0},{ -begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number", -begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{ -className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{ -name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{ -begin:e.UNDERSCORE_IDENT_RE+"\\s+\\{",returnBegin:!0,end:/\{/,contains:[{ -className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{ -begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|\\{",returnBegin:!0,contains:[{ -className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}], -illegal:"[^\\s\\}]"}}})());hljs.registerLanguage("xml",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")} -function a(...n){return n.map((n=>e(n))).join("")}function s(...n){ -return"("+n.map((n=>e(n))).join("|")+")"}return e=>{ -const t=a(/[A-Z_]/,a("(",/[A-Z0-9_.-]+:/,")?"),/[A-Z0-9_.-]*/),i={ -className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},r={begin:/\s/, -contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] -},c=e.inherit(r,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{ -className:"meta-string"}),g=e.inherit(e.QUOTE_STRING_MODE,{ -className:"meta-string"}),m={endsWithParent:!0,illegal:/`]+/}]}] -}]};return{name:"HTML, XML", -aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], -case_insensitive:!0,contains:[{className:"meta",begin://, -relevance:10,contains:[r,g,l,c,{begin:/\[/,end:/\]/,contains:[{className:"meta", -begin://,contains:[r,c,g,l]}]}]},e.COMMENT(//,{ -relevance:10}),{begin://,relevance:10},i,{ -className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag", -begin:/)/,end:/>/,keywords:{name:"style"},contains:[m],starts:{ -end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", -begin:/)/,end:/>/,keywords:{name:"script"},contains:[m],starts:{ -end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ -className:"tag",begin:/<>|<\/>/},{className:"tag", -begin:a(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name", -begin:t,relevance:0,starts:m}]},{className:"tag",begin:a(/<\//,n(a(t,/>/))), -contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0}]}]}} -})());hljs.registerLanguage("markdown",(()=>{"use strict";function n(...n){ -return n.map((n=>{return(e=n)?"string"==typeof e?e:e.source:null;var e -})).join("")}return e=>{const a={begin:/<\/?[A-Za-z_]/,end:">", -subLanguage:"xml",relevance:0},i={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0 -},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, -relevance:2},{begin:n(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), -relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ -begin:/\[.+?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{ -className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, -returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", -excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", -end:"\\]",excludeBegin:!0,excludeEnd:!0}]},s={className:"strong",contains:[], -variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},c={ -className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{ -begin:/_(?!_)/,end:/_/,relevance:0}]};s.contains.push(c),c.contains.push(s) -;let t=[a,i] -;return s.contains=s.contains.concat(t),c.contains=c.contains.concat(t), -t=t.concat(s,c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ -className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:t},{ -begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", -contains:t}]}]},a,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", -end:"\\s+",excludeEnd:!0},s,c,{className:"quote",begin:"^>\\s+",contains:t, -end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ -begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ -begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", -contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ -begin:"^[-\\*]{3,}",end:"$"},i,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ -className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ -className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})());hljs.registerLanguage("yaml",(()=>{"use strict";return e=>{ -var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ -className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ -},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", -variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ -variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ -end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={ -begin:/\{/,end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[", -end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr", -variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{ -begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)" -}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string", -begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ -begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, -relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", -begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a -},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", -begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", -relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ -className:"number", -begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" -},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b] -;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0, -aliases:["yml","YAML"],contains:b}}})());hljs.registerLanguage("bash",(()=>{"use strict";function e(...e){ -return e.map((e=>{return(s=e)?"string"==typeof s?s:s.source:null;var s -})).join("")}return s=>{const n={},t={begin:/\$\{/,end:/\}/,contains:["self",{ -begin:/:-/,contains:[n]}]};Object.assign(n,{className:"variable",variants:[{ -begin:e(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},t]});const a={ -className:"subst",begin:/\$\(/,end:/\)/,contains:[s.BACKSLASH_ESCAPE]},i={ -begin:/<<-?\s*(?=\w+)/,starts:{contains:[s.END_SAME_AS_BEGIN({begin:/(\w+)/, -end:/(\w+)/,className:"string"})]}},c={className:"string",begin:/"/,end:/"/, -contains:[s.BACKSLASH_ESCAPE,n,a]};a.contains.push(c);const o={begin:/\$\(\(/, -end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},s.NUMBER_MODE,n] -},r=s.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 -}),l={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, -contains:[s.inherit(s.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ -name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/, -keyword:"if then else elif fi for while in do done case esac function", -literal:"true false", -built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp" -},contains:[r,s.SHEBANG(),l,o,s.HASH_COMMENT_MODE,i,c,{className:"",begin:/\\"/ -},{className:"string",begin:/'/,end:/'/},n]}}})());hljs.registerLanguage("go",(()=>{"use strict";return e=>{const n={ -keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune", -literal:"true false iota nil", -built_in:"append cap close complex copy imag len make new panic print println real recover delete" -};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{"use strict" -;const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) -;return r=>{const t={ -keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((i=["var","const","let","function","static"], -e=>!i.includes(e))).join(" "), -literal:n.concat(["yes","no","on","off"]).join(" "), -built_in:a.concat(["npm","print"]).join(" ")};var i -;const s="[A-Za-z$_][0-9A-Za-z$_]*",o={className:"subst",begin:/#\{/,end:/\}/, -keywords:t},c=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{ -end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/, -end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/, -contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/, -contains:[r.BACKSLASH_ESCAPE,o]},{begin:/"/,end:/"/, -contains:[r.BACKSLASH_ESCAPE,o]}]},{className:"regexp",variants:[{begin:"///", -end:"///",contains:[o,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)", -relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+s -},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{ -begin:"```",end:"```"},{begin:"`",end:"`"}]}];o.contains=c -;const l=r.inherit(r.TITLE_MODE,{begin:s}),d="(\\(.*\\)\\s*)?\\B[-=]>",g={ -className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/, -end:/\)/,keywords:t,contains:["self"].concat(c)}]};return{name:"CoffeeScript", -aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/, -contains:c.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{ -className:"function",begin:"^\\s*"+s+"\\s*=\\s*"+d,end:"[-=]>",returnBegin:!0, -contains:[l,g]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function", -begin:d,end:"[-=]>",returnBegin:!0,contains:[g]}]},{className:"class", -beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{ -beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[l]},l] -},{begin:s+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}})());hljs.registerLanguage("csharp",(()=>{"use strict";return e=>{var n={ -keyword:["abstract","as","base","break","case","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]).join(" "), -built_in:"bool byte char decimal delegate double dynamic enum float int long nint nuint object sbyte short string ulong unit ushort", -literal:"default false null true"},a=e.inherit(e.TITLE_MODE,{ -begin:"[a-zA-Z](\\.?\\w)*"}),i={className:"number",variants:[{ -begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] -},t=e.inherit(s,{illegal:/\n/}),r={className:"subst",begin:/\{/,end:/\}/, -keywords:n},l=e.inherit(r,{illegal:/\n/}),c={className:"string",begin:/\$"/, -end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ -},e.BACKSLASH_ESCAPE,l]},o={className:"string",begin:/\$@"/,end:'"',contains:[{ -begin:/\{\{/},{begin:/\}\}/},{begin:'""'},r]},d=e.inherit(o,{illegal:/\n/, -contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},l]}) -;r.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.C_BLOCK_COMMENT_MODE], -l.contains=[d,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.inherit(e.C_BLOCK_COMMENT_MODE,{ -illegal:/\n/})];var g={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] -},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},a] -},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={ -begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], -keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, -contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ -begin:"\x3c!--|--\x3e"},{begin:""}]}] -}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", -end:"$",keywords:{ -"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum" -}},g,i,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, -illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" -},a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", -relevance:0,end:/[{;=]/,illegal:/[^\s:]/, -contains:[a,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ -beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, -contains:[a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", -begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ -className:"meta-string",begin:/"/,end:/"/}]},{ -beginKeywords:"new return throw await else",relevance:0},{className:"function", -begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(<.+>\\s*)?\\(",returnBegin:!0, -end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ -beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", -relevance:0},{begin:e.IDENT_RE+"\\s*(<.+>\\s*)?\\(",returnBegin:!0, -contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/, -excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, -contains:[g,i,e.C_BLOCK_COMMENT_MODE] -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}})());hljs.registerLanguage("scss",(()=>{"use strict";return e=>{var t="@[a-z-]+",i={ -className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},r={ -className:"number",begin:"#[0-9A-Fa-f]+"} -;return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE, -e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0, -illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{ -className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ -className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{ -className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{ -className:"selector-tag", -begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b", -relevance:0},{className:"selector-pseudo", -begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)" -},{className:"selector-pseudo", -begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)" -},i,{className:"attribute", -begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b", -illegal:"[^\\s]"},{ -begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" -},{begin:":",end:";", -contains:[i,r,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{ -className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:t, -keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0, -keywords:"and or not only",contains:[{begin:t,className:"keyword" -},i,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,r,e.CSS_NUMBER_MODE]}]}}})());hljs.registerLanguage("r",(()=>{"use strict";function e(...e){return e.map((e=>{ -return(a=e)?"string"==typeof a?a:a.source:null;var a})).join("")}return a=>{ -const n=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/;return{name:"R", -illegal:/->/,keywords:{$pattern:n, -keyword:"function if in break next repeat else for while", -literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", -built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" -},compilerExtensions:[(a,n)=>{if(!a.beforeMatch)return -;if(a.starts)throw Error("beforeMatch cannot be used with starts") -;const i=Object.assign({},a);Object.keys(a).forEach((e=>{delete a[e] -})),a.begin=e(i.beforeMatch,e("(?=",i.begin,")")),a.starts={relevance:0, -contains:[Object.assign(i,{endsParent:!0})]},a.relevance=0,delete i.beforeMatch -}],contains:[a.COMMENT(/#'/,/$/,{contains:[{className:"doctag", -begin:"@examples",starts:{contains:[{begin:/\n/},{begin:/#'\s*(?=@[a-zA-Z]+)/, -endsParent:!0},{begin:/#'/,end:/$/,excludeBegin:!0}]}},{className:"doctag", -begin:"@param",end:/$/,contains:[{className:"variable",variants:[{begin:n},{ -begin:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{className:"doctag", -begin:/@[a-zA-Z]+/},{className:"meta-keyword",begin:/\\[a-zA-Z]+/}] -}),a.HASH_COMMENT_MODE,{className:"string",contains:[a.BACKSLASH_ESCAPE], -variants:[a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', -relevance:0},{begin:"'",end:"'",relevance:0}]},{className:"number",relevance:0, -beforeMatch:/([^a-zA-Z0-9._])/,variants:[{ -match:/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/},{ -match:/0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?/},{ -match:/(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?/}]},{begin:"%",end:"%"},{ -begin:e(/[a-zA-Z][a-zA-Z_0-9]*/,"\\s+<-\\s+")},{begin:"`",end:"`",contains:[{ -begin:/\\./}]}]}}})());hljs.registerLanguage("less",(()=>{"use strict";return e=>{ -var n="([\\w-]+|@\\{[\\w-]+\\})",a=[],s=[],t=e=>({className:"string", -begin:"~?"+e+".*?"+e}),r=(e,n,a)=>({className:e,begin:n,relevance:a}),i={ -begin:"\\(",end:"\\)",contains:s,relevance:0} -;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{ -begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", -excludeEnd:!0} -},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@\\{[\\w-]+\\}"),r("built_in","~?`[^`]*?`"),{ -className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0 -},{className:"meta",begin:"!important"});var c=s.concat({begin:/\{/,end:/\}/, -contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{ -beginKeywords:"and not"}].concat(s)},g={begin:n+"\\s*:",returnBegin:!0, -end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":", -excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s} -}]},d={className:"keyword", -begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", -starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},o={ -className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{ -begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{ -begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:/\{/}],returnBegin:!0, -returnEnd:!0,illegal:"[<='$\"]",relevance:0, -contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@\\{[\\w-]+\\}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{ -className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo", -begin:/:(:)?[a-zA-Z0-9_\-+()"'.]+/},{begin:"\\(",end:"\\)",contains:c},{ -begin:"!important"}]} -;return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,d,o,g,b),{ -name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}})());hljs.registerLanguage("objectivec",(()=>{"use strict";return e=>{ -const n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n, -keyword:"@interface @class @protocol @implementation"};return{ -name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], -keywords:{$pattern:n, -keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN", -literal:"false true FALSE TRUE nil YES NO NULL", -built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once" -},illegal:"/,end:/$/, -illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ -className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:/(\{|$)/, -excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{ -begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}})());hljs.registerLanguage("typescript",(()=>{"use strict" -;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) -;function t(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{ -return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return r=>{ -const c={$pattern:e, -keyword:n.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "), -literal:a.join(" "), -built_in:s.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ") -},o={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},l=(e,n,a)=>{ -const s=e.contains.findIndex((e=>e.label===n)) -;if(-1===s)throw Error("can not find mode to replace");e.contains.splice(s,1,a) -},b=(r=>{const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/, -end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ -const a=e[0].length+e.index,s=e.input[a];"<"!==s?">"===s&&(((e,{after:n})=>{ -const a="", -returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{ -begin:r.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0 -},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:f}]}] -},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{ -variants:[{begin:"<>",end:""},{begin:o.begin,"on:begin":o.isTrulyOpeningTag, -end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0, -contains:["self"]}]}],relevance:0},{className:"function", -beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l, -contains:["self",r.inherit(r.TITLE_MODE,{begin:c}),A],illegal:/%/},{ -beginKeywords:"while if switch catch for"},{className:"function", -begin:r.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", -returnBegin:!0,contains:[A,r.inherit(r.TITLE_MODE,{begin:c})]},{variants:[{ -begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class", -beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{ -beginKeywords:"extends"},r.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/, -end:/[{;]/,excludeEnd:!0,contains:[r.inherit(r.TITLE_MODE,{begin:c}),"self",A] -},{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set", -contains:[r.inherit(r.TITLE_MODE,{begin:c}),{begin:/\(\)/},A]},{begin:/\$[(.]/}] -}})(r) -;return Object.assign(b.keywords,c),b.exports.PARAMS_CONTAINS.push(o),b.contains=b.contains.concat([o,{ -beginKeywords:"namespace",end:/\{/,excludeEnd:!0},{beginKeywords:"interface", -end:/\{/,excludeEnd:!0,keywords:"interface extends" -}]),l(b,"shebang",r.SHEBANG()),l(b,"use_strict",{className:"meta",relevance:10, -begin:/^\s*['"]use strict['"]/ -}),b.contains.find((e=>"function"===e.className)).relevance=0,Object.assign(b,{ -name:"TypeScript",aliases:["ts"]}),b}})());hljs.registerLanguage("plaintext",(()=>{"use strict";return t=>({ -name:"Plain text",aliases:["text","txt"],disableAutodetect:!0})})());hljs.registerLanguage("json",(()=>{"use strict";return n=>{const e={ -literal:"true false null" -},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],a=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],l={ -end:",",endsWithParent:!0,excludeEnd:!0,contains:a,keywords:e},t={begin:/\{/, -end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/, -contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(l,{begin:/:/ -})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(l)], -illegal:"\\S"};return a.push(t,s),i.forEach((n=>{a.push(n)})),{name:"JSON", -contains:a,keywords:e,illegal:"\\S"}}})());hljs.registerLanguage("perl",(()=>{"use strict";function e(...e){ -return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n -})).join("")}return n=>{const t=/[dualxmsipn]{0,12}/,s={$pattern:/[\w.]+/, -keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when" -},r={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:s},i={begin:/->\{/, -end:/\}/},a={variants:[{begin:/\$\d/},{ -begin:e(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") -},{begin:/[$%@][^\s\w{]/,relevance:0}] -},o=[n.BACKSLASH_ESCAPE,r,a],c=[a,n.HASH_COMMENT_MODE,n.COMMENT(/^=\w/,/=cut/,{ -endsWithParent:!0}),i,{className:"string",contains:o,variants:[{ -begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", -end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ -begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", -relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", -contains:[n.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", -contains:[n.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,contains:[],relevance:0},{ -begin:"-?\\w+\\s*=>",contains:[],relevance:0}]},{className:"number", -begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", -relevance:0},{ -begin:"(\\/\\/|"+n.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", -keywords:"split return print reverse grep",relevance:0, -contains:[n.HASH_COMMENT_MODE,{className:"regexp", -begin:e(/(s|tr|y)/,/\//,/(\\.|[^\\\/])*/,/\//,/(\\.|[^\\\/])*/,/\//,t), -relevance:10},{className:"regexp",begin:/(m|qr)?\//,end:e(/\//,t), -contains:[n.BACKSLASH_ESCAPE],relevance:0}]},{className:"function", -beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5, -contains:[n.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$", -end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$", -className:"comment"}]}];return r.contains=c,i.contains=c,{name:"Perl", -aliases:["pl","pm"],keywords:s,contains:c}}})());hljs.registerLanguage("shell",(()=>{"use strict";return s=>({ -name:"Shell Session",aliases:["console"],contains:[{className:"meta", -begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#]/,starts:{end:/[^\\](?=\s*$)/, -subLanguage:"bash"}}]})})());hljs.registerLanguage("lua",(()=>{"use strict";return e=>{ -const t="\\[=*\\[",a="\\]=*\\]",n={begin:t,end:a,contains:["self"] -},o=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[",a,{contains:[n], -relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, -literal:"true false nil", -keyword:"and break do else elseif end for goto if in local not or repeat return then until while", -built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" -},contains:o.concat([{className:"function",beginKeywords:"function",end:"\\)", -contains:[e.inherit(e.TITLE_MODE,{ -begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", -begin:"\\(",endsWithParent:!0,contains:o}].concat(o) -},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", -begin:t,end:a,contains:[n],relevance:5}])}}})());hljs.registerLanguage("makefile",(()=>{"use strict";return e=>{const i={ -className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", -contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{"use strict";return e=>{ -const n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!" -;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?", -keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield", -literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}})());hljs.registerLanguage("php",(()=>{"use strict";return e=>{const r={ -className:"variable", -begin:"\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?![A-Za-z0-9])(?![$])"},t={ -className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{ -begin:/\?>/}]},a={className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/, -end:/\}/}]},n=e.inherit(e.APOS_STRING_MODE,{illegal:null -}),i=e.inherit(e.QUOTE_STRING_MODE,{illegal:null, -contains:e.QUOTE_STRING_MODE.contains.concat(a)}),o=e.END_SAME_AS_BEGIN({ -begin:/<<<[ \t]*(\w+)\n/,end:/[ \t]*(\w+)\b/, -contains:e.QUOTE_STRING_MODE.contains.concat(a)}),l={className:"string", -contains:[e.BACKSLASH_ESCAPE,t],variants:[e.inherit(n,{begin:"b'",end:"'" -}),e.inherit(i,{begin:'b"',end:'"'}),i,n,o]},c={ -variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},s={ -keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list match|0 new object or private protected public real return string switch throw trait try unset use var void while xor yield", -literal:"false null true", -built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass" -};return{aliases:["php","php3","php4","php5","php6","php7","php8"], -case_insensitive:!0,keywords:s, -contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t] -}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}] -}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0, -keywords:"__halt_compiler"}),t,{className:"keyword",begin:/\$this\b/},r,{ -begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function", -relevance:0,beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0, -illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{begin:"=>"},{ -className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0, -keywords:s,contains:["self",r,e.C_BLOCK_COMMENT_MODE,l,c]}]},{className:"class", -beginKeywords:"class interface",relevance:0,end:/\{/,excludeEnd:!0, -illegal:/[:($"]/,contains:[{beginKeywords:"extends implements" -},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",relevance:0,end:";", -illegal:/[.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use", -relevance:0,end:";",contains:[e.UNDERSCORE_TITLE_MODE]},l,c]}}})());hljs.registerLanguage("vbnet",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function n(...n){ -return n.map((n=>e(n))).join("")}function t(...n){ -return"("+n.map((n=>e(n))).join("|")+")"}return e=>{ -const a=/\d{1,2}\/\d{1,2}\/\d{4}/,i=/\d{4}-\d{1,2}-\d{1,2}/,s=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,o={ -className:"literal",variants:[{begin:n(/# */,t(i,a),/ *#/)},{ -begin:n(/# */,r,/ *#/)},{begin:n(/# */,s,/ *#/)},{ -begin:n(/# */,t(i,a),/ +/,t(s,r),/ *#/)}]},l=e.COMMENT(/'''/,/$/,{contains:[{ -className:"doctag",begin:/<\/?/,end:/>/}]}),c=e.COMMENT(null,/$/,{variants:[{ -begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]});return{name:"Visual Basic .NET", -aliases:["vb"],case_insensitive:!0,classNameAliases:{label:"symbol"},keywords:{ -keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", -built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", -type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", -literal:"true false nothing"}, -illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ -className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, -end:/"/,illegal:/\n/,contains:[{begin:/""/}]},o,{className:"number",relevance:0, -variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ -},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ -begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ -className:"label",begin:/^\w+:/},l,c,{className:"meta", -begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, -end:/$/,keywords:{ -"meta-keyword":"const disable else elseif enable end externalsource if region then" -},contains:[c]}]}}})());hljs.registerLanguage("c",(()=>{"use strict";function e(e){ -return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?") -}return t=>{const n=(t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}] -}),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",s={ -className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},i={className:"string", -variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n", -contains:[t.BACKSLASH_ESCAPE]},{ -begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", -end:"'",illegal:"."},t.END_SAME_AS_BEGIN({ -begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ -className:"number",variants:[{begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" -},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ -"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" -},contains:[{begin:/\\\n/,relevance:0},t.inherit(i,{className:"meta-string"}),{ -className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n" -},n,t.C_BLOCK_COMMENT_MODE]},l={className:"title",begin:e(r)+t.IDENT_RE, -relevance:0},d=e(r)+t.IDENT_RE+"\\s*\\(",u={ -keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq", -built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary", -literal:"true false nullptr NULL"},m=[c,s,n,t.C_BLOCK_COMMENT_MODE,o,i],p={ -variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{ -beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:m.concat([{ -begin:/\(/,end:/\)/,keywords:u,contains:m.concat(["self"]),relevance:0}]), -relevance:0},_={className:"function",begin:"("+a+"[\\*&\\s]+)+"+d, -returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:u,illegal:/[^\w\s\*&:<>.]/, -contains:[{begin:"decltype\\(auto\\)",keywords:u,relevance:0},{begin:d, -returnBegin:!0,contains:[l],relevance:0},{className:"params",begin:/\(/, -end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,i,o,s,{ -begin:/\(/,end:/\)/,keywords:u,relevance:0, -contains:["self",n,t.C_BLOCK_COMMENT_MODE,i,o,s]}] -},s,n,t.C_BLOCK_COMMENT_MODE,c]};return{ -aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:u, -disableAutodetect:!0,illegal:"",keywords:u,contains:["self",s]},{begin:t.IDENT_RE+"::",keywords:u},{ -className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/, -contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{ -preprocessor:c,strings:i,keywords:u}}})(t) -;return n.name="C",n.aliases=["c","h"],n}})());hljs.registerLanguage("css",(()=>{"use strict";return e=>{ -var n="[a-zA-Z-][a-zA-Z0-9_-]*",a={ -begin:/([*]\s?)?(?:[A-Z_.\-\\]+|--[a-zA-Z0-9_-]+)\s*(\/\*\*\/)?:/, -returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute", -begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0, -contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in", -begin:/[\w-]+/},{begin:/\(/,end:/\)/, -contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}] -},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{ -className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}] -}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/, -contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id", -begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:"\\."+n},{ -className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", -contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo", -begin:/:(:)?[a-zA-Z0-9_+()"'.-]+/},{begin:"@(page|font-face)", -lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]", -illegal:/:/,returnBegin:!0,contains:[{className:"keyword", -begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0, -relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/, -className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE] -}]},{className:"selector-tag",begin:n,relevance:0},{begin:/\{/,end:/\}/, -illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,{begin:/;/},a]}]}}})());hljs.registerLanguage("sql",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function r(...r){ -return r.map((r=>e(r))).join("")}function t(...r){ -return"("+r.map((r=>e(r))).join("|")+")"}return e=>{ -const n=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],s=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],o=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],c=s,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update ","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!s.includes(e))),u={ -begin:r(/\b/,t(...c),/\s*\(/),keywords:{built_in:c.join(" ")}};return{ -name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ -$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:r,when:t}={})=>{const n=t -;return r=r||[],e.map((e=>e.match(/\|\d+$/)||r.includes(e)?e:n(e)?e+"|0":e)) -})(l,{when:e=>e.length<3}).join(" "),literal:a.join(" "),type:i.join(" "), -built_in:"current_catalog current_date current_default_transform_group current_path current_role current_schema current_transform_group_for_type current_user session_user system_time system_user current_time localtime current_timestamp localtimestamp" -},contains:[{begin:t(...o),keywords:{$pattern:/[\w\.]+/, -keyword:l.concat(o).join(" "),literal:a.join(" "),type:i.join(" ")}},{ -className:"type", -begin:t("double precision","large object","with timezone","without timezone") -},u,{className:"variable",begin:/@[a-z0-9]+/},{className:"string",variants:[{ -begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,contains:[{ -begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,n,{className:"operator", -begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}}})());hljs.registerLanguage("ini",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function n(...n){ -return n.map((n=>e(n))).join("")}return s=>{const a={className:"number", -relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:s.NUMBER_RE}] -},i=s.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const t={ -className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/ -}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={ -className:"string",contains:[s.BACKSLASH_ESCAPE],variants:[{begin:"'''", -end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"' -},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,a,"self"], -relevance:0 -},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map((n=>e(n))).join("|")+")" -;return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, -contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{ -begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr", -starts:{end:/$/,contains:[i,c,r,t,l,a]}}]}}})());hljs.registerLanguage("php-template",(()=>{"use strict";return n=>({ -name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/, -subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"', -end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{ -illegal:null,className:null,contains:null,skip:!0 -}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null, -skip:!0})]}]})})());hljs.registerLanguage("javascript",(()=>{"use strict" -;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) -;function r(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{ -return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return t=>{ -const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/, -isTrulyOpeningTag:(e,n)=>{const a=e[0].length+e.index,s=e.input[a] -;"<"!==s?">"===s&&(((e,{after:n})=>{const a="", -returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{ -begin:t.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0 -},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:A}]}] -},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{ -variants:[{begin:"<>",end:""},{begin:o.begin,"on:begin":o.isTrulyOpeningTag, -end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0, -contains:["self"]}]}],relevance:0},{className:"function", -beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l, -contains:["self",t.inherit(t.TITLE_MODE,{begin:c}),p],illegal:/%/},{ -beginKeywords:"while if switch catch for"},{className:"function", -begin:t.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", -returnBegin:!0,contains:[p,t.inherit(t.TITLE_MODE,{begin:c})]},{variants:[{ -begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class", -beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{ -beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/, -end:/[{;]/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:c}),"self",p] -},{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set", -contains:[t.inherit(t.TITLE_MODE,{begin:c}),{begin:/\(\)/},p]},{begin:/\$[(.]/}] -}}})()); \ No newline at end of file diff --git a/src/Blogifier/wwwroot/themes/standard/js/scripts.js b/src/Blogifier/wwwroot/themes/standard/js/scripts.js deleted file mode 100644 index 24cb37121..000000000 --- a/src/Blogifier/wwwroot/themes/standard/js/scripts.js +++ /dev/null @@ -1,24 +0,0 @@ -// enable highlight -hljs.initHighlightingOnLoad(); - -// search modal auto focus -var myModal = document.getElementById('searchModal') -var myInput = document.getElementById('searchFormInput') -myModal.addEventListener('shown.bs.modal', function () { - myInput.focus() -}) - -// copy input -function copyInput(elm) { - var copyText = document.getElementById(elm); - var copyTextStore = copyText.dataset.link; - copyText.select(); - copyText.setSelectionRange(0, 99999); - document.execCommand("copy"); - copyText.value = "Copied!"; - copyText.classList.add("copied"); - setTimeout(function () { - copyText.value = copyTextStore; - copyText.classList.remove("copied"); - }, 500); -} diff --git a/src/Blogifier/wwwroot/themes/standard/package-lock.json b/src/Blogifier/wwwroot/themes/standard/package-lock.json deleted file mode 100644 index 0d1bc9906..000000000 --- a/src/Blogifier/wwwroot/themes/standard/package-lock.json +++ /dev/null @@ -1,386 +0,0 @@ -{ - "name": "theme", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "theme", - "version": "1.0.0", - "devDependencies": { - "bootstrap": "^5.0.0", - "sass": "^1.32.12" - } - }, - "node_modules/@popperjs/core": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", - "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==", - "dev": true, - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bootstrap": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0.tgz", - "integrity": "sha512-tmhPET9B9qCl8dCofvHeiIhi49iBt0EehmIsziZib65k1erBW1rHhj2s/2JsuQh5Pq+xz2E9bEbzp9B7xHG+VA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - }, - "peerDependencies": { - "@popperjs/core": "^2.9.2" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/sass": { - "version": "1.32.12", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", - "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", - "dev": true, - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - } - }, - "dependencies": { - "@popperjs/core": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", - "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==", - "dev": true, - "peer": true - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bootstrap": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0.tgz", - "integrity": "sha512-tmhPET9B9qCl8dCofvHeiIhi49iBt0EehmIsziZib65k1erBW1rHhj2s/2JsuQh5Pq+xz2E9bEbzp9B7xHG+VA==", - "dev": true, - "requires": {} - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "sass": { - "version": "1.32.12", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", - "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", - "dev": true, - "requires": { - "chokidar": ">=3.0.0 <4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } -} diff --git a/src/Blogifier/wwwroot/themes/standard/package.json b/src/Blogifier/wwwroot/themes/standard/package.json deleted file mode 100644 index d9191f233..000000000 --- a/src/Blogifier/wwwroot/themes/standard/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "theme", - "version": "1.0.0", - "description": "only for compiling sass files", - "author": "Farzin", - "private": true, - "scripts": { - "start": "sass --watch scss:css --style=compressed" - }, - "devDependencies": { - "bootstrap": "^5.0.0", - "sass": "^1.32.12" - } -} diff --git a/src/Blogifier/wwwroot/themes/standard/scss/styles.scss b/src/Blogifier/wwwroot/themes/standard/scss/styles.scss deleted file mode 100644 index 8bddcbe98..000000000 --- a/src/Blogifier/wwwroot/themes/standard/scss/styles.scss +++ /dev/null @@ -1,36 +0,0 @@ -/*! - Author: https://farzin.dev -*/ - -@charset "utf-8"; - -// helpers -@import "helpers/variables"; -@import "helpers/mixins"; -@import "helpers/reset"; -@import "helpers/base"; - -// layouts -@import "layout/header"; -@import "layout/nav"; -@import "layout/widgets"; -@import "layout/footer"; - -// components - global -@import "components/search"; -@import "components/pagination"; -@import "components/newsletter"; -@import "components/dropdowns"; -@import "components/highlight"; - -// post -@import "post/featured"; -@import "post/view-grid"; -@import "post/view-list"; -@import "post/post"; -@import "post/nav"; -@import "post/share"; -@import "post/related"; - -// pages -@import "page/home"; diff --git a/src/Blogifier/wwwroot/themes/test/README.md b/src/Blogifier/wwwroot/themes/test/README.md deleted file mode 100644 index a3981c59d..000000000 --- a/src/Blogifier/wwwroot/themes/test/README.md +++ /dev/null @@ -1,59 +0,0 @@ -This document is not fully ready. - -## Standard Theme -The Standard theme comes with the Blogifier v3.0 with awesome features and customization. This theme has been built with Bootstrap v5.0 with no jQuery. - -## Customization - -Login to your Blogifier's admin panel, navigate to Settings > Customize. In the Customize page in the settings tab, you are able to customize: - -**Accent Color** - -You are able to change the accent color to anything you want, some links, buttons and etc on the theme will be that color. you can add your hex color code. - -**Title and Logo** - -You can have either the Title or the Logo. and you can customize the Title color as well. - -**Post List or Post Grid** - -The home page posts can be shown as a grid view or list view. - -**Featured Posts** - -You can disable or enable the featured posts on the home page. and also you can specify how many items. - -**Social Links** - -Add social links, the links should be full URL. Not just username. If you empty the field it will disappear. - -**Related Posts** - -You can disable or enable the related posts. - -**Popular Posts** - -You can disable or enable the popular posts and specify how many items. - -**Navigation Posts** - -You can disable or enable the Next Post and Previous Posts navigation in the details of the post. - -## Development -First make sure you make a copy of this theme and rename it. so you don't lose the original Standard Theme. - -And make sure you change the SCSS files instead of the actual CSS files. we use npm and node-sass to compile the SCSS files to CSS. - -``` -npm install -npm start -``` - -## Custom Theme For You -If you need a Custom Design Theme, We design and implement it for you. - -Check out the **[Blogifier Official Website >](https://blogifier.net/)** - - -## Copyright -Copyright 2021 - Designed by [Farzin](https://farzin.dev/link/github) diff --git a/src/Blogifier/wwwroot/themes/test/css/styles.css b/src/Blogifier/wwwroot/themes/test/css/styles.css deleted file mode 100644 index 386025104..000000000 --- a/src/Blogifier/wwwroot/themes/test/css/styles.css +++ /dev/null @@ -1,3 +0,0 @@ -/*! - Author: https://farzin.dev -*/img{max-width:100%}iframe{width:100% !important;margin-top:1rem;margin-bottom:1.5rem}figure,p,ul,ol{margin-bottom:1.5rem}table{width:100%}blockquote{font-weight:300;position:relative;padding:1rem 3rem;margin-bottom:1.5rem}blockquote::before,blockquote::after{position:absolute;content:"";font-size:5rem;width:2.5rem;height:2.5rem;background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4IiB3aWR0aD0iNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDM0aDZsNC04di0xMmgtMTJ2MTJoNnptMTYgMGg2bDQtOHYtMTJoLTEydjEyaDZ6Ii8+PHBhdGggZD0iTTAgMGg0OHY0OGgtNDh6IiBmaWxsPSJub25lIi8+PC9zdmc+");display:block;background-size:2.5rem;background-repeat:no-repeat;opacity:.1}blockquote::before{top:.75rem;left:0;transform:rotate(180deg)}blockquote::after{bottom:.75rem;right:0}blockquote p:last-child{margin-bottom:0}.container{width:61.5rem;max-width:100%}@media(max-width: 991px){:root{--bs-gutter-x: 1.5rem}}.header{padding-top:4rem;padding-bottom:4rem}.header-title{display:inline-block;margin:0;font-size:var(--bf-header-title-size);font-weight:var(--bf-header-title-weight);color:var(--bf-header-title-color);line-height:1.2;text-decoration:none}.header-title:hover,.header-title:focus{color:var(--bf-header-title-hover)}.header-desc{margin:0;font-size:.875rem;font-weight:400;color:var(--bf-header-desc-color, #666)}.header-logo{display:block}.header-logo-img{width:var(--bf-header-logo-width);height:var(--bf-header-logo-height)}@media(max-width: 767px){.header{padding:0;margin-bottom:2rem;background-color:var(--bs-light)}}.header-nav ul{list-style:none;margin:0;padding:0}.header-nav-link,.header-nav-button{color:#444;height:2.5rem;line-height:2.5rem;border:0;background:none;display:inline-block;border-radius:.125rem;transition:all ease-in-out .15s;text-align:center;border-radius:.25rem;font-size:1rem}.header-nav-link:hover,.header-nav-link:focus,.header-nav-button:hover,.header-nav-button:focus{color:#000;background-color:var(--bs-light)}.header-nav-link>.bi,.header-nav-link>img,.header-nav-button>.bi,.header-nav-button>img{transform:translateY(-0.1rem)}.header-nav-link{padding:0 1rem;font-weight:500}.header-nav-link .bi-chevron-down{width:.75rem;height:.75rem}.header-nav-button{padding:0;width:2.5rem}.header-nav .-login{position:relative}.header-nav .-login::after{content:"";width:1px;height:1rem;position:absolute;top:50%;transform:translateY(-50%);left:-0.75rem;background-color:rgba(0,0,0,.1);display:block}.header-nav .-login .bi-box-arrow-in-right{transform:translateX(-0.125rem)}@media(max-width: 767px){.header-nav{border-bottom:1px solid rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.03);padding:.25rem .75rem;margin:0 -1.5rem 1rem}}.social-link-blogifier:hover{color:#622aff}.social-link-facebook:hover{color:#1678f2}.social-link-twitter:hover{color:#1da1f2}.social-link-github:hover{color:#171617}.social-link-pinterest:hover{color:#e60023}.social-link-linkedin:hover{color:#0a66c2}.social-link-instagram:hover{color:#b900b4}.social-link-telegram:hover{color:#08c}.social-link-youtube:hover{color:#ff0300}.social-link-whatsapp:hover{color:#25d366}.bft-widget-title{font-weight:300;letter-spacing:.1em;color:#aaa;font-size:1rem;text-transform:uppercase;display:block;margin-bottom:2rem}.bft-widget-content ul{list-style:none;padding:0;margin:0}.footer{border-top:1px solid rgba(0,0,0,.05);margin-top:4rem;color:#444;font-size:.75rem}.footer a{color:#444;text-decoration:none}.footer a:hover{color:var(--bf-color)}.footer p{margin:0}.pagination{margin:4rem auto 4rem;height:3rem;align-items:center;justify-content:center}.pagination-item{margin:0 .5rem;z-index:5}.pagination-link{display:block;color:#888;font-weight:500;font-size:.875rem;height:3rem;line-height:3rem;text-decoration:none;text-transform:uppercase;transition:opacity ease .2s;opacity:.5}.pagination-link:hover{opacity:1;color:var(--bf-color)}.pagination-link:hover .bi{width:1.375rem;height:1.375rem}.pagination-link .bi{width:1.25rem;height:1.25rem;transition:all ease .2s;margin:0 .25rem;transform:translateY(-0.0625rem)}.newsletter{background-color:#000;color:#aaa;border-radius:var(--bf-radius, 0.125rem);margin-top:4rem;position:relative}.newsletter-body{padding:3rem}@media screen and (min-width: 991px){.newsletter-body{padding:4rem}}.newsletter-title{font-size:1.75rem;font-weight:600;color:#fff}.newsletter-desc{margin:0;font-size:1.125rem;font-weight:300}.newsletter-form{border:2px solid #222;border-radius:var(--bf-radius, 0.125rem)}.newsletter-input{background:none;border:0;flex-grow:1;width:100%;padding:.5rem 1rem;color:#fff;outline:none !important}.newsletter-btn{background:none;border:0;padding:.5rem 1rem;color:#fff;display:block;border-left:1px solid #222}.newsletter-btn:focus,.newsletter-btn:hover{outline:none;background:#222}.newsletter-msg{padding:2rem;text-align:center;background-color:#000;border-radius:var(--bf-radius, 0.125rem);position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;margin:0;color:#fff;cursor:pointer}.newsletter-msg .spinner-border{color:#fff}.dropdown-menu{border-radius:.125rem;box-shadow:0 0 3rem rgba(0,0,0,.2);border:none}.dropdown-item{font-size:.875rem;font-weight:500;text-transform:capitalize;padding:.5rem 1rem;color:#555;transition:all ease-in-out .2s}.dropdown-item:active,.dropdown-item:hover{color:#000;background-color:var(--bs-light)}.dropdown-divider{background:none;border-top:1px solid rgba(0,0,0,.3);margin:0}pre>code{display:block;overflow-x:auto;padding:2rem;color:#212529;background:#f8f9fa;margin:0;font-family:var(--bs-font-monospace);font-size:.875rem;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.4;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}.hljs-comment{color:#90a4ae}.hljs-keyword,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-section,.hljs-selector-class,.hljs-meta,.hljs-selector-pseudo,.hljs-attr{color:#006ee0}.hljs-attribute{color:#803378}.hljs-name{color:#168174}.hljs-type,.hljs-number,.hljs-selector-id,.hljs-quote,.hljs-template-tag,.hljs-literal{color:#168174}.hljs-title,.hljs-string,.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-meta-string{color:#c30}.hljs-bullet,.hljs-code{color:#ccc}.hljs-deletion{color:#de7176}.hljs-addition{color:#76c490}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}.featured{margin-bottom:5rem}.featured-cover{margin:0;max-height:100%;min-height:100%;overflow:hidden;position:relative;border-radius:var(--bf-radius, 0.125rem);box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}@media screen and (max-width: 991px){.featured-cover{height:20rem}}.featured-cover-link{display:block;position:absolute;top:0;left:0;width:100%;height:100%}.featured-cover-img{border-radius:var(--bf-radius, 0.125rem);position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;object-position:center}.featured-content{padding-top:2rem;padding-bottom:2rem;padding-left:2rem}.featured-title{font-size:1.75rem;font-weight:700;margin-bottom:1.5rem;line-height:1.3}.featured-link{text-decoration:none;color:#000}.featured-link:hover{color:var(--bf-color)}.featured-meta{font-size:.875rem;color:#777;margin-bottom:1.5rem;opacity:.7}.featured-meta a{color:#777}.featured-meta a:hover{color:#000}.featured-author{display:flex;align-items:center;margin-right:1rem}.featured-author-img{border-radius:50rem;margin-right:.25rem;filter:grayscale(100%)}.featured-author-name{text-transform:capitalize}.featured-date{display:flex;align-items:center;margin-right:.875rem}.featured-date-icon{margin-right:.375rem}.featured-cat{display:flex;align-items:center}.featured-cat-icon{margin-right:0rem}.featured-cat-title{text-decoration:none}.featured-desc{color:#444;line-height:1.8;margin-bottom:1.5rem}.featured-more{font-weight:500;font-size:.875rem;color:var(--bf-color);text-decoration:none}.featured-more:hover{color:#000}.featured-prev,.featured-next{width:3rem;opacity:0}.featured-prev{left:-4rem}.featured-next{right:-3.5rem}.featured:hover .featured-next,.featured:hover .featured-prev{opacity:.2}@media screen and (max-width: 767px){.featured{border-bottom:1px solid rgba(0,0,0,.03);margin:-2rem -1.5rem 2rem;background-color:var(--bs-light)}.featured-item{padding:1.5rem}.featured-content{padding:0;padding:1rem 1rem 0}.featured-title{font-size:1.25rem;margin-bottom:.5rem;font-weight:500}.featured-desc{font-size:.875rem;margin-bottom:.5rem}}.post-grid{height:100%;position:relative;transition:box-shadow ease-in-out .3s;border-radius:var(--bf-radius, 0.125rem);box-shadow:0 0 0rem 1px rgba(0,0,0,.05);overflow:hidden}.post-grid-cover{display:block;margin:0 0 1.5rem;position:relative;border-bottom:1px solid rgba(0,0,0,.05)}.post-grid-img{min-width:100%;height:11rem;object-fit:cover;object-position:center;border-top-left-radius:var(--bf-radius, 0.125rem);border-top-right-radius:var(--bf-radius, 0.125rem)}.post-grid-cats{padding:0 1rem;line-height:1.4;display:flex;flex-wrap:wrap}.post-grid-cats-link{text-decoration:none;font-size:.75rem;margin-right:.5rem;color:#999}.post-grid-cats-link::before{content:"#";display:inline-block;margin-right:.1rem}.post-grid-cats-link:hover{color:var(--bf-color)}.post-grid-title{font-size:1.125rem;font-weight:600;padding:1rem 1rem 0}.post-grid-link{color:#000;transition:color ease-in-out .2s;text-decoration:none}.post-grid-link:hover{color:var(--bf-color)}.post-grid-link::before{content:"";display:block;width:100%;height:11rem;background-color:transparent;top:0;left:0;position:absolute}.post-grid-desc{font-size:.875rem;color:#777;margin:0;line-height:1.75;transition:color ease-in-out .2s;padding:.5rem 1rem 1rem}.post-grid-meta{font-size:.75rem;color:#555;border-top:1px solid rgba(0,0,0,.05);opacity:.7;padding:1rem;margin-top:auto;position:relative}.post-grid-meta a{text-decoration:none;color:#555}.post-grid-meta a:hover,.post-grid-meta a:focus{color:var(--bf-color)}.post-grid-author{display:flex;align-items:center;margin-right:.875rem}.post-grid-author-img{border-radius:50rem;margin-right:.25rem;filter:grayscale(100%)}.post-grid-author-name{text-transform:capitalize}.post-grid-date{display:flex;align-items:center;margin-right:.875rem}.post-grid-date-icon{margin-right:.375rem}.post-grid-more{font-weight:500;position:absolute;top:0;right:0;padding-right:2.5rem;height:100%;line-height:3.125rem;font-weight:500}.post-grid-more .bi{margin-left:.1rem;width:1.5rem;height:1.5rem;position:absolute;right:1rem;top:50%;transform:translateY(-50%)}.post-grid:hover{box-shadow:0 0 10rem rgba(0,0,0,.1);border-color:#fff}.post-grid:hover .post-grid-link{color:var(--bf-color)}.post-grid:hover .post-grid-desc{color:#000}.post-list{align-items:center;height:100%;padding-left:20rem;margin-bottom:2rem;position:relative}.post-list-cover{position:absolute;top:0;left:0;margin:0;display:block;width:20rem;height:100%;border-radius:var(--bf-radius, 0.125rem);overflow:hidden}.post-list-cover::after{content:"";width:100%;height:100%;position:absolute;top:0;left:0;display:block;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);border-radius:var(--bf-radius, 0.125rem)}.post-list-img{width:100%;height:100%;object-fit:cover;object-position:center}.post-list-details{padding:1.5rem 0 1.5rem 2rem;flex-grow:1}.post-list-meta{font-size:.75rem;color:#555;margin-top:1rem;margin-bottom:1rem;opacity:.7}.post-list-meta a{color:#555;text-decoration:none}.post-list-meta-item{display:flex;align-items:center;margin-right:.875rem}.post-list-author-img{border-radius:5rem;margin-right:.25rem;filter:grayscale(100%)}.post-list-author-name{text-transform:capitalize}.post-list-date-icon{margin-right:.375rem}.post-list-cat-icon{margin-right:0rem}.post-list-title{font-size:1.5rem;font-weight:500;margin-bottom:1rem}.post-list-link{color:#000;transition:color ease-in-out .2s;text-decoration:none}.post-list-link:hover{color:var(--bf-color)}.post-list-link::before{content:"";display:block;width:20rem;height:100%;top:0;left:0;position:absolute}.post-list-desc{margin-bottom:.5rem;font-size:.875rem;color:#666;line-height:1.75;transition:color ease-in-out .2s;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.post-list-more{display:inline-block;font-weight:500;font-size:.875rem;color:#555;text-decoration:none}.post-list-more:hover{color:var(--bf-color)}.post-list:hover .post-list-desc{color:#000}.post-cover{width:100%;height:20rem;margin:0;position:relative;background-color:#000}.post-cover-img{height:100%;width:100%;object-fit:cover;object-position:center;opacity:.9}.post-cover-caption{display:block;max-width:100%;width:calc(var(--bf-post-width) + 8rem);position:relative;bottom:calc(8rem / 2);margin:-1.5rem auto 0;color:#fff;font-size:.625rem;text-align:right;line-height:1rem}.post-cover-caption a,.post-cover-caption span{border-radius:.125rem;display:inline-block;padding:0 .25rem;opacity:.7;color:#fff;background-color:rgba(0,0,0,.3)}.post-cover-caption a:hover,.post-cover-caption span:hover{opacity:1;background-color:#000}.post-cover::after{content:"";display:block;height:calc(8rem / 2);width:calc(var(--bf-post-width) + 8rem);max-width:100%;position:absolute;left:50%;bottom:0;transform:translateX(-50%);background-color:#fff;border-top-left-radius:var(--bf-radius, 0.125rem);border-top-right-radius:var(--bf-radius, 0.125rem)}.post-container{max-width:calc( var(--bf-post-width) + 3rem );width:100%;margin-right:auto;margin-left:auto;padding-right:1.5rem;padding-left:1.5rem}.post-title{margin-bottom:2.5rem;line-height:1.1;letter-spacing:-1px;font-weight:700;font-size:2.75rem;color:#000}.post-meta{margin-bottom:2.5rem;line-height:1.4;font-size:.875rem;font-weight:300;color:#666;white-space:nowrap;cursor:default}.post-meta-item:not(:last-of-type){margin-right:1.75rem}.post-meta-label{display:block;font-size:.75rem;color:#999}.post-meta a{color:#666;text-decoration:none}.post-meta a:hover{color:var(--bf-color)}.post-meta-btn{height:2rem;width:2rem;border:0;margin:0;padding:0;background:none;text-align:center}.post-meta-btn .bi{width:1.25rem;height:100%;fill:#888}.post-meta-btn:hover .bi{fill:var(--bf-color)}.post-meta-author{display:flex;align-items:center}.post-meta-author-img{margin-right:.5rem;border-radius:100%}.post-meta-author-name{text-transform:capitalize}.post-meta-cats{word-break:keep-all}.post-meta-cats-list{margin:0;padding:0;list-style:none}.post-meta-cats-item:not(:last-child){margin-right:.125rem}.post-meta-cats-item:not(:last-child) .post-meta-cats-link::after{content:","}.post-content{line-height:1.8;margin-bottom:4rem;hyphens:auto}.post-content a{overflow-wrap:break-word;word-break:break-all}.post-content iframe,.post-content video,.post-content img{border-radius:.125rem}.post-content audio,.post-content video{margin-bottom:1rem;width:100%;max-width:100%}.post-footer{margin-bottom:4rem}.post-author{margin-bottom:4rem;padding:2rem;border-radius:var(--bf-radius);background-color:var(--bs-light)}.post-author-name{margin:0 0 .25rem;font-size:1.125rem;text-transform:capitalize}.post-author-bio{margin-bottom:0;font-size:.875rem;font-weight:400;color:#000}.post-author-cover{margin:0 1.5rem 0 0;min-width:5rem}.post-author-img{width:5rem;height:5rem;border-radius:100%}.post #disqus_thread:not(:empty){padding:2rem;background-color:var(--bs-light);margin-bottom:4rem}.post #disqus_thread:not(:empty)>iframe{margin:0 !important}@media screen and (max-width: 767px){.post{margin-top:-2rem}.post-cover{margin-bottom:2rem;height:14rem}.post-cover-caption{display:none}.post-cover::after{border-radius:0;display:none}.post-title{font-size:1.5rem;letter-spacing:0}.post-meta{font-size:.75rem}.post-meta-item:not(:last-of-type){margin-right:1rem}.post-meta-cats-link::after{display:none}.post-meta-cats-item{display:none}.post-meta-cats-item:nth-child(1){display:block}}.post-nav{margin-bottom:4rem}.post-nav-item{position:relative;color:#000;text-decoration:none;display:block}.post-nav-item:hover{color:var(--bf-color)}.post-nav-title{z-index:2;font-size:.875rem;font-weight:600;margin:0}.post-nav-text{opacity:.5;text-transform:uppercase;font-size:.75rem}.share-modal .modal-dialog{max-width:25rem}.share-modal .modal-title{font-size:.875rem}.share-modal .btn-close{background-size:.75rem}.share-modal-list{list-style:none;padding:0;margin-bottom:2rem}.share-modal-link{padding:1.25rem 0 1.25rem;display:block;text-align:center;color:#666;background-color:rgba(68,68,68,.03);border-radius:.25rem;transition:all ease-in-out .2s;text-decoration:none}.share-modal-link-label{display:block;font-size:.75rem}.share-modal-link-icon{width:1.5rem;height:1.5rem;margin-bottom:.5rem;transition:fill ease .1s}.share-modal-link:hover{color:#fff;background-color:#444}.share-modal-link.-blogifier{background-color:rgba(98,42,255,.07);color:#622aff}.share-modal-link.-blogifier:hover{background-color:#622aff;color:#fff}.share-modal-link.-facebook{background-color:rgba(22,120,242,.07);color:#1678f2}.share-modal-link.-facebook:hover{background-color:#1678f2;color:#fff}.share-modal-link.-twitter{background-color:rgba(29,161,242,.07);color:#1da1f2}.share-modal-link.-twitter:hover{background-color:#1da1f2;color:#fff}.share-modal-link.-github{background-color:rgba(23,22,23,.07);color:#171617}.share-modal-link.-github:hover{background-color:#171617;color:#fff}.share-modal-link.-pinterest{background-color:rgba(230,0,35,.07);color:#e60023}.share-modal-link.-pinterest:hover{background-color:#e60023;color:#fff}.share-modal-link.-linkedin{background-color:rgba(10,102,194,.07);color:#0a66c2}.share-modal-link.-linkedin:hover{background-color:#0a66c2;color:#fff}.share-modal-link.-instagram{background-color:rgba(185,0,180,.07);color:#b900b4}.share-modal-link.-instagram:hover{background-color:#b900b4;color:#fff}.share-modal-link.-telegram{background-color:rgba(0,136,204,.07);color:#08c}.share-modal-link.-telegram:hover{background-color:#08c;color:#fff}.share-modal-link.-youtube{background-color:rgba(255,3,0,.07);color:#ff0300}.share-modal-link.-youtube:hover{background-color:#ff0300;color:#fff}.share-modal-link.-whatsapp{background-color:rgba(37,211,102,.07);color:#25d366}.share-modal-link.-whatsapp:hover{background-color:#25d366;color:#fff}.share-modal-input{border:0;background-color:rgba(68,68,68,.04);width:100%;padding:.75rem 1rem;outline:none !important;font-size:.875rem;border-radius:.25rem;cursor:pointer;color:#666;transition:all ease-in-out .3s}.share-modal-input:hover{color:#111;background-color:rgba(68,68,68,.07)}.share-modal-input.copied{background-color:#000;color:#fff;text-align:center}.related{margin-top:-1px;margin-bottom:4rem}.related-header{border-top:1px solid var(--bs-light);padding-top:3rem;margin-bottom:3rem;align-items:center;display:flex}.related-header-title{margin-bottom:0;font-size:1rem}.related-header-link{margin-bottom:0;color:rgba(0,0,0,.5);font-size:.875rem;font-weight:500;text-decoration:none}.related-header-link:hover{color:var(--bf-color)}/*# sourceMappingURL=styles.css.map */ diff --git a/src/Blogifier/wwwroot/themes/test/css/styles.css.map b/src/Blogifier/wwwroot/themes/test/css/styles.css.map deleted file mode 100644 index 22a3b3e48..000000000 --- a/src/Blogifier/wwwroot/themes/test/css/styles.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":["../scss/styles.scss","../scss/helpers/_reset.scss","../scss/helpers/_base.scss","../scss/layout/_header.scss","../scss/layout/_nav.scss","../scss/helpers/_variables.scss","../scss/helpers/_mixins.scss","../scss/layout/_widgets.scss","../scss/layout/_footer.scss","../scss/components/_pagination.scss","../scss/components/_newsletter.scss","../scss/components/_dropdowns.scss","../scss/components/_highlight.scss","../scss/post/_featured.scss","../scss/post/_view-grid.scss","../scss/post/_view-list.scss","../scss/post/_post.scss","../scss/post/_nav.scss","../scss/post/_share.scss","../scss/post/_related.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA,MCCE,eAGF,OACE,sBACA,gBACA,qBAGF,eAIE,qBAGF,MACE,WAGF,WACE,gBACA,kBACA,kBACA,qBACA,qCAEE,kBAEA,WACA,eACA,aACA,cACA,+UACA,cACA,uBACA,4BACA,WAGF,mBACE,WACA,OACA,yBAGF,kBACE,cACA,QAIA,wBACE,gBCtDN,WACE,cACA,eAGF,yBACE,MACE,uBCPJ,QACE,iBACA,oBAEA,cACE,qBACA,SACA,sCACA,0CACA,mCACA,gBACA,qBACA,wCAEE,mCAIJ,aACE,SACA,kBACA,gBACA,wCAGF,aACE,cACA,iBACE,kCACA,oCAIJ,yBAjCF,QAkCI,UACA,mBACA,kCCnCF,eACE,gBACA,SACA,UAGF,oCAEE,WACA,cACA,mBACA,SACA,gBACA,qBACA,cCwBK,QDvBL,gCACA,kBACA,qBACA,eAEA,gGAEE,WACA,iCAEF,wFAEE,8BAIJ,iBACE,eACA,gBACA,kCACE,aACA,cAGJ,mBACE,UACA,aAGF,oBACE,kBACA,2BACE,WACA,UACA,YACA,kBACA,QACA,2BACA,cACA,gCACA,cAEF,2CACE,gCAGJ,yBA9DF,YA+DI,wCACA,qCACA,sBACA,uBE1CA,6BACE,MDTG,QCQL,4BACE,MDTG,QCQL,2BACE,MDTG,QCQL,0BACE,MDTG,QCQL,6BACE,MDTG,QCQL,4BACE,MDTG,QCQL,6BACE,MDTG,QCQL,4BACE,MDTG,KCQL,2BACE,MDTG,QCQL,4BACE,MDTG,QEfP,kBACE,gBACA,oBACA,WACA,eACA,yBACA,cACA,mBAGA,uBACE,gBACA,UACA,SCdN,QACE,qCACA,gBACA,WACA,iBACA,UACE,WACA,qBACA,gBACE,MHRE,gBGWN,UACE,SCbJ,YACE,sBACA,YACA,mBACA,uBAEA,iBACE,eACA,UAEF,iBACE,cACA,WACA,gBACA,kBACA,YACA,iBACA,qBACA,yBACA,4BACA,WAEA,uBACE,UACA,MJvBE,gBIwBF,2BACE,eACA,gBAGJ,qBACE,cACA,eACA,wBACA,gBACA,iCCnCN,YACE,sBACA,WACA,cLqCW,2BKpCX,gBACA,kBAEA,iBACE,aAEA,qCAHF,iBAII,cAIJ,kBACE,kBACA,gBACA,WAGF,iBACE,SACA,mBACA,gBAGF,iBACE,sBACA,cLWS,2BKRX,kBACE,gBACA,SACA,YACA,WACA,mBACA,WACA,wBAEF,gBACE,gBACA,SACA,mBACA,WACA,cACA,2BACA,4CAEE,aACA,gBAIJ,gBACE,aACA,kBACA,sBACA,cLnBS,2BKoBT,kBACA,MACA,OACA,QACA,SACA,aACA,sBACA,SACA,WACA,eAEA,gCACE,WCvEN,eACE,cNqCO,QMpCP,mCACA,YAGF,eACE,kBACA,gBACA,0BACA,mBACA,WACA,+BACA,2CAEE,WACA,iCAGJ,kBACE,gBACA,oCACA,SCvBF,SACE,cACA,gBACA,aACA,MP0BgB,QOzBhB,WP0BgB,QOzBhB,SACA,qCACA,kBACA,gBACA,gBACA,oBACA,kBACA,iBACA,gBACA,gBACA,cACA,WACA,qBACA,kBACA,iBACA,aAGF,cACE,MPOc,QOJhB,gJASE,MPJU,QOOZ,gBACE,cAEF,WACE,cAEF,uFAOE,cAGF,2IASE,MP/BS,KOkCX,wBAEE,WAGF,eACE,cAGF,eACE,cAGF,eACE,kBAGF,aACE,iBCtFF,UACE,mBAGA,gBACE,SACA,gBACA,gBACA,gBACA,kBACA,cR8BS,2BQ7BT,2CAEA,qCATF,gBAUI,cAEF,qBACE,cACA,kBACA,MACA,OACA,WACA,YAEF,oBACE,cReO,2BQdP,kBACA,MACA,OACA,WACA,YACA,iBACA,uBAKJ,kBACE,iBACA,oBACA,kBAIF,gBACE,kBACA,gBACA,qBACA,gBAEF,eACE,qBACA,WACA,qBACE,MRrDE,gBQ0DN,eACE,kBACA,WACA,qBACA,WACA,iBACE,WACA,uBACE,WAIN,iBACE,aACA,mBACA,kBACA,qBACE,oBACA,oBACA,uBAEF,sBACE,0BAGJ,eACE,aACA,mBACA,qBAEA,oBACE,qBAKJ,cACE,aACA,mBACA,mBACE,kBAEF,oBACE,qBAKJ,eACE,WACA,gBACA,qBAEF,eACE,gBACA,kBACA,MRlHI,gBQmHJ,qBACA,qBACE,WAKJ,8BAEE,WACA,UAEF,eACE,WAEF,eACE,cAKA,8DAEE,WAIJ,qCA/IF,UAgJI,wCACA,0BACA,iCAEA,eACE,eAGF,kBACE,UACA,oBAGF,gBACE,kBACA,oBACA,gBAGF,eACE,kBACA,qBCrKN,WACE,YACA,kBACA,sCACA,cToCW,2BSnCX,wCACA,gBAGA,iBACE,cACA,kBACA,kBACA,wCAEF,eACE,eACA,aACA,iBACA,uBACA,uBToBS,2BSnBT,wBTmBS,2BShBX,gBACE,eACA,gBACA,aACA,eAEA,qBACE,qBACA,iBACA,mBACA,WACA,6BACE,YACA,qBACA,mBAGF,2BACE,MTzCA,gBS+CN,iBACE,mBACA,gBACA,oBAGF,gBACE,WACA,iCACA,qBACA,sBACE,MT1DE,gBS4DJ,wBACE,WACA,cACA,WACA,aACA,6BACA,MACA,OACA,kBAKJ,gBACE,kBACA,WACA,SACA,iBACA,iCACA,wBAIF,gBACE,iBACA,WACA,qCAEA,WACA,aACA,gBACA,kBACA,kBACE,qBACA,WACA,gDAEE,MTjGA,gBSqGN,kBACE,aACA,mBACA,qBACA,sBACE,oBACA,oBACA,uBAEF,uBACE,0BAGJ,gBACE,aACA,mBACA,qBACA,qBACE,qBAIJ,gBACE,gBACA,kBACA,MACA,QACA,qBACA,YACA,qBACA,gBAEA,oBACE,kBACA,aACA,cACA,kBACA,WACA,QACA,2BAKJ,iBACE,oCACA,kBACA,iCACE,MTrJE,gBSuJJ,iCACE,WCzJN,WACE,mBACA,YACA,mBACA,mBACA,kBAGA,iBACE,kBACA,MACA,OACA,SACA,cACA,YACA,YACA,cVwBS,2BUvBT,gBAEA,wBACE,WACA,WACA,YACA,kBACA,MACA,OACA,cACA,0CACA,cVYO,2BUTX,eACE,WACA,YACA,iBACA,uBAIF,mBACE,6BACA,YAIF,gBACE,iBACA,WACA,gBACA,mBACA,WACA,kBACE,WACA,qBAEF,qBACE,aACA,mBACA,qBAIF,sBACE,mBACA,oBACA,uBAEF,uBACE,0BAIF,qBACE,qBAIF,oBACE,kBAKJ,iBACE,iBACA,gBACA,mBAGF,gBACE,WACA,iCACA,qBACA,sBACE,MV7FE,gBUgGJ,wBACE,WACA,cACA,YACA,YACA,MACA,OACA,kBAKJ,gBACE,oBACA,kBACA,WACA,iBACA,iCAEA,oBACA,qBACA,4BACA,gBAIF,gBACE,qBACA,gBACA,kBACA,WACA,qBACA,sBACE,MVjIE,gBUuIJ,iCACE,WCxIJ,YACE,WACA,aACA,SACA,kBACA,sBACA,gBACE,YACA,WACA,iBACA,uBACA,WAEF,oBACE,cACA,eACA,wCACA,kBACA,sBACA,sBACA,WACA,kBACA,iBACA,iBACA,+CAEE,cXYC,QWXD,qBACA,iBACA,WACA,WACA,gCACA,2DACE,UACA,sBAMN,mBACE,WACA,cACA,sBACA,wCACA,eACA,kBACA,SACA,SACA,2BACA,sBACA,uBXZO,2BWaP,wBXbO,2BWiBX,gBACE,8CAGA,WACA,kBACA,iBACA,qBACA,oBAIF,YACE,qBACA,gBACA,oBACA,gBACA,kBACA,WAGF,WACE,qBACA,gBACA,kBACA,gBACA,WACA,mBACA,eAGE,mCACE,qBAIJ,iBACE,cACA,iBACA,WAGF,aACE,WACA,qBACA,mBACE,MXtGA,gBW0GJ,eACE,YACA,WACA,SACA,SACA,UACA,gBACA,kBAEA,mBACE,cACA,YACA,UAIA,yBACE,KX3HF,gBWgIJ,kBACE,aACA,mBACA,sBACE,mBACA,mBAEF,uBACE,0BAIJ,gBACE,oBACA,qBACE,SACA,UACA,gBAGA,sCACE,qBAEE,kEACE,YASZ,cACE,gBACA,mBACA,aACA,gBACE,yBACA,qBAEF,2DAGE,sBAGF,wCAEE,mBACA,WACA,eAKJ,aACE,mBAIF,aACE,mBACA,aACA,+BACA,iCACA,kBACE,kBACA,mBACA,0BAEF,iBACE,gBACA,kBACA,gBACA,WAEF,mBACE,oBACA,eAEF,iBACE,WACA,YACA,mBAMF,iCACE,aACA,iCACA,mBAEA,wCACE,oBAKN,qCAtOF,MAuOI,iBAEA,YACE,mBACA,aACA,oBACE,aAEF,mBACE,gBACA,aAIJ,YACE,iBACA,iBAGF,WACE,iBAEE,mCACE,kBAKA,4BACE,aAGJ,qBACE,aAEA,kCACE,eC3QZ,UACE,mBAGA,eACE,kBACA,WACA,qBACA,cACA,qBACE,MZTE,gBYcN,gBACE,UACA,kBACA,gBACA,SAIF,eACE,WACA,yBACA,iBCxBA,2BACE,gBAEF,0BACE,kBAEF,wBACE,uBAKF,kBACE,gBACA,UACA,mBAGF,kBACE,0BACA,cACA,kBACA,WACA,oCACA,qBACA,+BACA,qBACA,wBACE,cACA,iBAEF,uBACE,aACA,cACA,oBACA,yBAGF,wBACE,WACA,sBZnCN,6BACE,iBYwCM,oBZvCN,MDOK,QCLL,mCACE,iBDIG,QCHH,MYsCI,KZ5CR,4BACE,iBYwCM,qBZvCN,MDOK,QCLL,kCACE,iBDIG,QCHH,MYsCI,KZ5CR,2BACE,iBYwCM,qBZvCN,MDOK,QCLL,iCACE,iBDIG,QCHH,MYsCI,KZ5CR,0BACE,iBYwCM,mBZvCN,MDOK,QCLL,gCACE,iBDIG,QCHH,MYsCI,KZ5CR,6BACE,iBYwCM,mBZvCN,MDOK,QCLL,mCACE,iBDIG,QCHH,MYsCI,KZ5CR,4BACE,iBYwCM,qBZvCN,MDOK,QCLL,kCACE,iBDIG,QCHH,MYsCI,KZ5CR,6BACE,iBYwCM,oBZvCN,MDOK,QCLL,mCACE,iBDIG,QCHH,MYsCI,KZ5CR,4BACE,iBYwCM,oBZvCN,MDOK,KCLL,kCACE,iBDIG,KCHH,MYsCI,KZ5CR,2BACE,iBYwCM,kBZvCN,MDOK,QCLL,iCACE,iBDIG,QCHH,MYsCI,KZ5CR,4BACE,iBYwCM,qBZvCN,MDOK,QCLL,kCACE,iBDIG,QCHH,MYsCI,KAKN,mBACE,SACA,oCACA,WACA,oBACA,wBACA,kBACA,qBACA,eACA,WACA,+BACA,yBACE,WACA,oCAEF,0BACE,sBACA,WACA,kBC1ER,SACE,gBACA,mBAEA,gBACE,qCACA,iBACA,mBACA,mBACA,aACA,sBACE,gBACA,eAEF,qBACE,gBACA,qBACA,kBACA,gBACA,qBACA,2BACE,MdpBA","file":"styles.css"} \ No newline at end of file diff --git a/src/Blogifier/wwwroot/themes/test/img/logo-black.png b/src/Blogifier/wwwroot/themes/test/img/logo-black.png deleted file mode 100644 index 82e24f88d..000000000 Binary files a/src/Blogifier/wwwroot/themes/test/img/logo-black.png and /dev/null differ diff --git a/src/Blogifier/wwwroot/themes/test/img/logo-white.png b/src/Blogifier/wwwroot/themes/test/img/logo-white.png deleted file mode 100644 index bb4637bd3..000000000 Binary files a/src/Blogifier/wwwroot/themes/test/img/logo-white.png and /dev/null differ diff --git a/src/Blogifier/wwwroot/themes/test/js/highlight.js b/src/Blogifier/wwwroot/themes/test/js/highlight.js deleted file mode 100644 index 07ead4002..000000000 --- a/src/Blogifier/wwwroot/themes/test/js/highlight.js +++ /dev/null @@ -1,1257 +0,0 @@ -/* - Highlight.js 10.5.0 (af20048d) - License: BSD-3-Clause - Copyright (c) 2006-2020, Ivan Sagalaev -*/ -var hljs=function(){"use strict";function e(t){ -return t instanceof Map?t.clear=t.delete=t.set=()=>{ -throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ -throw Error("set is read-only") -}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{var s=t[n] -;"object"!=typeof s||Object.isFrozen(s)||e(s)})),t}var t=e,n=e;t.default=n -;class s{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data} -ignoreMatch(){this.ignore=!0}}function r(e){ -return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") -}function a(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] -;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const i=e=>!!e.kind -;class o{constructor(e,t){ -this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ -this.buffer+=r(e)}openNode(e){if(!i(e))return;let t=e.kind -;e.sublanguage||(t=`${this.classPrefix}${t}`),this.span(t)}closeNode(e){ -i(e)&&(this.buffer+="
")}value(){return this.buffer}span(e){ -this.buffer+=``}}class l{constructor(){this.rootNode={ -children:[]},this.stack=[this.rootNode]}get top(){ -return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ -this.top.children.push(e)}openNode(e){const t={kind:e,children:[]} -;this.add(t),this.stack.push(t)}closeNode(){ -if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ -for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} -walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ -return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), -t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ -"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ -l._collapse(e)})))}}class c extends l{constructor(e){super(),this.options=e} -addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())} -addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root -;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){ -return new o(this,this.options).value()}finalize(){return!0}}function u(e){ -return e?"string"==typeof e?e:e.source:null} -const g="[a-zA-Z]\\w*",d="[a-zA-Z_]\\w*",h="\\b\\d+(\\.\\d+)?",f="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",p="\\b(0b[01]+)",m={ -begin:"\\\\[\\s\\S]",relevance:0},b={className:"string",begin:"'",end:"'", -illegal:"\\n",contains:[m]},x={className:"string",begin:'"',end:'"', -illegal:"\\n",contains:[m]},E={ -begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ -},v=(e,t,n={})=>{const s=a({className:"comment",begin:e,end:t,contains:[]},n) -;return s.contains.push(E),s.contains.push({className:"doctag", -begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),s -},N=v("//","$"),w=v("/\\*","\\*/"),R=v("#","$");var y=Object.freeze({ -__proto__:null,IDENT_RE:g,UNDERSCORE_IDENT_RE:d,NUMBER_RE:h,C_NUMBER_RE:f, -BINARY_NUMBER_RE:p, -RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", -SHEBANG:(e={})=>{const t=/^#![ ]*\// -;return e.binary&&(e.begin=((...e)=>e.map((e=>u(e))).join(""))(t,/.*\b/,e.binary,/\b.*/)), -a({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{ -0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:m,APOS_STRING_MODE:b, -QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:E,COMMENT:v,C_LINE_COMMENT_MODE:N, -C_BLOCK_COMMENT_MODE:w,HASH_COMMENT_MODE:R,NUMBER_MODE:{className:"number", -begin:h,relevance:0},C_NUMBER_MODE:{className:"number",begin:f,relevance:0}, -BINARY_NUMBER_MODE:{className:"number",begin:p,relevance:0},CSS_NUMBER_MODE:{ -className:"number", -begin:h+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", -relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp", -begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[m,{begin:/\[/,end:/\]/, -relevance:0,contains:[m]}]}]},TITLE_MODE:{className:"title",begin:g,relevance:0 -},UNDERSCORE_TITLE_MODE:{className:"title",begin:d,relevance:0},METHOD_GUARD:{ -begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{ -"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ -t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function _(e,t){ -"."===e.input[e.index-1]&&t.ignoreMatch()}function k(e,t){ -t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", -e.__beforeBegin=_,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords) -}function M(e,t){ -Array.isArray(e.illegal)&&(e.illegal=((...e)=>"("+e.map((e=>u(e))).join("|")+")")(...e.illegal)) -}function O(e,t){if(e.match){ -if(e.begin||e.end)throw Error("begin & end are not supported with match") -;e.begin=e.match,delete e.match}}function A(e,t){ -void 0===e.relevance&&(e.relevance=1)} -const L=["of","and","for","in","not","or","if","then","parent","list","value"] -;function B(e,t){return t?Number(t):(e=>L.includes(e.toLowerCase()))(e)?0:1} -function I(e,{plugins:t}){function n(t,n){ -return RegExp(u(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class s{ -constructor(){ -this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} -addRule(e,t){ -t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), -this.matchAt+=(e=>RegExp(e.toString()+"|").exec("").length-1)(e)+1}compile(){ -0===this.regexes.length&&(this.exec=()=>null) -;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(((e,t="|")=>{ -const n=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;let s=0,r="" -;for(let a=0;a0&&(r+=t),r+="(";o.length>0;){const e=n.exec(o);if(null==e){r+=o;break} -r+=o.substring(0,e.index), -o=o.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+(Number(e[1])+i):(r+=e[0], -"("===e[0]&&s++)}r+=")"}return r})(e),!0),this.lastIndex=0}exec(e){ -this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e) -;if(!t)return null -;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),s=this.matchIndexes[n] -;return t.splice(0,n),Object.assign(t,s)}}class r{constructor(){ -this.rules=[],this.multiRegexes=[], -this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ -if(this.multiRegexes[e])return this.multiRegexes[e];const t=new s -;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), -t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ -return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ -this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ -const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex -;let n=t.exec(e) -;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ -const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} -return n&&(this.regexIndex+=n.position+1, -this.regexIndex===this.count&&this.considerAll()),n}} -if(e.compilerExtensions||(e.compilerExtensions=[]), -e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") -;return e.classNameAliases=a(e.classNameAliases||{}),function t(s,i){const o=s -;if(s.compiled)return o -;[O].forEach((e=>e(s,i))),e.compilerExtensions.forEach((e=>e(s,i))), -s.__beforeBegin=null,[k,M,A].forEach((e=>e(s,i))),s.compiled=!0;let l=null -;if("object"==typeof s.keywords&&(l=s.keywords.$pattern, -delete s.keywords.$pattern),s.keywords&&(s.keywords=((e,t)=>{const n={} -;return"string"==typeof e?s("keyword",e):Object.keys(e).forEach((t=>{s(t,e[t]) -})),n;function s(e,s){t&&(s=s.toLowerCase()),s.split(" ").forEach((t=>{ -const s=t.split("|");n[s[0]]=[e,B(s[0],s[1])]}))} -})(s.keywords,e.case_insensitive)), -s.lexemes&&l)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ") -;return l=l||s.lexemes||/\w+/, -o.keywordPatternRe=n(l,!0),i&&(s.begin||(s.begin=/\B|\b/), -o.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin), -s.end||s.endsWithParent||(s.end=/\B|\b/), -s.end&&(o.endRe=n(s.end)),o.terminatorEnd=u(s.end)||"", -s.endsWithParent&&i.terminatorEnd&&(o.terminatorEnd+=(s.end?"|":"")+i.terminatorEnd)), -s.illegal&&(o.illegalRe=n(s.illegal)), -s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>a(e,{ -variants:null},t)))),e.cachedVariants?e.cachedVariants:T(e)?a(e,{ -starts:e.starts?a(e.starts):null -}):Object.isFrozen(e)?a(e):e))("self"===e?s:e)))),s.contains.forEach((e=>{t(e,o) -})),s.starts&&t(s.starts,i),o.matcher=(e=>{const t=new r -;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" -}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" -}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(o),o}(e)}function T(e){ -return!!e&&(e.endsWithParent||T(e.starts))}function j(e){const t={ -props:["language","code","autodetect"],data:()=>({detectedLanguage:"", -unknownLanguage:!1}),computed:{className(){ -return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){ -if(!this.autoDetect&&!e.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`), -this.unknownLanguage=!0,r(this.code);let t={} -;return this.autoDetect?(t=e.highlightAuto(this.code), -this.detectedLanguage=t.language):(t=e.highlight(this.language,this.code,this.ignoreIllegals), -this.detectedLanguage=this.language),t.value},autoDetect(){ -return!(this.language&&(e=this.autodetect,!e&&""!==e));var e}, -ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{ -class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{ -Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}const S={ -"after:highlightBlock":({block:e,result:t,text:n})=>{const s=D(e) -;if(!s.length)return;const a=document.createElement("div") -;a.innerHTML=t.value,t.value=((e,t,n)=>{let s=0,a="";const i=[];function o(){ -return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset"}function c(e){ -a+=""}function u(e){("start"===e.event?l:c)(e.node)} -for(;e.length||t.length;){let t=o() -;if(a+=r(n.substring(s,t[0].offset)),s=t[0].offset,t===e){i.reverse().forEach(c) -;do{u(t.splice(0,1)[0]),t=o()}while(t===e&&t.length&&t[0].offset===s) -;i.reverse().forEach(l) -}else"start"===t[0].event?i.push(t[0].node):i.pop(),u(t.splice(0,1)[0])} -return a+r(n.substr(s))})(s,D(a),n)}};function P(e){ -return e.nodeName.toLowerCase()}function D(e){const t=[];return function e(n,s){ -for(let r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?s+=r.nodeValue.length:1===r.nodeType&&(t.push({ -event:"start",offset:s,node:r}),s=e(r,s),P(r).match(/br|hr|img|input/)||t.push({ -event:"stop",offset:s,node:r}));return s}(e,0),t}const C=e=>{console.error(e) -},H=(e,...t)=>{console.log("WARN: "+e,...t)},$=(e,t)=>{ -console.log(`Deprecated as of ${e}. ${t}`)},U=r,z=a,K=Symbol("nomatch") -;return(e=>{const n=Object.create(null),r=Object.create(null),a=[];let i=!0 -;const o=/(^(<[^>]+>|\t|)+|\n)/gm,l="Could not find the language '{}', did you forget to load/include a language module?",u={ -disableAutodetect:!0,name:"Plain text",contains:[]};let g={ -noHighlightRe:/^(no-?highlight)$/i, -languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", -tabReplace:null,useBR:!1,languages:null,__emitter:c};function d(e){ -return g.noHighlightRe.test(e)}function h(e,t,n,s){const r={code:t,language:e} -;_("before:highlight",r);const a=r.result?r.result:f(r.language,r.code,n,s) -;return a.code=r.code,_("after:highlight",a),a}function f(e,t,r,o){const c=t -;function u(e,t){const n=w.case_insensitive?t[0].toLowerCase():t[0] -;return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]} -function d(){null!=_.subLanguage?(()=>{if(""===O)return;let e=null -;if("string"==typeof _.subLanguage){ -if(!n[_.subLanguage])return void M.addText(O) -;e=f(_.subLanguage,O,!0,k[_.subLanguage]),k[_.subLanguage]=e.top -}else e=p(O,_.subLanguage.length?_.subLanguage:null) -;_.relevance>0&&(A+=e.relevance),M.addSublanguage(e.emitter,e.language) -})():(()=>{if(!_.keywords)return void M.addText(O);let e=0 -;_.keywordPatternRe.lastIndex=0;let t=_.keywordPatternRe.exec(O),n="";for(;t;){ -n+=O.substring(e,t.index);const s=u(_,t);if(s){const[e,r]=s -;M.addText(n),n="",A+=r;const a=w.classNameAliases[e]||e;M.addKeyword(t[0],a) -}else n+=t[0];e=_.keywordPatternRe.lastIndex,t=_.keywordPatternRe.exec(O)} -n+=O.substr(e),M.addText(n)})(),O=""}function h(e){ -return e.className&&M.openNode(w.classNameAliases[e.className]||e.className), -_=Object.create(e,{parent:{value:_}}),_}function m(e,t,n){let r=((e,t)=>{ -const n=e&&e.exec(t);return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){ -const n=new s(e);e["on:end"](t,n),n.ignore&&(r=!1)}if(r){ -for(;e.endsParent&&e.parent;)e=e.parent;return e}} -if(e.endsWithParent)return m(e.parent,t,n)}function b(e){ -return 0===_.matcher.regexIndex?(O+=e[0],1):(T=!0,0)}function x(e){ -const t=e[0],n=c.substr(e.index),s=m(_,e,n);if(!s)return K;const r=_ -;r.skip?O+=t:(r.returnEnd||r.excludeEnd||(O+=t),d(),r.excludeEnd&&(O=t));do{ -_.className&&M.closeNode(),_.skip||_.subLanguage||(A+=_.relevance),_=_.parent -}while(_!==s.parent) -;return s.starts&&(s.endSameAsBegin&&(s.starts.endRe=s.endRe), -h(s.starts)),r.returnEnd?0:t.length}let E={};function v(t,n){const a=n&&n[0] -;if(O+=t,null==a)return d(),0 -;if("begin"===E.type&&"end"===n.type&&E.index===n.index&&""===a){ -if(O+=c.slice(n.index,n.index+1),!i){const t=Error("0 width match regex") -;throw t.languageName=e,t.badRule=E.rule,t}return 1} -if(E=n,"begin"===n.type)return function(e){ -const t=e[0],n=e.rule,r=new s(n),a=[n.__beforeBegin,n["on:begin"]] -;for(const n of a)if(n&&(n(e,r),r.ignore))return b(t) -;return n&&n.endSameAsBegin&&(n.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")), -n.skip?O+=t:(n.excludeBegin&&(O+=t), -d(),n.returnBegin||n.excludeBegin||(O=t)),h(n),n.returnBegin?0:t.length}(n) -;if("illegal"===n.type&&!r){ -const e=Error('Illegal lexeme "'+a+'" for mode "'+(_.className||"")+'"') -;throw e.mode=_,e}if("end"===n.type){const e=x(n);if(e!==K)return e} -if("illegal"===n.type&&""===a)return 1 -;if(B>1e5&&B>3*n.index)throw Error("potential infinite loop, way more iterations than matches") -;return O+=a,a.length}const w=N(e) -;if(!w)throw C(l.replace("{}",e)),Error('Unknown language: "'+e+'"') -;const R=I(w,{plugins:a});let y="",_=o||R;const k={},M=new g.__emitter(g);(()=>{ -const e=[];for(let t=_;t!==w;t=t.parent)t.className&&e.unshift(t.className) -;e.forEach((e=>M.openNode(e)))})();let O="",A=0,L=0,B=0,T=!1;try{ -for(_.matcher.considerAll();;){ -B++,T?T=!1:_.matcher.considerAll(),_.matcher.lastIndex=L -;const e=_.matcher.exec(c);if(!e)break;const t=v(c.substring(L,e.index),e) -;L=e.index+t}return v(c.substr(L)),M.closeAllNodes(),M.finalize(),y=M.toHTML(),{ -relevance:A,value:y,language:e,illegal:!1,emitter:M,top:_}}catch(t){ -if(t.message&&t.message.includes("Illegal"))return{illegal:!0,illegalBy:{ -msg:t.message,context:c.slice(L-100,L+100),mode:t.mode},sofar:y,relevance:0, -value:U(c),emitter:M};if(i)return{illegal:!1,relevance:0,value:U(c),emitter:M, -language:e,top:_,errorRaised:t};throw t}}function p(e,t){ -t=t||g.languages||Object.keys(n);const s=(e=>{const t={relevance:0, -emitter:new g.__emitter(g),value:U(e),illegal:!1,top:u} -;return t.emitter.addText(e),t})(e),r=t.filter(N).filter(R).map((t=>f(t,e,!1))) -;r.unshift(s);const a=r.sort(((e,t)=>{ -if(e.relevance!==t.relevance)return t.relevance-e.relevance -;if(e.language&&t.language){if(N(e.language).supersetOf===t.language)return 1 -;if(N(t.language).supersetOf===e.language)return-1}return 0})),[i,o]=a,l=i -;return l.second_best=o,l}const m={"before:highlightBlock":({block:e})=>{ -g.useBR&&(e.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")) -},"after:highlightBlock":({result:e})=>{ -g.useBR&&(e.value=e.value.replace(/\n/g,"
"))}},b=/^(<[^>]+>|\t)+/gm,x={ -"after:highlightBlock":({result:e})=>{ -g.tabReplace&&(e.value=e.value.replace(b,(e=>e.replace(/\t/g,g.tabReplace))))}} -;function E(e){let t=null;const n=(e=>{let t=e.className+" " -;t+=e.parentNode?e.parentNode.className:"";const n=g.languageDetectRe.exec(t) -;if(n){const t=N(n[1]) -;return t||(H(l.replace("{}",n[1])),H("Falling back to no-highlight mode for this block.",e)), -t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>d(e)||N(e)))})(e) -;if(d(n))return;_("before:highlightBlock",{block:e,language:n}),t=e -;const s=t.textContent,a=n?h(n,s,!0):p(s);_("after:highlightBlock",{block:e, -result:a,text:s}),e.innerHTML=a.value,((e,t,n)=>{const s=t?r[t]:n -;e.classList.add("hljs"),s&&e.classList.add(s)})(e,n,a.language),e.result={ -language:a.language,re:a.relevance,relavance:a.relevance -},a.second_best&&(e.second_best={language:a.second_best.language, -re:a.second_best.relevance,relavance:a.second_best.relevance})}const v=()=>{ -v.called||(v.called=!0,document.querySelectorAll("pre code").forEach(E))} -;function N(e){return e=(e||"").toLowerCase(),n[e]||n[r[e]]} -function w(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{r[e]=t -}))}function R(e){const t=N(e);return t&&!t.disableAutodetect}function _(e,t){ -const n=e;a.forEach((e=>{e[n]&&e[n](t)}))}Object.assign(e,{highlight:h, -highlightAuto:p,fixMarkup:e=>{ -return $("10.2.0","fixMarkup will be removed entirely in v11.0"), -$("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"), -t=e, -g.tabReplace||g.useBR?t.replace(o,(e=>"\n"===e?g.useBR?"
":e:g.tabReplace?e.replace(/\t/g,g.tabReplace):e)):t -;var t},highlightBlock:E,configure:e=>{ -e.useBR&&($("10.3.0","'useBR' will be removed entirely in v11.0"), -$("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")), -g=z(g,e)},initHighlighting:v,initHighlightingOnLoad:()=>{ -window.addEventListener("DOMContentLoaded",v,!1)},registerLanguage:(t,s)=>{ -let r=null;try{r=s(e)}catch(e){ -if(C("Language definition for '{}' could not be registered.".replace("{}",t)), -!i)throw e;C(e),r=u} -r.name||(r.name=t),n[t]=r,r.rawDefinition=s.bind(null,e),r.aliases&&w(r.aliases,{ -languageName:t})},listLanguages:()=>Object.keys(n),getLanguage:N, -registerAliases:w,requireLanguage:e=>{ -$("10.4.0","requireLanguage will be removed entirely in v11."), -$("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844") -;const t=N(e);if(t)return t -;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))}, -autoDetection:R,inherit:z,addPlugin:e=>{a.push(e)},vuePlugin:j(e).VuePlugin -}),e.debugMode=()=>{i=!1},e.safeMode=()=>{i=!0},e.versionString="10.5.0" -;for(const e in y)"object"==typeof y[e]&&t(y[e]) -;return Object.assign(e,y),e.addPlugin(m),e.addPlugin(S),e.addPlugin(x),e})({}) -}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("apache",(()=>{"use strict";return e=>{const n={ -className:"number",begin:/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?/} -;return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0, -contains:[e.HASH_COMMENT_MODE,{className:"section",begin:/<\/?/,end:/>/, -contains:[n,{className:"number",begin:/:\d{1,5}/ -},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute", -begin:/\w+/,relevance:0,keywords:{ -nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername" -},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"}, -contains:[{className:"meta",begin:/\s\[/,end:/\]$/},{className:"variable", -begin:/[\$%]\{/,end:/\}/,contains:["self",{className:"number",begin:/[$%]\d+/}] -},n,{className:"number",begin:/\d+/},e.QUOTE_STRING_MODE]}}],illegal:/\S/}} -})());hljs.registerLanguage("properties",(()=>{"use strict";return e=>{ -var n="[ \\t\\f]*",a=n+"[:=]"+n,t="("+a+"|[ \\t\\f]+)",r="([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",s="([^\\\\:= \\t\\f\\n]|\\\\.)+",i={ -end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{ -begin:"\\\\\\\\"},{begin:"\\\\\\n"}]}};return{name:".properties", -case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{ -returnBegin:!0,variants:[{begin:r+a,relevance:1},{begin:r+"[ \\t\\f]+", -relevance:0}],contains:[{className:"attr",begin:r,endsParent:!0,relevance:0}], -starts:i},{begin:s+t,returnBegin:!0,relevance:0,contains:[{className:"meta", -begin:s,endsParent:!0,relevance:0}],starts:i},{className:"attr",relevance:0, -begin:s+n+"$"}]}}})());hljs.registerLanguage("diff",(()=>{"use strict";return e=>({name:"Diff", -aliases:["patch"],contains:[{className:"meta",relevance:10,variants:[{ -begin:/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/},{begin:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{ -begin:/^--- +\d+,\d+ +----$/}]},{className:"comment",variants:[{begin:/Index: /, -end:/$/},{begin:/^index/,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^-{3}/,end:/$/ -},{begin:/^\*{3} /,end:/$/},{begin:/^\+{3}/,end:/$/},{begin:/^\*{15}$/},{ -begin:/^diff --git/,end:/$/}]},{className:"addition",begin:/^\+/,end:/$/},{ -className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, -end:/$/}]})})());hljs.registerLanguage("cpp",(()=>{"use strict";function e(e){ -return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?") -}return t=>{const n=(t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}] -}),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",s={ -className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},i={className:"string", -variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n", -contains:[t.BACKSLASH_ESCAPE]},{ -begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", -end:"'",illegal:"."},t.END_SAME_AS_BEGIN({ -begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},c={ -className:"number",variants:[{begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" -},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},o={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ -"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" -},contains:[{begin:/\\\n/,relevance:0},t.inherit(i,{className:"meta-string"}),{ -className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n" -},n,t.C_BLOCK_COMMENT_MODE]},l={className:"title",begin:e(r)+t.IDENT_RE, -relevance:0},d=e(r)+t.IDENT_RE+"\\s*\\(",u={ -keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq", -built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary", -literal:"true false nullptr NULL"},p=[o,s,n,t.C_BLOCK_COMMENT_MODE,c,i],m={ -variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{ -beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:p.concat([{ -begin:/\(/,end:/\)/,keywords:u,contains:p.concat(["self"]),relevance:0}]), -relevance:0},_={className:"function",begin:"("+a+"[\\*&\\s]+)+"+d, -returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:u,illegal:/[^\w\s\*&:<>.]/, -contains:[{begin:"decltype\\(auto\\)",keywords:u,relevance:0},{begin:d, -returnBegin:!0,contains:[l],relevance:0},{className:"params",begin:/\(/, -end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,i,c,s,{ -begin:/\(/,end:/\)/,keywords:u,relevance:0, -contains:["self",n,t.C_BLOCK_COMMENT_MODE,i,c,s]}] -},s,n,t.C_BLOCK_COMMENT_MODE,o]};return{ -aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:u, -disableAutodetect:!0,illegal:"",keywords:u,contains:["self",s]},{begin:t.IDENT_RE+"::",keywords:u},{ -className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/, -contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{ -preprocessor:o,strings:i,keywords:u}}})(t) -;return n.disableAutodetect=!1,n.name="C++", -n.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],n}})());hljs.registerLanguage("kotlin",(()=>{"use strict" -;var e="\\.([0-9](_*[0-9])*)",n="[0-9a-fA-F](_*[0-9a-fA-F])*",a={ -className:"number",variants:[{ -begin:`(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` -},{begin:`\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ -begin:`(${e})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ -begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` -},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ -begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], -relevance:0};return e=>{const n={ -keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", -built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", -literal:"true false null"},i={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" -},s={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},t={ -className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", -variants:[{begin:'"""',end:'"""(?=[^"])',contains:[t,s]},{begin:"'",end:"'", -illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, -contains:[e.BACKSLASH_ESCAPE,t,s]}]};s.contains.push(r);const l={ -className:"meta", -begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" -},c={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, -end:/\)/,contains:[e.inherit(r,{className:"meta-string"})]}] -},o=a,b=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),E={ -variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, -contains:[]}]},d=E;return d.variants[1].contains=[E],E.variants[1].contains=[d], -{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{ -relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}] -}),e.C_LINE_COMMENT_MODE,b,{className:"keyword", -begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", -begin:/@\w+/}]}},i,l,c,{className:"function",beginKeywords:"fun",end:"[(]|$", -returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ -begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, -contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, -keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, -endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, -endsWithParent:!0,contains:[E,e.C_LINE_COMMENT_MODE,b],relevance:0 -},e.C_LINE_COMMENT_MODE,b,l,c,r,e.C_NUMBER_MODE]},b]},{className:"class", -beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, -illegal:"extends implements",contains:[{ -beginKeywords:"public protected internal private constructor" -},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, -excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/, -excludeBegin:!0,returnEnd:!0},l,c]},r,{className:"meta",begin:"^#!/usr/bin/env", -end:"$",illegal:"\n"},o]}}})());hljs.registerLanguage("ruby",(()=>{"use strict";function e(...e){ -return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n -})).join("")}return n=>{ -var a,i="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",s={ -keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor __FILE__", -built_in:"proc lambda",literal:"true false nil"},r={className:"doctag", -begin:"@[A-Za-z]+"},b={begin:"#<",end:">"},t=[n.COMMENT("#","$",{contains:[r] -}),n.COMMENT("^=begin","^=end",{contains:[r],relevance:10 -}),n.COMMENT("^__END__","\\n$")],c={className:"subst",begin:/#\{/,end:/\}/, -keywords:s},d={className:"string",contains:[n.BACKSLASH_ESCAPE,c],variants:[{ -begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/, -end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{ -begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/, -end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{ -begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{ -begin:/<<[-~]?'?(\w+)\n(?:[^\n]*\n)*?\s*\1\b/,returnBegin:!0,contains:[{ -begin:/<<[-~]?'?/},n.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, -contains:[n.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",l={className:"number", -relevance:0,variants:[{ -begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ -begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" -},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ -begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ -begin:"\\b0(_?[0-7])+r?i?\\b"}]},o={className:"params",begin:"\\(",end:"\\)", -endsParent:!0,keywords:s},_=[d,{className:"class",beginKeywords:"class module", -end:"$|;",illegal:/=/,contains:[n.inherit(n.TITLE_MODE,{ -begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|!)?"}),{begin:"<\\s*",contains:[{ -begin:"("+n.IDENT_RE+"::)?"+n.IDENT_RE}]}].concat(t)},{className:"function", -begin:e(/def\s*/,(a=i+"\\s*(\\(|;|$)",e("(?=",a,")"))),keywords:"def",end:"$|;", -contains:[n.inherit(n.TITLE_MODE,{begin:i}),o].concat(t)},{begin:n.IDENT_RE+"::" -},{className:"symbol",begin:n.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{ -className:"symbol",begin:":(?!\\s)",contains:[d,{begin:i}],relevance:0},l,{ -className:"variable", -begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ -className:"params",begin:/\|/,end:/\|/,relevance:0,keywords:s},{ -begin:"("+n.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{ -className:"regexp",contains:[n.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{ -begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(", -end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}] -}].concat(b,t),relevance:0}].concat(b,t);c.contains=_,o.contains=_;var E=[{ -begin:/^\s*=>/,starts:{end:"$",contains:_}},{className:"meta", -begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", -starts:{end:"$",contains:_}}];return t.unshift(b),{name:"Ruby", -aliases:["rb","gemspec","podspec","thor","irb"],keywords:s,illegal:/\/\*/, -contains:[n.SHEBANG({binary:"ruby"})].concat(E).concat(t).concat(_)}}})());hljs.registerLanguage("swift",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function n(e){return i("(?=",e,")")} -function i(...n){return n.map((n=>e(n))).join("")}function a(...n){ -return"("+n.map((n=>e(n))).join("|")+")"} -const t=e=>i(/\b/,e,/\w$/.test(e)?/\b/:/\B/),u=["Protocol","Type"].map(t),s=["init","self"].map(t),r=["Any","Self"],o=["associatedtype",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough","fileprivate(set)","fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout","internal(set)","internal","in","is","lazy","let","mutating","nonmutating","open(set)","open","operator","optional","override","postfix","precedencegroup","prefix","private(set)","private","protocol","public(set)","public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias","unowned(safe)","unowned(unsafe)","unowned","var","weak","where","while","willSet"],l=["false","nil","true"],c=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],b=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],p=a(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),F=a(p,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),d=i(p,F,"*"),g=a(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFFFD]/),f=a(g,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),m=i(g,f,"*"),w=i(/[A-Z]/,f,"*"),E=["autoclosure",i(/convention\(/,a("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",i(/objc\(/,m,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","testable","UIApplicationMain","unknown","usableFromInline"],y=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] -;return e=>{const p=e.COMMENT("/\\*","\\*/",{contains:["self"]}),g={ -className:"keyword",begin:i(/\./,n(a(...u,...s))),end:a(...u,...s), -excludeBegin:!0},A={begin:i(/\./,a(...o)),relevance:0 -},C=o.filter((e=>"string"==typeof e)).concat(["_|0"]),v={variants:[{ -className:"keyword", -begin:a(...o.filter((e=>"string"!=typeof e)).concat(r).map(t),...s)}]},_={ -$pattern:a(/\b\w+(\(\w+\))?/,/#\w+/),keyword:C.concat(c).join(" "), -literal:l.join(" ")},N=[g,A,v],D=[{begin:i(/\./,a(...b)),relevance:0},{ -className:"built_in",begin:i(/\b/,a(...b),/(?=\()/)}],B={begin:/->/,relevance:0 -},M=[B,{className:"operator",relevance:0,variants:[{begin:d},{ -begin:`\\.(\\.|${F})+`}]}],h="([0-9a-fA-F]_*)+",S={className:"number", -relevance:0,variants:[{ -begin:"\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b"},{ -begin:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(([0-9]_*)+))?\\b`},{ -begin:/\b0o([0-7]_*)+\b/},{begin:/\b0b([01]_*)+\b/}]},O=(e="")=>({ -className:"subst",variants:[{begin:i(/\\/,e,/[0\\tnr"']/)},{ -begin:i(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),x=(e="")=>({className:"subst", -begin:i(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),k=(e="")=>({className:"subst", -label:"interpol",begin:i(/\\/,e,/\(/),end:/\)/}),L=(e="")=>({begin:i(e,/"""/), -end:i(/"""/,e),contains:[O(e),x(e),k(e)]}),I=(e="")=>({begin:i(e,/"/), -end:i(/"/,e),contains:[O(e),k(e)]}),$={className:"string", -variants:[L(),L("#"),L("##"),L("###"),I(),I("#"),I("##"),I("###")]},T=[{ -begin:i(/`/,m,/`/)},{className:"variable",begin:/\$\d+/},{className:"variable", -begin:`\\$${f}+`}],j=[{begin:/(@|#)available\(/,end:/\)/,keywords:{ -$pattern:/[@#]?\w+/,keyword:y.concat(["@available","#available"]).join(" ")}, -contains:[...M,S,$]},{className:"keyword",begin:i(/@/,a(...E))},{ -className:"meta",begin:i(/@/,m)}],K={begin:n(/\b[A-Z]/),relevance:0,contains:[{ -className:"type", -begin:i(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,f,"+") -},{className:"type",begin:w,relevance:0},{begin:/[?!]+/,relevance:0},{ -begin:/\.\.\./,relevance:0},{begin:i(/\s+&\s+/,n(w)),relevance:0}]},P={ -begin://,keywords:_,contains:[...N,...j,B,K]};K.contains.push(P) -;for(const e of $.variants){const n=e.contains.find((e=>"interpol"===e.label)) -;n.keywords=_;const i=[...N,...D,...M,S,$,...T];n.contains=[...i,{begin:/\(/, -end:/\)/,contains:["self",...i]}]}return{name:"Swift",keywords:_, -contains:[e.C_LINE_COMMENT_MODE,p,{className:"function",beginKeywords:"func", -end:/\{/,excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{ -begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params", -begin:/\(/,end:/\)/,endsParent:!0,keywords:_, -contains:["self",...N,S,$,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}], -illegal:/\[|%/},{className:"class", -beginKeywords:"struct protocol class extension enum",end:"\\{",excludeEnd:!0, -keywords:_,contains:[e.inherit(e.TITLE_MODE,{ -begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...N]},{beginKeywords:"import", -end:/$/,contains:[e.C_LINE_COMMENT_MODE,p],relevance:0 -},...N,...D,...M,S,$,...T,...j,K]}}})());hljs.registerLanguage("http",(()=>{"use strict";function e(...e){ -return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n -})).join("")}return n=>{const a="HTTP/(2|1\\.[01])",s=[{className:"attribute", -begin:e("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{ -className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]} -},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{ -name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+a+" \\d{3})", -end:/$/,contains:[{className:"meta",begin:a},{className:"number", -begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:s}},{ -begin:"(?=^[A-Z]+ (.*?) "+a+"$)",end:/$/,contains:[{className:"string", -begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:a},{ -className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:s} -}]}}})());hljs.registerLanguage("python",(()=>{"use strict";return e=>{const n={ -keyword:"and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal|10 not or pass raise return try while with yield", -built_in:"__import__ abs all any ascii bin bool breakpoint bytearray bytes callable chr classmethod compile complex delattr dict dir divmod enumerate eval exec filter float format frozenset getattr globals hasattr hash help hex id input int isinstance issubclass iter len list locals map max memoryview min next object oct open ord pow print property range repr reversed round set setattr slice sorted staticmethod str sum super tuple type vars zip", -literal:"__debug__ Ellipsis False None NotImplemented True"},a={ -className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, -end:/\}/,keywords:n,illegal:/#/},i={begin:/\{\{/,relevance:0},r={ -className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ -begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, -contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{ -begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, -contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{ -begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, -contains:[e.BACKSLASH_ESCAPE,a,i,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, -end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,i,s]},{begin:/([uU]|[rR])'/,end:/'/, -relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ -begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, -end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, -contains:[e.BACKSLASH_ESCAPE,i,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, -contains:[e.BACKSLASH_ESCAPE,i,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] -},t="[0-9](_?[0-9])*",l=`(\\b(${t}))?\\.(${t})|\\b(${t})\\.`,b={ -className:"number",relevance:0,variants:[{ -begin:`(\\b(${t})|(${l}))[eE][+-]?(${t})[jJ]?\\b`},{begin:`(${l})[jJ]?`},{ -begin:"\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?\\b"},{ -begin:"\\b0[bB](_?[01])+[lL]?\\b"},{begin:"\\b0[oO](_?[0-7])+[lL]?\\b"},{ -begin:"\\b0[xX](_?[0-9a-fA-F])+[lL]?\\b"},{begin:`\\b(${t})[jJ]\\b`}]},o={ -className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{ -begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n, -contains:["self",a,b,r,e.HASH_COMMENT_MODE]}]};return s.contains=[r,b,a],{ -name:"Python",aliases:["py","gyp","ipython"],keywords:n, -illegal:/(<\/|->|\?)|=>/,contains:[a,b,{begin:/\bself\b/},{beginKeywords:"if", -relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function", -beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/, -illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,o,{begin:/->/, -endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/, -end:/(?=#)|$/,contains:[b,o,r]},{begin:/\b(print|exec)\(/}]}}})());hljs.registerLanguage("python-repl",(()=>{"use strict";return s=>({ -aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$", -subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{ -begin:/^\.\.\.(?=[ ]|$)/}]}]})})());hljs.registerLanguage("java",(()=>{"use strict" -;var e="\\.([0-9](_*[0-9])*)",n="[0-9a-fA-F](_*[0-9a-fA-F])*",a={ -className:"number",variants:[{ -begin:`(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` -},{begin:`\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ -begin:`(${e})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ -begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` -},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ -begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], -relevance:0};return e=>{ -var n="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",s={ -className:"meta",begin:"@[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*", -contains:[{begin:/\(/,end:/\)/,contains:["self"]}]};const r=a;return{ -name:"Java",aliases:["jsp"],keywords:n,illegal:/<\/|#/, -contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, -relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ -begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ -className:"class",beginKeywords:"class interface enum",end:/[{;=]/, -excludeEnd:!0,keywords:"class interface enum",illegal:/[:"\[\]]/,contains:[{ -beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ -beginKeywords:"new throw return else",relevance:0},{className:"class", -begin:"record\\s+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,excludeEnd:!0, -end:/[{;=]/,keywords:n,contains:[{beginKeywords:"record"},{ -begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, -contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/, -keywords:n,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE] -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"function", -begin:"([\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(<[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*(\\s*,\\s*[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(", -returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:n,contains:[{ -begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, -contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/, -keywords:n,relevance:0, -contains:[s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,r,e.C_BLOCK_COMMENT_MODE] -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},r,s]}}})());hljs.registerLanguage("nginx",(()=>{"use strict";return e=>{const n={ -className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/\}/},{ -begin:/[$@]/+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{ -$pattern:"[a-z/_]+", -literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll" -},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string", -contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/ -}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n] -},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^", -end:"\\s|\\{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|\\{|;",returnEnd:!0},{ -begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number", -begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{ -className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{ -name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{ -begin:e.UNDERSCORE_IDENT_RE+"\\s+\\{",returnBegin:!0,end:/\{/,contains:[{ -className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{ -begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|\\{",returnBegin:!0,contains:[{ -className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}], -illegal:"[^\\s\\}]"}}})());hljs.registerLanguage("xml",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")} -function a(...n){return n.map((n=>e(n))).join("")}function s(...n){ -return"("+n.map((n=>e(n))).join("|")+")"}return e=>{ -const t=a(/[A-Z_]/,a("(",/[A-Z0-9_.-]+:/,")?"),/[A-Z0-9_.-]*/),i={ -className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},r={begin:/\s/, -contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] -},c=e.inherit(r,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{ -className:"meta-string"}),g=e.inherit(e.QUOTE_STRING_MODE,{ -className:"meta-string"}),m={endsWithParent:!0,illegal:/`]+/}]}] -}]};return{name:"HTML, XML", -aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], -case_insensitive:!0,contains:[{className:"meta",begin://, -relevance:10,contains:[r,g,l,c,{begin:/\[/,end:/\]/,contains:[{className:"meta", -begin://,contains:[r,c,g,l]}]}]},e.COMMENT(//,{ -relevance:10}),{begin://,relevance:10},i,{ -className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag", -begin:/)/,end:/>/,keywords:{name:"style"},contains:[m],starts:{ -end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", -begin:/)/,end:/>/,keywords:{name:"script"},contains:[m],starts:{ -end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ -className:"tag",begin:/<>|<\/>/},{className:"tag", -begin:a(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name", -begin:t,relevance:0,starts:m}]},{className:"tag",begin:a(/<\//,n(a(t,/>/))), -contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0}]}]}} -})());hljs.registerLanguage("markdown",(()=>{"use strict";function n(...n){ -return n.map((n=>{return(e=n)?"string"==typeof e?e:e.source:null;var e -})).join("")}return e=>{const a={begin:/<\/?[A-Za-z_]/,end:">", -subLanguage:"xml",relevance:0},i={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0 -},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, -relevance:2},{begin:n(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), -relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ -begin:/\[.+?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{ -className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, -returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", -excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", -end:"\\]",excludeBegin:!0,excludeEnd:!0}]},s={className:"strong",contains:[], -variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},c={ -className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{ -begin:/_(?!_)/,end:/_/,relevance:0}]};s.contains.push(c),c.contains.push(s) -;let t=[a,i] -;return s.contains=s.contains.concat(t),c.contains=c.contains.concat(t), -t=t.concat(s,c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ -className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:t},{ -begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", -contains:t}]}]},a,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", -end:"\\s+",excludeEnd:!0},s,c,{className:"quote",begin:"^>\\s+",contains:t, -end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ -begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ -begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", -contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ -begin:"^[-\\*]{3,}",end:"$"},i,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ -className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ -className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})());hljs.registerLanguage("yaml",(()=>{"use strict";return e=>{ -var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ -className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ -},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", -variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ -variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ -end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={ -begin:/\{/,end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[", -end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr", -variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{ -begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)" -}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string", -begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ -begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, -relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", -begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a -},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", -begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", -relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ -className:"number", -begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" -},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b] -;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0, -aliases:["yml","YAML"],contains:b}}})());hljs.registerLanguage("bash",(()=>{"use strict";function e(...e){ -return e.map((e=>{return(s=e)?"string"==typeof s?s:s.source:null;var s -})).join("")}return s=>{const n={},t={begin:/\$\{/,end:/\}/,contains:["self",{ -begin:/:-/,contains:[n]}]};Object.assign(n,{className:"variable",variants:[{ -begin:e(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},t]});const a={ -className:"subst",begin:/\$\(/,end:/\)/,contains:[s.BACKSLASH_ESCAPE]},i={ -begin:/<<-?\s*(?=\w+)/,starts:{contains:[s.END_SAME_AS_BEGIN({begin:/(\w+)/, -end:/(\w+)/,className:"string"})]}},c={className:"string",begin:/"/,end:/"/, -contains:[s.BACKSLASH_ESCAPE,n,a]};a.contains.push(c);const o={begin:/\$\(\(/, -end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},s.NUMBER_MODE,n] -},r=s.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 -}),l={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, -contains:[s.inherit(s.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ -name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/, -keyword:"if then else elif fi for while in do done case esac function", -literal:"true false", -built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp" -},contains:[r,s.SHEBANG(),l,o,s.HASH_COMMENT_MODE,i,c,{className:"",begin:/\\"/ -},{className:"string",begin:/'/,end:/'/},n]}}})());hljs.registerLanguage("go",(()=>{"use strict";return e=>{const n={ -keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune", -literal:"true false iota nil", -built_in:"append cap close complex copy imag len make new panic print println real recover delete" -};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{"use strict" -;const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) -;return r=>{const t={ -keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((i=["var","const","let","function","static"], -e=>!i.includes(e))).join(" "), -literal:n.concat(["yes","no","on","off"]).join(" "), -built_in:a.concat(["npm","print"]).join(" ")};var i -;const s="[A-Za-z$_][0-9A-Za-z$_]*",o={className:"subst",begin:/#\{/,end:/\}/, -keywords:t},c=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{ -end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/, -end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/, -contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/, -contains:[r.BACKSLASH_ESCAPE,o]},{begin:/"/,end:/"/, -contains:[r.BACKSLASH_ESCAPE,o]}]},{className:"regexp",variants:[{begin:"///", -end:"///",contains:[o,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)", -relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+s -},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{ -begin:"```",end:"```"},{begin:"`",end:"`"}]}];o.contains=c -;const l=r.inherit(r.TITLE_MODE,{begin:s}),d="(\\(.*\\)\\s*)?\\B[-=]>",g={ -className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/, -end:/\)/,keywords:t,contains:["self"].concat(c)}]};return{name:"CoffeeScript", -aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/, -contains:c.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{ -className:"function",begin:"^\\s*"+s+"\\s*=\\s*"+d,end:"[-=]>",returnBegin:!0, -contains:[l,g]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function", -begin:d,end:"[-=]>",returnBegin:!0,contains:[g]}]},{className:"class", -beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{ -beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[l]},l] -},{begin:s+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}})());hljs.registerLanguage("csharp",(()=>{"use strict";return e=>{var n={ -keyword:["abstract","as","base","break","case","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]).join(" "), -built_in:"bool byte char decimal delegate double dynamic enum float int long nint nuint object sbyte short string ulong unit ushort", -literal:"default false null true"},a=e.inherit(e.TITLE_MODE,{ -begin:"[a-zA-Z](\\.?\\w)*"}),i={className:"number",variants:[{ -begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] -},t=e.inherit(s,{illegal:/\n/}),r={className:"subst",begin:/\{/,end:/\}/, -keywords:n},l=e.inherit(r,{illegal:/\n/}),c={className:"string",begin:/\$"/, -end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ -},e.BACKSLASH_ESCAPE,l]},o={className:"string",begin:/\$@"/,end:'"',contains:[{ -begin:/\{\{/},{begin:/\}\}/},{begin:'""'},r]},d=e.inherit(o,{illegal:/\n/, -contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},l]}) -;r.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.C_BLOCK_COMMENT_MODE], -l.contains=[d,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.inherit(e.C_BLOCK_COMMENT_MODE,{ -illegal:/\n/})];var g={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] -},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},a] -},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={ -begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], -keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, -contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ -begin:"\x3c!--|--\x3e"},{begin:""}]}] -}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", -end:"$",keywords:{ -"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum" -}},g,i,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, -illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" -},a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", -relevance:0,end:/[{;=]/,illegal:/[^\s:]/, -contains:[a,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ -beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, -contains:[a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", -begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ -className:"meta-string",begin:/"/,end:/"/}]},{ -beginKeywords:"new return throw await else",relevance:0},{className:"function", -begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(<.+>\\s*)?\\(",returnBegin:!0, -end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ -beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", -relevance:0},{begin:e.IDENT_RE+"\\s*(<.+>\\s*)?\\(",returnBegin:!0, -contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/, -excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, -contains:[g,i,e.C_BLOCK_COMMENT_MODE] -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}})());hljs.registerLanguage("scss",(()=>{"use strict";return e=>{var t="@[a-z-]+",i={ -className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},r={ -className:"number",begin:"#[0-9A-Fa-f]+"} -;return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE, -e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0, -illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{ -className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ -className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{ -className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{ -className:"selector-tag", -begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b", -relevance:0},{className:"selector-pseudo", -begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)" -},{className:"selector-pseudo", -begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)" -},i,{className:"attribute", -begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b", -illegal:"[^\\s]"},{ -begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" -},{begin:":",end:";", -contains:[i,r,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{ -className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:t, -keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0, -keywords:"and or not only",contains:[{begin:t,className:"keyword" -},i,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,r,e.CSS_NUMBER_MODE]}]}}})());hljs.registerLanguage("r",(()=>{"use strict";function e(...e){return e.map((e=>{ -return(a=e)?"string"==typeof a?a:a.source:null;var a})).join("")}return a=>{ -const n=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/;return{name:"R", -illegal:/->/,keywords:{$pattern:n, -keyword:"function if in break next repeat else for while", -literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", -built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" -},compilerExtensions:[(a,n)=>{if(!a.beforeMatch)return -;if(a.starts)throw Error("beforeMatch cannot be used with starts") -;const i=Object.assign({},a);Object.keys(a).forEach((e=>{delete a[e] -})),a.begin=e(i.beforeMatch,e("(?=",i.begin,")")),a.starts={relevance:0, -contains:[Object.assign(i,{endsParent:!0})]},a.relevance=0,delete i.beforeMatch -}],contains:[a.COMMENT(/#'/,/$/,{contains:[{className:"doctag", -begin:"@examples",starts:{contains:[{begin:/\n/},{begin:/#'\s*(?=@[a-zA-Z]+)/, -endsParent:!0},{begin:/#'/,end:/$/,excludeBegin:!0}]}},{className:"doctag", -begin:"@param",end:/$/,contains:[{className:"variable",variants:[{begin:n},{ -begin:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{className:"doctag", -begin:/@[a-zA-Z]+/},{className:"meta-keyword",begin:/\\[a-zA-Z]+/}] -}),a.HASH_COMMENT_MODE,{className:"string",contains:[a.BACKSLASH_ESCAPE], -variants:[a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ -}),a.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', -relevance:0},{begin:"'",end:"'",relevance:0}]},{className:"number",relevance:0, -beforeMatch:/([^a-zA-Z0-9._])/,variants:[{ -match:/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/},{ -match:/0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?/},{ -match:/(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?/}]},{begin:"%",end:"%"},{ -begin:e(/[a-zA-Z][a-zA-Z_0-9]*/,"\\s+<-\\s+")},{begin:"`",end:"`",contains:[{ -begin:/\\./}]}]}}})());hljs.registerLanguage("less",(()=>{"use strict";return e=>{ -var n="([\\w-]+|@\\{[\\w-]+\\})",a=[],s=[],t=e=>({className:"string", -begin:"~?"+e+".*?"+e}),r=(e,n,a)=>({className:e,begin:n,relevance:a}),i={ -begin:"\\(",end:"\\)",contains:s,relevance:0} -;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{ -begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", -excludeEnd:!0} -},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@\\{[\\w-]+\\}"),r("built_in","~?`[^`]*?`"),{ -className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0 -},{className:"meta",begin:"!important"});var c=s.concat({begin:/\{/,end:/\}/, -contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{ -beginKeywords:"and not"}].concat(s)},g={begin:n+"\\s*:",returnBegin:!0, -end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":", -excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s} -}]},d={className:"keyword", -begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", -starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},o={ -className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{ -begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{ -begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:/\{/}],returnBegin:!0, -returnEnd:!0,illegal:"[<='$\"]",relevance:0, -contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@\\{[\\w-]+\\}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{ -className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo", -begin:/:(:)?[a-zA-Z0-9_\-+()"'.]+/},{begin:"\\(",end:"\\)",contains:c},{ -begin:"!important"}]} -;return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,d,o,g,b),{ -name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}})());hljs.registerLanguage("objectivec",(()=>{"use strict";return e=>{ -const n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n, -keyword:"@interface @class @protocol @implementation"};return{ -name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], -keywords:{$pattern:n, -keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN", -literal:"false true FALSE TRUE nil YES NO NULL", -built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once" -},illegal:"/,end:/$/, -illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ -className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:/(\{|$)/, -excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{ -begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}})());hljs.registerLanguage("typescript",(()=>{"use strict" -;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) -;function t(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{ -return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return r=>{ -const c={$pattern:e, -keyword:n.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "), -literal:a.join(" "), -built_in:s.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ") -},o={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},l=(e,n,a)=>{ -const s=e.contains.findIndex((e=>e.label===n)) -;if(-1===s)throw Error("can not find mode to replace");e.contains.splice(s,1,a) -},b=(r=>{const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/, -end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ -const a=e[0].length+e.index,s=e.input[a];"<"!==s?">"===s&&(((e,{after:n})=>{ -const a="", -returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{ -begin:r.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0 -},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:f}]}] -},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{ -variants:[{begin:"<>",end:""},{begin:o.begin,"on:begin":o.isTrulyOpeningTag, -end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0, -contains:["self"]}]}],relevance:0},{className:"function", -beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l, -contains:["self",r.inherit(r.TITLE_MODE,{begin:c}),A],illegal:/%/},{ -beginKeywords:"while if switch catch for"},{className:"function", -begin:r.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", -returnBegin:!0,contains:[A,r.inherit(r.TITLE_MODE,{begin:c})]},{variants:[{ -begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class", -beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{ -beginKeywords:"extends"},r.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/, -end:/[{;]/,excludeEnd:!0,contains:[r.inherit(r.TITLE_MODE,{begin:c}),"self",A] -},{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set", -contains:[r.inherit(r.TITLE_MODE,{begin:c}),{begin:/\(\)/},A]},{begin:/\$[(.]/}] -}})(r) -;return Object.assign(b.keywords,c),b.exports.PARAMS_CONTAINS.push(o),b.contains=b.contains.concat([o,{ -beginKeywords:"namespace",end:/\{/,excludeEnd:!0},{beginKeywords:"interface", -end:/\{/,excludeEnd:!0,keywords:"interface extends" -}]),l(b,"shebang",r.SHEBANG()),l(b,"use_strict",{className:"meta",relevance:10, -begin:/^\s*['"]use strict['"]/ -}),b.contains.find((e=>"function"===e.className)).relevance=0,Object.assign(b,{ -name:"TypeScript",aliases:["ts"]}),b}})());hljs.registerLanguage("plaintext",(()=>{"use strict";return t=>({ -name:"Plain text",aliases:["text","txt"],disableAutodetect:!0})})());hljs.registerLanguage("json",(()=>{"use strict";return n=>{const e={ -literal:"true false null" -},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],a=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],l={ -end:",",endsWithParent:!0,excludeEnd:!0,contains:a,keywords:e},t={begin:/\{/, -end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/, -contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(l,{begin:/:/ -})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(l)], -illegal:"\\S"};return a.push(t,s),i.forEach((n=>{a.push(n)})),{name:"JSON", -contains:a,keywords:e,illegal:"\\S"}}})());hljs.registerLanguage("perl",(()=>{"use strict";function e(...e){ -return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n -})).join("")}return n=>{const t=/[dualxmsipn]{0,12}/,s={$pattern:/[\w.]+/, -keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when" -},r={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:s},i={begin:/->\{/, -end:/\}/},a={variants:[{begin:/\$\d/},{ -begin:e(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") -},{begin:/[$%@][^\s\w{]/,relevance:0}] -},o=[n.BACKSLASH_ESCAPE,r,a],c=[a,n.HASH_COMMENT_MODE,n.COMMENT(/^=\w/,/=cut/,{ -endsWithParent:!0}),i,{className:"string",contains:o,variants:[{ -begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", -end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ -begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", -relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", -contains:[n.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", -contains:[n.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,contains:[],relevance:0},{ -begin:"-?\\w+\\s*=>",contains:[],relevance:0}]},{className:"number", -begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", -relevance:0},{ -begin:"(\\/\\/|"+n.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", -keywords:"split return print reverse grep",relevance:0, -contains:[n.HASH_COMMENT_MODE,{className:"regexp", -begin:e(/(s|tr|y)/,/\//,/(\\.|[^\\\/])*/,/\//,/(\\.|[^\\\/])*/,/\//,t), -relevance:10},{className:"regexp",begin:/(m|qr)?\//,end:e(/\//,t), -contains:[n.BACKSLASH_ESCAPE],relevance:0}]},{className:"function", -beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5, -contains:[n.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$", -end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$", -className:"comment"}]}];return r.contains=c,i.contains=c,{name:"Perl", -aliases:["pl","pm"],keywords:s,contains:c}}})());hljs.registerLanguage("shell",(()=>{"use strict";return s=>({ -name:"Shell Session",aliases:["console"],contains:[{className:"meta", -begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#]/,starts:{end:/[^\\](?=\s*$)/, -subLanguage:"bash"}}]})})());hljs.registerLanguage("lua",(()=>{"use strict";return e=>{ -const t="\\[=*\\[",a="\\]=*\\]",n={begin:t,end:a,contains:["self"] -},o=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[",a,{contains:[n], -relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, -literal:"true false nil", -keyword:"and break do else elseif end for goto if in local not or repeat return then until while", -built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" -},contains:o.concat([{className:"function",beginKeywords:"function",end:"\\)", -contains:[e.inherit(e.TITLE_MODE,{ -begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", -begin:"\\(",endsWithParent:!0,contains:o}].concat(o) -},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", -begin:t,end:a,contains:[n],relevance:5}])}}})());hljs.registerLanguage("makefile",(()=>{"use strict";return e=>{const i={ -className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", -contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{"use strict";return e=>{ -const n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!" -;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?", -keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield", -literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}})());hljs.registerLanguage("php",(()=>{"use strict";return e=>{const r={ -className:"variable", -begin:"\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?![A-Za-z0-9])(?![$])"},t={ -className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{ -begin:/\?>/}]},a={className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/, -end:/\}/}]},n=e.inherit(e.APOS_STRING_MODE,{illegal:null -}),i=e.inherit(e.QUOTE_STRING_MODE,{illegal:null, -contains:e.QUOTE_STRING_MODE.contains.concat(a)}),o=e.END_SAME_AS_BEGIN({ -begin:/<<<[ \t]*(\w+)\n/,end:/[ \t]*(\w+)\b/, -contains:e.QUOTE_STRING_MODE.contains.concat(a)}),l={className:"string", -contains:[e.BACKSLASH_ESCAPE,t],variants:[e.inherit(n,{begin:"b'",end:"'" -}),e.inherit(i,{begin:'b"',end:'"'}),i,n,o]},c={ -variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},s={ -keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list match|0 new object or private protected public real return string switch throw trait try unset use var void while xor yield", -literal:"false null true", -built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass" -};return{aliases:["php","php3","php4","php5","php6","php7","php8"], -case_insensitive:!0,keywords:s, -contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t] -}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}] -}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0, -keywords:"__halt_compiler"}),t,{className:"keyword",begin:/\$this\b/},r,{ -begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function", -relevance:0,beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0, -illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{begin:"=>"},{ -className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0, -keywords:s,contains:["self",r,e.C_BLOCK_COMMENT_MODE,l,c]}]},{className:"class", -beginKeywords:"class interface",relevance:0,end:/\{/,excludeEnd:!0, -illegal:/[:($"]/,contains:[{beginKeywords:"extends implements" -},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",relevance:0,end:";", -illegal:/[.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use", -relevance:0,end:";",contains:[e.UNDERSCORE_TITLE_MODE]},l,c]}}})());hljs.registerLanguage("vbnet",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function n(...n){ -return n.map((n=>e(n))).join("")}function t(...n){ -return"("+n.map((n=>e(n))).join("|")+")"}return e=>{ -const a=/\d{1,2}\/\d{1,2}\/\d{4}/,i=/\d{4}-\d{1,2}-\d{1,2}/,s=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,o={ -className:"literal",variants:[{begin:n(/# */,t(i,a),/ *#/)},{ -begin:n(/# */,r,/ *#/)},{begin:n(/# */,s,/ *#/)},{ -begin:n(/# */,t(i,a),/ +/,t(s,r),/ *#/)}]},l=e.COMMENT(/'''/,/$/,{contains:[{ -className:"doctag",begin:/<\/?/,end:/>/}]}),c=e.COMMENT(null,/$/,{variants:[{ -begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]});return{name:"Visual Basic .NET", -aliases:["vb"],case_insensitive:!0,classNameAliases:{label:"symbol"},keywords:{ -keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", -built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", -type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", -literal:"true false nothing"}, -illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ -className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, -end:/"/,illegal:/\n/,contains:[{begin:/""/}]},o,{className:"number",relevance:0, -variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ -},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ -begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ -className:"label",begin:/^\w+:/},l,c,{className:"meta", -begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, -end:/$/,keywords:{ -"meta-keyword":"const disable else elseif enable end externalsource if region then" -},contains:[c]}]}}})());hljs.registerLanguage("c",(()=>{"use strict";function e(e){ -return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?") -}return t=>{const n=(t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}] -}),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",s={ -className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},i={className:"string", -variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n", -contains:[t.BACKSLASH_ESCAPE]},{ -begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", -end:"'",illegal:"."},t.END_SAME_AS_BEGIN({ -begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ -className:"number",variants:[{begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" -},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ -"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" -},contains:[{begin:/\\\n/,relevance:0},t.inherit(i,{className:"meta-string"}),{ -className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n" -},n,t.C_BLOCK_COMMENT_MODE]},l={className:"title",begin:e(r)+t.IDENT_RE, -relevance:0},d=e(r)+t.IDENT_RE+"\\s*\\(",u={ -keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq", -built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary", -literal:"true false nullptr NULL"},m=[c,s,n,t.C_BLOCK_COMMENT_MODE,o,i],p={ -variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{ -beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:m.concat([{ -begin:/\(/,end:/\)/,keywords:u,contains:m.concat(["self"]),relevance:0}]), -relevance:0},_={className:"function",begin:"("+a+"[\\*&\\s]+)+"+d, -returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:u,illegal:/[^\w\s\*&:<>.]/, -contains:[{begin:"decltype\\(auto\\)",keywords:u,relevance:0},{begin:d, -returnBegin:!0,contains:[l],relevance:0},{className:"params",begin:/\(/, -end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,i,o,s,{ -begin:/\(/,end:/\)/,keywords:u,relevance:0, -contains:["self",n,t.C_BLOCK_COMMENT_MODE,i,o,s]}] -},s,n,t.C_BLOCK_COMMENT_MODE,c]};return{ -aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:u, -disableAutodetect:!0,illegal:"",keywords:u,contains:["self",s]},{begin:t.IDENT_RE+"::",keywords:u},{ -className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/, -contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{ -preprocessor:c,strings:i,keywords:u}}})(t) -;return n.name="C",n.aliases=["c","h"],n}})());hljs.registerLanguage("css",(()=>{"use strict";return e=>{ -var n="[a-zA-Z-][a-zA-Z0-9_-]*",a={ -begin:/([*]\s?)?(?:[A-Z_.\-\\]+|--[a-zA-Z0-9_-]+)\s*(\/\*\*\/)?:/, -returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute", -begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0, -contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in", -begin:/[\w-]+/},{begin:/\(/,end:/\)/, -contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}] -},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{ -className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}] -}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/, -contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id", -begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:"\\."+n},{ -className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", -contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo", -begin:/:(:)?[a-zA-Z0-9_+()"'.-]+/},{begin:"@(page|font-face)", -lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]", -illegal:/:/,returnBegin:!0,contains:[{className:"keyword", -begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0, -relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/, -className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE] -}]},{className:"selector-tag",begin:n,relevance:0},{begin:/\{/,end:/\}/, -illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,{begin:/;/},a]}]}}})());hljs.registerLanguage("sql",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function r(...r){ -return r.map((r=>e(r))).join("")}function t(...r){ -return"("+r.map((r=>e(r))).join("|")+")"}return e=>{ -const n=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],s=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],o=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],c=s,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update ","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!s.includes(e))),u={ -begin:r(/\b/,t(...c),/\s*\(/),keywords:{built_in:c.join(" ")}};return{ -name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ -$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:r,when:t}={})=>{const n=t -;return r=r||[],e.map((e=>e.match(/\|\d+$/)||r.includes(e)?e:n(e)?e+"|0":e)) -})(l,{when:e=>e.length<3}).join(" "),literal:a.join(" "),type:i.join(" "), -built_in:"current_catalog current_date current_default_transform_group current_path current_role current_schema current_transform_group_for_type current_user session_user system_time system_user current_time localtime current_timestamp localtimestamp" -},contains:[{begin:t(...o),keywords:{$pattern:/[\w\.]+/, -keyword:l.concat(o).join(" "),literal:a.join(" "),type:i.join(" ")}},{ -className:"type", -begin:t("double precision","large object","with timezone","without timezone") -},u,{className:"variable",begin:/@[a-z0-9]+/},{className:"string",variants:[{ -begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,contains:[{ -begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,n,{className:"operator", -begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}}})());hljs.registerLanguage("ini",(()=>{"use strict";function e(e){ -return e?"string"==typeof e?e:e.source:null}function n(...n){ -return n.map((n=>e(n))).join("")}return s=>{const a={className:"number", -relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:s.NUMBER_RE}] -},i=s.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const t={ -className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/ -}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={ -className:"string",contains:[s.BACKSLASH_ESCAPE],variants:[{begin:"'''", -end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"' -},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,a,"self"], -relevance:0 -},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map((n=>e(n))).join("|")+")" -;return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, -contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{ -begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr", -starts:{end:/$/,contains:[i,c,r,t,l,a]}}]}}})());hljs.registerLanguage("php-template",(()=>{"use strict";return n=>({ -name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/, -subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"', -end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{ -illegal:null,className:null,contains:null,skip:!0 -}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null, -skip:!0})]}]})})());hljs.registerLanguage("javascript",(()=>{"use strict" -;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]) -;function r(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{ -return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return t=>{ -const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/, -isTrulyOpeningTag:(e,n)=>{const a=e[0].length+e.index,s=e.input[a] -;"<"!==s?">"===s&&(((e,{after:n})=>{const a="", -returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{ -begin:t.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0 -},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:A}]}] -},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{ -variants:[{begin:"<>",end:""},{begin:o.begin,"on:begin":o.isTrulyOpeningTag, -end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0, -contains:["self"]}]}],relevance:0},{className:"function", -beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l, -contains:["self",t.inherit(t.TITLE_MODE,{begin:c}),p],illegal:/%/},{ -beginKeywords:"while if switch catch for"},{className:"function", -begin:t.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", -returnBegin:!0,contains:[p,t.inherit(t.TITLE_MODE,{begin:c})]},{variants:[{ -begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class", -beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{ -beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/, -end:/[{;]/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:c}),"self",p] -},{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set", -contains:[t.inherit(t.TITLE_MODE,{begin:c}),{begin:/\(\)/},p]},{begin:/\$[(.]/}] -}}})()); \ No newline at end of file diff --git a/src/Blogifier/wwwroot/themes/test/js/newsletter.js b/src/Blogifier/wwwroot/themes/test/js/newsletter.js deleted file mode 100644 index 8b34eadd1..000000000 --- a/src/Blogifier/wwwroot/themes/test/js/newsletter.js +++ /dev/null @@ -1,71 +0,0 @@ -// get the newsletter form elements -const form = document.getElementById("newsletter"); -const form_email = document.getElementById("newsletter_email"); -const form_status = document.getElementById("newsletter_status"); -const newsletterSucessMsg = form_status.dataset.success, newsletterErrorMsg = form_status.dataset.error; - -// Success, Loading and Error functions -function successNewsletter() { - form_status.innerHTML = ``; - setTimeout(resetNewsletter, 2000); -} -function loadingNewsletter() { - form_status.innerHTML = '' -} -function errorNewsletter(msg) { - form_status.innerHTML = ``; -} -function resetNewsletter() { - form.reset(); - form_status.innerHTML = ""; -} - -function subscribeNewsletter(url, data) { - var options = { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data) - } - fetch(url, options) - .then((response) => { - if (response.status == 200) { - return response.json(); - } else { - throw new Error('The Newsletter is not working!'); - } - }) - .then(() => { - successNewsletter(); - }) - .catch((err) => { - errorNewsletter(err); - }); -} - -form.addEventListener("submit", function (e) { - e.preventDefault(); - loadingNewsletter(); - var subscriber_data = { - Email: form_email.value, - Ip: "unknown", - Country: "unknown", - Region: "unknown" - }; - fetch('https://ipapi.co/json/') - .then((response) => { - if (response.status == 200) { - return response.json(); - } else { - throw new Error('Not sure where you are!'); - } - }) - .then((loc) => { - subscriber_data.Ip = loc.ip; - subscriber_data.Country = loc.country_name; - subscriber_data.Region = loc.region; - subscribeNewsletter(form.action, subscriber_data); - }) - .catch((err) => { - subscribeNewsletter(form.action, subscriber_data); - }); -}); diff --git a/src/Blogifier/wwwroot/themes/test/js/scripts.js b/src/Blogifier/wwwroot/themes/test/js/scripts.js deleted file mode 100644 index 24cb37121..000000000 --- a/src/Blogifier/wwwroot/themes/test/js/scripts.js +++ /dev/null @@ -1,24 +0,0 @@ -// enable highlight -hljs.initHighlightingOnLoad(); - -// search modal auto focus -var myModal = document.getElementById('searchModal') -var myInput = document.getElementById('searchFormInput') -myModal.addEventListener('shown.bs.modal', function () { - myInput.focus() -}) - -// copy input -function copyInput(elm) { - var copyText = document.getElementById(elm); - var copyTextStore = copyText.dataset.link; - copyText.select(); - copyText.setSelectionRange(0, 99999); - document.execCommand("copy"); - copyText.value = "Copied!"; - copyText.classList.add("copied"); - setTimeout(function () { - copyText.value = copyTextStore; - copyText.classList.remove("copied"); - }, 500); -} diff --git a/src/Blogifier/wwwroot/themes/test/package-lock.json b/src/Blogifier/wwwroot/themes/test/package-lock.json deleted file mode 100644 index 0d1bc9906..000000000 --- a/src/Blogifier/wwwroot/themes/test/package-lock.json +++ /dev/null @@ -1,386 +0,0 @@ -{ - "name": "theme", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "theme", - "version": "1.0.0", - "devDependencies": { - "bootstrap": "^5.0.0", - "sass": "^1.32.12" - } - }, - "node_modules/@popperjs/core": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", - "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==", - "dev": true, - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bootstrap": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0.tgz", - "integrity": "sha512-tmhPET9B9qCl8dCofvHeiIhi49iBt0EehmIsziZib65k1erBW1rHhj2s/2JsuQh5Pq+xz2E9bEbzp9B7xHG+VA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - }, - "peerDependencies": { - "@popperjs/core": "^2.9.2" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/sass": { - "version": "1.32.12", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", - "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", - "dev": true, - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - } - }, - "dependencies": { - "@popperjs/core": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", - "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==", - "dev": true, - "peer": true - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bootstrap": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0.tgz", - "integrity": "sha512-tmhPET9B9qCl8dCofvHeiIhi49iBt0EehmIsziZib65k1erBW1rHhj2s/2JsuQh5Pq+xz2E9bEbzp9B7xHG+VA==", - "dev": true, - "requires": {} - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "sass": { - "version": "1.32.12", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", - "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", - "dev": true, - "requires": { - "chokidar": ">=3.0.0 <4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/package.json b/src/Blogifier/wwwroot/themes/test/package.json deleted file mode 100644 index d9191f233..000000000 --- a/src/Blogifier/wwwroot/themes/test/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "theme", - "version": "1.0.0", - "description": "only for compiling sass files", - "author": "Farzin", - "private": true, - "scripts": { - "start": "sass --watch scss:css --style=compressed" - }, - "devDependencies": { - "bootstrap": "^5.0.0", - "sass": "^1.32.12" - } -} diff --git a/src/Blogifier/wwwroot/themes/test/screenshot.png b/src/Blogifier/wwwroot/themes/test/screenshot.png deleted file mode 100644 index 5b53d3890..000000000 Binary files a/src/Blogifier/wwwroot/themes/test/screenshot.png and /dev/null differ diff --git a/src/Blogifier/wwwroot/themes/test/scss/components/_dropdowns.scss b/src/Blogifier/wwwroot/themes/test/scss/components/_dropdowns.scss deleted file mode 100644 index 569fb7394..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/components/_dropdowns.scss +++ /dev/null @@ -1,25 +0,0 @@ -// -.dropdown-menu { - border-radius: $radius; - box-shadow: 0 0 3rem rgba(#000, 0.2); - border: none; -} - -.dropdown-item { - font-size: 0.875rem; - font-weight: 500; - text-transform: capitalize; - padding: 0.5rem 1rem; - color: #555; - transition: all ease-in-out 0.2s; - &:active, - &:hover { - color: #000; - background-color: var(--bs-light); - } -} -.dropdown-divider { - background: none; - border-top: 1px solid rgba(#000, 0.3); - margin: 0; -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/components/_highlight.scss b/src/Blogifier/wwwroot/themes/test/scss/components/_highlight.scss deleted file mode 100644 index 90302b1b2..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/components/_highlight.scss +++ /dev/null @@ -1,88 +0,0 @@ -pre > code { - display: block; - overflow-x: auto; - padding: 2rem; - color: $hljs-foreground; - background: $hljs-background; - margin: 0; - font-family: var(--bs-font-monospace); - font-size: 0.875rem; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - line-height: 1.4; - -moz-tab-size: 2; - -o-tab-size: 2; - tab-size: 2; - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} - -.hljs-comment { - color: $hljs-comments; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-meta-keyword, -.hljs-doctag, -.hljs-section, -.hljs-selector-class, -.hljs-meta, -.hljs-selector-pseudo, -.hljs-attr { - color: $hljs-blue; -} - -.hljs-attribute { - color: #803378; -} -.hljs-name { - color: #168174; -} -.hljs-type, -.hljs-number, -.hljs-selector-id, -.hljs-quote, -.hljs-template-tag, -// .hljs-built_in, -.hljs-literal { - color: #168174; -} - -.hljs-title, -.hljs-string, -.hljs-regexp, -.hljs-symbol, -.hljs-variable, -.hljs-template-variable, -.hljs-link, -.hljs-selector-attr, -.hljs-meta-string { - color: $hljs-red; -} - -.hljs-bullet, -.hljs-code { - color: #cccccc; -} - -.hljs-deletion { - color: #de7176; -} - -.hljs-addition { - color: #76c490; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/components/_newsletter.scss b/src/Blogifier/wwwroot/themes/test/scss/components/_newsletter.scss deleted file mode 100644 index deede6c12..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/components/_newsletter.scss +++ /dev/null @@ -1,76 +0,0 @@ -.newsletter { - background-color: #000; - color: #aaa; - border-radius: $radius-var; - margin-top: 4rem; - position: relative; - - &-body { - padding: 3rem; - - @media screen and (min-width: 991px) { - padding: 4rem; - } - } - - &-title { - font-size: 1.75rem; - font-weight: 600; - color: #fff; - } - - &-desc { - margin: 0; - font-size: 1.125rem; - font-weight: 300; - } - - &-form { - border: 2px solid #222; - border-radius: $radius-var; - } - - &-input { - background: none; - border: 0; - flex-grow: 1; - width: 100%; - padding: 0.5rem 1rem; - color: #fff; - outline: none !important; - } - &-btn { - background: none; - border: 0; - padding: 0.5rem 1rem; - color: #fff; - display: block; - border-left: 1px solid #222; - &:focus, - &:hover { - outline: none; - background: #222; - } - } - - &-msg { - padding: 2rem; - text-align: center; - background-color: #000; - border-radius: $radius-var; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - flex-direction: column; - margin: 0; - color: #fff; - cursor: pointer; - - .spinner-border { - color: #fff; - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/components/_pagination.scss b/src/Blogifier/wwwroot/themes/test/scss/components/_pagination.scss deleted file mode 100644 index dd2e33f91..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/components/_pagination.scss +++ /dev/null @@ -1,39 +0,0 @@ -.pagination { - margin: 4rem auto 4rem; - height: 3rem; - align-items: center; - justify-content: center; - - &-item { - margin: 0 0.5rem; - z-index: 5; - } - &-link { - display: block; - color: #888; - font-weight: 500; - font-size: 0.875rem; - height: 3rem; - line-height: 3rem; - text-decoration: none; - text-transform: uppercase; - transition: opacity ease 0.2s; - opacity: 0.5; - - &:hover { - opacity: 1; - color: $color; - .bi { - width: 1.375rem; - height: 1.375rem; - } - } - .bi { - width: 1.25rem; - height: 1.25rem; - transition: all ease 0.2s; - margin: 0 0.25rem; - transform: translateY(-0.0625rem); - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/components/_search.scss b/src/Blogifier/wwwroot/themes/test/scss/components/_search.scss deleted file mode 100644 index a93852922..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/components/_search.scss +++ /dev/null @@ -1,4 +0,0 @@ -.search { - &-form{} - &-results{} -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/helpers/_base.scss b/src/Blogifier/wwwroot/themes/test/scss/helpers/_base.scss deleted file mode 100644 index 7e9b0c145..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/helpers/_base.scss +++ /dev/null @@ -1,10 +0,0 @@ -.container { - width: 61.5rem; // 2rem container gutters - max-width: 100%; -} - -@media (max-width: 991px) { - :root { - --bs-gutter-x: 1.5rem; - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/helpers/_mixins.scss b/src/Blogifier/wwwroot/themes/test/scss/helpers/_mixins.scss deleted file mode 100644 index ccb07828d..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/helpers/_mixins.scss +++ /dev/null @@ -1,29 +0,0 @@ -@mixin background-hover( - $class, - $color, - $color-hover, - $color-text, - $color-text-hover -) { - #{$class} { - background-color: $color; - color: $color-text; - - &:hover { - background-color: $color-hover; - color: $color-text-hover; - } - } -} - -@mixin text-hover($class, $color, $color-hover) { - #{$class} { - @if $color { - color: $color; - } - - &:hover { - color: $color-hover; - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/helpers/_reset.scss b/src/Blogifier/wwwroot/themes/test/scss/helpers/_reset.scss deleted file mode 100644 index af910d4e0..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/helpers/_reset.scss +++ /dev/null @@ -1,58 +0,0 @@ -img { - max-width: 100%; -} - -iframe { - width: 100% !important; - margin-top: 1rem; - margin-bottom: 1.5rem; -} - -figure, -p, -ul, -ol { - margin-bottom: 1.5rem; -} - -table { - width: 100%; -} - -blockquote { - font-weight: 300; - position: relative; - padding: 1rem 3rem; - margin-bottom: 1.5rem; - &::before, - &::after { - position: absolute; - - content: ""; - font-size: 5rem; - width: 2.5rem; - height: 2.5rem; - background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4IiB3aWR0aD0iNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDM0aDZsNC04di0xMmgtMTJ2MTJoNnptMTYgMGg2bDQtOHYtMTJoLTEydjEyaDZ6Ii8+PHBhdGggZD0iTTAgMGg0OHY0OGgtNDh6IiBmaWxsPSJub25lIi8+PC9zdmc+"); - display: block; - background-size: 2.5rem; - background-repeat: no-repeat; - opacity: 0.1; - } - - &::before { - top: 0.75rem; - left: 0; - transform: rotate(180deg); - } - - &::after { - bottom: 0.75rem; - right: 0; - } - - p { - &:last-child { - margin-bottom: 0; - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/helpers/_variables.scss b/src/Blogifier/wwwroot/themes/test/scss/helpers/_variables.scss deleted file mode 100644 index 41c94306f..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/helpers/_variables.scss +++ /dev/null @@ -1,41 +0,0 @@ -// colors -$color: var(--bf-color); -$color-hover: #888; - -// brands colors -$color-blogifier: #622aff; -$color-facebook: #1678f2; -$color-twitter: #1da1f2; -$color-github: #171617; -$color-pinterest: #e60023; -$color-linkedin: #0a66c2; -$color-instagram: #b900b4; -$color-youtube: #ff0300; -$color-telegram: #0088cc; -$color-whatsapp: #25d366; - -$brands: ( - "blogifier": $color-blogifier, - "facebook": $color-facebook, - "twitter": $color-twitter, - "github": $color-github, - "pinterest": $color-pinterest, - "linkedin": $color-linkedin, - "instagram": $color-instagram, - "telegram": $color-telegram, - "youtube": $color-youtube, - "whatsapp": $color-whatsapp, -); - -// highlighter -$hljs-foreground: #212529; -$hljs-background: #f8f9fa; -$hljs-comments: #90a4ae; -$hljs-blue: #006ee0; -$hljs-red: #c30; -$hljs-green: #168174; -$hljs-sky: #2f6f9f; - -// radius -$radius: 0.125rem; -$radius-var: var(--bf-radius, $radius); diff --git a/src/Blogifier/wwwroot/themes/test/scss/layout/_footer.scss b/src/Blogifier/wwwroot/themes/test/scss/layout/_footer.scss deleted file mode 100644 index ec01c3b23..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/layout/_footer.scss +++ /dev/null @@ -1,16 +0,0 @@ -.footer { - border-top: 1px solid rgba(#000, 0.05); - margin-top: 4rem; - color: #444; - font-size: 0.75rem; - a { - color: #444; - text-decoration: none; - &:hover { - color: $color; - } - } - p { - margin: 0; - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/layout/_header.scss b/src/Blogifier/wwwroot/themes/test/scss/layout/_header.scss deleted file mode 100644 index 8f5e89978..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/layout/_header.scss +++ /dev/null @@ -1,39 +0,0 @@ -.header { - padding-top: 4rem; - padding-bottom: 4rem; - - &-title { - display: inline-block; - margin: 0; - font-size: var(--bf-header-title-size); - font-weight: var(--bf-header-title-weight); - color: var(--bf-header-title-color); - line-height: 1.2; - text-decoration: none; - &:hover, - &:focus { - color: var(--bf-header-title-hover); - } - } - - &-desc { - margin: 0; - font-size: 0.875rem; - font-weight: 400; - color: var(--bf-header-desc-color, #666); - } - - &-logo { - display: block; - &-img { - width: var(--bf-header-logo-width); - height: var(--bf-header-logo-height); - } - } - - @media (max-width: 767px) { - padding: 0; - margin-bottom: 2rem; - background-color: var(--bs-light); - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/layout/_nav.scss b/src/Blogifier/wwwroot/themes/test/scss/layout/_nav.scss deleted file mode 100644 index e699eddb5..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/layout/_nav.scss +++ /dev/null @@ -1,74 +0,0 @@ -.header-nav { - ul { - list-style: none; - margin: 0; - padding: 0; - } - - &-link, - &-button { - color: #444; - height: 2.5rem; - line-height: 2.5rem; - border: 0; - background: none; - display: inline-block; - border-radius: $radius; - transition: all ease-in-out 0.15s; - text-align: center; - border-radius: 0.25rem; - font-size: 1rem; - - &:hover, - &:focus { - color: #000; - background-color: var(--bs-light); - } - > .bi, - > img { - transform: translateY(-0.1rem); - } - } - - &-link { - padding: 0 1rem; - font-weight: 500; - .bi-chevron-down { - width: 0.75rem; - height: 0.75rem; - } - } - &-button { - padding: 0; - width: 2.5rem; - } - - .-login { - position: relative; - &::after { - content: ""; - width: 1px; - height: 1rem; - position: absolute; - top: 50%; - transform: translateY(-50%); - left: -0.75rem; - background-color: rgba(#000, 0.1); - display: block; - } - .bi-box-arrow-in-right { - transform: translateX(-0.125rem); - } - } - @media (max-width: 767px) { - border-bottom: 1px solid rgba(#000, 0.03); - border-top: 1px solid rgba(#000, 0.03); - padding: 0.25rem 0.75rem; - margin: 0 -1.5rem 1rem; - } -} - -// social links -@each $name, $item in $brands { - @include text-hover(".social-link-#{$name}", null, $item); -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/layout/_widgets.scss b/src/Blogifier/wwwroot/themes/test/scss/layout/_widgets.scss deleted file mode 100644 index ebe8b6d5c..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/layout/_widgets.scss +++ /dev/null @@ -1,18 +0,0 @@ -.bft-widget { - &-title { - font-weight: 300; - letter-spacing: 0.1em; - color: #aaa; - font-size: 1rem; - text-transform: uppercase; - display: block; - margin-bottom: 2rem; - } - &-content { - ul { - list-style: none; - padding: 0; - margin: 0; - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/page/_home.scss b/src/Blogifier/wwwroot/themes/test/scss/page/_home.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/Blogifier/wwwroot/themes/test/scss/post/_featured.scss b/src/Blogifier/wwwroot/themes/test/scss/post/_featured.scss deleted file mode 100644 index 877cd27f0..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/post/_featured.scss +++ /dev/null @@ -1,169 +0,0 @@ -.featured { - margin-bottom: 5rem; - - // cover - &-cover { - margin: 0; - max-height: 100%; - min-height: 100%; - overflow: hidden; - position: relative; - border-radius: $radius-var; - box-shadow: inset 0 0 0 1px rgba(#000, 0.05); - - @media screen and (max-width: 991px) { - height: 20rem; - } - &-link { - display: block; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - } - &-img { - border-radius: $radius-var; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - object-fit: cover; - object-position: center; - } - } - - // content - &-content { - padding-top: 2rem; - padding-bottom: 2rem; - padding-left: 2rem; - } - - // title - &-title { - font-size: 1.75rem; - font-weight: 700; - margin-bottom: 1.5rem; - line-height: 1.3; - } - &-link { - text-decoration: none; - color: #000; - &:hover { - color: $color; - } - } - - // meta - &-meta { - font-size: 0.875rem; - color: #777; - margin-bottom: 1.5rem; - opacity: 0.7; - a { - color: #777; - &:hover { - color: #000; - } - } - } - &-author { - display: flex; - align-items: center; - margin-right: 1rem; - &-img { - border-radius: 50rem; - margin-right: 0.25rem; - filter: grayscale(100%); - } - &-name { - text-transform: capitalize; - } - } - &-date { - display: flex; - align-items: center; - margin-right: 0.875rem; - - &-icon { - margin-right: 0.375rem; - } - &-time { - } - } - &-cat { - display: flex; - align-items: center; - &-icon { - margin-right: 0rem; - } - &-title { - text-decoration: none; - } - } - - // desc - &-desc { - color: #444; - line-height: 1.8; - margin-bottom: 1.5rem; - } - &-more { - font-weight: 500; - font-size: 0.875rem; - color: $color; - text-decoration: none; - &:hover { - color: #000; - } - } - - // carousel nav - &-prev, - &-next { - width: 3rem; - opacity: 0; - } - &-prev { - left: -4rem; - } - &-next { - right: -3.5rem; - } - - // hover - &:hover { - .featured-next, - .featured-prev { - opacity: 0.2; - } - } - - @media screen and (max-width: 767px) { - border-bottom: 1px solid rgba(#000, 0.03); - margin: -2rem -1.5rem 2rem; - background-color: var(--bs-light); - - &-item { - padding: 1.5rem; - } - // content - &-content { - padding: 0; - padding: 1rem 1rem 0; - } - // title - &-title { - font-size: 1.25rem; - margin-bottom: 0.5rem; - font-weight: 500; - } - // desc - &-desc { - font-size: 0.875rem; - margin-bottom: 0.5rem; - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/post/_nav.scss b/src/Blogifier/wwwroot/themes/test/scss/post/_nav.scss deleted file mode 100644 index 2f8696043..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/post/_nav.scss +++ /dev/null @@ -1,29 +0,0 @@ -.post-nav { - margin-bottom: 4rem; - - // item - &-item { - position: relative; - color: #000; - text-decoration: none; - display: block; - &:hover { - color: $color; - } - } - - // title - &-title { - z-index: 2; - font-size: 0.875rem; - font-weight: 600; - margin: 0; - } - - // text - &-text { - opacity: 0.5; - text-transform: uppercase; - font-size: 0.75rem; - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/post/_post.scss b/src/Blogifier/wwwroot/themes/test/scss/post/_post.scss deleted file mode 100644 index ab2463676..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/post/_post.scss +++ /dev/null @@ -1,274 +0,0 @@ -.post { - &-cover { - width: 100%; - height: 20rem; // TODO: maybe make it possible to customize it from admin panel? - margin: 0; // reset the figure margin - position: relative; - background-color: #000; - &-img { - height: 100%; - width: 100%; - object-fit: cover; - object-position: center; - opacity: 0.9; // For when we have a very white cover. - } - &-caption { - display: block; - max-width: 100%; - width: calc(var(--bf-post-width) + 8rem); - position: relative; - bottom: calc(8rem / 2); - margin: -1.5rem auto 0; - color: #fff; - font-size: 0.625rem; - text-align: right; - line-height: 1rem; - a, - span { - border-radius: $radius; - display: inline-block; - padding: 0 0.25rem; - opacity: 0.7; - color: #fff; - background-color: rgba(0, 0, 0, 0.3); - &:hover { - opacity: 1; - background-color: #000; - } - } - } - - // This ::after make a white header background on the top of the post title. - &::after { - content: ""; - display: block; - height: calc(8rem / 2); - width: calc(var(--bf-post-width) + 8rem); - max-width: 100%; - position: absolute; - left: 50%; - bottom: 0; - transform: translateX(-50%); - background-color: #fff; - border-top-left-radius: $radius-var; - border-top-right-radius: $radius-var; - } - } - - &-container { - max-width: calc( - var(--bf-post-width) + 3rem - ); // calc and maintain the actual with - width: 100%; - margin-right: auto; - margin-left: auto; - padding-right: 1.5rem; - padding-left: 1.5rem; - } - - // header - &-title { - margin-bottom: 2.5rem; - line-height: 1.1; - letter-spacing: -1px; - font-weight: 700; - font-size: 2.75rem; - color: #000; - } - - &-meta { - margin-bottom: 2.5rem; - line-height: 1.4; - font-size: 0.875rem; - font-weight: 300; - color: #666; - white-space: nowrap; - cursor: default; - - &-item { - &:not(:last-of-type) { - margin-right: 1.75rem; - } - } - - &-label { - display: block; - font-size: 0.75rem; - color: #999; - } - - a { - color: #666; - text-decoration: none; - &:hover { - color: $color; - } - } - - &-btn { - height: 2rem; - width: 2rem; - border: 0; - margin: 0; - padding: 0; - background: none; - text-align: center; - - .bi { - width: 1.25rem; - height: 100%; - fill: #888; - } - - &:hover { - .bi { - fill: $color; - } - } - } - - &-author { - display: flex; - align-items: center; - &-img { - margin-right: 0.5rem; - border-radius: 100%; - } - &-name { - text-transform: capitalize; - } - } - - &-cats { - word-break: keep-all; - &-list { - margin: 0; - padding: 0; - list-style: none; - } - &-item { - &:not(:last-child) { - margin-right: 0.125rem; - .post-meta-cats-link { - &::after { - content: ","; - } - } - } - } - } - } - - // content - &-content { - line-height: 1.8; - margin-bottom: 4rem; - hyphens: auto; - a { - overflow-wrap: break-word; - word-break: break-all; - } - iframe, - video, - img { - border-radius: 0.125rem; - } - - audio, - video { - margin-bottom: 1rem; - width: 100%; - max-width: 100%; - } - } - - // footer - &-footer { - margin-bottom: 4rem; - } - - // author - &-author { - margin-bottom: 4rem; - padding: 2rem; - border-radius: var(--bf-radius); - background-color: var(--bs-light); - &-name { - margin: 0 0 0.25rem; - font-size: 1.125rem; - text-transform: capitalize; - } - &-bio { - margin-bottom: 0; - font-size: 0.875rem; - font-weight: 400; - color: #000; - } - &-cover { - margin: 0 1.5rem 0 0; - min-width: 5rem; - } - &-img { - width: 5rem; - height: 5rem; - border-radius: 100%; - } - } - - // comments - #disqus_thread { - &:not(:empty) { - padding: 2rem; - background-color: var(--bs-light); - margin-bottom: 4rem; - - > iframe { - margin: 0 !important; - } - } - } - - @media screen and (max-width: 767px) { - margin-top: -2rem; - - &-cover { - margin-bottom: 2rem; - height: 14rem; - &-caption { - display: none; - } - &::after { - border-radius: 0; - display: none; - } - } - - &-title { - font-size: 1.5rem; - letter-spacing: 0; - } - - &-meta { - font-size: 0.75rem; - &-item { - &:not(:last-of-type) { - margin-right: 1rem; - } - } - &-cats { - &-link { - &::after { - display: none; - } - } - &-item { - display: none; - - &:nth-child(1) { - display: block; - } - } - } - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/post/_related.scss b/src/Blogifier/wwwroot/themes/test/scss/post/_related.scss deleted file mode 100644 index a103b4744..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/post/_related.scss +++ /dev/null @@ -1,26 +0,0 @@ -.related { - margin-top: -1px; - margin-bottom: 4rem; - - &-header { - border-top: 1px solid var(--bs-light); - padding-top: 3rem; - margin-bottom: 3rem; - align-items: center; - display: flex; - &-title { - margin-bottom: 0; - font-size: 1rem; - } - &-link { - margin-bottom: 0; - color: rgba(#000, 0.5); - font-size: 0.875rem; - font-weight: 500; - text-decoration: none; - &:hover { - color: $color; - } - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/post/_share.scss b/src/Blogifier/wwwroot/themes/test/scss/post/_share.scss deleted file mode 100644 index a835ac56a..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/post/_share.scss +++ /dev/null @@ -1,79 +0,0 @@ -.share { - &-modal { - .modal-dialog { - max-width: 25rem; - } - .modal-title { - font-size: 0.875rem; - } - .btn-close { - background-size: 0.75rem; - } - &-body { - } - - &-list { - list-style: none; - padding: 0; - margin-bottom: 2rem; - } - - &-link { - padding: 1.25rem 0 1.25rem; - display: block; - text-align: center; - color: #666; - background-color: rgba(#444, 0.03); - border-radius: 0.25rem; - transition: all ease-in-out 0.2s; - text-decoration: none; - &-label { - display: block; - font-size: 0.75rem; - } - &-icon { - width: 1.5rem; - height: 1.5rem; - margin-bottom: 0.5rem; - transition: fill ease 0.1s; - } - - &:hover { - color: #fff; - background-color: #444; - } - - @each $name, $item in $brands { - @include background-hover( - "&.-#{$name}", - rgba($item, 0.07), - $item, - $item, - #fff - ); - } - } - - &-input { - border: 0; - background-color: rgba(#444, 0.04); - width: 100%; - padding: 0.75rem 1rem; - outline: none !important; - font-size: 0.875rem; - border-radius: 0.25rem; - cursor: pointer; - color: #666; - transition: all ease-in-out 0.3s; - &:hover { - color: #111; - background-color: rgba(#444, 0.07); - } - &.copied { - background-color: #000; - color: #fff; - text-align: center; - } - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/post/_view-grid.scss b/src/Blogifier/wwwroot/themes/test/scss/post/_view-grid.scss deleted file mode 100644 index 49dd8044e..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/post/_view-grid.scss +++ /dev/null @@ -1,159 +0,0 @@ -.post-grid { - height: 100%; - position: relative; - transition: box-shadow ease-in-out 0.3s; - border-radius: $radius-var; - box-shadow: 0 0 0rem 1px rgba(#000, 0.05); - overflow: hidden; - - // cover - &-cover { - display: block; - margin: 0 0 1.5rem; - position: relative; - border-bottom: 1px solid rgba(#000, 0.05); - } - &-img { - min-width: 100%; - height: 11rem; - object-fit: cover; - object-position: center; - border-top-left-radius: $radius-var; - border-top-right-radius: $radius-var; - } - - &-cats { - padding: 0 1rem; - line-height: 1.4; - display: flex; - flex-wrap: wrap; - - &-link { - text-decoration: none; - font-size: 0.75rem; - margin-right: 0.5rem; - color: #999; - &::before { - content: "#"; - display: inline-block; - margin-right: 0.1rem; - } - - &:hover { - color: $color; - } - } - } - - // title - &-title { - font-size: 1.125rem; - font-weight: 600; - padding: 1rem 1rem 0; - } - - &-link { - color: #000; - transition: color ease-in-out 0.2s; - text-decoration: none; - &:hover { - color: $color; - } - &::before { - content: ""; - display: block; - width: 100%; - height: 11rem; - background-color: transparent; - top: 0; - left: 0; - position: absolute; - } - } - - // desc - &-desc { - font-size: 0.875rem; - color: #777; - margin: 0; - line-height: 1.75; - transition: color ease-in-out 0.2s; - padding: .5rem 1rem 1rem; - } - - // meta - &-meta { - font-size: 0.75rem; - color: #555; - border-top: 1px solid rgba(#000, 0.05); - // margin-bottom: 1.5rem; - opacity: 0.7; - padding: 1rem; - margin-top: auto; - position: relative; - a { - text-decoration: none; - color: #555; - &:hover, - &:focus { - color: $color; - } - } - } - &-author { - display: flex; - align-items: center; - margin-right: 0.875rem; - &-img { - border-radius: 50rem; - margin-right: 0.25rem; - filter: grayscale(100%); - } - &-name { - text-transform: capitalize; - } - } - &-date { - display: flex; - align-items: center; - margin-right: 0.875rem; - &-icon { - margin-right: 0.375rem; - } - } - - &-more { - font-weight: 500; - position: absolute; - top: 0; - right: 0; - padding-right: 2.5rem; - height: 100%; - line-height: 3.125rem; - font-weight: 500; - - .bi { - margin-left: 0.1rem; - width: 1.5rem; - height: 1.5rem; - position: absolute; - right: 1rem; - top: 50%; - transform: translateY(-50%); - } - } - - // hover item - &:hover { - box-shadow: 0 0 10rem rgba(#000, 0.1); - border-color: #fff; - .post-grid-link { - color: $color; - } - .post-grid-desc { - color: #000; - } - .post-grid-more { - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/scss/post/_view-list.scss b/src/Blogifier/wwwroot/themes/test/scss/post/_view-list.scss deleted file mode 100644 index 671ab5df8..000000000 --- a/src/Blogifier/wwwroot/themes/test/scss/post/_view-list.scss +++ /dev/null @@ -1,141 +0,0 @@ -.post-list { - align-items: center; - height: 100%; - padding-left: 20rem; - margin-bottom: 2rem; - position: relative; - - // cover - &-cover { - position: absolute; - top: 0; - left: 0; - margin: 0; - display: block; - width: 20rem; - height: 100%; - border-radius: $radius-var; - overflow: hidden; - - &::after { - content: ""; - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - display: block; - box-shadow: inset 0 0 0 1px rgba(#000, 0.1); - border-radius: $radius-var; - } - } - &-img { - width: 100%; - height: 100%; - object-fit: cover; - object-position: center; - } - - // details - &-details { - padding: 1.5rem 0 1.5rem 2rem; - flex-grow: 1; - } - - // meta - &-meta { - font-size: 0.75rem; - color: #555; - margin-top: 1rem; - margin-bottom: 1rem; - opacity: 0.7; - a { - color: #555; - text-decoration: none; - } - &-item { - display: flex; - align-items: center; - margin-right: 0.875rem; - } - } - &-author { - &-img { - border-radius: 5rem; - margin-right: 0.25rem; - filter: grayscale(100%); - } - &-name { - text-transform: capitalize; - } - } - &-date { - &-icon { - margin-right: 0.375rem; - } - } - &-cat { - &-icon { - margin-right: 0rem; - } - } - - // title - &-title { - font-size: 1.5rem; - font-weight: 500; - margin-bottom: 1rem; - } - - &-link { - color: #000; - transition: color ease-in-out 0.2s; - text-decoration: none; - &:hover { - color: $color; - } - - &::before { - content: ""; - display: block; - width: 20rem; - height: 100%; - top: 0; - left: 0; - position: absolute; - } - } - - // desc - &-desc { - margin-bottom: 0.5rem; - font-size: 0.875rem; - color: #666; - line-height: 1.75; - transition: color ease-in-out 0.2s; - // keep in two line - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - - // more link - &-more { - display: inline-block; - font-weight: 500; - font-size: 0.875rem; - color: #555; - text-decoration: none; - &:hover { - color: $color; - } - } - - // hover item - &:hover { - .post-list-desc { - color: #000; - } - } -} diff --git a/src/Blogifier/wwwroot/themes/test/settings.json b/src/Blogifier/wwwroot/themes/test/settings.json deleted file mode 100644 index 60de2e2fa..000000000 --- a/src/Blogifier/wwwroot/themes/test/settings.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "Sections": [ - { - "Label": "Social Buttons", - "Fields": [ - { - "Id": "enabled", - "Label": "Enabled", - "Type": "checkbox", - "Value": "False", - "Options": null - }, - { - "Id": "facebook", - "Label": "Facebook", - "Type": "text", - "Value": "blogifierdotnet", - "Options": null - }, - { - "Id": "twitter", - "Label": "Twitter", - "Type": "text", - "Value": "blogifierdotnet", - "Options": null - } - ] - }, - { - "Label": "Select Dropdown Sample", - "Fields": [ - { - "Id": "dd-sample", - "Label": "Select Sample", - "Type": "select", - "Value": "two", - "Options": [ - "one", - "two", - "three" - ] - } - ] - }, - { - "Label": "Section Three", - "Fields": [ - { - "Id": "txt-area", - "Label": "Text Area Sample", - "Type": "textarea", - "Value": "Some test text here", - "Options": null - } - ] - } - ] -} \ No newline at end of file diff --git a/testEnvironments.json b/testEnvironments.json new file mode 100644 index 000000000..2c5e92936 --- /dev/null +++ b/testEnvironments.json @@ -0,0 +1,17 @@ +{ + "version": "1", + "environments": [ + // 请参阅 https://aka.ms/remotetesting 获取更多信息 + // 了解如何配置远程环境。 + //{ + // "name": "WSL Ubuntu", + // "type": "wsl", + // "wslDistribution": "Ubuntu" + //}, + //{ + // "name": "Docker dotnet/sdk", + // "type": "docker", + // "dockerImage": "mcr.microsoft.com/dotnet/sdk" + //} + ] +} \ No newline at end of file diff --git a/tests/Blogifier.Tests/BaseTests.cs b/tests/Blogifier.Tests/BaseTests.cs new file mode 100644 index 000000000..b65b0904d --- /dev/null +++ b/tests/Blogifier.Tests/BaseTests.cs @@ -0,0 +1,6 @@ +namespace Blogifier.Tests; + +public class BaseTests +{ + +} diff --git a/tests/Blogifier.Tests/Blogifier.Tests.csproj b/tests/Blogifier.Tests/Blogifier.Tests.csproj index d0116a81f..bea36e164 100644 --- a/tests/Blogifier.Tests/Blogifier.Tests.csproj +++ b/tests/Blogifier.Tests/Blogifier.Tests.csproj @@ -1,25 +1,25 @@ - - net6.0 - + + net7.0 + - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - - - - + + + diff --git a/tests/Blogifier.Tests/StorageProviderTests.cs b/tests/Blogifier.Tests/StorageProviderTests.cs deleted file mode 100644 index 85505a235..000000000 --- a/tests/Blogifier.Tests/StorageProviderTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Blogifier.Core.Providers; -using Microsoft.Extensions.Configuration; -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; - -namespace Blogifier.Tests -{ - public class StorageProviderTests - { - private readonly string _imgUrl = "https://user-images.githubusercontent.com/1932785/81506457-1611e580-92bc-11ea-927e-b826c56ba21b.png"; - private readonly string _img64 = ""; - - [Fact] - public async Task CanUploadImageOrFileFromUrl() - { - var sut = GetSut(); - - var result = await sut.UploadFromWeb(new System.Uri(_imgUrl), "/", "1/2020/12"); - - Assert.True(result.Length > 0); - } - - [Fact] - public async Task CanUploadBase64Image() - { - var sut = GetSut(); - - var result = await sut.UploadBase64Image(_img64, "/", "1/2020/12"); - - Assert.True(result.Length > 0); - } - - IStorageProvider GetSut() - { - var inMemorySettings = new Dictionary { - {"Blgofier:FileExtensions", "png,gif,jpeg,jpg,zip"} - }; - - IConfiguration configuration = new ConfigurationBuilder() - .AddInMemoryCollection(inMemorySettings) - .Build(); - - return new StorageProvider(configuration); - } - } -} diff --git a/tests/Blogifier.Tests/TestHelper.cs b/tests/Blogifier.Tests/TestHelper.cs index cd5d72498..8c9383566 100644 --- a/tests/Blogifier.Tests/TestHelper.cs +++ b/tests/Blogifier.Tests/TestHelper.cs @@ -1,30 +1,29 @@ -using Blogifier.Core.Data; +using Blogifier.Data; using Microsoft.EntityFrameworkCore; using System.IO; namespace Blogifier.Tests { - public class TestHelper - { - public string Slash { get { return Path.DirectorySeparatorChar.ToString(); } } - public string ContextRoot - { - get - { - string path = Directory.GetCurrentDirectory(); - return path.Substring(0, path.IndexOf($"tests{Slash}Blogifier.Tests")); - } - } + public class TestHelper + { + public string Slash { get { return Path.DirectorySeparatorChar.ToString(); } } + public string ContextRoot + { + get + { + string path = Directory.GetCurrentDirectory(); + return path.Substring(0, path.IndexOf($"tests{Slash}Blogifier.Tests")); + } + } - public AppDbContext GetDbContext() - { - return new AppDbContext(new DbContextOptionsBuilder() - .UseSqlite(GetDataSource()).Options); - } + public AppDbContext GetDbContext() + { + return new AppDbContext(new DbContextOptionsBuilder().UseSqlite(GetDataSource()).Options); + } - private string GetDataSource() - { - return $"DataSource={ContextRoot}src{Slash}Blogifier{Slash}Blog.db"; - } - } + private string GetDataSource() + { + return $"DataSource={ContextRoot}src{Slash}Blogifier{Slash}Blog.db"; + } + } }