diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2c1b6e7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,42 @@ +# You can modify the rules from these initially generated values to suit your own policies +# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference +[*.cs] + +#Core editorconfig formatting - indentation + +#use hard tabs for indentation +indent_style = tab + +#Formatting - new line options + +#require braces to be on a new line for types and methods (also known as "Allman" style) +csharp_new_line_before_open_brace = types, methods + +#Formatting - organize using options + +#sort System.* using directives alphabetically, and place them before other usings +dotnet_sort_system_directives_first = true + +#Formatting - spacing options + +#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false + +#Style - expression bodied member options + +#prefer block bodies for constructors +csharp_style_expression_bodied_constructors = false : suggestion +#prefer block bodies for methods +csharp_style_expression_bodied_methods = false : suggestion + +#Style - language keyword and framework type options + +#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true : suggestion + +#Style - qualification options + +#prefer properties not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_property = false : suggestion + +csharp_new_line_before_open_brace = all \ No newline at end of file diff --git a/.gitignore b/.gitignore index dd69add..61262c6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +*.trx + # User-specific files *.suo *.user @@ -23,6 +25,7 @@ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +artifacts/ # Visual Studio 2015/2017 cache/options directory .vs/ diff --git a/.nuke b/.nuke new file mode 100644 index 0000000..0f03334 --- /dev/null +++ b/.nuke @@ -0,0 +1 @@ +Beffyman.AspNetCore.Client.sln diff --git a/AspNetCore.Client.sln b/Beffyman.AspNetCore.Client.sln similarity index 63% rename from AspNetCore.Client.sln rename to Beffyman.AspNetCore.Client.sln index 9fd6a58..9f46fba 100644 --- a/AspNetCore.Client.sln +++ b/Beffyman.AspNetCore.Client.sln @@ -1,225 +1,236 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28407.52 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Client", "src\AspNetCore.Client\AspNetCore.Client.csproj", "{3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0A0F6ACE-08C3-4894-B7D2-537B06B057BC}" - ProjectSection(SolutionItems) = preProject - .gitignore = .gitignore - after.AspNetCore.Client.sln.targets = after.AspNetCore.Client.sln.targets - appveyor.yml = appveyor.yml - Build.ps1 = Build.ps1 - Directory.Build.targets = Directory.Build.targets - global.json = global.json - LICENSE = LICENSE - NuGet.config = NuGet.config - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F0EC1B44-13B2-4817-80A8-D67960BD79E1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp", "test\TestWebApp\TestWebApp.csproj", "{303D91A0-1A4E-4F98-A81F-35072F18C19A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp.Clients", "test\TestWebApp.Clients\TestWebApp.Clients.csproj", "{D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetCore", "AspNetCore", "{EBC264EF-636C-42D0-9CF9-F5FD874354D4}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{DAA1FF40-201F-444E-A3EF-FB83FB5D8F96}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Server", "test\TestBlazorApp.Server\TestBlazorApp.Server.csproj", "{61F66164-B2DE-4623-99B1-0407A8F56F1E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Views", "test\TestBlazorApp.Views\TestBlazorApp.Views.csproj", "{AC543DFE-E8AD-4EB6-9C7D-1DED7A93938A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Shared", "test\TestBlazorApp.Shared\TestBlazorApp.Shared.csproj", "{CA3BA183-5937-4484-8FDB-1A1E80C9077D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Clients", "test\TestBlazorApp.Clients\TestBlazorApp.Clients.csproj", "{BBE6C596-7295-420D-9814-829933A8F6AF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp.Tests", "test\TestWebApp.Tests\TestWebApp.Tests.csproj", "{65D34793-90AA-4641-80DC-A8B092E40DAC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp.Contracts", "test\TestWebApp.Contracts\TestWebApp.Contracts.csproj", "{D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Client.Protobuf", "src\AspNetCore.Client.Protobuf\AspNetCore.Client.Protobuf.csproj", "{4F89E001-24D2-4464-9577-6768E31C510D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Client.BlazorJson", "src\AspNetCore.Client.BlazorJson\AspNetCore.Client.BlazorJson.csproj", "{F911F6A0-CBE9-4B4C-91AA-41C3EF3B62A3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Tests", "test\TestBlazorApp.Tests\TestBlazorApp.Tests.csproj", "{B28C16E6-4FDB-4245-AB1B-F4928CC9B611}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Client.Generator.Framework", "src\AspNetCore.Client.Generator.Framework\AspNetCore.Client.Generator.Framework.csproj", "{862DD062-0098-4700-8CDB-7570AF9655F7}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp.Console", "test\TestWebApp.Console\TestWebApp.Console.csproj", "{8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Functions", "Functions", "{344CED97-6C7C-4EC5-BA25-B967521598F5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAzureFunction", "test\TestAzureFunction\TestAzureFunction.csproj", "{EC486723-64AF-45BE-AC74-8021A24994AB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAzureFunction.Clients", "test\TestAzureFunction.Clients\TestAzureFunction.Clients.csproj", "{54656466-4617-4B86-AD56-4DA8C15CA640}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAzureFunction.Contracts", "test\TestAzureFunction.Contracts\TestAzureFunction.Contracts.csproj", "{11133C2A-4AB2-45F5-876F-F22AB024A67C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Server", "src\AspNetCore.Server\AspNetCore.Server.csproj", "{60B21E11-0738-4A29-B518-329EA8470140}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Client.MessagePack", "src\AspNetCore.Client.MessagePack\AspNetCore.Client.MessagePack.csproj", "{2075D5BC-D4B9-4B0D-948C-399200598110}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAzureFunction.Tests", "test\TestAzureFunction.Tests\TestAzureFunction.Tests.csproj", "{E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Functions2", "Functions2", "{7BFCD5A4-715F-47CA-BFB4-ED3CD7D70F97}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionApp2.Clients", "test\FunctionApp2.Clients\FunctionApp2.Clients.csproj", "{B1B79A7A-DDEC-466A-9755-78E52641C7B7}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionApp2", "test\FunctionApp2\FunctionApp2.csproj", "{3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Client.Test.Generator", "test\AspNetCore.Client.Test.Generator\AspNetCore.Client.Test.Generator.csproj", "{EC477C06-BCEF-439B-A434-3EC894B7A44A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Client.Generator", "src\AspNetCore.Client.Generator\AspNetCore.Client.Generator.csproj", "{34C4FE4E-7236-4A5B-98A9-BE17EB31F0D0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Client.JSInterop", "src\AspNetCore.Client.JSInterop\AspNetCore.Client.JSInterop.csproj", "{C2D5F971-B739-4BE6-B947-8540C81E3F77}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}.Release|Any CPU.Build.0 = Release|Any CPU - {303D91A0-1A4E-4F98-A81F-35072F18C19A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {303D91A0-1A4E-4F98-A81F-35072F18C19A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {303D91A0-1A4E-4F98-A81F-35072F18C19A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {303D91A0-1A4E-4F98-A81F-35072F18C19A}.Release|Any CPU.Build.0 = Release|Any CPU - {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}.Release|Any CPU.Build.0 = Release|Any CPU - {61F66164-B2DE-4623-99B1-0407A8F56F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {61F66164-B2DE-4623-99B1-0407A8F56F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {61F66164-B2DE-4623-99B1-0407A8F56F1E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {61F66164-B2DE-4623-99B1-0407A8F56F1E}.Release|Any CPU.Build.0 = Release|Any CPU - {AC543DFE-E8AD-4EB6-9C7D-1DED7A93938A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AC543DFE-E8AD-4EB6-9C7D-1DED7A93938A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC543DFE-E8AD-4EB6-9C7D-1DED7A93938A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AC543DFE-E8AD-4EB6-9C7D-1DED7A93938A}.Release|Any CPU.Build.0 = Release|Any CPU - {CA3BA183-5937-4484-8FDB-1A1E80C9077D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CA3BA183-5937-4484-8FDB-1A1E80C9077D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA3BA183-5937-4484-8FDB-1A1E80C9077D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CA3BA183-5937-4484-8FDB-1A1E80C9077D}.Release|Any CPU.Build.0 = Release|Any CPU - {BBE6C596-7295-420D-9814-829933A8F6AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BBE6C596-7295-420D-9814-829933A8F6AF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BBE6C596-7295-420D-9814-829933A8F6AF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BBE6C596-7295-420D-9814-829933A8F6AF}.Release|Any CPU.Build.0 = Release|Any CPU - {65D34793-90AA-4641-80DC-A8B092E40DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {65D34793-90AA-4641-80DC-A8B092E40DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {65D34793-90AA-4641-80DC-A8B092E40DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {65D34793-90AA-4641-80DC-A8B092E40DAC}.Release|Any CPU.Build.0 = Release|Any CPU - {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}.Release|Any CPU.Build.0 = Release|Any CPU - {4F89E001-24D2-4464-9577-6768E31C510D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4F89E001-24D2-4464-9577-6768E31C510D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4F89E001-24D2-4464-9577-6768E31C510D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4F89E001-24D2-4464-9577-6768E31C510D}.Release|Any CPU.Build.0 = Release|Any CPU - {F911F6A0-CBE9-4B4C-91AA-41C3EF3B62A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F911F6A0-CBE9-4B4C-91AA-41C3EF3B62A3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F911F6A0-CBE9-4B4C-91AA-41C3EF3B62A3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F911F6A0-CBE9-4B4C-91AA-41C3EF3B62A3}.Release|Any CPU.Build.0 = Release|Any CPU - {B28C16E6-4FDB-4245-AB1B-F4928CC9B611}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B28C16E6-4FDB-4245-AB1B-F4928CC9B611}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B28C16E6-4FDB-4245-AB1B-F4928CC9B611}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B28C16E6-4FDB-4245-AB1B-F4928CC9B611}.Release|Any CPU.Build.0 = Release|Any CPU - {862DD062-0098-4700-8CDB-7570AF9655F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {862DD062-0098-4700-8CDB-7570AF9655F7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {862DD062-0098-4700-8CDB-7570AF9655F7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {862DD062-0098-4700-8CDB-7570AF9655F7}.Release|Any CPU.Build.0 = Release|Any CPU - {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}.Release|Any CPU.Build.0 = Release|Any CPU - {EC486723-64AF-45BE-AC74-8021A24994AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC486723-64AF-45BE-AC74-8021A24994AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC486723-64AF-45BE-AC74-8021A24994AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC486723-64AF-45BE-AC74-8021A24994AB}.Release|Any CPU.Build.0 = Release|Any CPU - {54656466-4617-4B86-AD56-4DA8C15CA640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {54656466-4617-4B86-AD56-4DA8C15CA640}.Debug|Any CPU.Build.0 = Debug|Any CPU - {54656466-4617-4B86-AD56-4DA8C15CA640}.Release|Any CPU.ActiveCfg = Release|Any CPU - {54656466-4617-4B86-AD56-4DA8C15CA640}.Release|Any CPU.Build.0 = Release|Any CPU - {11133C2A-4AB2-45F5-876F-F22AB024A67C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {11133C2A-4AB2-45F5-876F-F22AB024A67C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {11133C2A-4AB2-45F5-876F-F22AB024A67C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {11133C2A-4AB2-45F5-876F-F22AB024A67C}.Release|Any CPU.Build.0 = Release|Any CPU - {60B21E11-0738-4A29-B518-329EA8470140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {60B21E11-0738-4A29-B518-329EA8470140}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60B21E11-0738-4A29-B518-329EA8470140}.Release|Any CPU.ActiveCfg = Release|Any CPU - {60B21E11-0738-4A29-B518-329EA8470140}.Release|Any CPU.Build.0 = Release|Any CPU - {2075D5BC-D4B9-4B0D-948C-399200598110}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2075D5BC-D4B9-4B0D-948C-399200598110}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2075D5BC-D4B9-4B0D-948C-399200598110}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2075D5BC-D4B9-4B0D-948C-399200598110}.Release|Any CPU.Build.0 = Release|Any CPU - {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}.Release|Any CPU.Build.0 = Release|Any CPU - {B1B79A7A-DDEC-466A-9755-78E52641C7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B1B79A7A-DDEC-466A-9755-78E52641C7B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B1B79A7A-DDEC-466A-9755-78E52641C7B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B1B79A7A-DDEC-466A-9755-78E52641C7B7}.Release|Any CPU.Build.0 = Release|Any CPU - {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}.Release|Any CPU.Build.0 = Release|Any CPU - {EC477C06-BCEF-439B-A434-3EC894B7A44A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC477C06-BCEF-439B-A434-3EC894B7A44A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC477C06-BCEF-439B-A434-3EC894B7A44A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC477C06-BCEF-439B-A434-3EC894B7A44A}.Release|Any CPU.Build.0 = Release|Any CPU - {34C4FE4E-7236-4A5B-98A9-BE17EB31F0D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {34C4FE4E-7236-4A5B-98A9-BE17EB31F0D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {34C4FE4E-7236-4A5B-98A9-BE17EB31F0D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {34C4FE4E-7236-4A5B-98A9-BE17EB31F0D0}.Release|Any CPU.Build.0 = Release|Any CPU - {C2D5F971-B739-4BE6-B947-8540C81E3F77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C2D5F971-B739-4BE6-B947-8540C81E3F77}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C2D5F971-B739-4BE6-B947-8540C81E3F77}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C2D5F971-B739-4BE6-B947-8540C81E3F77}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} - {303D91A0-1A4E-4F98-A81F-35072F18C19A} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} - {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} - {EBC264EF-636C-42D0-9CF9-F5FD874354D4} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} - {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} - {61F66164-B2DE-4623-99B1-0407A8F56F1E} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} - {AC543DFE-E8AD-4EB6-9C7D-1DED7A93938A} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} - {CA3BA183-5937-4484-8FDB-1A1E80C9077D} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} - {BBE6C596-7295-420D-9814-829933A8F6AF} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} - {65D34793-90AA-4641-80DC-A8B092E40DAC} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} - {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} - {4F89E001-24D2-4464-9577-6768E31C510D} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} - {F911F6A0-CBE9-4B4C-91AA-41C3EF3B62A3} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} - {B28C16E6-4FDB-4245-AB1B-F4928CC9B611} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} - {862DD062-0098-4700-8CDB-7570AF9655F7} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} - {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} - {344CED97-6C7C-4EC5-BA25-B967521598F5} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} - {EC486723-64AF-45BE-AC74-8021A24994AB} = {344CED97-6C7C-4EC5-BA25-B967521598F5} - {54656466-4617-4B86-AD56-4DA8C15CA640} = {344CED97-6C7C-4EC5-BA25-B967521598F5} - {11133C2A-4AB2-45F5-876F-F22AB024A67C} = {344CED97-6C7C-4EC5-BA25-B967521598F5} - {60B21E11-0738-4A29-B518-329EA8470140} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} - {2075D5BC-D4B9-4B0D-948C-399200598110} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} - {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92} = {344CED97-6C7C-4EC5-BA25-B967521598F5} - {7BFCD5A4-715F-47CA-BFB4-ED3CD7D70F97} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} - {B1B79A7A-DDEC-466A-9755-78E52641C7B7} = {7BFCD5A4-715F-47CA-BFB4-ED3CD7D70F97} - {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314} = {7BFCD5A4-715F-47CA-BFB4-ED3CD7D70F97} - {EC477C06-BCEF-439B-A434-3EC894B7A44A} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} - {34C4FE4E-7236-4A5B-98A9-BE17EB31F0D0} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} - {C2D5F971-B739-4BE6-B947-8540C81E3F77} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FCFD7864-E2E5-4CF3-9A6D-D53B4F4E8AE0} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28407.52 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Client", "src\Beffyman.AspNetCore.Client\Beffyman.AspNetCore.Client.csproj", "{3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0A0F6ACE-08C3-4894-B7D2-537B06B057BC}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + global.json = global.json + LICENSE.txt = LICENSE.txt + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F0EC1B44-13B2-4817-80A8-D67960BD79E1}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC}" + ProjectSection(SolutionItems) = preProject + tests\Directory.Build.props = tests\Directory.Build.props + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp", "tests\TestWebApp\TestWebApp.csproj", "{303D91A0-1A4E-4F98-A81F-35072F18C19A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp.Clients", "tests\TestWebApp.Clients\TestWebApp.Clients.csproj", "{D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetCore", "AspNetCore", "{EBC264EF-636C-42D0-9CF9-F5FD874354D4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{DAA1FF40-201F-444E-A3EF-FB83FB5D8F96}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Shared", "tests\TestBlazorApp.Shared\TestBlazorApp.Shared.csproj", "{CA3BA183-5937-4484-8FDB-1A1E80C9077D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Clients", "tests\TestBlazorApp.Clients\TestBlazorApp.Clients.csproj", "{BBE6C596-7295-420D-9814-829933A8F6AF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp.Tests", "tests\TestWebApp.Tests\TestWebApp.Tests.csproj", "{65D34793-90AA-4641-80DC-A8B092E40DAC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp.Contracts", "tests\TestWebApp.Contracts\TestWebApp.Contracts.csproj", "{D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Client.Protobuf", "src\Beffyman.AspNetCore.Client.Protobuf\Beffyman.AspNetCore.Client.Protobuf.csproj", "{4F89E001-24D2-4464-9577-6768E31C510D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Tests", "tests\TestBlazorApp.Tests\TestBlazorApp.Tests.csproj", "{B28C16E6-4FDB-4245-AB1B-F4928CC9B611}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Client.Generator.Framework", "src\Beffyman.AspNetCore.Client.Generator.Framework\Beffyman.AspNetCore.Client.Generator.Framework.csproj", "{862DD062-0098-4700-8CDB-7570AF9655F7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApp.Console", "tests\TestWebApp.Console\TestWebApp.Console.csproj", "{8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Functions", "Functions", "{344CED97-6C7C-4EC5-BA25-B967521598F5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAzureFunction", "tests\TestAzureFunction\TestAzureFunction.csproj", "{EC486723-64AF-45BE-AC74-8021A24994AB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAzureFunction.Clients", "tests\TestAzureFunction.Clients\TestAzureFunction.Clients.csproj", "{54656466-4617-4B86-AD56-4DA8C15CA640}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAzureFunction.Contracts", "tests\TestAzureFunction.Contracts\TestAzureFunction.Contracts.csproj", "{11133C2A-4AB2-45F5-876F-F22AB024A67C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Server", "src\Beffyman.AspNetCore.Server\Beffyman.AspNetCore.Server.csproj", "{60B21E11-0738-4A29-B518-329EA8470140}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Client.MessagePack", "src\Beffyman.AspNetCore.Client.MessagePack\Beffyman.AspNetCore.Client.MessagePack.csproj", "{2075D5BC-D4B9-4B0D-948C-399200598110}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestAzureFunction.Tests", "tests\TestAzureFunction.Tests\TestAzureFunction.Tests.csproj", "{E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Functions2", "Functions2", "{7BFCD5A4-715F-47CA-BFB4-ED3CD7D70F97}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionApp2.Clients", "tests\FunctionApp2.Clients\FunctionApp2.Clients.csproj", "{B1B79A7A-DDEC-466A-9755-78E52641C7B7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionApp2", "tests\FunctionApp2\FunctionApp2.csproj", "{3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{933E4A0A-7712-4E6C-961E-9AE9147B493C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{B16EEEFF-40C6-4DC7-8CE0-2387B1AEA9E7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Server", "tests\TestBlazorApp.Server\TestBlazorApp.Server.csproj", "{D7FA09D6-2E49-4504-BD25-83799A3E2ACA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBlazorApp.Views", "tests\TestBlazorApp.Views\TestBlazorApp.Views.csproj", "{69C58805-0300-4463-AA5B-84823DC0B443}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Client.Http", "src\Beffyman.AspNetCore.Client.Http\Beffyman.AspNetCore.Client.Http.csproj", "{ACE003CE-5640-41EE-B232-71C4F574B74C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Client.Generator", "src\Beffyman.AspNetCore.Client.Generator\Beffyman.AspNetCore.Client.Generator.csproj", "{BD2BA351-40FE-4729-8E63-E18522858ACF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Client.Test.Generator", "tests\Beffyman.AspNetCore.Client.Test.Generator\Beffyman.AspNetCore.Client.Test.Generator.csproj", "{C841D007-BDD7-4AC9-BFCC-317BF96A9299}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beffyman.AspNetCore.Client.NewtonsoftJson", "src\Beffyman.AspNetCore.Client.NewtonsoftJson\Beffyman.AspNetCore.Client.NewtonsoftJson.csproj", "{CCFCBB7F-2DB8-44A6-B142-2879DB563968}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012}.Release|Any CPU.Build.0 = Release|Any CPU + {303D91A0-1A4E-4F98-A81F-35072F18C19A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {303D91A0-1A4E-4F98-A81F-35072F18C19A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {303D91A0-1A4E-4F98-A81F-35072F18C19A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {303D91A0-1A4E-4F98-A81F-35072F18C19A}.Release|Any CPU.Build.0 = Release|Any CPU + {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3}.Release|Any CPU.Build.0 = Release|Any CPU + {CA3BA183-5937-4484-8FDB-1A1E80C9077D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA3BA183-5937-4484-8FDB-1A1E80C9077D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA3BA183-5937-4484-8FDB-1A1E80C9077D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA3BA183-5937-4484-8FDB-1A1E80C9077D}.Release|Any CPU.Build.0 = Release|Any CPU + {BBE6C596-7295-420D-9814-829933A8F6AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBE6C596-7295-420D-9814-829933A8F6AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBE6C596-7295-420D-9814-829933A8F6AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BBE6C596-7295-420D-9814-829933A8F6AF}.Release|Any CPU.Build.0 = Release|Any CPU + {65D34793-90AA-4641-80DC-A8B092E40DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65D34793-90AA-4641-80DC-A8B092E40DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65D34793-90AA-4641-80DC-A8B092E40DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65D34793-90AA-4641-80DC-A8B092E40DAC}.Release|Any CPU.Build.0 = Release|Any CPU + {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA}.Release|Any CPU.Build.0 = Release|Any CPU + {4F89E001-24D2-4464-9577-6768E31C510D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F89E001-24D2-4464-9577-6768E31C510D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F89E001-24D2-4464-9577-6768E31C510D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F89E001-24D2-4464-9577-6768E31C510D}.Release|Any CPU.Build.0 = Release|Any CPU + {B28C16E6-4FDB-4245-AB1B-F4928CC9B611}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B28C16E6-4FDB-4245-AB1B-F4928CC9B611}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B28C16E6-4FDB-4245-AB1B-F4928CC9B611}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B28C16E6-4FDB-4245-AB1B-F4928CC9B611}.Release|Any CPU.Build.0 = Release|Any CPU + {862DD062-0098-4700-8CDB-7570AF9655F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {862DD062-0098-4700-8CDB-7570AF9655F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {862DD062-0098-4700-8CDB-7570AF9655F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {862DD062-0098-4700-8CDB-7570AF9655F7}.Release|Any CPU.Build.0 = Release|Any CPU + {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7}.Release|Any CPU.Build.0 = Release|Any CPU + {EC486723-64AF-45BE-AC74-8021A24994AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC486723-64AF-45BE-AC74-8021A24994AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC486723-64AF-45BE-AC74-8021A24994AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC486723-64AF-45BE-AC74-8021A24994AB}.Release|Any CPU.Build.0 = Release|Any CPU + {54656466-4617-4B86-AD56-4DA8C15CA640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54656466-4617-4B86-AD56-4DA8C15CA640}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54656466-4617-4B86-AD56-4DA8C15CA640}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54656466-4617-4B86-AD56-4DA8C15CA640}.Release|Any CPU.Build.0 = Release|Any CPU + {11133C2A-4AB2-45F5-876F-F22AB024A67C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11133C2A-4AB2-45F5-876F-F22AB024A67C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11133C2A-4AB2-45F5-876F-F22AB024A67C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11133C2A-4AB2-45F5-876F-F22AB024A67C}.Release|Any CPU.Build.0 = Release|Any CPU + {60B21E11-0738-4A29-B518-329EA8470140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60B21E11-0738-4A29-B518-329EA8470140}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60B21E11-0738-4A29-B518-329EA8470140}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60B21E11-0738-4A29-B518-329EA8470140}.Release|Any CPU.Build.0 = Release|Any CPU + {2075D5BC-D4B9-4B0D-948C-399200598110}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2075D5BC-D4B9-4B0D-948C-399200598110}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2075D5BC-D4B9-4B0D-948C-399200598110}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2075D5BC-D4B9-4B0D-948C-399200598110}.Release|Any CPU.Build.0 = Release|Any CPU + {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92}.Release|Any CPU.Build.0 = Release|Any CPU + {B1B79A7A-DDEC-466A-9755-78E52641C7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1B79A7A-DDEC-466A-9755-78E52641C7B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1B79A7A-DDEC-466A-9755-78E52641C7B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1B79A7A-DDEC-466A-9755-78E52641C7B7}.Release|Any CPU.Build.0 = Release|Any CPU + {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314}.Release|Any CPU.Build.0 = Release|Any CPU + {933E4A0A-7712-4E6C-961E-9AE9147B493C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {933E4A0A-7712-4E6C-961E-9AE9147B493C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7FA09D6-2E49-4504-BD25-83799A3E2ACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7FA09D6-2E49-4504-BD25-83799A3E2ACA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7FA09D6-2E49-4504-BD25-83799A3E2ACA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7FA09D6-2E49-4504-BD25-83799A3E2ACA}.Release|Any CPU.Build.0 = Release|Any CPU + {69C58805-0300-4463-AA5B-84823DC0B443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69C58805-0300-4463-AA5B-84823DC0B443}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69C58805-0300-4463-AA5B-84823DC0B443}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69C58805-0300-4463-AA5B-84823DC0B443}.Release|Any CPU.Build.0 = Release|Any CPU + {ACE003CE-5640-41EE-B232-71C4F574B74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACE003CE-5640-41EE-B232-71C4F574B74C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACE003CE-5640-41EE-B232-71C4F574B74C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACE003CE-5640-41EE-B232-71C4F574B74C}.Release|Any CPU.Build.0 = Release|Any CPU + {BD2BA351-40FE-4729-8E63-E18522858ACF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD2BA351-40FE-4729-8E63-E18522858ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD2BA351-40FE-4729-8E63-E18522858ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD2BA351-40FE-4729-8E63-E18522858ACF}.Release|Any CPU.Build.0 = Release|Any CPU + {C841D007-BDD7-4AC9-BFCC-317BF96A9299}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C841D007-BDD7-4AC9-BFCC-317BF96A9299}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C841D007-BDD7-4AC9-BFCC-317BF96A9299}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C841D007-BDD7-4AC9-BFCC-317BF96A9299}.Release|Any CPU.Build.0 = Release|Any CPU + {CCFCBB7F-2DB8-44A6-B142-2879DB563968}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCFCBB7F-2DB8-44A6-B142-2879DB563968}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCFCBB7F-2DB8-44A6-B142-2879DB563968}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCFCBB7F-2DB8-44A6-B142-2879DB563968}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3709B2AC-A6E2-4707-A2F4-DBE6AFC3A012} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} + {303D91A0-1A4E-4F98-A81F-35072F18C19A} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} + {D59C431C-CAB3-4BAD-8AAA-2D2042E492D3} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} + {EBC264EF-636C-42D0-9CF9-F5FD874354D4} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} + {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} + {CA3BA183-5937-4484-8FDB-1A1E80C9077D} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} + {BBE6C596-7295-420D-9814-829933A8F6AF} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} + {65D34793-90AA-4641-80DC-A8B092E40DAC} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} + {D21B21B7-CCF3-4C68-8F90-DC045ABE3DCA} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} + {4F89E001-24D2-4464-9577-6768E31C510D} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} + {B28C16E6-4FDB-4245-AB1B-F4928CC9B611} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} + {862DD062-0098-4700-8CDB-7570AF9655F7} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} + {8150D22C-D1F4-4D14-9B39-2D81A2B75FA7} = {EBC264EF-636C-42D0-9CF9-F5FD874354D4} + {344CED97-6C7C-4EC5-BA25-B967521598F5} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} + {EC486723-64AF-45BE-AC74-8021A24994AB} = {344CED97-6C7C-4EC5-BA25-B967521598F5} + {54656466-4617-4B86-AD56-4DA8C15CA640} = {344CED97-6C7C-4EC5-BA25-B967521598F5} + {11133C2A-4AB2-45F5-876F-F22AB024A67C} = {344CED97-6C7C-4EC5-BA25-B967521598F5} + {60B21E11-0738-4A29-B518-329EA8470140} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} + {2075D5BC-D4B9-4B0D-948C-399200598110} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} + {E58B7973-8AF8-4FEF-8855-F10DEFCB7A92} = {344CED97-6C7C-4EC5-BA25-B967521598F5} + {7BFCD5A4-715F-47CA-BFB4-ED3CD7D70F97} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} + {B1B79A7A-DDEC-466A-9755-78E52641C7B7} = {7BFCD5A4-715F-47CA-BFB4-ED3CD7D70F97} + {3AF190F0-6B8E-47B3-9C29-F4A0EE19D314} = {7BFCD5A4-715F-47CA-BFB4-ED3CD7D70F97} + {933E4A0A-7712-4E6C-961E-9AE9147B493C} = {B16EEEFF-40C6-4DC7-8CE0-2387B1AEA9E7} + {D7FA09D6-2E49-4504-BD25-83799A3E2ACA} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} + {69C58805-0300-4463-AA5B-84823DC0B443} = {DAA1FF40-201F-444E-A3EF-FB83FB5D8F96} + {ACE003CE-5640-41EE-B232-71C4F574B74C} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} + {BD2BA351-40FE-4729-8E63-E18522858ACF} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} + {C841D007-BDD7-4AC9-BFCC-317BF96A9299} = {765F8CBA-ECD0-4E5B-AB1D-AC0F508A05AC} + {CCFCBB7F-2DB8-44A6-B142-2879DB563968} = {F0EC1B44-13B2-4817-80A8-D67960BD79E1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FCFD7864-E2E5-4CF3-9A6D-D53B4F4E8AE0} + EndGlobalSection +EndGlobal diff --git a/Build.ps1 b/Build.ps1 index 261f2b0..658f1f1 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -1,126 +1,139 @@ -# Make so the script will stop when it hits an error. -$ErrorActionPreference = "Stop" - -# Get the executing directory and set it to the current directory. -$scriptBin = "" -Try { $scriptBin = "$(Split-Path -Parent $MyInvocation.MyCommand.Definition)" } Catch {} -If ([string]::IsNullOrEmpty($scriptBin)) { $scriptBin = $pwd } -Set-Location $scriptBin - -$version = $env:APPVEYOR_BUILD_VERSION; -$localBuild = $false; -#For local builds, appveyor will be provided version -if([System.String]::IsNullOrEmpty($version)){ - $version = & git describe --tags; - $localBuild = $true; +[CmdletBinding()] +Param( + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$BuildArguments +) + +Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" + +Set-StrictMode -Version 2.0; +$ErrorActionPreference = "Stop"; +$ConfirmPreference = "None"; +trap { + Write-Error $_; + exit 1 } -#Filter out - branch commit locally -if($version -Match "-"){ - $version = $version.Split("-")[0]; -} - -#Filter out +Build# from CI builds -if($version -Match "\+"){ - $version = $version.Split("+")[0]; -} - - -if($localBuild -eq $true){ - $build = & git rev-list --count HEAD; - $version = "$($version)$build"; -} - +$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent -Write-Host "---Version $version will be used---" -ForegroundColor Magenta; +########################################################################### +# CONFIGURATION +########################################################################### -$artifacts = "$scriptBin/artifacts"; -$testGenerator = Resolve-Path "$scriptBin/test/AspNetCore.Client.Test.Generator"; +$BuildProjectFile = "$PSScriptRoot\build\_build.csproj" +$TempDirectory = "$PSScriptRoot\\.tmp" -Get-ChildItem -Path $artifacts -Filter "*.nupkg" -Recurse | Remove-item -ErrorAction Ignore; -$outputDir = Resolve-Path $artifacts; +$DotNetGlobalFile = "$PSScriptRoot\\global.json" +$DotNetInstallUrlPowershell = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1" +$DotNetInstallUrlBash = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.sh" +$DotNetChannel = "Current" -Write-Host ">> dotnet --info" -ForegroundColor Magenta; -dotnet --info +$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 +$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 +$env:NUGET_XMLDOC_MODE = "skip" -Write-Host ">> dotnet clean -c Release -v m" -ForegroundColor Magenta; -dotnet clean -c Release -v m +########################################################################### +# EXECUTION +########################################################################### -Write-Host ">> dotnet build -c Release -v m;" -ForegroundColor Magenta; -dotnet build -c Release -v m; +function ExecSafe([scriptblock] $cmd) { + & $cmd -if($LastExitCode -ne 0){ - throw "Build failed" + if((Get-Variable -Name "LASTEXITCODE" -Scope Global -ErrorAction SilentlyContinue) -ne $null) { + if ($LASTEXITCODE) { + exit $LASTEXITCODE + } + } } -#Run the test project generators -Push-Location -Path $testGenerator -StackName "Run"; -Write-Host ">> dotnet run -c Release -v m;" -ForegroundColor Magenta; -dotnet run -c Release -v m --framework netcoreapp2.2; -Pop-Location -StackName "Run"; - -#Build again, making sure that our clients that were just regenerated via the previous command build -Write-Host ">> dotnet build -c Release -v m;" -ForegroundColor Magenta; -dotnet build -c Release -v m; - - -if($LastExitCode -ne 0){ - throw "Build failed" +# If global.json exists, load expected version +if (Test-Path $DotNetGlobalFile) { + $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) + if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { + $DotNetVersion = $DotNetGlobal.sdk.version + } } -Write-Host ">> dotnet test -c Release -v m;" -ForegroundColor Magenta; -dotnet test -c Release -v m; - - -if($LastExitCode -ne 0){ - throw "Tests failed" +# If dotnet is installed locally, and expected version is not set or installation matches the expected version +if((Get-Variable IsWindows -ErrorAction SilentlyContinue) -eq $null){ + $DotNetSuffix = "win"; + $DotNetExtension = ".exe"; + $DotNetInstallUrl = $DotNetInstallUrlPowershell; + $DotNetInstallExtension = "ps1"; } - -Write-Host ">> dotnet pack -c Release /p:Version=$version -o $outputDir -v m" -ForegroundColor Magenta; -dotnet pack -c Release /p:Version="$version" -o $outputDir -v m - - -if($LastExitCode -ne 0){ - throw "Pack failed" +else{ + $DotNetSuffix = if($IsWindows -eq $false -or $IsWindows -eq $null){"unix"} else{"win"}; + $DotNetExtension = if($IsWindows -eq $false -or $IsWindows -eq $null){""} else{".exe"}; + $DotNetInstallUrl = if($IsWindows -eq $false -or $IsWindows -eq $null){$DotNetInstallUrlBash} else{$DotNetInstallUrlPowershell}; + $DotNetInstallExtension = if($IsWindows -eq $false -or $IsWindows -eq $null){"sh"} else{"ps1"}; } -Write-Host "Remove Client.cs files so we can regenerate them with the msbuild task to make that works" -ForegroundColor Magenta; -Get-ChildItem -Path "**/Clients.cs" -Recurse | Remove-Item -Force - -#Remove the generator from the solution so we don't have two references to it's assembly which causes the generator to fail. - -dotnet sln remove "test/AspNetCore.Client.Test.Generator/AspNetCore.Client.Test.Generator.csproj" -dotnet sln remove "src/AspNetCore.Client.Generator/AspNetCore.Client.Generator.csproj" - -try{ - Write-Host ">> dotnet clean -c Release -v m" -ForegroundColor Magenta; - dotnet clean -c Release -v m - - #Build again, making sure our build task works - Write-Host ">> dotnet build -c Release -v m /p:GenerateWithNuget=true" -ForegroundColor Magenta; - dotnet build -c Release /p:GenerateWithNuget=true; +$DotNetDirectory = "$TempDirectory\dotnet-$DotNetSuffix" +$DotNetVersionDirectory = "$DotNetDirectory\sdk\$DotNetVersion" +$env:DOTNET_EXE = "$DotNetDirectory\dotnet$DotNetExtension" - if($LastExitCode -ne 0){ - throw "Build failed" - } +if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` + (!(Test-Path variable:DotNetVersion) -or "$(& dotnet --version | Select-Object -First 1)" -eq "$DotNetVersion")) { - Write-Host ">> dotnet test -c Release -v m /p:GenerateWithNuget=true" -ForegroundColor Magenta; - dotnet test -c Release; + Write-Host "Existing dotnet install discovered"; - if($LastExitCode -ne 0){ - throw "Tests failed" - } + (dotnet --list-sdks) | Out-Host + $env:DOTNET_EXE = (Get-Command "dotnet").Path } -catch{ - throw $_ -} -finally{ - #Readd them, for locals - dotnet sln add "test/AspNetCore.Client.Test.Generator/AspNetCore.Client.Test.Generator.csproj" - dotnet sln add "src/AspNetCore.Client.Generator/AspNetCore.Client.Generator.csproj" +else{ + if(!(Test-Path $DotNetVersionDirectory)){ + # Download install script + $DotNetInstallFile = "$TempDirectory\dotnet-install.$DotNetInstallExtension" + New-Item -ItemType Directory -Force -Path $TempDirectory | Out-Null + (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) + + # Install by channel or version + if (!(Test-Path variable:DotNetVersion)) { + if($DotNetInstallExtension -eq "ps1"){ + ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } + } + elseif ($DotNetInstallExtension -eq "sh"){ + ExecSafe { & "$DotNetInstallFile" --install-dir "$DotNetDirectory" --channel "$DotNetChannel" --no-path } + } + else{ + throw "Unknown install extension"; + } + } else { + if($DotNetInstallExtension -eq "ps1"){ + ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } + } + elseif ($DotNetInstallExtension -eq "sh"){ + ExecSafe { & "$DotNetInstallFile" --install-dir "$DotNetDirectory" --version "$DotNetVersion" --no-path } + } + else{ + throw "Unknown install extension"; + } + } + } } +$env:PATH += $DotNetDirectory; +$env:DOTNET_ROOT = $DotNetDirectory; +Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" +#Output a bunch of diag info +Write-Host "[DotNetVersion] = $DotNetVersion"; +Write-Host "[DotNetSuffix] = $DotNetSuffix"; +Write-Host "[DotNetExtension] = $DotNetExtension"; +Write-Host "[DotNetInstallUrl] = $DotNetInstallUrl"; +Write-Host "[DotNetInstallExtension] = $DotNetInstallExtension"; +Write-Host "[DotNetDirectory] = $DotNetDirectory"; +Write-Host "[DotNetVersionDirectory] = $DotNetVersionDirectory"; +Write-Host "[env:DOTNET_EXE] = $env:DOTNET_EXE"; +Write-Host "[env:DOTNET_ROOT] = $env:DOTNET_ROOT"; +Write-Host "[env:PATH] = $env:PATH"; + +try{ + ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile -c Release /nodeReuse:false } + ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile -c Release --no-build -- $BuildArguments } +}finally{ + ExecSafe { & $env:DOTNET_EXE build-server shutdown --msbuild --vbcscompiler} +} diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..a020512 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,33 @@ + + + latest + false + false + true + true + + + + + Copyright 2020 + Beffyman + git + https://github.com/Beffyman/AspNetCore.Client + false + LICENSE.txt + snupkg + true + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..c1a0dc7 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1 @@ +mode: ContinuousDeployment \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index e5083db..86e4ef0 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Beffyman +Copyright (c) 2020 Beffyman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index 0c1532a..0000000 --- a/NuGet.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index d1bdc8d..8dac77b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# AspNetCore.Client -[![AppVeyor](https://ci.appveyor.com/api/projects/status/984mqqfnwytd3oga?svg=true)](https://ci.appveyor.com/project/Beffyman/aspnetcore-client) +# Beffyman.AspNetCore.Client +[![Build Status](https://dev.azure.com/beffyman/Beffyman.Github/_apis/build/status/Beffyman.AspNetCore.Client?branchName=master)](https://dev.azure.com/beffyman/Beffyman.Github/_build/latest?definitionId=7&branchName=master) --- If you are anything like me, you look at @@ -17,7 +17,7 @@ and think the following - How can I pool the HttpClient usage? - HttpClient is injected! Which allows you to control it's lifecycle - Yuck, hard coded routes, these can lead to issues if my endpoint is still under development. - - Generated On Build! AspNetCore.Client.Generator is a before compile build task that will generate clients inside the project that implements it. + - Generated On Build! Beffyman.AspNetCore.Client.Generator is a before compile build task that will generate clients inside the project that implements it. - How do I unit test this without spinning up a full web app? - Works with Microsoft.AspNetCore.TestHost! - CancellationTokens are not respected inside the TestServer without some hacks though *(registering a kill of the server)* due to the Token not being checked. @@ -29,27 +29,34 @@ and think the following - What if sometimes I want to intercept requests before they go out? - IHttpOverride! Which allows for potential cache interception of requests. - If I own the endpoint's code, why can't I just generate clients from it to make interacting with it as simple as injecting it? - - Introducing AspNetCore.Client.Generator! + - Introducing Beffyman.AspNetCore.Client.Generator! [First Time Setup](https://github.com/Beffyman/AspNetCore.Client/wiki/First-Time-Setup) ## Supported Frameworks -- AspNetCore 2.2 HTTP Controllers -- AspNetCore 2.2 SignalR Hubs -- Http Trigger Azure Functions v2 +- AspNetCore 3.1 HTTP Controllers +- AspNetCore 3.1 SignalR Hubs +- AspNetCore 3.1 Blazor Client Side +- Http Trigger Azure Functions v3 -## AspNetCore.Client -[![NuGet](https://img.shields.io/nuget/v/AspNetCore.Client.svg)](https://www.nuget.org/packages/AspNetCore.Client) +## Beffyman.AspNetCore.Client +[![NuGet](https://img.shields.io/nuget/v/Beffyman.AspNetCore.Client.svg)](https://www.nuget.org/packages/Beffyman.AspNetCore.Client) Includes ServiceCollection registration logic, used on the Client -## AspNetCore.Server -[![NuGet](https://img.shields.io/nuget/v/AspNetCore.Server.svg)](https://www.nuget.org/packages/AspNetCore.Server) +## Beffyman.AspNetCore.Client.Generator +[![NuGet](https://img.shields.io/nuget/v/Beffyman.AspNetCore.Client.Generator.svg)](https://www.nuget.org/packages/Beffyman.AspNetCore.Client.Generator) + +On Build generator that will generate a Clients.cs file based on the Properties in the csproj. + + +## Beffyman.AspNetCore.Server +[![NuGet](https://img.shields.io/nuget/v/Beffyman.AspNetCore.Server.svg)](https://www.nuget.org/packages/Beffyman.AspNetCore.Server) Includes attributes that can affect generation, used on your AspNetCore api app -## AspNetCore.Client.Protobuf -[![NuGet](https://img.shields.io/nuget/v/AspNetCore.Client.Protobuf.svg)](https://www.nuget.org/packages/AspNetCore.Client.Protobuf) +## Beffyman.AspNetCore.Client.Protobuf +[![NuGet](https://img.shields.io/nuget/v/Beffyman.AspNetCore.Client.Protobuf.svg)](https://www.nuget.org/packages/Beffyman.AspNetCore.Client.Protobuf) Contains a protobuf serializer which can override the default json one via the UseProtobufSerlaizer on the ClientConfiguration. @@ -64,11 +71,13 @@ services.AddTestWebClients(config=> ``` -## AspNetCore.Client.MessagePack -[![NuGet](https://img.shields.io/nuget/v/AspNetCore.Client.MessagePack.svg)](https://www.nuget.org/packages/AspNetCore.Client.MessagePack) +## Beffyman.AspNetCore.Client.MessagePack +[![NuGet](https://img.shields.io/nuget/v/Beffyman.AspNetCore.Client.MessagePack.svg)](https://www.nuget.org/packages/Beffyman.AspNetCore.Client.MessagePack) Contains a MessagePack serializer which can override the default json one via the UseMessagePackSerializer on the ClientConfiguration. +Requires version 1.7.3.7 at the moment due to https://github.com/dotnet/aspnetcore/issues/18074 + ```c# services.AddTestWebClients(config=> { @@ -80,40 +89,31 @@ services.AddTestWebClients(config=> ``` -## AspNetCore.Client.BlazorJson -[![NuGet](https://img.shields.io/nuget/v/AspNetCore.Client.BlazorJson.svg)](https://www.nuget.org/packages/AspNetCore.Client.BlazorJson) +## Beffyman.AspNetCore.Client.NewtonsoftJson +[![NuGet](https://img.shields.io/nuget/v/Beffyman.AspNetCore.Client.NewtonsoftJson.svg)](https://www.nuget.org/packages/Beffyman.AspNetCore.Client.NewtonsoftJson) -Contains a blazor simpleJson serializer which can override the default json one via the UseBlazorSimpleJsonSerlaizer on the ClientConfiguration. +Contains a Newtonsoft Json serializer which can override the default json one via the UseNewtonsoftJsonHttpSerializer on the ClientConfiguration. ```c# -services.AddTestBlazorClients(config=> +services.AddTestWebClients(config=> { - config.UseBlazorSimpleJsonSerlaizer() - .UseBlazorSimpleJsonDeserlaizer() - .WithJsonBody() - .UseExistingHttpClient(); + config.UseNewtonsoftJsonHttpSerializer() + .UseNewtonsoftJsonHttpDeserializer() + .WithJsonBody(); }); ``` -## AspNetCore.Client.JSInterop -[![NuGet](https://img.shields.io/nuget/v/AspNetCore.Client.JSInterop.svg)](https://www.nuget.org/packages/AspNetCore.Client.JSInterop) +## Beffyman.AspNetCore.Client.Http +[![NuGet](https://img.shields.io/nuget/v/Beffyman.AspNetCore.Client.Http.svg)](https://www.nuget.org/packages/Beffyman.AspNetCore.Client.Http) -Contains a preview js interop simpleJson serializer which can override the default json one via the UseJSInteropJsonSerializer on the ClientConfiguration. +Uses Microsoft.Extensions.Http to inject a client factory. This allows for better reuse of the underlying HttpMessageHandler. ```c# -services.AddTestRazorComponentsClients(config=> +services.AddTestWebClients(config=> { - config.UseJSInteropJsonSerializer() - .UseJSInteropJsonDeserializer() - .WithJsonBody(); + config.UseHttpClientFactory(); }); -``` - - -## AspNetCore.Client.Generator -[![NuGet](https://img.shields.io/nuget/v/AspNetCore.Client.Generator.svg)](https://www.nuget.org/packages/AspNetCore.Client.Generator) - -On Build generator that will generate a Clients.cs file based on the Properties in the csproj. +``` \ No newline at end of file diff --git a/after.AspNetCore.Client.sln.targets b/after.AspNetCore.Client.sln.targets deleted file mode 100644 index cd5bcd7..0000000 --- a/after.AspNetCore.Client.sln.targets +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index ecf68be..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,44 +0,0 @@ -version: '0.19.1+{build}' -configuration: Release -image: Visual Studio 2017 - -clone_depth: 1 - -nuget: - account_feed: true - project_feed: true - disable_publish_on_pr: true - -dotnet_csproj: - patch: true - file: '**\*.csproj' - version: '{version}' - -install: -- ps: dotnet restore -s "https://api.nuget.org/v3/index.json" --verbosity m - -after_build: -- ps: ./Build.ps1 - -artifacts: - - path: '**\*.nupkg' - -deploy: - - provider: NuGet - api_key: - secure: /94KlcfrZ7Y+6+XcDU62slu43dotBG3MMvpH0yk0LqTLdQWaed62SGVB2sQD+K4p - skip_symbols: false - artifact: /.*\.nupkg/ - on: - branch: master - appveyor_repo_tag: true - - - provider: GitHub - artifact: /.*\.nupkg/ # upload all NuGet packages to release assets - draft: false - prerelease: false - auth_token: - secure: ciT780F14J207Y8r947Il6TBZQG8xJfvYqOZaU+EsBhDzzzN/sdtETKju59U6hbs - on: - branch: master # release from master branch only - appveyor_repo_tag: true \ No newline at end of file diff --git a/artifacts/.include b/artifacts/.include deleted file mode 100644 index e69de29..0000000 diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..c7ce0f1 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,68 @@ +# ASP.NET Core +# Build and test ASP.NET Core projects targeting .NET Core. +# Add steps that run tests, create a NuGet package, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core + +trigger: +- master +- refs/tags/* +- feature/* + +pr: + autoCancel: true + branches: + include: + - master + paths: + include: + - src/* + - tests/* + +pool: + vmImage: 'windows-latest' + +variables: + buildConfiguration: 'Release' + +steps: +- task: UseDotNet@2 + inputs: + installationPath: "$(Build.SourcesDirectory)/.tmp/dotnet-win" + packageType: 'sdk' + useGlobalJson: true + +#https://github.com/dotnet/cli/issues/6589 +- task: PowerShell@2 + displayName: 'Run CI' + inputs: + targetType: filePath + filePath: Build.ps1 + arguments: -target CI + failOnStderr: true + workingDirectory: $(Build.SourcesDirectory) + env: + DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR: "$(Build.SourcesDirectory)/.tmp/dotnet-win" + DOTNET_MSBUILD_SDK_RESOLVER_SDKS_DIR: "$(Build.SourcesDirectory)/.tmp/dotnet-win" + DOTNET_ROOT: "$(Build.SourcesDirectory)/.tmp/dotnet-win" + +- task: PublishTestResults@2 + displayName: 'Publish Test results' + inputs: + testResultsFormat: 'VSTest' + testResultsFiles: '*.trx' + searchFolder: 'artifacts/tests' + condition: always() + +- task: PublishCodeCoverageResults@1 + displayName: 'Publish Code Coverage' + inputs: + codeCoverageTool: cobertura + summaryFileLocation: artifacts/tests/coverage.cobertura.xml + reportDirectory: artifacts/tests/Reports + failIfCoverageEmpty: true + +- task: PublishBuildArtifacts@1 + displayName: 'Publish Packages' + inputs: + pathtoPublish: 'artifacts/nuget' + artifactName: 'Nuget' \ No newline at end of file diff --git a/build/.editorconfig b/build/.editorconfig new file mode 100644 index 0000000..94be682 --- /dev/null +++ b/build/.editorconfig @@ -0,0 +1,10 @@ +[*.cs] +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning +dotnet_style_require_accessibility_modifiers = never:warning + +csharp_style_expression_bodied_properties = true:warning +csharp_style_expression_bodied_indexers = true:warning +csharp_style_expression_bodied_accessors = true:warning diff --git a/build/BuildScripts.cs b/build/BuildScripts.cs new file mode 100644 index 0000000..2e8133a --- /dev/null +++ b/build/BuildScripts.cs @@ -0,0 +1,172 @@ +using System; +using System.Linq; +using Nuke.Common; +using Nuke.Common.CI; +using Nuke.Common.CI.AzurePipelines; +using Nuke.Common.Execution; +using Nuke.Common.Git; +using Nuke.Common.ProjectModel; +using Nuke.Common.Tooling; +using Nuke.Common.Tools.DotNet; +using Nuke.Common.Tools.GitVersion; +using Nuke.Common.Utilities.Collections; +using static Nuke.Common.EnvironmentInfo; +using static Nuke.Common.IO.FileSystemTasks; +using static Nuke.Common.IO.PathConstruction; +using static Nuke.Common.Tools.DotNet.DotNetTasks; + +[CheckBuildProjectConfigurations(TimeoutInMilliseconds = 5000)] +[UnsetVisualStudioEnvironmentVariables] +public class BuildScripts : NukeBuild +{ + public static int Main() => Execute(x => x.Build); + + [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] + readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; + + [Solution] readonly Solution Solution; + [GitRepository] readonly GitRepository GitRepository; + [GitVersion] readonly GitVersion GitVersion; + [CI] readonly AzurePipelines AzurePipelines; + + const string SourceFolder = "src"; + const string TestsFolder = "tests"; + + AbsolutePath SourceDirectory => RootDirectory / SourceFolder; + AbsolutePath TestsDirectory => RootDirectory / TestsFolder; + AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; + AbsolutePath TestArtifactsDirectory => ArtifactsDirectory / "tests"; + AbsolutePath NugetDirectory => ArtifactsDirectory / "nuget"; + AbsolutePath CodeCoverageReportOutput => TestArtifactsDirectory / "Reports"; + AbsolutePath CodeCoverageFile => TestArtifactsDirectory / "coverage.cobertura.xml"; + + + RelativePath TestGeneratorProject => (RelativePath)TestsFolder / "Beffyman.AspNetCore.Client.Test.Generator" / "Beffyman.AspNetCore.Client.Test.Generator.csproj"; + RelativePath GeneratorProject => (RelativePath)SourceFolder / "Beffyman.AspNetCore.Client.Generator" / "Beffyman.AspNetCore.Client.Generator.csproj"; + + const string TestGeneratorFramework = "netcoreapp3.1"; + + private void CleanArtifacts(bool packages = true) + { + SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); + TestsDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); + + if (packages) + { + EnsureCleanDirectory(ArtifactsDirectory); + } + } + + Target Clean => _ => _ + .Before(Restore) + .Executes(() => + { + CleanArtifacts(); + }); + + Target Restore => _ => _ + .Executes(() => + { + DotNetRestore(s => s + .SetProjectFile(Solution)); + }); + + Target Build => _ => _ + .DependsOn(Restore) + .Executes(() => + { + DotNetBuild(s => s + .SetProjectFile(Solution) + .SetConfiguration(Configuration) + .SetAssemblyVersion(GitVersion.AssemblySemVer) + .SetFileVersion(GitVersion.AssemblySemFileVer) + .SetInformationalVersion(GitVersion.InformationalVersion) + .EnableNoRestore()); + }); + + private void RunTests() + { + DotNetTest(s => s.SetConfiguration(Configuration) + .EnableNoBuild() + .EnableNoRestore() + .SetLogger("trx") + .SetResultsDirectory(TestArtifactsDirectory) + .SetLogOutput(true) + .SetArgumentConfigurator(arguments => arguments.Add("/p:CollectCoverage={0}", "true") + .Add("/p:CoverletOutput={0}/", TestArtifactsDirectory) + //.Add("/p:Threshold={0}", 90) + .Add("/p:Exclude=\"[xunit*]*%2c[*.Tests]*\"") + .Add("/p:UseSourceLink={0}", "true") + .Add("/p:CoverletOutputFormat={0}", "cobertura")) + .SetProjectFile(Solution)); + + FileExists(CodeCoverageFile); + } + + Target Test => _ => _ + .DependsOn(Build) + .Executes(() => + { + RunTests(); + }); + + + Target Pack => _ => _ + .DependsOn(Build) + .After(Test) + .Executes(() => + { + DotNetPack(s => s.SetProject(Solution) + .SetVersion(GitVersion.NuGetVersionV2) + .SetConfiguration(Configuration) + .SetAssemblyVersion(GitVersion.AssemblySemVer) + .SetFileVersion(GitVersion.AssemblySemFileVer) + .SetInformationalVersion(GitVersion.InformationalVersion) + .SetOutputDirectory(NugetDirectory)); + }); + + Target GenerateTestProjectClients => _ => _ + .DependsOn(Build) + .Before(Test) + .Executes(() => + { + DotNetRun(s => s.SetProjectFile(TestGeneratorProject) + .SetConfiguration(Configuration) + .SetFramework(TestGeneratorFramework) + .EnableNoBuild()); + }); + + Target BuildWithGenerator => _ => _ + .After(Pack) + .Executes(() => + { + CleanArtifacts(false); + + DotNetBuild(s => s + .SetProjectFile(Solution) + .SetConfiguration(Configuration) + .SetAssemblyVersion(GitVersion.AssemblySemVer) + .SetFileVersion(GitVersion.AssemblySemFileVer) + .SetInformationalVersion(GitVersion.InformationalVersion) + .EnableNoCache() + .AddProperty("GenerateWithNuget", "true") + .AddProperty("GeneratorVersion", GitVersion.NuGetVersionV2) + .AddSources(NugetDirectory)); + + RunTests(); + }); + + Target CI => _ => _ + .DependsOn(Clean) + .DependsOn(Build) + .DependsOn(GenerateTestProjectClients) + .DependsOn(Test) + .DependsOn(Pack) + .DependsOn(BuildWithGenerator) + .Executes(() => + { + AzurePipelines?.UpdateBuildNumber(GitVersion.NuGetVersionV2); + }); + + +} diff --git a/build/_build.csproj b/build/_build.csproj new file mode 100644 index 0000000..3a0d154 --- /dev/null +++ b/build/_build.csproj @@ -0,0 +1,40 @@ + + + + Exe + netcoreapp3.0 + false + + False + CS0649;CS0169 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/_build.csproj.DotSettings b/build/_build.csproj.DotSettings new file mode 100644 index 0000000..96e392e --- /dev/null +++ b/build/_build.csproj.DotSettings @@ -0,0 +1,23 @@ + + False + Implicit + Implicit + ExpressionBody + 0 + NEXT_LINE + True + False + 120 + IF_OWNER_IS_SINGLE_LINE + WRAP_IF_LONG + False + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True + True + True + True + True diff --git a/global.json b/global.json index 224a5e8..27eb78f 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.2.102" + "version": "3.1.100" } } \ No newline at end of file diff --git a/src/AspNetCore.Client.BlazorJson/AspNetCore.Client.BlazorJson.csproj b/src/AspNetCore.Client.BlazorJson/AspNetCore.Client.BlazorJson.csproj deleted file mode 100644 index 91fe1b6..0000000 --- a/src/AspNetCore.Client.BlazorJson/AspNetCore.Client.BlazorJson.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - bin\$(Configuration)\$(TargetFramework)\AspNetCore.Client.BlazorJson.xml - - - - Copyright 2018 - Beffyman - Contains Blazor SimpleJson serializer for AspNetCore.Client.Generator clients - git - https://github.com/Beffyman/AspNetCore.Client - false - - true - true - LICENSE.txt - - - - - - - - - - - - - - diff --git a/src/AspNetCore.Client.BlazorJson/BlazorJsonInstaller.cs b/src/AspNetCore.Client.BlazorJson/BlazorJsonInstaller.cs deleted file mode 100644 index ad9968d..0000000 --- a/src/AspNetCore.Client.BlazorJson/BlazorJsonInstaller.cs +++ /dev/null @@ -1,29 +0,0 @@ -using AspNetCore.Client.Serializers; - -namespace AspNetCore.Client -{ - /// - /// Static extension class for the AspnetCore.Client.BlazorJson library - /// - public static class BlazorJsonInstaller - { - /// - /// Uses to serialize requests - /// - /// - public static ClientConfiguration UseBlazorSimpleJsonSerializer(this ClientConfiguration config) - { - return config.UseSerializer(); - } - - /// - /// Uses to deserialize requests when Json is detected - /// - /// - /// - public static ClientConfiguration UseBlazorSimpleJsonDeserializer(this ClientConfiguration config) - { - return config.UseDeserializer(); - } - } -} diff --git a/src/AspNetCore.Client.BlazorJson/BlazorSimpleJsonSerializer.cs b/src/AspNetCore.Client.BlazorJson/BlazorSimpleJsonSerializer.cs deleted file mode 100644 index ae2d513..0000000 --- a/src/AspNetCore.Client.BlazorJson/BlazorSimpleJsonSerializer.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Threading.Tasks; -using Microsoft.JSInterop; - -namespace AspNetCore.Client.Serializers -{ - /// - /// Uses Blazor's SimpleJson for serializing and deserializing the http content - /// - internal class BlazorSimpleJsonSerializer : IHttpContentSerializer - { - internal static readonly string CONTENT_TYPE = "application/json"; - internal static readonly string PROBLEM_TYPE = "application/problem+json"; - public string[] ContentTypes => new string[] { CONTENT_TYPE, PROBLEM_TYPE }; - - private static readonly IDictionary> _knownJsonPrimitives = new Dictionary> - { - { typeof(char), (_)=> char.Parse(_) }, - { typeof(byte), (_)=> byte.Parse(_) }, - { typeof(sbyte), (_)=> sbyte.Parse(_) }, - { typeof(ushort), (_)=> ushort.Parse(_) }, - { typeof(int), (_)=> int.Parse(_) }, - { typeof(uint), (_)=> uint.Parse(_) }, - { typeof(long), (_)=> long.Parse(_) }, - { typeof(ulong), (_)=> ulong.Parse(_) }, - { typeof(float), (_)=> float.Parse(_) }, - { typeof(double), (_)=> double.Parse(_) }, - { typeof(string), (_)=> _.TrimStart('"').TrimEnd('"') }, - { typeof(bool), (_)=> bool.Parse(_) }, - { typeof(DateTime), (_)=> DateTime.Parse(_.TrimStart('"').TrimEnd('"')) }, - { typeof(DateTimeOffset), (_)=> DateTime.Parse(_.TrimStart('"').TrimEnd('"')) }, - { typeof(Guid), (_)=> Guid.Parse(_.TrimStart('"').TrimEnd('"')) }, - }; - - - /// - /// Deserializes the request content which is assumed to be simpleJson into a object of - /// - /// - /// - /// - public async Task Deserialize(HttpContent content) - { - if (_knownJsonPrimitives.ContainsKey(typeof(T))) - { - return (T)_knownJsonPrimitives[typeof(T)](await content.ReadAsStringAsync().ConfigureAwait(false)); - } - else - { - //Can't use the same stream reading as AspNetCore.Client.Serializers.JsonHttpSerializer because Blazor's json doesn't expose those AFAIK - var str = await content.ReadAsStringAsync().ConfigureAwait(false); - return Json.Deserialize(str); - } - } - - /// - /// Serializes the request into a StringContent with a json media type, but serialized with SimpleJson - /// - /// - /// - /// - public HttpContent Serialize(T request) - { - //Can't use the same stream writing as AspNetCore.Client.Serializers.JsonHttpSerializer because Blazor's json doesn't expose those AFAIK - - var json = Json.Serialize(request); - return new StringContent(json, Encoding.UTF8, CONTENT_TYPE); - } - } -} diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCore.Client.Generator.Framework.csproj b/src/AspNetCore.Client.Generator.Framework/AspNetCore.Client.Generator.Framework.csproj deleted file mode 100644 index 468796e..0000000 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCore.Client.Generator.Framework.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - bin\$(Configuration)\$(TargetFramework)\AspNetCore.Client.Generator.Framework.xml - false - - - - - - - - - - - - diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/HostJson.cs b/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/HostJson.cs deleted file mode 100644 index 3ac66c6..0000000 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/HostJson.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions -{ - public class HostJson - { - public Http http { get; set; } - } - - public class Http - { - public string routePrefix { get; set; } - } - -} diff --git a/src/AspNetCore.Client.Generator.Framework/RequestModifiers/IRequestModifier.cs b/src/AspNetCore.Client.Generator.Framework/RequestModifiers/IRequestModifier.cs deleted file mode 100644 index 336b3ac..0000000 --- a/src/AspNetCore.Client.Generator.Framework/RequestModifiers/IRequestModifier.cs +++ /dev/null @@ -1,12 +0,0 @@ -using AspNetCore.Client.Generator.Framework.Navigation; - -namespace AspNetCore.Client.Generator.Framework.RequestModifiers -{ - /// - /// Applies a Flurl modification to the request - /// - public interface IRequestModifier : INavNode - { - - } -} diff --git a/src/AspNetCore.Client.Generator/Json/HostJsonFile.cs b/src/AspNetCore.Client.Generator/Json/HostJsonFile.cs deleted file mode 100644 index cd7bb4f..0000000 --- a/src/AspNetCore.Client.Generator/Json/HostJsonFile.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Newtonsoft.Json; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; - -namespace AspNetCore.Client.Generator.Json -{ - public class HostJsonFile - { - public HostJson Data { get; } - - private readonly static JsonSerializerSettings _settings = new JsonSerializerSettings - { - MissingMemberHandling = MissingMemberHandling.Ignore - }; - - public HostJsonFile(string filePath) - { - var fileText = Helpers.SafelyReadFromFile(filePath); - - - Data = JsonConvert.DeserializeObject(fileText, _settings); - } - } - - - -} diff --git a/src/AspNetCore.Client.JSInterop/AspNetCore.Client.JSInterop.csproj b/src/AspNetCore.Client.JSInterop/AspNetCore.Client.JSInterop.csproj deleted file mode 100644 index e606881..0000000 --- a/src/AspNetCore.Client.JSInterop/AspNetCore.Client.JSInterop.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - bin\$(Configuration)\$(TargetFramework)\AspNetCore.Client.BlazorJson.xml - - - - Copyright 2018 - Beffyman - Contains Microsoft.JSInterop SimpleJson serializer for AspNetCore.Client.Generator clients - git - https://github.com/Beffyman/AspNetCore.Client - false - - true - true - LICENSE.txt - - - - - - - - - - - - - - - diff --git a/src/AspNetCore.Client.JSInterop/JSInteropInstaller.cs b/src/AspNetCore.Client.JSInterop/JSInteropInstaller.cs deleted file mode 100644 index 37f21ac..0000000 --- a/src/AspNetCore.Client.JSInterop/JSInteropInstaller.cs +++ /dev/null @@ -1,29 +0,0 @@ -using AspNetCore.Client.Serializers; - -namespace AspNetCore.Client -{ - /// - /// Static extension class for the AspNetCore.Client.JSInterop library - /// - public static class JSInteropInstaller - { - /// - /// Uses to serialize requests - /// - /// - public static ClientConfiguration UseJSInteropJsonSerializer(this ClientConfiguration config) - { - return config.UseSerializer(); - } - - /// - /// Uses to deserialize requests when Json is detected - /// - /// - /// - public static ClientConfiguration UseJSInteropJsonDeserializer(this ClientConfiguration config) - { - return config.UseDeserializer(); - } - } -} diff --git a/src/AspNetCore.Client.MessagePack/AspNetCore.Client.MessagePack.csproj b/src/AspNetCore.Client.MessagePack/AspNetCore.Client.MessagePack.csproj deleted file mode 100644 index af663bd..0000000 --- a/src/AspNetCore.Client.MessagePack/AspNetCore.Client.MessagePack.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - bin\$(Configuration)\$(TargetFramework)\AspNetCore.Client.MessagePack.xml - - - - Copyright 2018 - Beffyman - Contains MessagePack serializer for AspNetCore.Client.Generator clients - git - https://github.com/Beffyman/AspNetCore.Client - false - - true - true - LICENSE.txt - - - - - - - - - - - - - - - - diff --git a/src/AspNetCore.Client.Protobuf/AspNetCore.Client.Protobuf.csproj b/src/AspNetCore.Client.Protobuf/AspNetCore.Client.Protobuf.csproj deleted file mode 100644 index cd9907b..0000000 --- a/src/AspNetCore.Client.Protobuf/AspNetCore.Client.Protobuf.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - bin\$(Configuration)\$(TargetFramework)\AspNetCore.Client.Protobuf.xml - - - - Copyright 2018 - Beffyman - Contains protobuf serializer for AspNetCore.Client.Generator clients - git - https://github.com/Beffyman/AspNetCore.Client - false - - true - true - LICENSE.txt - - - - - - - - - - - - - - - diff --git a/src/AspNetCore.Client/.editorconfig b/src/AspNetCore.Client/.editorconfig deleted file mode 100644 index d4c1808..0000000 --- a/src/AspNetCore.Client/.editorconfig +++ /dev/null @@ -1,75 +0,0 @@ -# You can modify the rules from these initially generated values to suit your own policies -# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference -[*.cs] - -#Core editorconfig formatting - indentation - -#use hard tabs for indentation -indent_style = tab - -#Formatting - new line options - -#require braces to be on a new line for types, methods, object_collection, control_blocks, and lambdas (also known as "Allman" style) -csharp_new_line_before_open_brace = types, methods, object_collection, control_blocks, lambdas - -#Formatting - organize using options - -#sort System.* using directives alphabetically, and place them before other usings -dotnet_sort_system_directives_first = true - -#Formatting - spacing options - -#require NO space between a cast and the value -csharp_space_after_cast = false -#require a space before the colon for bases or interfaces in a type declaration -csharp_space_after_colon_in_inheritance_clause = true -#require a space before the colon for bases or interfaces in a type declaration -csharp_space_before_colon_in_inheritance_clause = true -#remove space within empty argument list parentheses -csharp_space_between_method_call_empty_parameter_list_parentheses = false -#remove space between method call name and opening parenthesis -csharp_space_between_method_call_name_and_opening_parenthesis = false -#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call -csharp_space_between_method_call_parameter_list_parentheses = false -#remove space within empty parameter list parentheses for a method declaration -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. -csharp_space_between_method_declaration_parameter_list_parentheses = false - -#Formatting - wrapping options - -#leave code block on single line -csharp_preserve_single_line_blocks = true - -#Style - expression bodied member options - -#prefer block bodies for constructors -csharp_style_expression_bodied_constructors = false:suggestion -#prefer block bodies for methods -csharp_style_expression_bodied_methods = false:suggestion - -#Style - expression level options - -#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them -dotnet_style_predefined_type_for_member_access = true:suggestion - -#Style - implicit and explicit types - -#prefer var is used to declare variables with built-in system types such as int -csharp_style_var_for_built_in_types = true:suggestion -#prefer var when the type is already mentioned on the right-hand side of a declaration expression -csharp_style_var_when_type_is_apparent = true:suggestion - -#Style - language keyword and framework type options - -#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion - -#Style - qualification options - -#prefer fields not to be prefaced with this. or Me. in Visual Basic -dotnet_style_qualification_for_field = false:suggestion -#prefer methods to be prefaced with this. in C# or Me. in Visual Basic -dotnet_style_qualification_for_method = true:suggestion -#prefer properties not to be prefaced with this. or Me. in Visual Basic -dotnet_style_qualification_for_property = false:suggestion diff --git a/src/AspNetCore.Client/AspNetCore.Client.csproj b/src/AspNetCore.Client/AspNetCore.Client.csproj deleted file mode 100644 index ed89745..0000000 --- a/src/AspNetCore.Client/AspNetCore.Client.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - bin\$(Configuration)\$(TargetFramework)\AspNetCore.Client.xml - - - - Copyright 2018 - Beffyman - Implements registration of clients into a ServiceCollection - git - https://github.com/Beffyman/AspNetCore.Client - false - - true - true - LICENSE.txt - - - - - - - - - - - - - - - diff --git a/src/AspNetCore.Server/AspNetCore.Server.csproj b/src/AspNetCore.Server/AspNetCore.Server.csproj deleted file mode 100644 index dcee7c3..0000000 --- a/src/AspNetCore.Server/AspNetCore.Server.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - bin\$(Configuration)\$(TargetFramework)\AspNetCore.Server.xml - - - - Copyright 2018 - Beffyman - A helper package for AspNetCore.Client.Generator that contains known attributes that affect the generation - git - https://github.com/Beffyman/AspNetCore.Client - false - - true - true - LICENSE.txt - - - - - - diff --git a/src/AspNetCore.Client.Generator/.editorconfig b/src/Beffyman.AspNetCore.Client.Generator.Framework/.editorconfig similarity index 88% rename from src/AspNetCore.Client.Generator/.editorconfig rename to src/Beffyman.AspNetCore.Client.Generator.Framework/.editorconfig index 7d5ba78..89aea01 100644 --- a/src/AspNetCore.Client.Generator/.editorconfig +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/.editorconfig @@ -1,4 +1,4 @@ -# Rules in this file were initially inferred by Visual Studio IntelliCode from the E:\Git_Github\AspNetCore.Client\src\AspNetCore.Client.BlazorJson\ codebase based on best match to current usage at 10/25/2018 +# Rules in this file were initially inferred by Visual Studio IntelliCode from the E:\Git_Github\Beffyman.AspNetCore.Client\src\Beffyman.AspNetCore.Client.BlazorJson\ codebase based on best match to current usage at 10/25/2018 # You can modify the rules from these initially generated values to suit your own policies # You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference [*.cs] diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpController.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpController.cs similarity index 93% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpController.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpController.cs index af54ffc..16b3519 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpController.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpController.cs @@ -2,15 +2,15 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; -using AspNetCore.Client.Generator.Framework.AttributeInterfaces; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; +using Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; using AspNetCore.Server.Attributes.Http; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp { /// /// Information about a group of endpoints used for generation diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpEndpoint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpEndpoint.cs similarity index 93% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpEndpoint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpEndpoint.cs index 840cc6a..44b7b72 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpEndpoint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/AspNetCoreHttpEndpoint.cs @@ -2,17 +2,17 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; -using AspNetCore.Client.Generator.Framework.AttributeInterfaces; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; +using Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; using AspNetCore.Server.Attributes.Http; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp { /// /// The information about an endpoint used for generation diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/ClientDependency.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/ClientDependency.cs similarity index 94% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/ClientDependency.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/ClientDependency.cs index 11d7eb3..90bd555 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/ClientDependency.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/ClientDependency.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies { /// /// Dependency that handles the generated client wrapper diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpOverrideDependency.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpOverrideDependency.cs similarity index 90% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpOverrideDependency.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpOverrideDependency.cs index 87016c9..12e9692 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpOverrideDependency.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpOverrideDependency.cs @@ -1,6 +1,6 @@ -using AspNetCore.Client.Http; +using Beffyman.AspNetCore.Client.Http; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies { /// /// Injection for diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpSerializerDependency.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpSerializerDependency.cs similarity index 90% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpSerializerDependency.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpSerializerDependency.cs index 9d9507d..3c03e92 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpSerializerDependency.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/HttpSerializerDependency.cs @@ -1,6 +1,6 @@ -using AspNetCore.Client.Serializers; +using Beffyman.AspNetCore.Client.Serializers; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies { /// /// Injection for diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/IDependency.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/IDependency.cs similarity index 91% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/IDependency.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/IDependency.cs index 62a66db..df660f4 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/IDependency.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/IDependency.cs @@ -1,4 +1,4 @@ -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies { /// /// Indicates that the class can be injected into the client diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/RequestModifierDependency.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/RequestModifierDependency.cs similarity index 84% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/RequestModifierDependency.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/RequestModifierDependency.cs index b030ac5..65e2f25 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/RequestModifierDependency.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Dependencies/RequestModifierDependency.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; -using AspNetCore.Client.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.RequestModifiers; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies { /// /// Injection for diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/FunctionEndpoint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/FunctionEndpoint.cs similarity index 90% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/FunctionEndpoint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/FunctionEndpoint.cs index 34635a1..ec29f06 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/FunctionEndpoint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/FunctionEndpoint.cs @@ -1,330 +1,332 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Reflection; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; -using AspNetCore.Client.Generator.Framework.AttributeInterfaces; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; -using AspNetCore.Server.Attributes.Http; - -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions -{ - /// - /// The information about an endpoint used for generation - /// - public class FunctionEndpoint : IResponseTypes, IHeaders, IIgnored, IObsolete, INavNode - { - /// - /// Name of the endpoint/controller generated from - /// - public string Name { get; set; } - - /// - /// Client name of the function - /// - public string ClientName => $"{Name}Client"; - - /// - /// Determines whether or not to have a void method depending on if it is a ActionResult return - /// - public string ReturnType { get; set; } - - /// - /// Indicates that the endpoint returns a stream and to not deserialize it - /// - public bool ReturnsStream { get; set; } - - /// - /// List of response types that can be added to the context - /// - public IList ResponseTypes { get; set; } = new List(); - - /// - /// List of headers that can be added to the context that never change in value - /// - public IList ConstantHeader { get; set; } = new List(); - - /// - /// List of headers that can be added to the context that are used as a parameter - /// - public IList ParameterHeader { get; set; } = new List(); - - /// - /// Parameters that the endpoint has, can be placed in many different locations in a request - /// - public IList Parameters { get; set; } = new List(); - - /// - /// Parameters specific to a specific http method that is expected - /// - public IDictionary> HttpParameters { get; set; } = new Dictionary>(); - - /// - /// Supported HttpMethods listed out - /// - public IList SupportedMethods { get; set; } = new List(); - - - //IRoute - - /// - /// Route required to hit the function, can be null - /// - public HttpRoute Route { get; set; } - - //IIgnored - - /// - /// Should this endpoint be ignored because it has the - /// - public bool Ignored { get; set; } - - - //IObsolete - - /// - /// Whether or not the endpoint is obsolete - /// - public bool Obsolete { get; set; } - - /// - /// Message - /// - public string ObsoleteMessage { get; set; } - - /// - /// Whether of not the controller loaded correctly - /// - public bool Failed { get; set; } - - /// - /// Unexpected error found - /// - public bool UnexpectedFailure { get; set; } - - /// - /// Expected error found - /// - public string Error { get; set; } - - /// - /// Should this endpoint be generated - /// - public bool Generated => !Ignored && !Failed; - - /// - /// Creates an function - /// - public FunctionEndpoint() - { - } - - /// - /// Retrieve all the implemented children of this node - /// - /// - public IEnumerable GetChildren() - { - return Enumerable.Empty() - .Union(ResponseTypes) - .Union(ConstantHeader) - .Union(Parameters) - .Union(ParameterHeader) - .Where(x => x != null); - } - - /// - /// Gets all of the parameters for this function for the httpMethod that is sorted - /// - /// - /// - public IEnumerable GetParametersForHttpMethod(HttpMethod method) - { - return GetChildren().OfType().Union(GetHttpParameters(method)).OrderBy(x => x.DefaultValue == null ? 0 : 1).ThenBy(x => x.SortOrder); - } - - /// - /// Gets all the parameters of the function for the httpMethod that are not response types, used for creating a Raw request - /// - /// - /// - public IEnumerable GetParametersWithoutResponseTypesForHttpMethod(HttpMethod method) - { - return GetChildren().Where(x => !(x is ResponseType) || (x is ExceptionResponseType)).OfType().Union(GetHttpParameters(method)).OrderBy(x => x.DefaultValue == null ? 0 : 1).ThenBy(x => x.SortOrder); - } - - private IEnumerable GetHttpParameters(HttpMethod method) - { - if (HttpParameters.ContainsKey(method)) - { - return HttpParameters[method]; - } - else - { - return Enumerable.Empty(); - } - } - - /// - /// Gets all of the parameters that exist inside the route - /// - /// - public IEnumerable GetRouteParameters() - { - return GetChildren().OfType().OrderBy(x => x.SortOrder); - } - - /// - /// Gets all the parameters that exist as query string in the uri - /// - /// - public IEnumerable GetQueryParameters(HttpMethod method) - { - return GetChildren().Union(GetHttpParameters(method)).OfType().OrderBy(x => x.SortOrder); - } - - - /// - /// Gets the body parameter for the method - /// - /// - public BodyParameter GetBodyParameter(HttpMethod method) - { - return GetChildren().Union(GetHttpParameters(method)).OfType().OrderBy(x => x.SortOrder).SingleOrDefault(); - } - - /// - /// Gets all headers that exist as children for the request - /// - /// - public IEnumerable
GetHeaders() - { - return GetChildren().OfType
(); - } - - /// - /// Gets all response types that are associated with the endpoint - /// - /// - public IEnumerable GetResponseTypes() - { - return GetChildren().OfType().OrderBy(x => x.SortOrder); - } - - /// - /// Gets all request modifiers that affect this endpoint - /// - /// - public IEnumerable GetRequestModifiers() - { - return GetChildren().OfType(); - } - - /// - /// Full route template for the endpoint - /// - public HttpRoute GetFullRoute() - { - return Route; - } - - /// - /// Get all route constraints associated with the endpoint, with the caller being accounted for - /// - /// - public IEnumerable GetRouteConstraints() - { - return Route?.Constraints ?? Enumerable.Empty(); - } - - /// - /// Returns a string that represents the current object under the context of the caller - /// - /// - public override string ToString() - { - return $"{Name}"; - } - - /// - /// Get the signature of the endpoint, for equality/grouping purposes - /// - /// - /// - public string GetSignature(HttpMethod method) - { - return $"{ToString()}(${string.Join(", ", GetParametersForHttpMethod(method).Select(x => x.ToString()).Where(x => !string.IsNullOrEmpty(x)))}"; - } - - - /// - /// Validates the endpoint for anything that might lead to a compile or runtime error - /// - public void Validate() - { - var duplicateResponseTypes = this.GetResponseTypes().Where(x => x.Status != null).GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList(); - - if (duplicateResponseTypes.Any()) - { - throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}"); - } - - foreach (var method in SupportedMethods) - { - var duplicateParameters = this.GetParametersWithoutResponseTypesForHttpMethod(method).GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); - - if (duplicateParameters.Any()) - { - throw new NotSupportedException($"Endpoint has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); - } - } - - - - var duplicateHeaders = this.GetHeaders().GroupBy(x => x.Key).Where(x => x.Count() > 1).ToList(); - - if (duplicateHeaders.Any()) - { - throw new NotSupportedException($"Endpoint has multiple headers of the same key defined. {string.Join(", ", duplicateHeaders.Select(x => x.Key?.ToString()))}"); - } - } - - private static IEnumerable _allDependencies = typeof(IDependency).GetTypeInfo().Assembly - .GetTypes() - .Where(x => typeof(IDependency).IsAssignableFrom(x) && !x.GetTypeInfo().IsAbstract) - .ToList(); - - /// - /// Gets all of the injectable properties for the clients generated - /// - /// - public IEnumerable GetInjectionDependencies() - { - return _allDependencies.Where(x => x != typeof(ClientDependency)).Select(x => Activator.CreateInstance(x) as IDependency); - } - - /// - /// Gets the method's name, if there is only 1 method, use the base name - /// - /// - /// - public string GetEndpointName(HttpMethod method, bool raw, bool async) - { - if (SupportedMethods.Count > 1) - { - return $"{Name}{(raw ? "Raw" : string.Empty)}_{method.Method.ToUpper()}{(async ? "Async" : string.Empty)}"; - } - else - { - return $"{Name}{(raw ? "Raw" : string.Empty)}{(async ? "Async" : string.Empty)}"; - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Reflection; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; +using Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; +using AspNetCore.Server.Attributes.Http; + +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions +{ + /// + /// The information about an endpoint used for generation + /// + public class FunctionEndpoint : IResponseTypes, IHeaders, IIgnored, IObsolete, INavNode + { + /// + /// Name of the endpoint/controller generated from + /// + public string Name { get; set; } + + /// + /// Client name of the function + /// + public string ClientName => $"{Name}Client"; + + /// + /// Determines whether or not to have a void method depending on if it is a ActionResult return + /// + public string ReturnType { get; set; } + + /// + /// Indicates that the endpoint returns a stream and to not deserialize it + /// + public bool ReturnsStream { get; set; } + + /// + /// List of response types that can be added to the context + /// + public IList ResponseTypes { get; set; } = new List(); + + /// + /// List of headers that can be added to the context that never change in value + /// + public IList ConstantHeader { get; set; } = new List(); + + /// + /// List of headers that can be added to the context that are used as a parameter + /// + public IList ParameterHeader { get; set; } = new List(); + + /// + /// Parameters that the endpoint has, can be placed in many different locations in a request + /// + public IList Parameters { get; set; } = new List(); + + /// + /// Parameters specific to a specific http method that is expected + /// + public IDictionary> HttpParameters { get; set; } = new Dictionary>(); + + /// + /// Supported HttpMethods listed out + /// + public IList SupportedMethods { get; set; } = new List(); + + + //IRoute + + /// + /// Route required to hit the function, can be null + /// + public HttpRoute Route { get; set; } + + //IIgnored + + /// + /// Should this endpoint be ignored because it has the + /// + public bool Ignored { get; set; } + + + //IObsolete + + /// + /// Whether or not the endpoint is obsolete + /// + public bool Obsolete { get; set; } + + /// + /// Message + /// + public string ObsoleteMessage { get; set; } + + /// + /// Whether of not the controller loaded correctly + /// + public bool Failed { get; set; } + + /// + /// Unexpected error found + /// + public bool UnexpectedFailure { get; set; } + + /// + /// Expected error found + /// + public string Error { get; set; } + + /// + /// Should this endpoint be generated + /// + public bool Generated => !Ignored && !Failed; + + /// + /// Creates an function + /// + public FunctionEndpoint() + { + } + + /// + /// Retrieve all the implemented children of this node + /// + /// + public IEnumerable GetChildren() + { + return Enumerable.Empty() + .Union(ResponseTypes) + .Union(ConstantHeader) + .Union(Parameters) + .Union(ParameterHeader) + .Where(x => x != null); + } + + /// + /// Gets all of the parameters for this function for the httpMethod that is sorted + /// + /// + /// + public IEnumerable GetParametersForHttpMethod(HttpMethod method) + { + return GetChildren().OfType().Union(GetHttpParameters(method)).OrderBy(x => x.DefaultValue == null ? 0 : 1).ThenBy(x => x.SortOrder); + } + + /// + /// Gets all the parameters of the function for the httpMethod that are not response types, used for creating a Raw request + /// + /// + /// + public IEnumerable GetParametersWithoutResponseTypesForHttpMethod(HttpMethod method) + { + return GetChildren().Where(x => !(x is ResponseType) || (x is ExceptionResponseType)).OfType().Union(GetHttpParameters(method)).OrderBy(x => x.DefaultValue == null ? 0 : 1).ThenBy(x => x.SortOrder); + } + + private IEnumerable GetHttpParameters(HttpMethod method) + { + if (HttpParameters.ContainsKey(method)) + { + return HttpParameters[method]; + } + else + { + return Enumerable.Empty(); + } + } + + /// + /// Gets all of the parameters that exist inside the route + /// + /// + public IEnumerable GetRouteParameters() + { + return GetChildren().OfType().OrderBy(x => x.SortOrder); + } + + /// + /// Gets all the parameters that exist as query string in the uri + /// + /// + public IEnumerable GetQueryParameters(HttpMethod method) + { + return GetChildren().Union(GetHttpParameters(method)).OfType().OrderBy(x => x.SortOrder); + } + + + /// + /// Gets the body parameter for the method + /// + /// + public BodyParameter GetBodyParameter(HttpMethod method) + { + return GetChildren().Union(GetHttpParameters(method)).OfType().OrderBy(x => x.SortOrder).SingleOrDefault(); + } + + /// + /// Gets all headers that exist as children for the request + /// + /// + public IEnumerable
GetHeaders() + { + return GetChildren().OfType
(); + } + + /// + /// Gets all response types that are associated with the endpoint + /// + /// + public IEnumerable GetResponseTypes() + { + return GetChildren().OfType().OrderBy(x => x.SortOrder); + } + + /// + /// Gets all request modifiers that affect this endpoint + /// + /// + public IEnumerable GetRequestModifiers() + { + return GetChildren().OfType(); + } + + /// + /// Full route template for the endpoint + /// + public HttpRoute GetFullRoute() + { + return Route; + } + + /// + /// Get all route constraints associated with the endpoint, with the caller being accounted for + /// + /// + public IEnumerable GetRouteConstraints() + { + return Route?.Constraints ?? Enumerable.Empty(); + } + + /// + /// Returns a string that represents the current object under the context of the caller + /// + /// + public override string ToString() + { + return $"{Name}"; + } + + /// + /// Get the signature of the endpoint, for equality/grouping purposes + /// + /// + /// + public string GetSignature(HttpMethod method) + { + return $"{ToString()}(${string.Join(", ", GetParametersForHttpMethod(method).Select(x => x.ToString()).Where(x => !string.IsNullOrEmpty(x)))}"; + } + + + /// + /// Validates the endpoint for anything that might lead to a compile or runtime error + /// + public void Validate() + { + var duplicateResponseTypes = this.GetResponseTypes().Where(x => x.Status != null).GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList(); + + if (duplicateResponseTypes.Any()) + { + throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}"); + } + + foreach (var method in SupportedMethods) + { + var duplicateParameters = this.GetParametersWithoutResponseTypesForHttpMethod(method).GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); + + if (duplicateParameters.Any()) + { + throw new NotSupportedException($"Endpoint has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); + } + } + + + + var duplicateHeaders = this.GetHeaders().GroupBy(x => x.Key).Where(x => x.Count() > 1).ToList(); + + if (duplicateHeaders.Any()) + { + throw new NotSupportedException($"Endpoint has multiple headers of the same key defined. {string.Join(", ", duplicateHeaders.Select(x => x.Key?.ToString()))}"); + } + } + + private static IEnumerable _allDependencies = typeof(IDependency).GetTypeInfo().Assembly + .GetTypes() + .Where(x => typeof(IDependency).IsAssignableFrom(x) && !x.GetTypeInfo().IsAbstract) + .ToList(); + + /// + /// Gets all of the injectable properties for the clients generated + /// + /// + public IEnumerable GetInjectionDependencies() + { + return _allDependencies.Where(x => x != typeof(ClientDependency)).Select(x => Activator.CreateInstance(x) as IDependency); + } + + /// + /// Gets the method's name, if there is only 1 method, use the base name + /// + /// + /// + /// + /// + public string GetEndpointName(HttpMethod method, bool raw, bool async) + { + if (SupportedMethods.Count > 1) + { + return $"{Name}{(raw ? "Raw" : string.Empty)}_{method.Method.ToUpper()}{(async ? "Async" : string.Empty)}"; + } + else + { + return $"{Name}{(raw ? "Raw" : string.Empty)}{(async ? "Async" : string.Empty)}"; + } + } + } +} diff --git a/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/HostJson.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/HostJson.cs new file mode 100644 index 0000000..81abcb5 --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Functions/HostJson.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions +{ + /// + /// Root Json object for the host.json + /// + public class HostJson + { + /// + /// Http section + /// + public Http http { get; set; } + } + + /// + /// Http section + /// + public class Http + { + /// + /// Prefix for http requests + /// + public string routePrefix { get; set; } + } + +} diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ConstantHeader.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ConstantHeader.cs similarity index 86% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ConstantHeader.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ConstantHeader.cs index 7c19fb5..e799271 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ConstantHeader.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ConstantHeader.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; using AspNetCore.Server.Attributes.Http; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers { /// /// Handles diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/Header.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/Header.cs similarity index 73% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/Header.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/Header.cs index 6c60feb..bd25fe8 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/Header.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/Header.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers { /// /// Contains basic info about a header diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/IHeaders.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/IHeaders.cs similarity index 86% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/IHeaders.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/IHeaders.cs index c2b5358..a4cdd28 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/IHeaders.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/IHeaders.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers { /// /// Detailing that this component contains headers that can be added diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ParameterHeader.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ParameterHeader.cs similarity index 90% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ParameterHeader.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ParameterHeader.cs index b0445ce..fa92be9 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ParameterHeader.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Headers/ParameterHeader.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; using AspNetCore.Server.Attributes.Http; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers { /// /// Handles diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/BodyParameter.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/BodyParameter.cs similarity index 90% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/BodyParameter.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/BodyParameter.cs index e0ef13f..1ea57d9 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/BodyParameter.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/BodyParameter.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters { /// /// Parameter for the endpoint parameter that is placed in the request body diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/QueryParameter.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/QueryParameter.cs similarity index 85% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/QueryParameter.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/QueryParameter.cs index f6b942e..90206ac 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/QueryParameter.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/QueryParameter.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters { /// /// Parameter for the endpoint parameter that is placed in the uri as a query parameter @@ -33,8 +33,15 @@ public class QueryParameter : IParameter /// public int SortOrder => 3; + /// + /// Whether or not the Type is empty + /// public bool IsConstant => string.IsNullOrEmpty(Type); + /// + /// Default Constructor + /// + /// public QueryParameter(string value) { Name = value; diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/RouteParameter.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/RouteParameter.cs similarity index 90% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/RouteParameter.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/RouteParameter.cs index 7b71680..9c3ae90 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/RouteParameter.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Parameters/RouteParameter.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters { /// /// Parameter for the endpoint parameter that is placed in the uri as a route parameter diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/CookieModifier.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/CookieModifier.cs similarity index 84% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/CookieModifier.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/CookieModifier.cs index 206b30b..937fb1b 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/CookieModifier.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/CookieModifier.cs @@ -1,10 +1,10 @@ using System.Collections; using System.Collections.Generic; using System.Net; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers { /// /// Parameter for the additional cookies for the request diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/FunctionAuthModifier.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/FunctionAuthModifier.cs similarity index 77% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/FunctionAuthModifier.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/FunctionAuthModifier.cs index a2da548..be42000 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/FunctionAuthModifier.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/FunctionAuthModifier.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; using System.Text; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers { + /// + /// Detects whether a authkey was provided for the function + /// public class FunctionAuthModifier : IRequestModifier, IParameter { diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/HeadersModifier.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/HeadersModifier.cs similarity index 84% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/HeadersModifier.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/HeadersModifier.cs index 064d371..0e4daa4 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/HeadersModifier.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/HeadersModifier.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers { /// /// Parameter for the additional headers for the request diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/SecurityModifier.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/SecurityModifier.cs similarity index 80% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/SecurityModifier.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/SecurityModifier.cs index 3ced934..301371b 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/SecurityModifier.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/SecurityModifier.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; -using AspNetCore.Client.Authorization; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Authorization; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers { /// /// Parameter for the security information diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/TimeoutModifier.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/TimeoutModifier.cs similarity index 83% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/TimeoutModifier.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/TimeoutModifier.cs index d86fc46..77b0c60 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/TimeoutModifier.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/RequestModifiers/TimeoutModifier.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers { /// /// Parameter for the timeout of the request diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ExceptionResponseType.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ExceptionResponseType.cs similarity index 93% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ExceptionResponseType.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ExceptionResponseType.cs index 25b073c..ac0b136 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ExceptionResponseType.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ExceptionResponseType.cs @@ -2,7 +2,7 @@ using System.Net; using Flurl.Http; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes { /// /// Used for the callback for when the http call throws a FlurlHttpException meaning the request didn't get a proper response diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/IResponseTypes.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/IResponseTypes.cs similarity index 79% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/IResponseTypes.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/IResponseTypes.cs index 35f9ed6..85c8a0d 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/IResponseTypes.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/IResponseTypes.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes { /// /// Detailing that this component contains response types that can be added diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ResponseType.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ResponseType.cs similarity index 95% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ResponseType.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ResponseType.cs index aeb3a23..cd37a54 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ResponseType.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/ResponseTypes/ResponseType.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Net; using System.Net.Http; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes { /// /// Handles mapping ProducesResponseType to a suitable call diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/ApiVersion.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/ApiVersion.cs similarity index 65% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/ApiVersion.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/ApiVersion.cs index 1a19b6d..a4baede 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/ApiVersion.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/ApiVersion.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes { /// /// Representation of an API version @@ -19,12 +19,21 @@ public class ApiVersion /// public bool Query { get; set; } + /// + /// Default Constructor + /// + /// + /// public ApiVersion(string version, bool query) { Version = version; Query = query; } + /// + /// ToString + /// + /// public override string ToString() { return Version; diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/AlphaConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/AlphaConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/AlphaConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/AlphaConstraint.cs index 516abe6..2fc5b2a 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/AlphaConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/AlphaConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// String must consist of one or more alphabetical characters (a-z, case-insensitive) diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/ApiVersionContraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/ApiVersionContraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/ApiVersionContraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/ApiVersionContraint.cs index 33e77d0..0a7f4ac 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/ApiVersionContraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/ApiVersionContraint.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// RouteConstraint for apiVersioning diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/BoolConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/BoolConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/BoolConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/BoolConstraint.cs index 9b5cf13..2278117 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/BoolConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/BoolConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Matches true or false (case-insensitive) diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DateTimeConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DateTimeConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DateTimeConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DateTimeConstraint.cs index 3fb50bc..d179dab 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DateTimeConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DateTimeConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Matches a valid DateTime value (in the invariant culture - see warning) diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DecimalConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DecimalConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DecimalConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DecimalConstraint.cs index 32fefc2..378c4ce 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DecimalConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/DecimalConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Matches a valid decimal value (in the invariant culture - see warning) diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/FloatConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/FloatConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/FloatConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/FloatConstraint.cs index 782f44b..cb95bae 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/FloatConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/FloatConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Matches a valid float value (in the invariant culture - see warning) diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/GuidConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/GuidConstraint.cs similarity index 87% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/GuidConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/GuidConstraint.cs index c634dc6..e42ee18 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/GuidConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/GuidConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Matches a valid Guid value diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/IntConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/IntConstraint.cs similarity index 87% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/IntConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/IntConstraint.cs index e849f64..9a5adb4 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/IntConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/IntConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Matches any integer diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LengthConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LengthConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LengthConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LengthConstraint.cs index fa91ca7..36daa54 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LengthConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LengthConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// String must be exactly x characters long diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LongConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LongConstraint.cs similarity index 87% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LongConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LongConstraint.cs index d8c4089..15f3c5e 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LongConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/LongConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Matches a valid long value diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxConstraint.cs index 3e28d16..8a5bcce 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Integer value must be no more than x diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxLengthConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxLengthConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxLengthConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxLengthConstraint.cs index d4e4e9b..00d8302 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxLengthConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MaxLengthConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// String must be no more than x characters diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinConstraint.cs similarity index 87% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinConstraint.cs index 38ea527..61b8b46 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Integer value must be at least x diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinLengthConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinLengthConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinLengthConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinLengthConstraint.cs index e40bfcc..c5a42bb 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinLengthConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/MinLengthConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// String must be at least x characters diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RangeConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RangeConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RangeConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RangeConstraint.cs index e35d901..3d86b2b 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RangeConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RangeConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Integer value must be at least x but no more than y diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RegexConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RegexConstraint.cs similarity index 91% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RegexConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RegexConstraint.cs index 45a86a0..2e0dc20 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RegexConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RegexConstraint.cs @@ -1,7 +1,7 @@ using System; using System.Text.RegularExpressions; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// String must match the regular expression diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RequiredConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RequiredConstraint.cs similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RequiredConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RequiredConstraint.cs index a6bdfc1..eccaff0 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RequiredConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RequiredConstraint.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Used to enforce that a non-parameter value is present during URL generation diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RouteConstraint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RouteConstraint.cs similarity index 95% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RouteConstraint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RouteConstraint.cs index 0975791..4d466e1 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RouteConstraint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/Constraints/RouteConstraint.cs @@ -4,7 +4,7 @@ using System.Reflection; using System.Text.RegularExpressions; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints { /// /// Base route constraint implementation diff --git a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/HttpRoute.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/HttpRoute.cs similarity index 84% rename from src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/HttpRoute.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/HttpRoute.cs index b911168..e16fa64 100644 --- a/src/AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/HttpRoute.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AspNetCoreHttp/Routes/HttpRoute.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; using Microsoft.AspNetCore.Routing.Template; -namespace AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes +namespace Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes { /// /// Route details @@ -64,6 +64,10 @@ public HttpRoute(string value) return parameters; } + /// + /// Gets the parameters for the route + /// + /// public IDictionary GetRouteParameters() { IDictionary parameters = new Dictionary(); @@ -99,11 +103,20 @@ public HttpRoute Merge(HttpRoute route) }; } + /// + /// Does the Route contain the provided string? + /// + /// + /// public bool Contains(string str) { return Value?.Contains(str) ?? false; } + /// + /// ToString + /// + /// public override string ToString() { return Value; diff --git a/src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IAuthorize.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IAuthorize.cs similarity index 76% rename from src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IAuthorize.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IAuthorize.cs index 954051b..fb09ecf 100644 --- a/src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IAuthorize.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IAuthorize.cs @@ -1,4 +1,4 @@ -namespace AspNetCore.Client.Generator.Framework.AttributeInterfaces +namespace Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces { /// /// Determines if the endpoint requires credentials because AuthorizeAttribute on it diff --git a/src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IIgnored.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IIgnored.cs similarity index 83% rename from src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IIgnored.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IIgnored.cs index aab3951..9e22ca6 100644 --- a/src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IIgnored.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IIgnored.cs @@ -1,6 +1,6 @@ using AspNetCore.Server.Attributes.Http; -namespace AspNetCore.Client.Generator.Framework.AttributeInterfaces +namespace Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces { /// /// Determines if the endpoint should not be generated because it has the on it diff --git a/src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/INamespaceSuffix.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/INamespaceSuffix.cs similarity index 80% rename from src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/INamespaceSuffix.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/INamespaceSuffix.cs index b9bdc3c..c99b572 100644 --- a/src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/INamespaceSuffix.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/INamespaceSuffix.cs @@ -1,6 +1,6 @@ using AspNetCore.Server.Attributes; -namespace AspNetCore.Client.Generator.Framework.AttributeInterfaces +namespace Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces { /// /// This value is populated by the diff --git a/src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IObsolete.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IObsolete.cs similarity index 80% rename from src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IObsolete.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IObsolete.cs index fa98ca9..8166d10 100644 --- a/src/AspNetCore.Client.Generator.Framework/AttributeInterfaces/IObsolete.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/AttributeInterfaces/IObsolete.cs @@ -1,4 +1,4 @@ -namespace AspNetCore.Client.Generator.Framework.AttributeInterfaces +namespace Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces { /// /// This value is populated by the Obsolete attribute diff --git a/src/Beffyman.AspNetCore.Client.Generator.Framework/Beffyman.AspNetCore.Client.Generator.Framework.csproj b/src/Beffyman.AspNetCore.Client.Generator.Framework/Beffyman.AspNetCore.Client.Generator.Framework.csproj new file mode 100644 index 0000000..cb258c0 --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/Beffyman.AspNetCore.Client.Generator.Framework.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + bin\$(Configuration)\$(TargetFramework)\Beffyman.AspNetCore.Client.Generator.Framework.xml + + + + + + + + + + + diff --git a/src/AspNetCore.Client.Generator.Framework/GenerationContext.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/GenerationContext.cs similarity index 93% rename from src/AspNetCore.Client.Generator.Framework/GenerationContext.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/GenerationContext.cs index e2be417..de0b88f 100644 --- a/src/AspNetCore.Client.Generator.Framework/GenerationContext.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/GenerationContext.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; -using AspNetCore.Client.Generator.Framework.SignalR; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; +using Beffyman.AspNetCore.Client.Generator.Framework.SignalR; -namespace AspNetCore.Client.Generator.Framework +namespace Beffyman.AspNetCore.Client.Generator.Framework { /// /// Context used to keep track of generation details diff --git a/src/AspNetCore.Client.Generator.Framework/IParameter.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/IParameter.cs similarity index 87% rename from src/AspNetCore.Client.Generator.Framework/IParameter.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/IParameter.cs index bcfb8e5..886d5d1 100644 --- a/src/AspNetCore.Client.Generator.Framework/IParameter.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/IParameter.cs @@ -1,6 +1,6 @@ -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; -namespace AspNetCore.Client.Generator.Framework +namespace Beffyman.AspNetCore.Client.Generator.Framework { /// /// Indicates that the object should be placed as a parameter diff --git a/src/AspNetCore.Client.Generator.Framework/Navigation/INavNode.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/Navigation/INavNode.cs similarity index 82% rename from src/AspNetCore.Client.Generator.Framework/Navigation/INavNode.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/Navigation/INavNode.cs index 8bddd7a..396ac39 100644 --- a/src/AspNetCore.Client.Generator.Framework/Navigation/INavNode.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/Navigation/INavNode.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace AspNetCore.Client.Generator.Framework.Navigation +namespace Beffyman.AspNetCore.Client.Generator.Framework.Navigation { /// /// A node that can provide its children diff --git a/src/AspNetCore.Client.Generator.Framework/RequestModifiers/CancellationTokenModifier.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/RequestModifiers/CancellationTokenModifier.cs similarity index 89% rename from src/AspNetCore.Client.Generator.Framework/RequestModifiers/CancellationTokenModifier.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/RequestModifiers/CancellationTokenModifier.cs index a6dc4c1..e2b76f0 100644 --- a/src/AspNetCore.Client.Generator.Framework/RequestModifiers/CancellationTokenModifier.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/RequestModifiers/CancellationTokenModifier.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Threading; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; -namespace AspNetCore.Client.Generator.Framework.RequestModifiers +namespace Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers { /// /// Parameter for the cancellation token for the request diff --git a/src/Beffyman.AspNetCore.Client.Generator.Framework/RequestModifiers/IRequestModifier.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/RequestModifiers/IRequestModifier.cs new file mode 100644 index 0000000..14ea211 --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/RequestModifiers/IRequestModifier.cs @@ -0,0 +1,12 @@ +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; + +namespace Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers +{ + /// + /// Applies a Flurl modification to the request + /// + public interface IRequestModifier : INavNode + { + + } +} diff --git a/src/AspNetCore.Client.Generator.Framework/SignalR/HubController.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubController.cs similarity index 96% rename from src/AspNetCore.Client.Generator.Framework/SignalR/HubController.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubController.cs index c8c60c6..79b8b27 100644 --- a/src/AspNetCore.Client.Generator.Framework/SignalR/HubController.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubController.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using AspNetCore.Client.Generator.Framework.AttributeInterfaces; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; using AspNetCore.Server.Attributes.Http; -namespace AspNetCore.Client.Generator.Framework.SignalR +namespace Beffyman.AspNetCore.Client.Generator.Framework.SignalR { /// /// Data structure that represents a HubController and all associated properties used for generation diff --git a/src/AspNetCore.Client.Generator.Framework/SignalR/HubEndpoint.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubEndpoint.cs similarity index 93% rename from src/AspNetCore.Client.Generator.Framework/SignalR/HubEndpoint.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubEndpoint.cs index a0972a6..6eedad8 100644 --- a/src/AspNetCore.Client.Generator.Framework/SignalR/HubEndpoint.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubEndpoint.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using System.Linq; -using AspNetCore.Client.Generator.Framework.AttributeInterfaces; -using AspNetCore.Client.Generator.Framework.Navigation; -using AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; using AspNetCore.Server.Attributes.Http; -namespace AspNetCore.Client.Generator.Framework.SignalR +namespace Beffyman.AspNetCore.Client.Generator.Framework.SignalR { /// /// The information about an endpoint used for generation diff --git a/src/AspNetCore.Client.Generator.Framework/SignalR/HubParameter.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubParameter.cs similarity index 91% rename from src/AspNetCore.Client.Generator.Framework/SignalR/HubParameter.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubParameter.cs index 4fa7f82..bcb8fad 100644 --- a/src/AspNetCore.Client.Generator.Framework/SignalR/HubParameter.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/HubParameter.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; -namespace AspNetCore.Client.Generator.Framework.SignalR +namespace Beffyman.AspNetCore.Client.Generator.Framework.SignalR { /// /// Represents a parameter that can be used inside a Hub Endpoint diff --git a/src/AspNetCore.Client.Generator.Framework/SignalR/Message.cs b/src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/Message.cs similarity index 90% rename from src/AspNetCore.Client.Generator.Framework/SignalR/Message.cs rename to src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/Message.cs index d284b11..66a9f27 100644 --- a/src/AspNetCore.Client.Generator.Framework/SignalR/Message.cs +++ b/src/Beffyman.AspNetCore.Client.Generator.Framework/SignalR/Message.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace AspNetCore.Client.Generator.Framework.SignalR +namespace Beffyman.AspNetCore.Client.Generator.Framework.SignalR { /// /// Represents a message that can be fired by a HubEndpoint diff --git a/src/AspNetCore.Client.Generator.Framework/.editorconfig b/src/Beffyman.AspNetCore.Client.Generator/.editorconfig similarity index 88% rename from src/AspNetCore.Client.Generator.Framework/.editorconfig rename to src/Beffyman.AspNetCore.Client.Generator/.editorconfig index 7d5ba78..89aea01 100644 --- a/src/AspNetCore.Client.Generator.Framework/.editorconfig +++ b/src/Beffyman.AspNetCore.Client.Generator/.editorconfig @@ -1,4 +1,4 @@ -# Rules in this file were initially inferred by Visual Studio IntelliCode from the E:\Git_Github\AspNetCore.Client\src\AspNetCore.Client.BlazorJson\ codebase based on best match to current usage at 10/25/2018 +# Rules in this file were initially inferred by Visual Studio IntelliCode from the E:\Git_Github\Beffyman.AspNetCore.Client\src\Beffyman.AspNetCore.Client.BlazorJson\ codebase based on best match to current usage at 10/25/2018 # You can modify the rules from these initially generated values to suit your own policies # You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference [*.cs] diff --git a/src/AspNetCore.Client.Generator/AspNetCore.Client.Generator.csproj b/src/Beffyman.AspNetCore.Client.Generator/Beffyman.AspNetCore.Client.Generator.csproj similarity index 59% rename from src/AspNetCore.Client.Generator/AspNetCore.Client.Generator.csproj rename to src/Beffyman.AspNetCore.Client.Generator/Beffyman.AspNetCore.Client.Generator.csproj index ea8bb86..8ac5986 100644 --- a/src/AspNetCore.Client.Generator/AspNetCore.Client.Generator.csproj +++ b/src/Beffyman.AspNetCore.Client.Generator/Beffyman.AspNetCore.Client.Generator.csproj @@ -1,55 +1,37 @@  - netstandard2.0;net462 - PackageReference - true - false - true - latest - true + netstandard2.1;net472 + A build task that will generate clients from a asp.net core project true true - - - {9A19103F-16F7-4668-BE54-9A1E7A4F7556} - - - - - Copyright 2018 - Beffyman - A build task that will generate clients from a asp.net core project - git - https://github.com/Beffyman/AspNetCore.Client - false - LICENSE.txt - + true + false - + - - - - - - + + + + + + - + - + - + - - + + @@ -81,7 +63,7 @@ - true + true @@ -89,8 +71,4 @@ <_FrameworkAssemblyReferences Remove="@(_FrameworkAssemblyReferences)" /> - - - - diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedBodyParamterDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedBodyParamterDefinition.cs similarity index 94% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedBodyParamterDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedBodyParamterDefinition.cs index 8fd5d9d..8fa5973 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedBodyParamterDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedBodyParamterDefinition.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions { public class ExpectedBodyParamterDefinition { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedQueryParamterDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedQueryParamterDefinition.cs similarity index 95% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedQueryParamterDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedQueryParamterDefinition.cs index ea62cf2..2097ed4 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedQueryParamterDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/ExpectedQueryParamterDefinition.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions { public class ExpectedQueryParamterDefinition { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/FunctionsCSharpFile.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/FunctionsCSharpFile.cs similarity index 89% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/FunctionsCSharpFile.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/FunctionsCSharpFile.cs index c4f4366..6e1990e 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/FunctionsCSharpFile.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/FunctionsCSharpFile.cs @@ -1,14 +1,14 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; -using AspNetCore.Client.Generator.Framework; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; -using AspNetCore.Client.Generator.Output; +using Beffyman.AspNetCore.Client.Generator.Framework; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; +using Beffyman.AspNetCore.Client.Generator.Output; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions { public class FunctionsCSharpFile { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/HttpTriggerParameter.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/HttpTriggerParameter.cs similarity index 97% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/HttpTriggerParameter.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/HttpTriggerParameter.cs index 83db1dd..760b721 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/HttpTriggerParameter.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreFunctions/HttpTriggerParameter.cs @@ -8,7 +8,7 @@ using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions { public class HttpTriggerParameter { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ApiVersionDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ApiVersionDefinition.cs similarity index 84% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ApiVersionDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ApiVersionDefinition.cs index 2ed21d3..703cf97 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ApiVersionDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ApiVersionDefinition.cs @@ -4,7 +4,7 @@ using System.Text; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public class ApiVersionDefinition { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ClassDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ClassDefinition.cs similarity index 98% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ClassDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ClassDefinition.cs index fe35d58..8afd11a 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ClassDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ClassDefinition.cs @@ -9,7 +9,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public class ClassDefinition { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HTTPType.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HTTPType.cs similarity index 53% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HTTPType.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HTTPType.cs index 27fdedb..dc4820a 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HTTPType.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HTTPType.cs @@ -1,4 +1,4 @@ -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public enum HttpAttributeType { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HeaderDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HeaderDefinition.cs similarity index 92% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HeaderDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HeaderDefinition.cs index 60efec4..8942f5e 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HeaderDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HeaderDefinition.cs @@ -2,7 +2,7 @@ using AspNetCore.Server.Attributes.Http; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public class HeaderDefinition { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HttpControllerCSharpFile.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HttpControllerCSharpFile.cs similarity index 92% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HttpControllerCSharpFile.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HttpControllerCSharpFile.cs index 5e93d6a..09c6fed 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HttpControllerCSharpFile.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/HttpControllerCSharpFile.cs @@ -1,13 +1,13 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; -using AspNetCore.Client.Generator.Framework; -using AspNetCore.Client.Generator.Output; +using Beffyman.AspNetCore.Client.Generator.Framework; +using Beffyman.AspNetCore.Client.Generator.Output; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public class HttpControllerCSharpFile { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/MethodDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/MethodDefinition.cs similarity index 97% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/MethodDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/MethodDefinition.cs index ffe94c5..effda5f 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/MethodDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/MethodDefinition.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; using AspNetCore.Server.Attributes.Http; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public class MethodDefinition { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterDefinition.cs similarity index 93% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterDefinition.cs index b4352bb..5fb850c 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterDefinition.cs @@ -1,17 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public class ParameterDefinition { public string Name { get; } public string Type { get; } public string Default { get; } + public bool Invalid { get; } public ParameterAttributeOptions Options { get; } @@ -22,6 +23,13 @@ public ParameterDefinition(ParameterSyntax parameter, HttpRoute fullRoute) Type = parameter.Type.ToFullString().Trim(); Default = parameter.Default?.Value.ToFullString().Trim(); + if (Helpers.IsInvalidParameterType(Type)) + { + Invalid = true; + return; + } + + var attributes = parameter.AttributeLists.SelectMany(x => x.Attributes).ToList(); Options = new ParameterAttributeOptions diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterHeaderDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterHeaderDefinition.cs similarity index 96% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterHeaderDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterHeaderDefinition.cs index e455b21..ad791a6 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterHeaderDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ParameterHeaderDefinition.cs @@ -3,7 +3,7 @@ using AspNetCore.Server.Attributes.Http; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public class ParameterHeaderDefinition { diff --git a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ResponseTypeDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ResponseTypeDefinition.cs similarity index 96% rename from src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ResponseTypeDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ResponseTypeDefinition.cs index 3e45595..b6490d2 100644 --- a/src/AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ResponseTypeDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/AspNetCoreHttp/ResponseTypeDefinition.cs @@ -2,7 +2,7 @@ using System.Net; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.AspNetCoreHttp +namespace Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp { public class ResponseTypeDefinition { diff --git a/src/AspNetCore.Client.Generator/CSharp/SignalR/HubCSharpFile.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/HubCSharpFile.cs similarity index 92% rename from src/AspNetCore.Client.Generator/CSharp/SignalR/HubCSharpFile.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/HubCSharpFile.cs index 216c8bd..59a2a12 100644 --- a/src/AspNetCore.Client.Generator/CSharp/SignalR/HubCSharpFile.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/HubCSharpFile.cs @@ -1,13 +1,13 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; -using AspNetCore.Client.Generator.Framework; -using AspNetCore.Client.Generator.Output; +using Beffyman.AspNetCore.Client.Generator.Framework; +using Beffyman.AspNetCore.Client.Generator.Output; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.SignalR +namespace Beffyman.AspNetCore.Client.Generator.CSharp.SignalR { public class HubCSharpFile { diff --git a/src/AspNetCore.Client.Generator/CSharp/SignalR/HubParameterDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/HubParameterDefinition.cs similarity index 77% rename from src/AspNetCore.Client.Generator/CSharp/SignalR/HubParameterDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/HubParameterDefinition.cs index e9b976f..d992360 100644 --- a/src/AspNetCore.Client.Generator/CSharp/SignalR/HubParameterDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/HubParameterDefinition.cs @@ -1,12 +1,13 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.CSharp.SignalR +namespace Beffyman.AspNetCore.Client.Generator.CSharp.SignalR { public class HubParameterDefinition { public string Name { get; } public string Type { get; } public string Default { get; } + public bool Invalid { get; } public HubParameterDefinition(ParameterSyntax parameter) @@ -14,6 +15,11 @@ public HubParameterDefinition(ParameterSyntax parameter) Name = parameter.Identifier.ValueText.Trim(); Type = parameter.Type.ToFullString().Trim(); Default = parameter.Default?.Value.ToFullString().Trim(); + + if (Helpers.IsInvalidParameterType(Type)) + { + Invalid = true; + } } public override string ToString() diff --git a/src/AspNetCore.Client.Generator/CSharp/SignalR/MessageDefinition.cs b/src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/MessageDefinition.cs similarity index 94% rename from src/AspNetCore.Client.Generator/CSharp/SignalR/MessageDefinition.cs rename to src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/MessageDefinition.cs index 949eb8f..d7027a9 100644 --- a/src/AspNetCore.Client.Generator/CSharp/SignalR/MessageDefinition.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/CSharp/SignalR/MessageDefinition.cs @@ -2,7 +2,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace AspNetCore.Client.Generator.SignalR +namespace Beffyman.AspNetCore.Client.Generator.SignalR { public class MessageDefinition { diff --git a/src/AspNetCore.Client.Generator/Constants.cs b/src/Beffyman.AspNetCore.Client.Generator/Constants.cs similarity index 85% rename from src/AspNetCore.Client.Generator/Constants.cs rename to src/Beffyman.AspNetCore.Client.Generator/Constants.cs index 3df6a9b..dc9e7f6 100644 --- a/src/AspNetCore.Client.Generator/Constants.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/Constants.cs @@ -1,8 +1,8 @@ -using AspNetCore.Client.Http; -using AspNetCore.Client.RequestModifiers; -using AspNetCore.Client.Serializers; +using Beffyman.AspNetCore.Client.Http; +using Beffyman.AspNetCore.Client.RequestModifiers; +using Beffyman.AspNetCore.Client.Serializers; -namespace AspNetCore.Client.Generator +namespace Beffyman.AspNetCore.Client.Generator { public static class Constants { @@ -25,7 +25,7 @@ public static class Constants public const string RequestModifier = nameof(IHttpRequestModifier); public const string RequestModifierField = "Modifier"; - + public const string ApiVersionAttribute = "ApiVersionAttribute"; public const string IActionResult = "IActionResult"; public const string ControllerRouteReserved = "controller"; diff --git a/src/AspNetCore.Client.Generator/ContextIsolatedTask.cs b/src/Beffyman.AspNetCore.Client.Generator/ContextIsolatedTask.cs similarity index 98% rename from src/AspNetCore.Client.Generator/ContextIsolatedTask.cs rename to src/Beffyman.AspNetCore.Client.Generator/ContextIsolatedTask.cs index 6dac208..74e77ad 100644 --- a/src/AspNetCore.Client.Generator/ContextIsolatedTask.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/ContextIsolatedTask.cs @@ -1,5 +1,4 @@ - -#if NETSTANDARD2_0 +#if NETSTANDARD2_1 using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using System; @@ -11,7 +10,7 @@ using System.Threading; using System.Runtime.Loader; -namespace AspNetCore.Client.Generator +namespace Beffyman.AspNetCore.Client.Generator { /// /// https://github.com/AArnott/Nerdbank.MSBuildExtension @@ -194,5 +193,4 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) } } - -#endif +#endif \ No newline at end of file diff --git a/src/AspNetCore.Client.Generator/GeneratorTask.cs b/src/Beffyman.AspNetCore.Client.Generator/GeneratorTask.cs similarity index 92% rename from src/AspNetCore.Client.Generator/GeneratorTask.cs rename to src/Beffyman.AspNetCore.Client.Generator/GeneratorTask.cs index df21994..76375db 100644 --- a/src/AspNetCore.Client.Generator/GeneratorTask.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/GeneratorTask.cs @@ -2,22 +2,21 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions; -using AspNetCore.Client.Generator.CSharp.AspNetCoreHttp; -using AspNetCore.Client.Generator.CSharp.SignalR; -using AspNetCore.Client.Generator.Framework; -using AspNetCore.Client.Generator.Json; -using AspNetCore.Client.Generator.Output; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; - -namespace AspNetCore.Client.Generator +using Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions; +using Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp; +using Beffyman.AspNetCore.Client.Generator.CSharp.SignalR; +using Beffyman.AspNetCore.Client.Generator.Framework; +using Beffyman.AspNetCore.Client.Generator.Json; +using Beffyman.AspNetCore.Client.Generator.Output; + +namespace Beffyman.AspNetCore.Client.Generator { public class GeneratorTask : -#if NET462 +#if NET472 Microsoft.Build.Utilities.Task #endif -#if NETSTANDARD2_0 +#if NETSTANDARD2_1 ContextIsolatedTask #endif { @@ -57,31 +56,28 @@ public void Fill(IDictionary properties) public bool ByPassExecute() { -#if NET462 +#if NET472 return Execute(); #endif -#if NETSTANDARD2_0 +#if NETSTANDARD2_1 return ExecuteIsolated(); #endif - } -#if NET462 +#if NET472 public override bool Execute() #endif -#if NETSTANDARD2_0 +#if NETSTANDARD2_1 protected override bool ExecuteIsolated() #endif - { Log.LogCommandLine($">> [{typeof(GeneratorTask).Namespace}] START"); #if !DEBUG try { - #endif #region Settings Map diff --git a/src/AspNetCore.Client.Generator/Helpers.cs b/src/Beffyman.AspNetCore.Client.Generator/Helpers.cs similarity index 87% rename from src/AspNetCore.Client.Generator/Helpers.cs rename to src/Beffyman.AspNetCore.Client.Generator/Helpers.cs index 987a216..0edc716 100644 --- a/src/AspNetCore.Client.Generator/Helpers.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/Helpers.cs @@ -8,17 +8,17 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using AspNetCore.Client.Generator.CSharp.AspNetCoreHttp; -using AspNetCore.Client.Generator.Framework; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; -using AspNetCore.Client.Generator.Framework.Navigation; +using Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp; +using Beffyman.AspNetCore.Client.Generator.Framework; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; +using Beffyman.AspNetCore.Client.Generator.Framework.Navigation; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing.Template; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Newtonsoft.Json; +using System.Text.Json; -namespace AspNetCore.Client.Generator +namespace Beffyman.AspNetCore.Client.Generator { internal static class Helpers { @@ -169,26 +169,25 @@ public static void SafelyWriteToFile(string path, string text) - private static readonly JsonSerializerSettings SETTINGS = new JsonSerializerSettings + private static readonly JsonSerializerOptions SETTINGS = new JsonSerializerOptions { - Formatting = Formatting.Indented, - NullValueHandling = NullValueHandling.Include, - MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead + PropertyNameCaseInsensitive = true, + WriteIndented = true }; public static string SerializeToJson(this object obj) { - return JsonConvert.SerializeObject(obj, SETTINGS); + return JsonSerializer.Serialize(obj, SETTINGS); } public static T DeserializeFromJson(this string str) { - return JsonConvert.DeserializeObject(str, SETTINGS); + return JsonSerializer.Deserialize(str, SETTINGS); } public static object DeserializeFromJson(this string str, Type t) { - return JsonConvert.DeserializeObject(str, t, SETTINGS); + return JsonSerializer.Deserialize(str, t, SETTINGS); } @@ -264,24 +263,28 @@ public static string GetEnumerableType(string type) } } - - public static string[] KnownPrimitives = new string[] + public static HashSet KnownExcludedParameters = new HashSet(StringComparer.CurrentCultureIgnoreCase) { - "char",typeof(char).Name, - "byte",typeof(byte).Name, - "sbyte",typeof(sbyte).Name, - "ushort",typeof(ushort).Name, - "int",typeof(int).Name, - "uint",typeof(uint).Name, - "long",typeof(long).Name, - "ulong",typeof(ulong).Name, - "float",typeof(float).Name, - "double",typeof(double).Name, - "string",typeof(string).Name, - "bool",typeof(bool).Name, - "DateTime",typeof(DateTime).Name, - "DateTimeOffset",typeof(DateTimeOffset).Name, - "Guid",typeof(Guid).Name, + nameof(CancellationToken) + }; + + public static HashSet KnownPrimitives = new HashSet(StringComparer.CurrentCultureIgnoreCase) + { + "char",typeof(char).Name, + "byte",typeof(byte).Name, + "sbyte",typeof(sbyte).Name, + "ushort",typeof(ushort).Name, + "int",typeof(int).Name, + "uint",typeof(uint).Name, + "long",typeof(long).Name, + "ulong",typeof(ulong).Name, + "float",typeof(float).Name, + "double",typeof(double).Name, + "string",typeof(string).Name, + "bool",typeof(bool).Name, + "DateTime",typeof(DateTime).Name, + "DateTimeOffset",typeof(DateTimeOffset).Name, + "Guid",typeof(Guid).Name, }; private static Regex NULLABLE_MATCHER = new Regex(@"((.+)\?)|(Nullable<(.+)>)"); @@ -306,7 +309,14 @@ public static bool IsRoutableType(string type) { type = ConvertFromNullable(type, out _); - return KnownPrimitives.Contains(type, StringComparer.CurrentCultureIgnoreCase); + return KnownPrimitives.Contains(type); + } + + public static bool IsInvalidParameterType(string type) + { + type = ConvertFromNullable(type, out _); + + return KnownExcludedParameters.Contains(type); } public static bool IsRouteParameter(string name, HttpRoute fullRouteTemplate) @@ -585,6 +595,12 @@ public static string GetAttributeValue(this AttributeSyntax attr) { return source.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(typeof(T).Name)); } + + public static AttributeSyntax GetAttribute(this IEnumerable source, string attributeName) + { + return source.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(attributeName)); + } + public static IEnumerable GetAttributes(this IEnumerable source) where T : Attribute { return source.Where(x => x.Name.ToFullString().MatchesAttribute(typeof(T).Name)); diff --git a/src/Beffyman.AspNetCore.Client.Generator/Json/HostJsonFile.cs b/src/Beffyman.AspNetCore.Client.Generator/Json/HostJsonFile.cs new file mode 100644 index 0000000..d20b609 --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.Generator/Json/HostJsonFile.cs @@ -0,0 +1,16 @@ +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; + +namespace Beffyman.AspNetCore.Client.Generator.Json +{ + public class HostJsonFile + { + public HostJson Data { get; } + + public HostJsonFile(string filePath) + { + var fileText = Helpers.SafelyReadFromFile(filePath); + + Data = Helpers.DeserializeFromJson(fileText); + } + } +} diff --git a/src/AspNetCore.Client.Generator/Output/ClassParser.cs b/src/Beffyman.AspNetCore.Client.Generator/Output/ClassParser.cs similarity index 92% rename from src/AspNetCore.Client.Generator/Output/ClassParser.cs rename to src/Beffyman.AspNetCore.Client.Generator/Output/ClassParser.cs index b866841..b6903cb 100644 --- a/src/AspNetCore.Client.Generator/Output/ClassParser.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/Output/ClassParser.cs @@ -1,824 +1,824 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Text.RegularExpressions; -using System.Threading.Channels; -using System.Threading.Tasks; -using AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions; -using AspNetCore.Client.Generator.CSharp.AspNetCoreHttp; -using AspNetCore.Client.Generator.CSharp.SignalR; -using AspNetCore.Client.Generator.Framework; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; -using AspNetCore.Client.Generator.Framework.RequestModifiers; -using AspNetCore.Client.Generator.Framework.SignalR; -using AspNetCore.Client.Generator.SignalR; -using AspNetCore.Server.Attributes; -using AspNetCore.Server.Attributes.Functions; -using AspNetCore.Server.Attributes.Http; -using AspNetCore.Server.Attributes.SignalR; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace AspNetCore.Client.Generator.Output -{ - public static class ClassParser - { - private static readonly Regex RouteVersionRegex = new Regex(@"\/([v|V]\d+)\/"); - - #region Http - - public static AspNetCoreHttpController ReadClassAsHttpController(ClassDeclarationSyntax syntax) - { - var attributes = syntax.AttributeLists.SelectMany(x => x.Attributes).ToList(); - - - var controller = new AspNetCoreHttpController(); - try - { - controller.Name = $@"{syntax.Identifier.ValueText.Trim().Replace("Controller", "")}"; - - controller.Abstract = syntax.Modifiers.Any(x => x.Text == "abstract"); - - if (syntax.BaseList == null) - { - controller.Ignored = true; - return controller; - } - - controller.BaseClass = syntax.BaseList.Types.Where(x => x.ToFullString().Trim().EndsWith("Controller")).SingleOrDefault()?.ToFullString().Trim().Replace("Controller", ""); - - controller.Ignored = attributes.HasAttribute(); - - - var namespaceAttribute = attributes.GetAttribute(); - if (namespaceAttribute != null) - { - controller.NamespaceSuffix = namespaceAttribute.GetAttributeValue(); - } - - var routeAttribute = attributes.GetAttribute(); - if (routeAttribute != null)//Fetch route from RouteAttribute - { - controller.Route = new HttpRoute(routeAttribute.GetAttributeValue()); - } - - if (controller.Route == null && !controller.Abstract && !controller.Ignored)//No Route, invalid controller - { - controller.Ignored = true; - throw new NotSupportedException("Controller must have a route to be valid for generation."); - } - - if (controller.Route != null) - { - var match = RouteVersionRegex.Match(controller.Route.Value); - if (match.Success) - { - var group = match.Groups[1]; - controller.NamespaceVersion = group.Value.ToUpper(); - } - } - else - { - controller.Route = new HttpRoute(string.Empty); - } - - var versionAttribute = attributes.GetAttribute(); - if (versionAttribute != null) - { - var version = new ApiVersionDefinition(versionAttribute); - - controller.NamespaceVersion = $"V{version.Version.Replace(".", "_")}"; - - var versionConstraint = controller.Route.Constraints.OfType().SingleOrDefault(); - if (versionConstraint != null) - { - controller.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, false); - } - else - { - controller.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, true); - } - } - - //Response types - var responseTypes = attributes.GetAttributes(); - var responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList(); - controller.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse(x.StatusValue))).ToList(); - - - - var parameterHeaders = attributes.GetAttributes() - .Select(x => new ParameterHeaderDefinition(x)) - .ToList(); - controller.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList(); - - - - var headers = attributes.GetAttributes() - .Select(x => new HeaderDefinition(x)) - .ToList(); - controller.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList(); - - //Authorize Attribute - controller.IsSecured = attributes.HasAttribute(); - - //Obsolete Attribute - var obsoleteAttribute = attributes.GetAttribute(); - if (obsoleteAttribute != null) - { - controller.Obsolete = true; - controller.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); - } - - //Only public endpoints can be hit anyways - var methods = syntax.DescendantNodes().OfType() - .Where(x => x.Modifiers.Any(y => y.Text == "public")) - .ToList(); - controller.Endpoints = methods.Select(x => ReadMethodAsHttpEndpoint(controller, x)).ToList(); - - if (!controller.Endpoints.Any(x => !x.Ignored) - && controller.Endpoints.Count > 0 - && !controller.Abstract) - { - controller.Ignored = true; - } - } - catch (NotSupportedException nse) - { - if (controller.Ignored) - { - return controller; - } - - controller.Failed = true; - controller.Error = nse.Message; - } -#if !DEBUG - catch (Exception ex) - { - controller.Failed = true; - controller.UnexpectedFailure = true; - controller.Error = ex.ToString(); - } -#endif - return controller; - } - - - private static AspNetCoreHttpEndpoint ReadMethodAsHttpEndpoint(AspNetCoreHttpController parent, MethodDeclarationSyntax syntax) - { - var attributes = syntax.DescendantNodes().OfType().SelectMany(x => x.Attributes).ToList(); - - var endpoint = new AspNetCoreHttpEndpoint(parent); - - endpoint.Name = syntax.Identifier.ValueText.Trim(); - endpoint.FormattedName = syntax.Identifier.ValueText.CleanMethodName(); - - - endpoint.Virtual = syntax.Modifiers.Any(x => x.Text == "virtual"); - endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override"); - endpoint.New = syntax.Modifiers.Any(x => x.Text == "new"); - - - //Ignore generator attribute - endpoint.Ignored = attributes.HasAttribute(); - - - //Route Attribute - - var routeAttribute = attributes.GetAttribute(); - if (routeAttribute != null)//Fetch route from RouteAttribute - { - endpoint.Route = new HttpRoute(routeAttribute.GetAttributeValue()); - } - - - //HTTP Attribute - var knownHttpAttributes = new List - { - $"{Constants.Http}{HttpAttributeType.Delete}", - $"{Constants.Http}{HttpAttributeType.Get}", - $"{Constants.Http}{HttpAttributeType.Patch}", - $"{Constants.Http}{HttpAttributeType.Post}", - $"{Constants.Http}{HttpAttributeType.Put}", - }; - - var httpAttribute = attributes.SingleOrDefault(x => knownHttpAttributes.Any(y => x.Name.ToFullString().MatchesAttribute(y))); - if (httpAttribute == null) - { - endpoint.Ignored = true; - } - else - { - var httpType = (HttpAttributeType)Enum.Parse(typeof(HttpAttributeType), - httpAttribute.Name - .ToFullString() - .Replace(Constants.Http, "") - .Replace(Constants.Attribute, "")); - - endpoint.HttpType = Helpers.HttpMethodFromEnum(httpType); - } - - - - if (endpoint.Route == null && httpAttribute?.ArgumentList != null)//If Route was never fetched from RouteAttribute or if they used the Http(template) override - { - endpoint.Route = new HttpRoute(httpAttribute.GetAttributeValue()); - } - - //Ignore method if it doesn't have a route or http attribute - if (endpoint.Route == null && httpAttribute == null) - { - endpoint.Ignored = true; - return endpoint; - } - - if (endpoint.Route == null) - { - endpoint.Route = new HttpRoute(string.Empty); - } - - var versionAttribute = attributes.GetAttribute(); - if (versionAttribute != null) - { - var version = new ApiVersionDefinition(versionAttribute); - - var versionConstraint = endpoint.Route.Constraints.OfType().SingleOrDefault(); - if (versionConstraint != null) - { - endpoint.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, false); - } - else - { - endpoint.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, true); - } - } - - - if (endpoint.Route.Version != null && parent.Route.Version != null) - { - throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has {nameof(ApiVersionAttribute)} on both it's method and class"); - } - - //Obsolete Attribute - var obsoleteAttribute = attributes.GetAttribute(); - if (obsoleteAttribute != null) - { - endpoint.Obsolete = true; - endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); - } - - //Authorize Attribute - endpoint.IsSecured = attributes.HasAttribute(); - - - //Response types - var responseTypes = attributes.GetAttributes(); - var responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList(); - responses.Add(new ResponseTypeDefinition(true)); - - endpoint.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse(x.StatusValue))).ToList(); - - var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList(); - - if (duplicateResponseTypes.Any()) - { - throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}"); - } - //Add after so we don't get duplicate error from the null Status - endpoint.ResponseTypes.Add(new ExceptionResponseType()); - - - - - var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute(parent))).ToList(); - - - var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList(); - var queryParams = parameters.Where(x => x.Options.FromQuery).Select(x => new QueryParameter(x.Options.QueryName, x.Type, x.Default, x.Options.QueryObject)).ToList(); - var bodyParam = parameters.Where(x => x.Options.FromBody).Select(x => new BodyParameter(x.Name, x.Type, x.Default)).SingleOrDefault(); - - - endpoint.Parameters = routeParams.Cast().Union(queryParams).Union(new List { bodyParam }).NotNull().ToList(); - - endpoint.Parameters.Add(new CancellationTokenModifier()); - endpoint.Parameters.Add(new CookieModifier()); - endpoint.Parameters.Add(new HeadersModifier()); - endpoint.Parameters.Add(new TimeoutModifier()); - if (endpoint.IsSecured) - { - endpoint.Parameters.Add(new SecurityModifier()); - } - - - var parameterHeaders = attributes.GetAttributes() - .Select(x => new ParameterHeaderDefinition(x)) - .ToList(); - endpoint.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList(); - - - - var headers = attributes.GetAttributes() - .Select(x => new HeaderDefinition(x)) - .ToList(); - endpoint.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList(); - - - var rawReturnType = syntax.ReturnType?.ToFullString(); - - var returnType = Helpers.GetTypeFromString(rawReturnType.Trim()); - - while (returnType.IsContainerReturnType()) - { - returnType = returnType.Arguments.SingleOrDefault(); - } - - if (Helpers.IsType(typeof(IActionResult).FullName, returnType?.Name)) - { - returnType = null; - } - - if (returnType?.Name == "void" - || (Helpers.IsType(typeof(Task).FullName, returnType?.Name) && (!returnType?.Arguments.Any() ?? false))) - { - returnType = null; - } - - if (returnType.IsFileReturnType()) - { - returnType = new Helpers.TypeString(typeof(Stream).FullName); - endpoint.ReturnsStream = true; - } - - rawReturnType = returnType?.ToString(); - - endpoint.ReturnType = rawReturnType?.Trim(); - - var okStatus = endpoint.ResponseTypes.SingleOrDefault(x => x.Status == HttpStatusCode.OK); - - if (okStatus != null - && endpoint.ReturnType != null - && Helpers.IsType(okStatus.ActionType, endpoint.ReturnType)) - { - //Remove the OkStatus since it is the same as the method return - endpoint.ResponseTypes.Remove(okStatus); - } - else if (okStatus != null - && endpoint.ReturnType != null - && !Helpers.IsType(okStatus.ActionType, endpoint.ReturnType)) - { - throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has a OK response type of {okStatus.ActionType}, but the method return {endpoint.ReturnType}"); - } - - var duplicateParameters = endpoint.GetParametersWithoutResponseTypes().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); - - if (duplicateParameters.Any()) - { - throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); - } - - var invalidParameters = endpoint.GetParameters().Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList(); - - if (invalidParameters.Any()) - { - throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}"); - } - - var fullRoute = endpoint.GetFullRoute(parent); - if (fullRoute?.Version?.Query ?? false) - { - endpoint.Parameters.Add(new QueryParameter($"api-version={fullRoute?.Version}")); - } - - - return endpoint; - } - - #endregion Http - - - #region SignalR - - public static HubController ReadClassAsHubController(ClassDeclarationSyntax syntax) - { - var attributes = syntax.AttributeLists.SelectMany(x => x.Attributes).ToList(); - - var controller = new HubController(); - try - { - controller.Name = $@"{syntax.Identifier.ValueText.Trim().Replace("Hub", "")}"; - - controller.Abstract = syntax.Modifiers.Any(x => x.Text == "abstract"); - - if (syntax.BaseList == null) - { - controller.Ignored = true; - return controller; - } - - var generatedAttribute = attributes.GetAttribute(); - if (generatedAttribute == null) - { - controller.Ignored = true; - return controller; - } - - controller.BaseClass = syntax.BaseList.Types.Where(x => x.ToFullString().Trim().EndsWith("Hub")).SingleOrDefault()?.ToFullString().Trim().Replace("Hub", ""); - - controller.Ignored = attributes.HasAttribute(); - - - var namespaceAttribute = attributes.GetAttribute(); - if (namespaceAttribute != null) - { - controller.NamespaceSuffix = namespaceAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", ""); - } - - var routeAttribute = attributes.GetAttribute(); - if (routeAttribute != null)//Fetch route from RouteAttribute - { - controller.Route = routeAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", ""); - } - - if (controller.Route == null && !controller.Abstract && !controller.Ignored)//No Route, invalid controller - { - controller.Ignored = true; - throw new NotSupportedException("Controller must have a route to be valid for generation."); - } - - if (controller.Route != null) - { - var match = RouteVersionRegex.Match(controller.Route); - if (match.Success) - { - var group = match.Groups[1]; - controller.NamespaceVersion = group.Value.ToUpper(); - } - } - - //Obsolete Attribute - var obsoleteAttribute = attributes.GetAttribute(); - if (obsoleteAttribute != null) - { - controller.Obsolete = true; - controller.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); - } - - //Only public endpoints can be hit anyways - var methods = syntax.DescendantNodes().OfType() - .Where(x => x.Modifiers.Any(y => y.Text == "public")) - .ToList(); - controller.Endpoints = methods.Select(x => ReadMethodAsHubEndpoint(controller, x)).ToList(); - - if (!controller.Endpoints.Any(x => !x.Ignored)) - { - controller.Ignored = true; - } - } - catch (NotSupportedException nse) - { - if (controller.Ignored) - { - return controller; - } - - controller.Failed = true; - controller.Error = nse.Message; - } -#if !DEBUG - catch (Exception ex) - { - controller.Failed = true; - controller.UnexpectedFailure = true; - controller.Error = ex.ToString(); - } -#endif - return controller; - } - - - - private static HubEndpoint ReadMethodAsHubEndpoint(HubController parent, MethodDeclarationSyntax syntax) - { - var attributes = syntax.DescendantNodes().OfType().SelectMany(x => x.Attributes).ToList(); - - var endpoint = new HubEndpoint(parent); - - endpoint.Name = syntax.Identifier.ValueText.CleanMethodName(); - - - endpoint.Virtual = syntax.Modifiers.Any(x => x.Text == "virtual"); - endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override"); - endpoint.New = syntax.Modifiers.Any(x => x.Text == "new"); - - - //Ignore generator attribute - endpoint.Ignored = attributes.HasAttribute(); - - //Obsolete Attribute - var obsoleteAttribute = attributes.GetAttribute(); - if (obsoleteAttribute != null) - { - endpoint.Obsolete = true; - endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); - } - - //Response types - var messageAttributes = attributes.GetAttributes(); - var messages = messageAttributes.Select(x => new MessageDefinition(x)).ToList(); - - endpoint.Messages = messages.Select(x => new Message(x.Name, x.Types)).ToList(); - - var duplicateMessages = endpoint.Messages.GroupBy(x => x.Name).Where(x => x.Count() > 1 && !x.All(y => y.Types.SequenceEqual(x.First().Types))).ToList(); - - if (duplicateMessages.Any()) - { - throw new NotSupportedException($"Hub has the same message with different parameters defined on different endpoints. {string.Join(", ", duplicateMessages.Select(x => x.Key?.ToString()))}"); - } - - - - - var parameters = syntax.ParameterList.Parameters.Select(x => new HubParameterDefinition(x)).ToList(); - var hubParams = parameters.Select(x => new HubParameter(x.Name, x.Type, x.Default)).ToList(); - - endpoint.Parameters = hubParams.Cast().NotNull().ToList(); - - var duplicateParameters = endpoint.GetParameters().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); - - if (duplicateParameters.Any()) - { - throw new NotSupportedException($"Endpoint has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); - } - - var invalidParameters = endpoint.GetParameters().Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList(); - - if (invalidParameters.Any()) - { - throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.Name} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}"); - } - - - var rawReturnType = syntax.ReturnType?.ToFullString(); - - var returnType = Helpers.GetTypeFromString(rawReturnType.Trim()); - - while (returnType.IsContainerReturnType()) - { - returnType = returnType.Arguments.SingleOrDefault(); - } - - if (Helpers.IsType(typeof(ChannelReader<>).FullName.CleanGenericTypeDefinition(), returnType?.Name)) - { - endpoint.Channel = true; - endpoint.ChannelType = returnType.Arguments.SingleOrDefault().ToString(); - } - - - return endpoint; - } - - - - - #endregion SignalR - - - #region Functions - - public static FunctionEndpoint ReadMethodAsFunction(MethodDeclarationSyntax syntax, HostJson hostData) - { - var attributes = syntax.DescendantNodes().OfType().SelectMany(x => x.Attributes).ToList(); - - var endpoint = new FunctionEndpoint(); - try - { - var endpointName = attributes.GetAttribute(); - - if (endpointName == null) - { - endpoint.Ignored = true; - return endpoint; - } - - endpoint.Name = endpointName.GetAttributeValue(); - - //Ignore generator attribute - endpoint.Ignored = attributes.HasAttribute(); - - - //Obsolete Attribute - var obsoleteAttribute = attributes.GetAttribute(); - if (obsoleteAttribute != null) - { - endpoint.Obsolete = true; - endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); - } - - //Response types - var responseTypes = attributes.GetAttributes(); - var responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList(); - responses.Add(new ResponseTypeDefinition(true)); - - endpoint.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse(x.StatusValue))).ToList(); - - var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList(); - - if (duplicateResponseTypes.Any()) - { - throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}"); - } - //Add after so we don't get duplicate error from the null Status - endpoint.ResponseTypes.Add(new ExceptionResponseType()); - - - //Need to check if the function has a HttpTrigger - var httpTriggerAttribute = syntax.ParameterList.Parameters.SingleOrDefault(x => x.AttributeLists.SelectMany(y => y.Attributes).HasAttribute()); - - if (httpTriggerAttribute == null) - { - endpoint.Ignored = true; - return endpoint; - } - - var triggerAttribute = new HttpTriggerParameter(httpTriggerAttribute); - - endpoint.SupportedMethods = triggerAttribute.Methods; - - var routePrefix = hostData?.http?.routePrefix ?? "api"; - - if (triggerAttribute.Route != null) - { - var route = triggerAttribute.Route.TrimStart('/'); - - if (!string.IsNullOrEmpty(routePrefix)) - { - if (!route.StartsWith(routePrefix)) - { - route = $"{routePrefix}/" + route; - } - - route = "/" + route; - } - - endpoint.Route = new HttpRoute(route); - } - else - { - if (!string.IsNullOrEmpty(routePrefix)) - { - endpoint.Route = new HttpRoute($"{routePrefix}/{endpoint.Name}"); - } - else - { - endpoint.Route = new HttpRoute($"{endpoint.Name}"); - } - } - - - var expectedBodyParameters = attributes.GetAttributes() - .Select(x => new ExpectedBodyParamterDefinition(x)) - .GroupBy(x => x.Method) - .ToDictionary(x => x.Key, y => y.Select(z => (IParameter)new BodyParameter("body", z.Type, null))); - - var expectedQueryParameters = attributes.GetAttributes() - .Select(x => new ExpectedQueryParamterDefinition(x)) - .GroupBy(x => x.Method) - .ToDictionary(x => x.Key, y => y.Select(z => (IParameter)new QueryParameter(z.Name, z.Type, null, z.IsQueryObject))); - - endpoint.HttpParameters = expectedBodyParameters.Union(expectedQueryParameters).ToDictionary(); - - var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute())).ToList(); - - var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList(); - - endpoint.Parameters = routeParams.Cast().NotNull().ToList(); - - endpoint.Parameters.Add(new CancellationTokenModifier()); - endpoint.Parameters.Add(new CookieModifier()); - endpoint.Parameters.Add(new HeadersModifier()); - endpoint.Parameters.Add(new TimeoutModifier()); - - if (triggerAttribute.AuthLevel == AuthorizationLevel.User) - { - if (!endpoint.ResponseTypes.Any(x => x.Status == HttpStatusCode.Unauthorized)) - { - endpoint.ResponseTypes.Add(new ResponseType(HttpStatusCode.Unauthorized)); - } - - endpoint.Parameters.Add(new SecurityModifier()); - } - else if (triggerAttribute.AuthLevel == AuthorizationLevel.Anonymous) - { - - } - else - { - if (!endpoint.ResponseTypes.Any(x => x.Status == HttpStatusCode.Unauthorized)) - { - endpoint.ResponseTypes.Add(new ResponseType(HttpStatusCode.Unauthorized)); - } - - endpoint.Parameters.Add(new FunctionAuthModifier()); - } - - - var parameterHeaders = attributes.GetAttributes() - .Select(x => new ParameterHeaderDefinition(x)) - .ToList(); - endpoint.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList(); - - - - var headers = attributes.GetAttributes() - .Select(x => new HeaderDefinition(x)) - .ToList(); - endpoint.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList(); - - - var rawReturnType = syntax.ReturnType?.ToFullString(); - - var returnType = Helpers.GetTypeFromString(rawReturnType.Trim()); - - while (returnType.IsContainerReturnType()) - { - returnType = returnType.Arguments.SingleOrDefault(); - } - - if (Helpers.IsType(typeof(IActionResult).FullName, returnType?.Name)) - { - returnType = null; - } - - if (returnType?.Name == "void" - || (Helpers.IsType(typeof(Task).FullName, returnType?.Name) && (!returnType?.Arguments.Any() ?? false))) - { - returnType = null; - } - - if (returnType.IsFileReturnType()) - { - returnType = new Helpers.TypeString(typeof(Stream).FullName); - endpoint.ReturnsStream = true; - } - - rawReturnType = returnType?.ToString(); - - endpoint.ReturnType = rawReturnType?.Trim(); - - foreach (var method in endpoint.SupportedMethods) - { - var duplicateParameters = endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); - - if (duplicateParameters.Any()) - { - throw new NotSupportedException($"Function has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); - } - - - var invalidParameters = endpoint.GetParametersForHttpMethod(method).Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList(); - - if (invalidParameters.Any()) - { - throw new NotSupportedException($"Function {endpoint.Name} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}"); - } - } - } - catch (NotSupportedException nse) - { - if (endpoint.Ignored) - { - return endpoint; - } - - endpoint.Failed = true; - endpoint.Error = nse.Message; - } -#if !DEBUG - catch (Exception ex) - { - endpoint.Failed = true; - endpoint.UnexpectedFailure = true; - endpoint.Error = ex.ToString(); - } -#endif - - - return endpoint; - } - - #endregion - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text.RegularExpressions; +using System.Threading.Channels; +using System.Threading.Tasks; +using Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreFunctions; +using Beffyman.AspNetCore.Client.Generator.CSharp.AspNetCoreHttp; +using Beffyman.AspNetCore.Client.Generator.CSharp.SignalR; +using Beffyman.AspNetCore.Client.Generator.Framework; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.SignalR; +using Beffyman.AspNetCore.Client.Generator.SignalR; +using AspNetCore.Server.Attributes; +using AspNetCore.Server.Attributes.Functions; +using AspNetCore.Server.Attributes.Http; +using AspNetCore.Server.Attributes.SignalR; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Beffyman.AspNetCore.Client.Generator.Output +{ + public static class ClassParser + { + private static readonly Regex RouteVersionRegex = new Regex(@"\/([v|V]\d+)\/"); + + #region Http + + public static AspNetCoreHttpController ReadClassAsHttpController(ClassDeclarationSyntax syntax) + { + var attributes = syntax.AttributeLists.SelectMany(x => x.Attributes).ToList(); + + + var controller = new AspNetCoreHttpController(); + try + { + controller.Name = $@"{syntax.Identifier.ValueText.Trim().Replace("Controller", "")}"; + + controller.Abstract = syntax.Modifiers.Any(x => x.Text == "abstract"); + + if (syntax.BaseList == null) + { + controller.Ignored = true; + return controller; + } + + controller.BaseClass = syntax.BaseList.Types.Where(x => x.ToFullString().Trim().EndsWith("Controller")).SingleOrDefault()?.ToFullString().Trim().Replace("Controller", ""); + + controller.Ignored = attributes.HasAttribute(); + + + var namespaceAttribute = attributes.GetAttribute(); + if (namespaceAttribute != null) + { + controller.NamespaceSuffix = namespaceAttribute.GetAttributeValue(); + } + + var routeAttribute = attributes.GetAttribute(); + if (routeAttribute != null)//Fetch route from RouteAttribute + { + controller.Route = new HttpRoute(routeAttribute.GetAttributeValue()); + } + + if (controller.Route == null && !controller.Abstract && !controller.Ignored)//No Route, invalid controller + { + controller.Ignored = true; + throw new NotSupportedException("Controller must have a route to be valid for generation."); + } + + if (controller.Route != null) + { + var match = RouteVersionRegex.Match(controller.Route.Value); + if (match.Success) + { + var group = match.Groups[1]; + controller.NamespaceVersion = group.Value.ToUpper(); + } + } + else + { + controller.Route = new HttpRoute(string.Empty); + } + + var versionAttribute = attributes.GetAttribute(Constants.ApiVersionAttribute); + if (versionAttribute != null) + { + var version = new ApiVersionDefinition(versionAttribute); + + controller.NamespaceVersion = $"V{version.Version.Replace(".", "_")}"; + + var versionConstraint = controller.Route.Constraints.OfType().SingleOrDefault(); + if (versionConstraint != null) + { + controller.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, false); + } + else + { + controller.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, true); + } + } + + //Response types + var responseTypes = attributes.GetAttributes(); + var responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList(); + controller.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse(x.StatusValue))).ToList(); + + + + var parameterHeaders = attributes.GetAttributes() + .Select(x => new ParameterHeaderDefinition(x)) + .ToList(); + controller.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList(); + + + + var headers = attributes.GetAttributes() + .Select(x => new HeaderDefinition(x)) + .ToList(); + controller.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList(); + + //Authorize Attribute + controller.IsSecured = attributes.HasAttribute(); + + //Obsolete Attribute + var obsoleteAttribute = attributes.GetAttribute(); + if (obsoleteAttribute != null) + { + controller.Obsolete = true; + controller.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); + } + + //Only public endpoints can be hit anyways + var methods = syntax.DescendantNodes().OfType() + .Where(x => x.Modifiers.Any(y => y.Text == "public")) + .ToList(); + controller.Endpoints = methods.Select(x => ReadMethodAsHttpEndpoint(controller, x)).ToList(); + + if (!controller.Endpoints.Any(x => !x.Ignored) + && controller.Endpoints.Count > 0 + && !controller.Abstract) + { + controller.Ignored = true; + } + } + catch (NotSupportedException nse) + { + if (controller.Ignored) + { + return controller; + } + + controller.Failed = true; + controller.Error = nse.Message; + } +#if !DEBUG + catch (Exception ex) + { + controller.Failed = true; + controller.UnexpectedFailure = true; + controller.Error = ex.ToString(); + } +#endif + return controller; + } + + + private static AspNetCoreHttpEndpoint ReadMethodAsHttpEndpoint(AspNetCoreHttpController parent, MethodDeclarationSyntax syntax) + { + var attributes = syntax.DescendantNodes().OfType().SelectMany(x => x.Attributes).ToList(); + + var endpoint = new AspNetCoreHttpEndpoint(parent); + + endpoint.Name = syntax.Identifier.ValueText.Trim(); + endpoint.FormattedName = syntax.Identifier.ValueText.CleanMethodName(); + + + endpoint.Virtual = syntax.Modifiers.Any(x => x.Text == "virtual"); + endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override"); + endpoint.New = syntax.Modifiers.Any(x => x.Text == "new"); + + + //Ignore generator attribute + endpoint.Ignored = attributes.HasAttribute(); + + + //Route Attribute + + var routeAttribute = attributes.GetAttribute(); + if (routeAttribute != null)//Fetch route from RouteAttribute + { + endpoint.Route = new HttpRoute(routeAttribute.GetAttributeValue()); + } + + + //HTTP Attribute + var knownHttpAttributes = new List + { + $"{Constants.Http}{HttpAttributeType.Delete}", + $"{Constants.Http}{HttpAttributeType.Get}", + $"{Constants.Http}{HttpAttributeType.Patch}", + $"{Constants.Http}{HttpAttributeType.Post}", + $"{Constants.Http}{HttpAttributeType.Put}", + }; + + var httpAttribute = attributes.SingleOrDefault(x => knownHttpAttributes.Any(y => x.Name.ToFullString().MatchesAttribute(y))); + if (httpAttribute == null) + { + endpoint.Ignored = true; + } + else + { + var httpType = (HttpAttributeType)Enum.Parse(typeof(HttpAttributeType), + httpAttribute.Name + .ToFullString() + .Replace(Constants.Http, "") + .Replace(Constants.Attribute, "")); + + endpoint.HttpType = Helpers.HttpMethodFromEnum(httpType); + } + + + + if (endpoint.Route == null && httpAttribute?.ArgumentList != null)//If Route was never fetched from RouteAttribute or if they used the Http(template) override + { + endpoint.Route = new HttpRoute(httpAttribute.GetAttributeValue()); + } + + //Ignore method if it doesn't have a route or http attribute + if (endpoint.Route == null && httpAttribute == null) + { + endpoint.Ignored = true; + return endpoint; + } + + if (endpoint.Route == null) + { + endpoint.Route = new HttpRoute(string.Empty); + } + + var versionAttribute = attributes.GetAttribute(Constants.ApiVersionAttribute); + if (versionAttribute != null) + { + var version = new ApiVersionDefinition(versionAttribute); + + var versionConstraint = endpoint.Route.Constraints.OfType().SingleOrDefault(); + if (versionConstraint != null) + { + endpoint.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, false); + } + else + { + endpoint.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, true); + } + } + + + if (endpoint.Route.Version != null && parent.Route.Version != null) + { + throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has {Constants.ApiVersionAttribute} on both it's method and class"); + } + + //Obsolete Attribute + var obsoleteAttribute = attributes.GetAttribute(); + if (obsoleteAttribute != null) + { + endpoint.Obsolete = true; + endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); + } + + //Authorize Attribute + endpoint.IsSecured = attributes.HasAttribute(); + + + //Response types + var responseTypes = attributes.GetAttributes(); + var responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList(); + responses.Add(new ResponseTypeDefinition(true)); + + endpoint.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse(x.StatusValue))).ToList(); + + var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList(); + + if (duplicateResponseTypes.Any()) + { + throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}"); + } + //Add after so we don't get duplicate error from the null Status + endpoint.ResponseTypes.Add(new ExceptionResponseType()); + + + + + var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute(parent))).Where(x => !x.Invalid).ToList(); + + + var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList(); + var queryParams = parameters.Where(x => x.Options.FromQuery).Select(x => new QueryParameter(x.Options.QueryName, x.Type, x.Default, x.Options.QueryObject)).ToList(); + var bodyParam = parameters.Where(x => x.Options.FromBody).Select(x => new BodyParameter(x.Name, x.Type, x.Default)).SingleOrDefault(); + + + endpoint.Parameters = routeParams.Cast().Union(queryParams).Union(new List { bodyParam }).NotNull().ToList(); + + endpoint.Parameters.Add(new CancellationTokenModifier()); + endpoint.Parameters.Add(new CookieModifier()); + endpoint.Parameters.Add(new HeadersModifier()); + endpoint.Parameters.Add(new TimeoutModifier()); + if (endpoint.IsSecured) + { + endpoint.Parameters.Add(new SecurityModifier()); + } + + + var parameterHeaders = attributes.GetAttributes() + .Select(x => new ParameterHeaderDefinition(x)) + .ToList(); + endpoint.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList(); + + + + var headers = attributes.GetAttributes() + .Select(x => new HeaderDefinition(x)) + .ToList(); + endpoint.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList(); + + + var rawReturnType = syntax.ReturnType?.ToFullString(); + + var returnType = Helpers.GetTypeFromString(rawReturnType.Trim()); + + while (returnType.IsContainerReturnType()) + { + returnType = returnType.Arguments.SingleOrDefault(); + } + + if (Helpers.IsType(typeof(IActionResult).FullName, returnType?.Name)) + { + returnType = null; + } + + if (returnType?.Name == "void" + || (Helpers.IsType(typeof(Task).FullName, returnType?.Name) && (!returnType?.Arguments.Any() ?? false))) + { + returnType = null; + } + + if (returnType.IsFileReturnType()) + { + returnType = new Helpers.TypeString(typeof(Stream).FullName); + endpoint.ReturnsStream = true; + } + + rawReturnType = returnType?.ToString(); + + endpoint.ReturnType = rawReturnType?.Trim(); + + var okStatus = endpoint.ResponseTypes.SingleOrDefault(x => x.Status == HttpStatusCode.OK); + + if (okStatus != null + && endpoint.ReturnType != null + && Helpers.IsType(okStatus.ActionType, endpoint.ReturnType)) + { + //Remove the OkStatus since it is the same as the method return + endpoint.ResponseTypes.Remove(okStatus); + } + else if (okStatus != null + && endpoint.ReturnType != null + && !Helpers.IsType(okStatus.ActionType, endpoint.ReturnType)) + { + throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has a OK response type of {okStatus.ActionType}, but the method return {endpoint.ReturnType}"); + } + + var duplicateParameters = endpoint.GetParametersWithoutResponseTypes().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); + + if (duplicateParameters.Any()) + { + throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); + } + + var invalidParameters = endpoint.GetParameters().Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList(); + + if (invalidParameters.Any()) + { + throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}"); + } + + var fullRoute = endpoint.GetFullRoute(parent); + if (fullRoute?.Version?.Query ?? false) + { + endpoint.Parameters.Add(new QueryParameter($"api-version={fullRoute?.Version}")); + } + + + return endpoint; + } + + #endregion Http + + + #region SignalR + + public static HubController ReadClassAsHubController(ClassDeclarationSyntax syntax) + { + var attributes = syntax.AttributeLists.SelectMany(x => x.Attributes).ToList(); + + var controller = new HubController(); + try + { + controller.Name = $@"{syntax.Identifier.ValueText.Trim().Replace("Hub", "")}"; + + controller.Abstract = syntax.Modifiers.Any(x => x.Text == "abstract"); + + if (syntax.BaseList == null) + { + controller.Ignored = true; + return controller; + } + + var generatedAttribute = attributes.GetAttribute(); + if (generatedAttribute == null) + { + controller.Ignored = true; + return controller; + } + + controller.BaseClass = syntax.BaseList.Types.Where(x => x.ToFullString().Trim().EndsWith("Hub")).SingleOrDefault()?.ToFullString().Trim().Replace("Hub", ""); + + controller.Ignored = attributes.HasAttribute(); + + + var namespaceAttribute = attributes.GetAttribute(); + if (namespaceAttribute != null) + { + controller.NamespaceSuffix = namespaceAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", ""); + } + + var routeAttribute = attributes.GetAttribute(); + if (routeAttribute != null)//Fetch route from RouteAttribute + { + controller.Route = routeAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", ""); + } + + if (controller.Route == null && !controller.Abstract && !controller.Ignored)//No Route, invalid controller + { + controller.Ignored = true; + throw new NotSupportedException("Controller must have a route to be valid for generation."); + } + + if (controller.Route != null) + { + var match = RouteVersionRegex.Match(controller.Route); + if (match.Success) + { + var group = match.Groups[1]; + controller.NamespaceVersion = group.Value.ToUpper(); + } + } + + //Obsolete Attribute + var obsoleteAttribute = attributes.GetAttribute(); + if (obsoleteAttribute != null) + { + controller.Obsolete = true; + controller.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); + } + + //Only public endpoints can be hit anyways + var methods = syntax.DescendantNodes().OfType() + .Where(x => x.Modifiers.Any(y => y.Text == "public")) + .ToList(); + controller.Endpoints = methods.Select(x => ReadMethodAsHubEndpoint(controller, x)).ToList(); + + if (!controller.Endpoints.Any(x => !x.Ignored)) + { + controller.Ignored = true; + } + } + catch (NotSupportedException nse) + { + if (controller.Ignored) + { + return controller; + } + + controller.Failed = true; + controller.Error = nse.Message; + } +#if !DEBUG + catch (Exception ex) + { + controller.Failed = true; + controller.UnexpectedFailure = true; + controller.Error = ex.ToString(); + } +#endif + return controller; + } + + + + private static HubEndpoint ReadMethodAsHubEndpoint(HubController parent, MethodDeclarationSyntax syntax) + { + var attributes = syntax.DescendantNodes().OfType().SelectMany(x => x.Attributes).ToList(); + + var endpoint = new HubEndpoint(parent); + + endpoint.Name = syntax.Identifier.ValueText.CleanMethodName(); + + + endpoint.Virtual = syntax.Modifiers.Any(x => x.Text == "virtual"); + endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override"); + endpoint.New = syntax.Modifiers.Any(x => x.Text == "new"); + + + //Ignore generator attribute + endpoint.Ignored = attributes.HasAttribute(); + + //Obsolete Attribute + var obsoleteAttribute = attributes.GetAttribute(); + if (obsoleteAttribute != null) + { + endpoint.Obsolete = true; + endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); + } + + //Response types + var messageAttributes = attributes.GetAttributes(); + var messages = messageAttributes.Select(x => new MessageDefinition(x)).ToList(); + + endpoint.Messages = messages.Select(x => new Message(x.Name, x.Types)).ToList(); + + var duplicateMessages = endpoint.Messages.GroupBy(x => x.Name).Where(x => x.Count() > 1 && !x.All(y => y.Types.SequenceEqual(x.First().Types))).ToList(); + + if (duplicateMessages.Any()) + { + throw new NotSupportedException($"Hub has the same message with different parameters defined on different endpoints. {string.Join(", ", duplicateMessages.Select(x => x.Key?.ToString()))}"); + } + + + + + var parameters = syntax.ParameterList.Parameters.Select(x => new HubParameterDefinition(x)).Where(x => !x.Invalid).ToList(); + var hubParams = parameters.Select(x => new HubParameter(x.Name, x.Type, x.Default)).ToList(); + + endpoint.Parameters = hubParams.Cast().NotNull().ToList(); + + var duplicateParameters = endpoint.GetParameters().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); + + if (duplicateParameters.Any()) + { + throw new NotSupportedException($"Endpoint has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); + } + + var invalidParameters = endpoint.GetParameters().Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList(); + + if (invalidParameters.Any()) + { + throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.Name} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}"); + } + + + var rawReturnType = syntax.ReturnType?.ToFullString(); + + var returnType = Helpers.GetTypeFromString(rawReturnType.Trim()); + + while (returnType.IsContainerReturnType()) + { + returnType = returnType.Arguments.SingleOrDefault(); + } + + if (Helpers.IsType(typeof(ChannelReader<>).FullName.CleanGenericTypeDefinition(), returnType?.Name)) + { + endpoint.Channel = true; + endpoint.ChannelType = returnType.Arguments.SingleOrDefault().ToString(); + } + + + return endpoint; + } + + + + + #endregion SignalR + + + #region Functions + + public static FunctionEndpoint ReadMethodAsFunction(MethodDeclarationSyntax syntax, HostJson hostData) + { + var attributes = syntax.DescendantNodes().OfType().SelectMany(x => x.Attributes).ToList(); + + var endpoint = new FunctionEndpoint(); + try + { + var endpointName = attributes.GetAttribute(); + + if (endpointName == null) + { + endpoint.Ignored = true; + return endpoint; + } + + endpoint.Name = endpointName.GetAttributeValue(); + + //Ignore generator attribute + endpoint.Ignored = attributes.HasAttribute(); + + + //Obsolete Attribute + var obsoleteAttribute = attributes.GetAttribute(); + if (obsoleteAttribute != null) + { + endpoint.Obsolete = true; + endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue(); + } + + //Response types + var responseTypes = attributes.GetAttributes(); + var responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList(); + responses.Add(new ResponseTypeDefinition(true)); + + endpoint.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse(x.StatusValue))).ToList(); + + var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList(); + + if (duplicateResponseTypes.Any()) + { + throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}"); + } + //Add after so we don't get duplicate error from the null Status + endpoint.ResponseTypes.Add(new ExceptionResponseType()); + + + //Need to check if the function has a HttpTrigger + var httpTriggerAttribute = syntax.ParameterList.Parameters.SingleOrDefault(x => x.AttributeLists.SelectMany(y => y.Attributes).HasAttribute()); + + if (httpTriggerAttribute == null) + { + endpoint.Ignored = true; + return endpoint; + } + + var triggerAttribute = new HttpTriggerParameter(httpTriggerAttribute); + + endpoint.SupportedMethods = triggerAttribute.Methods; + + var routePrefix = hostData?.http?.routePrefix ?? "api"; + + if (triggerAttribute.Route != null) + { + var route = triggerAttribute.Route.TrimStart('/'); + + if (!string.IsNullOrEmpty(routePrefix)) + { + if (!route.StartsWith(routePrefix)) + { + route = $"{routePrefix}/" + route; + } + + route = "/" + route; + } + + endpoint.Route = new HttpRoute(route); + } + else + { + if (!string.IsNullOrEmpty(routePrefix)) + { + endpoint.Route = new HttpRoute($"{routePrefix}/{endpoint.Name}"); + } + else + { + endpoint.Route = new HttpRoute($"{endpoint.Name}"); + } + } + + + var expectedBodyParameters = attributes.GetAttributes() + .Select(x => new ExpectedBodyParamterDefinition(x)) + .GroupBy(x => x.Method) + .ToDictionary(x => x.Key, y => y.Select(z => (IParameter)new BodyParameter("body", z.Type, null))); + + var expectedQueryParameters = attributes.GetAttributes() + .Select(x => new ExpectedQueryParamterDefinition(x)) + .GroupBy(x => x.Method) + .ToDictionary(x => x.Key, y => y.Select(z => (IParameter)new QueryParameter(z.Name, z.Type, null, z.IsQueryObject))); + + endpoint.HttpParameters = expectedBodyParameters.Union(expectedQueryParameters).ToDictionary(); + + var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute())).Where(x => !x.Invalid).ToList(); + + var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList(); + + endpoint.Parameters = routeParams.Cast().NotNull().ToList(); + + endpoint.Parameters.Add(new CancellationTokenModifier()); + endpoint.Parameters.Add(new CookieModifier()); + endpoint.Parameters.Add(new HeadersModifier()); + endpoint.Parameters.Add(new TimeoutModifier()); + + if (triggerAttribute.AuthLevel == AuthorizationLevel.User) + { + if (!endpoint.ResponseTypes.Any(x => x.Status == HttpStatusCode.Unauthorized)) + { + endpoint.ResponseTypes.Add(new ResponseType(HttpStatusCode.Unauthorized)); + } + + endpoint.Parameters.Add(new SecurityModifier()); + } + else if (triggerAttribute.AuthLevel == AuthorizationLevel.Anonymous) + { + + } + else + { + if (!endpoint.ResponseTypes.Any(x => x.Status == HttpStatusCode.Unauthorized)) + { + endpoint.ResponseTypes.Add(new ResponseType(HttpStatusCode.Unauthorized)); + } + + endpoint.Parameters.Add(new FunctionAuthModifier()); + } + + + var parameterHeaders = attributes.GetAttributes() + .Select(x => new ParameterHeaderDefinition(x)) + .ToList(); + endpoint.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList(); + + + + var headers = attributes.GetAttributes() + .Select(x => new HeaderDefinition(x)) + .ToList(); + endpoint.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList(); + + + var rawReturnType = syntax.ReturnType?.ToFullString(); + + var returnType = Helpers.GetTypeFromString(rawReturnType.Trim()); + + while (returnType.IsContainerReturnType()) + { + returnType = returnType.Arguments.SingleOrDefault(); + } + + if (Helpers.IsType(typeof(IActionResult).FullName, returnType?.Name)) + { + returnType = null; + } + + if (returnType?.Name == "void" + || (Helpers.IsType(typeof(Task).FullName, returnType?.Name) && (!returnType?.Arguments.Any() ?? false))) + { + returnType = null; + } + + if (returnType.IsFileReturnType()) + { + returnType = new Helpers.TypeString(typeof(Stream).FullName); + endpoint.ReturnsStream = true; + } + + rawReturnType = returnType?.ToString(); + + endpoint.ReturnType = rawReturnType?.Trim(); + + foreach (var method in endpoint.SupportedMethods) + { + var duplicateParameters = endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList(); + + if (duplicateParameters.Any()) + { + throw new NotSupportedException($"Function has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}"); + } + + + var invalidParameters = endpoint.GetParametersForHttpMethod(method).Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList(); + + if (invalidParameters.Any()) + { + throw new NotSupportedException($"Function {endpoint.Name} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}"); + } + } + } + catch (NotSupportedException nse) + { + if (endpoint.Ignored) + { + return endpoint; + } + + endpoint.Failed = true; + endpoint.Error = nse.Message; + } +#if !DEBUG + catch (Exception ex) + { + endpoint.Failed = true; + endpoint.UnexpectedFailure = true; + endpoint.Error = ex.ToString(); + } +#endif + + + return endpoint; + } + + #endregion + } } \ No newline at end of file diff --git a/src/AspNetCore.Client.Generator/Output/ClassWriter.cs b/src/Beffyman.AspNetCore.Client.Generator/Output/ClassWriter.cs similarity index 92% rename from src/AspNetCore.Client.Generator/Output/ClassWriter.cs rename to src/Beffyman.AspNetCore.Client.Generator/Output/ClassWriter.cs index 7baa3e3..f377648 100644 --- a/src/AspNetCore.Client.Generator/Output/ClassWriter.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/Output/ClassWriter.cs @@ -1,2011 +1,2022 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using AspNetCore.Client.Generator.Framework; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; -using AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; -using AspNetCore.Client.Generator.Framework.AttributeInterfaces; -using AspNetCore.Client.Generator.Framework.RequestModifiers; -using AspNetCore.Client.Generator.Framework.SignalR; -using Flurl.Http; -using Microsoft.AspNetCore.Routing.Template; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.Extensions.DependencyInjection; - -namespace AspNetCore.Client.Generator.Output -{ - public static class ClassWriter - { - public static void WriteClientsFile(GenerationContext context) - { - var files = WriteFiles(context); - - - var generatorUsings = new List(); - - if (context.HubEndpoints.Any(x => !x.Ignored)) - { - generatorUsings.Add("//Requires nuget Microsoft.AspNetCore.SignalR.Client"); - generatorUsings.Add("//Requires nuget System.Threading.Channels"); - generatorUsings.Add("//Requires nuget Microsoft.Extensions.Logging"); - generatorUsings.Add("using Microsoft.AspNetCore.SignalR.Client;"); - generatorUsings.Add("using Microsoft.AspNetCore.SignalR.Protocol;"); - generatorUsings.Add("using System.Threading.Channels;"); - generatorUsings.Add("using Microsoft.AspNetCore.Http.Connections;"); - generatorUsings.Add("using Microsoft.AspNetCore.Http.Connections.Client;"); - generatorUsings.Add("using Microsoft.Extensions.Logging;"); - } - - var usings = new List - { - @"using AspNetCore.Client;", - "using AspNetCore.Client.Authorization;", - "using AspNetCore.Client.Exceptions;", - "using AspNetCore.Client.Http;", - "using AspNetCore.Client.RequestModifiers;", - "using AspNetCore.Client.Serializers;", - "using Flurl.Http;", - "using Microsoft.Extensions.DependencyInjection;", - "using System;", - "using System.Linq;", - "using System.Collections.Generic;", - "using System.Net;", - "using System.Net.Http;", - "using System.Runtime.CompilerServices;", - "using System.Threading;", - "using System.Threading.Tasks;", - "using System.IO;", - "using AspNetCore.Client.GeneratorExtensions;" - } - .Union(context.UsingStatements) - .Union(generatorUsings) - .Distinct() - .OrderBy(x => x) - .ToList(); - - var fileHeader = -$@"//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -//------------------------------------------------------------------------------ - -{string.Join(Environment.NewLine, usings)} -"; - //Can also probably make the split into file per controller - - if (Settings.MultipleFiles) - { - foreach (var fil in files) - { - if (fil.Value == null) - { - Helpers.SafelyDeleteFile($"{Environment.CurrentDirectory}/{fil.Key}.cs"); - continue; - } - - - var file = fileHeader + fil.Value; - - var syntaxTree = CSharpSyntaxTree.ParseText(file, new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.None, SourceCodeKind.Regular)); - file = syntaxTree.GetRoot().NormalizeWhitespace(" ", false).ToFullString(); - Helpers.SafelyWriteToFile($"{Environment.CurrentDirectory}/{fil.Key}.cs", file); - } - } - else - { - var file = fileHeader + string.Join(Environment.NewLine, files.Values.NotNull()); - - var syntaxTree = CSharpSyntaxTree.ParseText(file, new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.None, SourceCodeKind.Regular)); - file = syntaxTree.GetRoot().NormalizeWhitespace(" ", false).ToFullString(); - Helpers.SafelyWriteToFile($"{Environment.CurrentDirectory}/Clients.cs", file); - } - } - - public static IDictionary WriteFiles(GenerationContext context) - { - Dictionary files = new Dictionary(); - - if (Settings.GenerateStaticRoutes) - { - files.Add("Routes", SharedWriter.WriteStaticRoutes(context)); - } - else - { - files.Add("Routes", null); - } - - files.Add("Installer", SharedWriter.WriteInstallFile(context)); - - if (context.HttpClients.Any(x => x.Generated)) - { - files.Add("Clients", HttpClassWriter.WriteVersionBlocks(context)); - } - else - { - files.Add("Clients", null); - } - - if (context.Functions.Any(x => x.Generated)) - { - files.Add("Functions", FunctionClassWriter.WriteBlocks(context)); - } - else - { - files.Add("Functions", null); - } - - if (context.HubClients.Any(x => x.Generated)) - { - files.Add("Hubs", SignalRClassWriter.WriteVersionBlocks(context)); - } - else - { - files.Add("Hubs", null); - } - - return files; - } - } - - #region SignalR - - public static class SignalRClassWriter - { - public static string WriteErrorMessage(HubController controller) - { - if (controller.Failed) - { - return $@"{(controller.UnexpectedFailure ? "#error PLEASE MAKE A GITHUB REPO ISSUE" : "#warning")} {controller.Name}Hub {(controller.UnexpectedFailure ? "has failed generation with unexpected error" : "is misconfigured for generation")} :: {controller.Error.Replace('\r', ' ').Replace('\n', ' ')}"; - } - else - { - return null; - } - } - - - - public static string WriteVersionBlocks(GenerationContext context) - { - var versions = context.HubClients.Where(x => x.Generated) - .GroupBy(x => x.NamespaceVersion) - .OrderBy(x => x.Key) - .ToList(); - - if (!versions.Any()) - { - return string.Empty; - } - - return -$@" - -{string.Join(Environment.NewLine, versions.Select(WriteVersionGroup))} - -"; - - } - - public static string WriteVersionGroup(IGrouping version) - { - return -$@" -namespace { Settings.HubNamespace }{(version.Key != null ? "." : "")}{version.Key} -{{ -{string.Join(Environment.NewLine, version.OrderBy(x => x.Name).Select(WriteHub))} -}} -"; - } - - - - public static string WriteHub(HubController controller) - { - return -$@" -{(controller.NamespaceSuffix != null ? $@"namespace {controller.NamespaceSuffix} -{{" : string.Empty)} - - {WriteConnectionBuilder(controller)} - - {WriteConnection(controller)} - -{(controller.NamespaceSuffix != null ? $@"}}" : string.Empty)} -"; - } - - private static string WriteConnectionBuilder(HubController controller) - { - return -$@" -{SharedWriter.GetObsolete(controller)} -public class {controller.Name}HubConnectionBuilder : HubConnectionBuilder -{{ - private bool _hubConnectionBuilt; - - public {controller.Name}HubConnectionBuilder(Uri host, HttpTransportType? transports = null, Action configureHttpConnection = null) : base() - {{ - //Remove default HubConnection to use custom one - Services.Remove(Services.Where(x => x.ServiceType == typeof(HubConnection)).Single()); - Services.AddSingleton<{controller.Name}HubConnection>(); - - Services.Configure(o => - {{ - o.Url = new Uri(host,""{controller.Route}""); - if (transports != null) - {{ - o.Transports = transports.Value; - }} - }}); - - if (configureHttpConnection != null) - {{ - Services.Configure(configureHttpConnection); - }} - - Services.AddSingleton(); - }} - - - public new {controller.Name}HubConnection Build() - {{ - // Build can only be used once - if (_hubConnectionBuilt) - {{ - throw new InvalidOperationException(""HubConnectionBuilder allows creation only of a single instance of HubConnection.""); - }} - - _hubConnectionBuilt = true; - - // The service provider is disposed by the HubConnection - var serviceProvider = Services.BuildServiceProvider(); - - var connectionFactory = serviceProvider.GetService(); - if (connectionFactory == null) - {{ - throw new InvalidOperationException($""Cannot create {{nameof(HubConnection)}} instance.An {{nameof(IConnectionFactory)}} was not configured.""); - }} - - return serviceProvider.GetService<{controller.Name}HubConnection>(); - }} -}} -"; - } - - private static string WriteConnection(HubController controller) - { - return $@" -{SharedWriter.GetObsolete(controller)} -public class {controller.Name}HubConnection : HubConnection -{{ - - public {controller.Name}HubConnection(IConnectionFactory connectionFactory, - IHubProtocol protocol, - IServiceProvider serviceProvider, - ILoggerFactory loggerFactory) - : base(connectionFactory, protocol, serviceProvider, loggerFactory) {{ }} - - - public {controller.Name}HubConnection(IConnectionFactory connectionFactory, - IHubProtocol protocol, - ILoggerFactory loggerFactory) - : base(connectionFactory, protocol, loggerFactory) {{ }} - - - {string.Join(Environment.NewLine, controller.GetEndpoints().Select(WriteEndpoint))} - {string.Join(Environment.NewLine, controller.GetMessages().Select(WriteMessage))} -}} -"; - } - - private static string WriteEndpoint(HubEndpoint endpoint) - { - var parameters = endpoint.GetParameters().NotOfType().Select(x => x.Name); - var cancellationToken = endpoint.GetParameters().OfType().Select(x => x.Name).SingleOrDefault(); - - string parameterText = null; - if (parameters.Any()) - { - parameterText = $"new object[]{{{string.Join(", ", parameters)}}}, "; - } - else - { - - parameterText = $"null, "; - } - - if (endpoint.Channel) - { - return $@" -{SharedWriter.GetObsolete(endpoint)} -public Task> Stream{endpoint.Name}Async({string.Join(", ", endpoint.GetParameters().Select(SharedWriter.GetParameter).NotNull())}) -{{ - return this.StreamAsChannelCoreAsync<{endpoint.ChannelType}>(""{endpoint.Name}"", {parameterText}{cancellationToken}); -}} - -{SharedWriter.GetObsolete(endpoint)} -public async Task> Read{endpoint.Name}BlockingAsync({string.Join(", ", endpoint.GetParameters().Select(SharedWriter.GetParameter).NotNull())}) -{{ - var channel = await this.StreamAsChannelCoreAsync<{endpoint.ChannelType}>(""{endpoint.Name}"", {parameterText}{cancellationToken}); - IList<{endpoint.ChannelType}> items = new List<{endpoint.ChannelType}>(); - while(await channel.WaitToReadAsync()) - {{ - while (channel.TryRead(out var item)) - {{ - items.Add(item); - }} - }} - return items; -}} -"; - } - else - { - return $@" -{SharedWriter.GetObsolete(endpoint)} -public Task {endpoint.Name}Async({string.Join(", ", endpoint.GetParameters().Select(SharedWriter.GetParameter).NotNull())}) -{{ - return this.InvokeCoreAsync(""{endpoint.Name}"", {parameterText}{cancellationToken}); -}} -"; - } - - } - - private static string WriteMessage(Message message) - { - return $@" -public IDisposable On{message.Name}(Action<{string.Join(",", message.Types)}> action) -{{ - return this.On(""{message.Name}"", action); -}} -"; - } - } - - - - - #endregion SignalR - - #region HTTP - - public static class HttpClassWriter - { - public static string WriteErrorMessage(AspNetCoreHttpController controller) - { - if (controller.Failed) - { - return $@"#warning {(controller.UnexpectedFailure ? "PLEASE MAKE A GITHUB REPO ISSUE" : "")} {controller.Name}Controller {(controller.UnexpectedFailure ? "has failed generation with unexpected error" : "is misconfigured for generation")} :: {controller.Error.Replace('\r', ' ').Replace('\n', ' ')}"; - } - else - { - return null; - } - } - - public static string GetRelativePath(string file) - { - var root = Path.GetFullPath($"{Environment.CurrentDirectory}/{Settings.RouteToServiceProjectFolder}"); - var fullFile = Path.GetFullPath(file); - - return fullFile.Replace(root, "").Trim('\\'); - } - - #region Installer - - - public static string WriteRepositoryRegistration(string version) - { - return $@" services.AddScoped();"; - } - - public static string WriteClientRegistration(AspNetCoreHttpController controller) - { - string namespaceVersion = $@"{(controller.NamespaceVersion != null ? $"{controller.NamespaceVersion}." : "")}{(controller.NamespaceSuffix != null ? $"{controller.NamespaceSuffix}." : string.Empty)}"; - string interfaceName = $@"{namespaceVersion}I{controller.ClientName}"; - string implementationName = $@"{namespaceVersion}{controller.ClientName}"; - return $@" services.AddScoped<{interfaceName}, {implementationName}>();"; - } - - - #endregion Installer - - #region Static Routes - - public static string WriteStaticRoutesRepository(GenerationContext context) - { - var versions = context.HttpClients.Where(x => x.Generated) - .GroupBy(x => x.NamespaceVersion) - .OrderBy(x => x.Key) - .ToList(); - - - return string.Join(Environment.NewLine, versions.Select(WriteRouteRepositoryVersion)); - } - - private static string WriteRouteRepositoryVersion(IGrouping version) - { - return -$@" -namespace { Settings.ClientNamespace }{(version.Key != null ? "." : "")}{version.Key}{(string.IsNullOrEmpty(Settings.RoutesNamespace) ? string.Empty : ".")}{Settings.RoutesNamespace} -{{ -{string.Join($@"{Environment.NewLine}", version.OrderBy(x => x.Name).Select(WriteRouteRepositoryController))} -}} -"; - } - - private static string WriteRouteRepositoryController(AspNetCoreHttpController controller) - { - return -$@" -{(controller.NamespaceSuffix != null ? $@"namespace {controller.NamespaceSuffix} -{{" : string.Empty)} -public static class {controller.ClientName}Routes -{{ -{string.Join($@"{Environment.NewLine}", controller.GetEndpoints().Select(x=> WriteRouteRepositoryEndpoint(controller,x)))} -}} -{(controller.NamespaceSuffix != null ? $@"}}" : string.Empty)} -"; - } - - private static string WriteRouteRepositoryEndpoint(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint) - { - return -$@" - public static string {endpoint.FormattedName}({string.Join(",", endpoint.GetStaticRouteParameters().Select(SharedWriter.GetParameter).NotNull())}) - {{ - {GetEndpointInfoVariables(controller, endpoint)} - string url = $@""{GetRoute(controller, endpoint, false)}""; - return url; - }} -"; - } - - #endregion - - - #region Repository - - public static string WriteRepositories(GenerationContext context) - { - var versions = context.HttpClients.Where(x => x.Generated) - .GroupBy(x => x.NamespaceVersion) - .OrderBy(x => x.Key) - .ToList(); - - - return -$@" - -{string.Join(Environment.NewLine, versions.Select(WriteRepository))} - -"; - } - - public static string WriteRepository(IGrouping version) - { - - - return -$@" -public interface I{Settings.ClientInterfaceName}{version.Key}Repository -{{ -{string.Join($@"{Environment.NewLine}", version.OrderBy(x => x.Name).Select(x => WriteRepositoryInterfaceProperty(version.Key, x)))} -}} - -{(Settings.UseInternalClients ? "internal" : "public")} class {Settings.ClientInterfaceName}{version.Key}Repository : I{Settings.ClientInterfaceName}{version.Key}Repository -{{ -{string.Join($@"{Environment.NewLine}", version.OrderBy(x => x.Name).Select(x => WriteRepositoryProperty(version.Key, x)))} - - public {Settings.ClientInterfaceName}{version.Key}Repository - ( -{string.Join($@",{Environment.NewLine}", version.OrderBy(x => x.Name).Select(x => WriteRepositoryParameter(version.Key, x)))} - ) - {{ -{string.Join($@"{Environment.NewLine}", version.OrderBy(x => x.Name).Select(x => WriteRepositoryAssignment(version.Key, x)))} - }} -}} - -"; - } - - - public static string WriteRepositoryInterfaceProperty(string key, AspNetCoreHttpController controller) - { - return $@"{key}{(key != null ? "." : "")}{(controller.NamespaceSuffix != null ? $"{controller.NamespaceSuffix}." : string.Empty)}I{controller.ClientName} {controller.Name} {{ get; }}"; - } - - public static string WriteRepositoryProperty(string key, AspNetCoreHttpController controller) - { - return $@"public {key}{(key != null ? "." : "")}{(controller.NamespaceSuffix != null ? $"{controller.NamespaceSuffix}." : string.Empty)}I{controller.ClientName} {controller.Name} {{ get; }}"; - } - - public static string WriteRepositoryParameter(string key, AspNetCoreHttpController controller) - { - return $@"{key}{(key != null ? "." : "")}{(controller.NamespaceSuffix != null ? $"{controller.NamespaceSuffix}." : string.Empty)}I{controller.ClientName} param_{controller.Name.ToLower()}"; - } - - public static string WriteRepositoryAssignment(string key, AspNetCoreHttpController controller) - { - return $@"this.{controller.Name} = param_{controller.Name.ToLower()};"; - } - - - #endregion Repository - - #region Version Blocks - - public static string WriteVersionBlocks(GenerationContext context) - { - var versions = context.HttpClients.Where(x => x.Generated) - .GroupBy(x => x.NamespaceVersion) - .OrderBy(x => x.Key) - .ToList(); - - if (!versions.Any()) - { - return string.Empty; - } - - return -$@" - -{string.Join(Environment.NewLine, versions.Select(WriteVersionGroup))} - -"; - - } - - public static string WriteVersionGroup(IGrouping version) - { - return -$@" -namespace { Settings.ClientNamespace }{(version.Key != null ? "." : "")}{version.Key} -{{ -{string.Join(Environment.NewLine, version.OrderBy(x => x.Name).Select(WriteController))} -}} -"; - } - - #endregion Version Blocks - - - #region Class - - - public static string WriteController(AspNetCoreHttpController controller) - { - return -$@" -{(controller.NamespaceSuffix != null ? $@"namespace {controller.NamespaceSuffix} -{{" : string.Empty)} - -{WriteClassInterface(controller)} - -{WriteClassImplementation(controller)} - -{(controller.NamespaceSuffix != null ? $@"}}" : string.Empty)} -"; - } - - public static string WriteClassInterface(AspNetCoreHttpController controller) - { - return -$@" -{SharedWriter.GetObsolete(controller)} -public interface I{controller.ClientName} : I{Settings.ClientInterfaceName} -{{ -{string.Join($"{Environment.NewLine}", controller.GetEndpoints().Select(WriteEndpointInterface))} -}} -"; - } - - public static string WriteClassImplementation(AspNetCoreHttpController controller) - { - var dependencies = controller.GetInjectionDependencies().ToList(); - dependencies.Insert(0, new ClientDependency($"I{Settings.ClientInterfaceName}Wrapper")); - - return -$@" -{SharedWriter.GetObsolete(controller)} -{(Settings.UseInternalClients ? "internal" : "public")} class {controller.ClientName} : I{controller.ClientName} -{{ -{string.Join($"{Environment.NewLine}", dependencies.Select(WriteDependenciesField))} - - public {controller.ClientName}( -{string.Join($",{Environment.NewLine}", dependencies.Select(WriteDependenciesParameter))}) - {{ -{string.Join($"{Environment.NewLine}", dependencies.Select(WriteDependenciesAssignment))} - }} - -{string.Join($"{Environment.NewLine}", controller.GetEndpoints().Select(x => WriteEndpointImplementation(controller, x)))} -}} -"; - } - - #endregion Class - - #region Dependencies - - public static string WriteDependenciesField(IDependency dependency) - { - return $@"protected readonly {dependency.GetDependencyFieldType($"I{Settings.ClientInterfaceName}")} {dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")};"; - } - - public static string WriteDependenciesParameter(IDependency dependency) - { - return $@"{dependency.GetDependencyParameterType($"I{Settings.ClientInterfaceName}")} param_{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}").ToLower()}"; - } - - public static string WriteDependenciesAssignment(IDependency dependency) - { - string assignmentValue = $"param_{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}").ToLower()}"; - - if (dependency.HasAssignmentOverride) - { - return $@"{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")} = {dependency.GetAssignmentOverride(assignmentValue)};"; - } - else - { - return $@"{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")} = {assignmentValue};"; - } - - } - - #endregion Dependencies - - - #region Endpoint - - public static string WriteEndpointInterface(AspNetCoreHttpEndpoint endpoint) - { - return -$@" -{SharedWriter.GetObsolete(endpoint)} -{SharedWriter.GetInterfaceReturnType(endpoint.ReturnType, false)} {endpoint.FormattedName} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParameters().FilterResponseTypes(endpoint.ResponseTypes).Select(SharedWriter.GetParameter).NotNull())} -); - -{SharedWriter.GetObsolete(endpoint)} -{SharedWriter.GetInterfaceReturnType(nameof(HttpResponseMessage), false)} {endpoint.FormattedName}Raw -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypes().Select(SharedWriter.GetParameter).NotNull())} -); - -{SharedWriter.GetObsolete(endpoint)} -{SharedWriter.GetInterfaceReturnType(endpoint.ReturnType, true)} {endpoint.FormattedName}Async -( -{string.Join($",{Environment.NewLine}", endpoint.GetParameters().FilterResponseTypes(endpoint.ResponseTypes).Select(SharedWriter.GetParameter).NotNull())} -); - -{SharedWriter.GetObsolete(endpoint)} -{SharedWriter.GetInterfaceReturnType(nameof(HttpResponseMessage), true)} {endpoint.FormattedName}RawAsync -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypes().Select(SharedWriter.GetParameter).NotNull())} -); - -"; - } - - public static string WriteEndpointImplementation(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint) - { - return -$@" - -{SharedWriter.GetObsolete(endpoint)} -public {SharedWriter.GetImplementationReturnType(endpoint.ReturnType, false)} {endpoint.FormattedName} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParameters().FilterResponseTypes(endpoint.ResponseTypes).Select(SharedWriter.GetParameter).NotNull())} -) -{{ -{GetMethodDetails(controller, endpoint, false, false)} -}} - -{SharedWriter.GetObsolete(endpoint)} -public {SharedWriter.GetImplementationReturnType(nameof(HttpResponseMessage), false)} {endpoint.FormattedName}Raw -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypes().Select(SharedWriter.GetParameter).NotNull())} -) -{{ -{GetMethodDetails(controller, endpoint, false, true)} -}} - -{SharedWriter.GetObsolete(endpoint)} -public {SharedWriter.GetImplementationReturnType(endpoint.ReturnType, true)} {endpoint.FormattedName}Async -( -{string.Join($",{Environment.NewLine}", endpoint.GetParameters().FilterResponseTypes(endpoint.ResponseTypes).Select(SharedWriter.GetParameter).NotNull())} -) -{{ -{GetMethodDetails(controller, endpoint, true, false)} -}} - -{SharedWriter.GetObsolete(endpoint)} -public {SharedWriter.GetImplementationReturnType(nameof(HttpResponseMessage), true)} {endpoint.FormattedName}RawAsync -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypes().Select(SharedWriter.GetParameter).NotNull())} -) -{{ -{GetMethodDetails(controller, endpoint, true, true)} -}} - -"; - } - - - public static string GetMethodDetails(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint, bool async, bool raw) - { - var cancellationToken = endpoint.GetRequestModifiers().OfType().SingleOrDefault(); - var clientDependency = new ClientDependency($"I{Settings.ClientInterfaceName}Wrapper"); - - var requestModifiers = endpoint.GetRequestModifiers().ToList(); - - var bodyParameter = endpoint.GetBodyParameter(); - string bodyVariable = bodyParameter?.Name ?? "null"; - - var responseTypes = endpoint.GetResponseTypes(); - - var routeConstraints = endpoint.GetRouteConstraints(controller); - - return -$@"{string.Join(Environment.NewLine, routeConstraints.Select(SharedWriter.WriteRouteConstraint).NotNull())} -{GetEndpointInfoVariables(controller, endpoint)} -string url = $@""{GetRoute(controller, endpoint, async)}""; -HttpResponseMessage response = null; -response = {SharedWriter.GetAwait(async)}HttpOverride.GetResponseAsync({SharedWriter.GetHttpMethod(endpoint.HttpType)}, url, null, {cancellationToken.Name}){SharedWriter.GetAsyncEnding(async)}; -bool {Constants.ResponseHandledVariable} = response != null; - -if(response == null) -{{ - try - {{ - response = {SharedWriter.GetAwait(async)}{clientDependency.GetDependencyName($"I{Settings.ClientInterfaceName}")}.{nameof(IClientWrapper.ClientWrapper)} - .Request(url) -{string.Join($" {Environment.NewLine}", requestModifiers.Select(SharedWriter.WriteRequestModifiers).NotNull())} - .AllowAnyHttpStatus() - {GetHttpMethod(endpoint)} - {SharedWriter.GetAsyncEnding(async)}; - }} - catch({nameof(FlurlHttpException)} fhex) - {{ -{SharedWriter.WriteResponseType(responseTypes.OfType().Single(), async, false)} -{WriteErrorActionResultReturn(endpoint, async, raw)} - }} - - {SharedWriter.GetAwait(async)}HttpOverride.OnNonOverridedResponseAsync({SharedWriter.GetHttpMethod(endpoint.HttpType)}, url, {bodyVariable}, response, {cancellationToken.Name}){SharedWriter.GetAsyncEnding(async)}; -}} -{string.Join(Environment.NewLine, responseTypes.Where(x => x.GetType() != typeof(ExceptionResponseType)).Select(x => SharedWriter.WriteResponseType(x, async, raw)).NotNull())} -{WriteActionResultReturn(endpoint, async, raw)}"; - } - - - - public static string WriteErrorActionResultReturn(AspNetCoreHttpEndpoint endpoint, bool async, bool raw) - { - if (raw) - { - return @"return null;"; - } - - if (endpoint.ReturnType != null) - { - return $@"return default({endpoint.ReturnType});"; - } - - return "return;"; - } - - public static string WriteActionResultReturn(AspNetCoreHttpEndpoint endpoint, bool async, bool raw) - { - if (raw) - { - return @"return response;"; - } - - if (endpoint.ReturnsStream) - { - return -$@" -if(response.IsSuccessStatusCode) -{{ - return {SharedWriter.GetAwait(async)}response.Content.ReadAsStreamAsync(){SharedWriter.GetAsyncEnding(async)}; -}} -else -{{" + (Settings.ErrorOnUnhandledCallback ? $@" - if(!{Constants.ResponseHandledVariable}) - {{ - throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); - }}" : "") + - - $@"return default({endpoint.ReturnType}); -}}"; - } - else - { - if (endpoint.ReturnType != null) - { - return - $@" -if(response.IsSuccessStatusCode) -{{ - return {SharedWriter.GetAwait(async)}Serializer.Deserialize<{endpoint.ReturnType}>(response.Content){SharedWriter.GetAsyncEnding(async)}; -}} -else -{{" + (Settings.ErrorOnUnhandledCallback ? $@" - if(!{Constants.ResponseHandledVariable}) - {{ - throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); - }}" : "") + - - $@"return default({endpoint.ReturnType}); -}}"; - } - } - - if (Settings.ErrorOnUnhandledCallback) - { - return -$@" -if(!{Constants.ResponseHandledVariable}) -{{ - throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); -}} - -return;"; - } - else - { - return $@"return;"; - } - - } - - - public static string GetHttpMethod(AspNetCoreHttpEndpoint endpoint) - { - var cancellationToken = endpoint.GetRequestModifiers().OfType().SingleOrDefault(); - var bodyParameter = endpoint.GetBodyParameter(); - - string bodyString = null; - - if (bodyParameter?.Name == null) - { - bodyString = "null"; - } - else - { - bodyString = $@"Serializer.Serialize({bodyParameter?.Name})"; - } - - if (endpoint.HttpType.Method.Equals(HttpMethod.Delete.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.DeleteAsync)}({cancellationToken?.Name})"; - } - else if (endpoint.HttpType.Method.Equals(HttpMethod.Get.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.GetAsync)}({cancellationToken?.Name})"; - } - else if (endpoint.HttpType.Method.Equals(HttpMethod.Put.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.PutAsync)}({bodyString}, {cancellationToken?.Name})"; - } - else if (endpoint.HttpType.Method.Equals(new HttpMethod("PATCH").Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.PatchAsync)}({bodyString}, {cancellationToken?.Name})"; - } - else if (endpoint.HttpType.Method.Equals(HttpMethod.Post.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.PostAsync)}({bodyString}, {cancellationToken?.Name})"; - } - else - { - return $"#error Unsupported HttpMethod of {endpoint.HttpType.Method}"; - } - } - - public static string GetEndpointInfoVariables(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint) - { - - var controllerVar = $@"var {Constants.ControllerRouteReserved} = ""{endpoint.Parent.Name}"";"; - var actionVar = $@"var {Constants.ActionRouteReserved} = ""{endpoint.Name}"";"; - - - if (!endpoint.GetFullRoute(controller)?.Contains($"[{Constants.ControllerRouteReserved}]") ?? false) - { - controllerVar = null; - } - - if (!endpoint.GetFullRoute(controller)?.Contains($"[{Constants.ActionRouteReserved}]") ?? false) - { - actionVar = null; - } - - - return -$@"{controllerVar} -{actionVar}"; - } - - public static string GetRoute(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint, bool async) - { - var route = endpoint.GetFullRoute(controller); - string routeUnformatted = route?.Value; - - - var template = TemplateParser.Parse(routeUnformatted); - - var routeParameters = endpoint.GetRouteParameters().ToList(); - var queryParameters = endpoint.GetQueryParameters().ToList(); - - foreach (var parameter in template.Parameters) - { - if (parameter.Name.Equals("version", StringComparison.CurrentCultureIgnoreCase)) - { - routeUnformatted = routeUnformatted.Replace(parameter.BackToRouteParameter(), route.Version.Version); - continue; - } - - if (!routeParameters.Any(x => x.Name.Equals(parameter.Name, StringComparison.CurrentCultureIgnoreCase))) - { - throw new Exception($"{parameter.Name} is missing from passed in parameters. Please check your route."); - } - - var routeParameter = routeParameters.SingleOrDefault(x => x.Name.Equals(parameter.Name, StringComparison.CurrentCultureIgnoreCase)); - if (Helpers.IsRoutableType(routeParameter.Type)) - { - routeUnformatted = routeUnformatted.Replace(parameter.BackToRouteParameter(), $"{{{Helpers.GetRouteStringTransform(routeParameter.Name, routeParameter.Type)}.{nameof(GeneratorExtensions.GeneratorExtensions.EncodeForUrl)}()}}"); - } - } - - if (queryParameters.Any()) - { - string queryString = $"?{string.Join("&", queryParameters.Select(x => SharedWriter.WriteQueryParameter(x, async)))}"; - - routeUnformatted += $"{queryString}"; - } - - - - routeUnformatted = routeUnformatted.Replace($"[{Constants.ControllerRouteReserved}]", $"{{{Constants.ControllerRouteReserved}}}"); - routeUnformatted = routeUnformatted.Replace($"[{Constants.ActionRouteReserved}]", $"{{{Constants.ActionRouteReserved}}}"); - return routeUnformatted; - } - - - - #endregion Endpoint - } - - #endregion HTTP - - #region Functions - - public static class FunctionClassWriter - { - public static string WriteErrorMessage(FunctionEndpoint endpoint) - { - if (endpoint.Failed) - { - return $@"#warning {(endpoint.UnexpectedFailure ? "PLEASE MAKE A GITHUB REPO ISSUE" : "")} {endpoint.Name}Controller {(endpoint.UnexpectedFailure ? "has failed generation with unexpected error" : "is misconfigured for generation")} :: {endpoint.Error.Replace('\r', ' ').Replace('\n', ' ')}"; - } - else - { - return null; - } - } - - #region Installer - - public static string WriteClientRegistration(FunctionEndpoint controller) - { - string interfaceName = $@"I{controller.ClientName}"; - string implementationName = $@"{controller.ClientName}"; - return $@" services.AddScoped<{interfaceName}, {implementationName}>();"; - } - - - #endregion Installer - - - #region Class - - - public static string WriteBlocks(GenerationContext context) - { - var functions = context.Functions.Where(x => x.Generated) - .OrderBy(x => x.Name) - .ToList(); - - if (!functions.Any()) - { - return string.Empty; - } - - return -$@" -namespace {Settings.ClientNamespace} -{{ -{string.Join(Environment.NewLine, functions.Select(WriteController))} -}} - -"; - - } - - - public static string WriteController(FunctionEndpoint endpoint) - { - return -$@" -{WriteClassInterface(endpoint)} - -{WriteClassImplementation(endpoint)} -"; - } - - public static string WriteClassInterface(FunctionEndpoint endpoint) - { - return -$@" -{SharedWriter.GetObsolete(endpoint)} -public interface I{endpoint.ClientName} : I{Settings.ClientInterfaceName} -{{ -{string.Join($"{Environment.NewLine}", endpoint.SupportedMethods.Select(x => WriteEndpointInterface(endpoint, x)))} -}} -"; - } - - public static string WriteClassImplementation(FunctionEndpoint endpoint) - { - var dependencies = endpoint.GetInjectionDependencies().ToList(); - dependencies.Insert(0, new ClientDependency($"I{Settings.ClientInterfaceName}Wrapper")); - - return -$@" -{SharedWriter.GetObsolete(endpoint)} -{(Settings.UseInternalClients ? "internal" : "public")} class {endpoint.ClientName} : I{endpoint.ClientName} -{{ -{string.Join($"{Environment.NewLine}", dependencies.Select(WriteDependenciesField))} - - public {endpoint.ClientName}( -{string.Join($",{Environment.NewLine}", dependencies.Select(WriteDependenciesParameter))}) - {{ -{string.Join($"{Environment.NewLine}", dependencies.Select(WriteDependenciesAssignment))} - }} - -{string.Join($"{Environment.NewLine}", endpoint.SupportedMethods.Select(x => WriteEndpointImplementation(endpoint, x)))} -}} -"; - } - - #endregion Class - - #region Dependencies - - public static string WriteDependenciesField(IDependency dependency) - { - return $@"protected readonly {dependency.GetDependencyFieldType($"I{Settings.ClientInterfaceName}")} {dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")};"; - } - - public static string WriteDependenciesParameter(IDependency dependency) - { - return $@"{dependency.GetDependencyParameterType($"I{Settings.ClientInterfaceName}")} param_{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}").ToLower()}"; - } - - public static string WriteDependenciesAssignment(IDependency dependency) - { - string assignmentValue = $"param_{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}").ToLower()}"; - - if (dependency.HasAssignmentOverride) - { - return $@"{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")} = {dependency.GetAssignmentOverride(assignmentValue)};"; - } - else - { - return $@"{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")} = {assignmentValue};"; - } - - } - - #endregion Dependencies - - - #region Endpoint - - public static string WriteEndpointInterface(FunctionEndpoint endpoint, HttpMethod method) - { - return -$@" -{SharedWriter.GetObsolete(endpoint)} -{SharedWriter.GetInterfaceReturnType(endpoint.ReturnType, false)} {endpoint.GetEndpointName(method, false, false)} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} -); - -{SharedWriter.GetObsolete(endpoint)} -{SharedWriter.GetInterfaceReturnType(nameof(HttpResponseMessage), false)} {endpoint.GetEndpointName(method, true, false)} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} -); - -{SharedWriter.GetObsolete(endpoint)} -{SharedWriter.GetInterfaceReturnType(endpoint.ReturnType, true)} {endpoint.GetEndpointName(method, false, true)} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} -); - -{SharedWriter.GetObsolete(endpoint)} -{SharedWriter.GetInterfaceReturnType(nameof(HttpResponseMessage), true)} {endpoint.GetEndpointName(method, true, true)} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} -); - -"; - } - - public static string WriteEndpointImplementation(FunctionEndpoint endpoint, HttpMethod method) - { - return -$@" - -{SharedWriter.GetObsolete(endpoint)} -public {SharedWriter.GetImplementationReturnType(endpoint.ReturnType, false)} {endpoint.GetEndpointName(method, false, false)} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} -) -{{ -{GetMethodDetails(endpoint, method, false, false)} -}} - -{SharedWriter.GetObsolete(endpoint)} -public {SharedWriter.GetImplementationReturnType(nameof(HttpResponseMessage), false)} {endpoint.GetEndpointName(method, true, false)} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} -) -{{ -{GetMethodDetails(endpoint, method, false, true)} -}} - -{SharedWriter.GetObsolete(endpoint)} -public {SharedWriter.GetImplementationReturnType(endpoint.ReturnType, true)} {endpoint.GetEndpointName(method, false, true)} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} -) -{{ -{GetMethodDetails(endpoint, method, true, false)} -}} - -{SharedWriter.GetObsolete(endpoint)} -public {SharedWriter.GetImplementationReturnType(nameof(HttpResponseMessage), true)} {endpoint.GetEndpointName(method, true, true)} -( -{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} -) -{{ -{GetMethodDetails(endpoint, method, true, true)} -}} - -"; - } - - - public static string GetMethodDetails(FunctionEndpoint endpoint, HttpMethod method, bool async, bool raw) - { - var cancellationToken = endpoint.GetRequestModifiers().OfType().SingleOrDefault(); - var clientDependency = new ClientDependency($"I{Settings.ClientInterfaceName}Wrapper"); - - var requestModifiers = endpoint.GetRequestModifiers().ToList(); - - var bodyParameter = endpoint.GetBodyParameter(method); - string bodyVariable = bodyParameter?.Name ?? "null"; - - var responseTypes = endpoint.GetResponseTypes(); - - var routeConstraints = endpoint.GetRouteConstraints(); - - return -$@"{string.Join(Environment.NewLine, routeConstraints.Select(SharedWriter.WriteRouteConstraint).NotNull())} -string url = $@""{GetRoute(endpoint, method, async)}""; -HttpResponseMessage response = null; -response = {SharedWriter.GetAwait(async)}HttpOverride.GetResponseAsync({SharedWriter.GetHttpMethod(method)}, url, null, {cancellationToken.Name}){SharedWriter.GetAsyncEnding(async)}; -bool {Constants.ResponseHandledVariable} = response != null; - -if(response == null) -{{ - try - {{ - response = {SharedWriter.GetAwait(async)}{clientDependency.GetDependencyName($"I{Settings.ClientInterfaceName}")}.{nameof(IClientWrapper.ClientWrapper)} - .Request(url) -{string.Join($" {Environment.NewLine}", requestModifiers.Select(SharedWriter.WriteRequestModifiers).NotNull())} - .AllowAnyHttpStatus() - {GetHttpMethod(endpoint, method)} - {SharedWriter.GetAsyncEnding(async)}; - }} - catch({nameof(FlurlHttpException)} fhex) - {{ -{SharedWriter.WriteResponseType(responseTypes.OfType().Single(), async, false)} -{WriteErrorActionResultReturn(endpoint, async, raw)} - }} - - {SharedWriter.GetAwait(async)}HttpOverride.OnNonOverridedResponseAsync({SharedWriter.GetHttpMethod(method)}, url, {bodyVariable}, response, {cancellationToken.Name}){SharedWriter.GetAsyncEnding(async)}; -}} -{string.Join(Environment.NewLine, responseTypes.Where(x => x.GetType() != typeof(ExceptionResponseType)).Select(x => SharedWriter.WriteResponseType(x, async, raw)).NotNull())} -{WriteActionResultReturn(endpoint, async, raw)}"; - } - - - public static string WriteErrorActionResultReturn(FunctionEndpoint endpoint, bool async, bool raw) - { - if (raw) - { - return @"return null;"; - } - - if (endpoint.ReturnType != null) - { - return $@"return default({endpoint.ReturnType});"; - } - - return "return;"; - } - - public static string WriteActionResultReturn(FunctionEndpoint endpoint, bool async, bool raw) - { - if (raw) - { - return @"return response;"; - } - - if (endpoint.ReturnsStream) - { - return -$@" -if(response.IsSuccessStatusCode) -{{ - return {SharedWriter.GetAwait(async)}response.Content.ReadAsStreamAsync(){SharedWriter.GetAsyncEnding(async)}; -}} -else -{{" + (Settings.ErrorOnUnhandledCallback ? $@" - if(!{Constants.ResponseHandledVariable}) - {{ - throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); - }}" : "") + - - $@"return default({endpoint.ReturnType}); -}}"; - } - else - { - if (endpoint.ReturnType != null) - { - return -$@" -if(response.IsSuccessStatusCode) -{{ - return {SharedWriter.GetAwait(async)}Serializer.Deserialize<{endpoint.ReturnType}>(response.Content){SharedWriter.GetAsyncEnding(async)}; -}} -else -{{" + (Settings.ErrorOnUnhandledCallback ? $@" - if(!{Constants.ResponseHandledVariable}) - {{ - throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); - }}" : "") + - - $@"return default({endpoint.ReturnType}); -}}"; - } - } - - - - if (Settings.ErrorOnUnhandledCallback) - { - return -$@" -if(!{Constants.ResponseHandledVariable}) -{{ - throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); -}} - -return;"; - } - else - { - return $@"return;"; - } - } - - public static string GetHttpMethod(FunctionEndpoint endpoint, HttpMethod method) - { - var cancellationToken = endpoint.GetRequestModifiers().OfType().SingleOrDefault(); - var bodyParameter = endpoint.GetBodyParameter(method); - - string bodyString = null; - - if (bodyParameter?.Name == null) - { - bodyString = "null"; - } - else - { - bodyString = $@"Serializer.Serialize({bodyParameter?.Name})"; - } - - if (method.Method.Equals(HttpMethod.Delete.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.DeleteAsync)}({cancellationToken?.Name})"; - } - else if (method.Method.Equals(HttpMethod.Get.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.GetAsync)}({cancellationToken?.Name})"; - } - else if (method.Method.Equals(HttpMethod.Put.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.PutAsync)}({bodyString}, {cancellationToken?.Name})"; - } - else if (method.Method.Equals(new HttpMethod("PATCH").Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.PatchAsync)}({bodyString}, {cancellationToken?.Name})"; - } - else if (method.Method.Equals(HttpMethod.Post.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $".{nameof(GeneratedExtensions.PostAsync)}({bodyString}, {cancellationToken?.Name})"; - } - else - { - return $"#error Unsupported HttpMethod of {method}"; - } - } - - public static string GetRoute(FunctionEndpoint endpoint, HttpMethod method, bool async) - { - var route = endpoint.GetFullRoute(); - string routeUnformatted = endpoint.GetFullRoute()?.Value; - - if (routeUnformatted == null) - { - return string.Empty; - } - - var template = TemplateParser.Parse(routeUnformatted); - - var routeParameters = endpoint.GetRouteParameters().ToList(); - var queryParameters = endpoint.GetQueryParameters(method).ToList(); - - foreach (var parameter in template.Parameters) - { - if (parameter.Name.Equals("version", StringComparison.CurrentCultureIgnoreCase)) - { - routeUnformatted = routeUnformatted.Replace(parameter.BackToRouteParameter(), route.Version.Version); - continue; - } - - if (!routeParameters.Any(x => x.Name.Equals(parameter.Name, StringComparison.CurrentCultureIgnoreCase))) - { - throw new Exception($"{parameter.Name} is missing from passed in parameters. Please check your route."); - } - - var routeParameter = routeParameters.SingleOrDefault(x => x.Name.Equals(parameter.Name, StringComparison.CurrentCultureIgnoreCase)); - if (Helpers.IsRoutableType(routeParameter.Type)) - { - routeUnformatted = routeUnformatted.Replace(parameter.BackToRouteParameter(), $"{{{Helpers.GetRouteStringTransform(routeParameter.Name, routeParameter.Type)}}}"); - } - } - - if (queryParameters.Any()) - { - string queryString = $"?{string.Join("&", queryParameters.Select(x => SharedWriter.WriteQueryParameter(x, async)))}"; - - routeUnformatted += $"{queryString}"; - } - - - - routeUnformatted = routeUnformatted.Replace($"[{Constants.ControllerRouteReserved}]", $"{{{Constants.ControllerRouteReserved}}}"); - routeUnformatted = routeUnformatted.Replace($"[{Constants.ActionRouteReserved}]", $"{{{Constants.ActionRouteReserved}}}"); - return routeUnformatted; - } - - - - #endregion Endpoint - } - - #endregion Functions - - public static class SharedWriter - { - - public static string WriteStaticRoutes(GenerationContext context) - { - return -$@" -{HttpClassWriter.WriteStaticRoutesRepository(context)} -"; - } - - public static string WriteInstallFile(GenerationContext context) - { - return -$@"namespace {Settings.ClientNamespace} -{{ - -{SharedWriter.WriteInstaller(context)} - -{SharedWriter.WriteBaseClients()} - -{HttpClassWriter.WriteRepositories(context)} - -}}"; - } - - - public static string GetParameter(IParameter parameter) - { - if (parameter is QueryParameter qp - && qp.IsConstant) - { - return null; - } - - return $@"{parameter.Type} {parameter.Name}{(parameter.DefaultValue != null ? $" = {parameter.DefaultValue}" : $"")}"; - } - - public static string GetObsolete(IObsolete ob) - { - if (ob.Obsolete) - { - return $@"[{nameof(ObsoleteAttribute)}(""{ob.ObsoleteMessage}"")]"; - } - else - { - return string.Empty; - } - - } - - - public static string WriteInstaller(GenerationContext context) - { - var versions = context.HttpClients.Where(x => x.Generated) - .GroupBy(x => x.NamespaceVersion) - .OrderBy(x => x.Key) - .Select(x => x.Key) - .ToList(); - - return -$@" - -{string.Join(Environment.NewLine, context.HttpClients.Select(HttpClassWriter.WriteErrorMessage).NotNull())} -{string.Join(Environment.NewLine, context.HubClients.Select(SignalRClassWriter.WriteErrorMessage).NotNull())} -{string.Join(Environment.NewLine, context.Functions.Select(FunctionClassWriter.WriteErrorMessage).NotNull())} - - - public static class {Settings.ClientInterfaceName}Installer - {{ - /// - /// Register the autogenerated clients into the container with a lifecycle of scoped. - /// - /// - /// Overrides for client configuration - /// - public static {nameof(IServiceCollection)} Add{Settings.RegisterName}Clients(this {nameof(IServiceCollection)} services, Action<{nameof(ClientConfiguration)}> configure) - {{ - var configuration = new {nameof(ClientConfiguration)}(); - - configuration.{nameof(ClientConfiguration.RegisterClientWrapperCreator)}({Settings.ClientInterfaceName}Wrapper.Create); - configuration.{nameof(ClientConfiguration.UseClientWrapper)}((provider) => new {Settings.ClientInterfaceName}Wrapper(provider.GetService>(), configuration.{nameof(ClientConfiguration.GetSettings)}(), provider)); - - configure?.Invoke(configuration); - -{string.Join(Environment.NewLine, versions.Select(HttpClassWriter.WriteRepositoryRegistration))} -{string.Join(Environment.NewLine, context.HttpClients.Where(x => x.Generated).OrderBy(x => x.Name).Select(HttpClassWriter.WriteClientRegistration))} - -{string.Join(Environment.NewLine, context.Functions.Where(x => x.Generated).OrderBy(x => x.Name).Select(FunctionClassWriter.WriteClientRegistration))} - - return configuration.{nameof(ClientConfiguration.ApplyConfiguration)}(services); - }} - }} -"; - } - - - - public static string WriteBaseClients() - { - return -$@" -public interface I{Settings.ClientInterfaceName}Wrapper : IClientWrapper {{ }} - -public class {Settings.ClientInterfaceName}Wrapper : I{Settings.ClientInterfaceName}Wrapper -{{ - public TimeSpan Timeout {{ get; internal set; }} - public {nameof(IFlurlClient)} {Constants.FlurlClientVariable} {{ get; internal set; }} - - public {Settings.ClientInterfaceName}Wrapper(Func client, {nameof(ClientSettings)} settings, {nameof(IServiceProvider)} provider) - {{ - {Constants.FlurlClientVariable} = client(null); - if (settings.{nameof(ClientSettings.BaseAddress)} != null) - {{ - {Constants.FlurlClientVariable}.BaseUrl = settings.{nameof(ClientSettings.BaseAddress)}(provider); - }} - - Timeout = settings.{nameof(ClientSettings.Timeout)}; - }} - - public static I{Settings.ClientInterfaceName}Wrapper Create(Func client, {nameof(ClientSettings)} settings, {nameof(IServiceProvider)} provider) - {{ - return new {Settings.ClientInterfaceName}Wrapper(client, settings, provider); - }} -}} - -public interface I{Settings.ClientInterfaceName} : {nameof(IClient)} {{ }} -"; - } - - - - public static string GetAsyncEnding(bool async) - { - if (async) - { - return $@".ConfigureAwait(false)"; - } - else - { - return $@".ConfigureAwait(false).GetAwaiter().GetResult()"; - } - } - - public static string GetAwait(bool async) - { - if (async) - { - return "await "; - } - return null; - } - - public static string GetInterfaceReturnType(string returnType, bool async) - { - if (async) - { - if (returnType == null) - { - return $"Task"; - } - else - { - return $"{Helpers.GetTaskType()}<{returnType}>"; - } - } - else - { - if (returnType == null) - { - return $"void"; - } - else - { - return $"{returnType}"; - } - } - } - - public static string GetImplementationReturnType(string returnType, bool async) - { - if (async) - { - if (returnType == null) - { - return $"async Task"; - } - else - { - return $"async {Helpers.GetTaskType()}<{returnType}>"; - } - } - else - { - if (returnType == null) - { - return $"void"; - } - else - { - return $"{returnType}"; - } - } - } - - - public static string WriteQueryParameter(QueryParameter parameter, bool async) - { - if (parameter.IsConstant) - { - return parameter.Name; - } - - string name = $"{{nameof({parameter.Name})}}"; - - if (Helpers.IsEnumerable(parameter.Type)) - { - return $@"{{string.Join(""&"",{parameter.Name}.Select(x => $""{name}={{{Helpers.GetRouteStringTransform("x", parameter.Type)}.{nameof(GeneratorExtensions.GeneratorExtensions.EncodeForUrl)}()}}""))}}"; - } - else - { - if (parameter.QueryObject) - { - return $"{{{GetAwait(async)}{parameter.Name}.{nameof(GeneratorExtensions.GeneratorExtensions.GetQueryObjectString)}(nameof({parameter.Name})){GetAsyncEnding(async)}}}"; - } - else - { - return $"{name}={{{Helpers.GetRouteStringTransform(parameter.Name, parameter.Type)}.{nameof(GeneratorExtensions.GeneratorExtensions.EncodeForUrl)}()}}"; - } - } - } - - - public static string WriteRequestModifiers(IRequestModifier modifier) - { - if (modifier is CookieModifier cm) - { - return $@".WithCookies({cm.Name})"; - } - else if (modifier is HeadersModifier hm) - { - return $@".WithHeaders({hm.Name})"; - } - else if (modifier is RequestModifierDependency rm) - { - return $@".WithRequestModifiers({rm.GetDependencyName($"I{Settings.ClientInterfaceName}")})"; - } - else if (modifier is SecurityModifier sm) - { - return $@".{nameof(GeneratorExtensions.GeneratorExtensions.WithAuth)}({sm.Name})"; - } - else if (modifier is FunctionAuthModifier fam) - { - return $@".{nameof(GeneratorExtensions.GeneratorExtensions.WithFunctionAuthorizationKey)}({fam.Name})"; - } - else if (modifier is TimeoutModifier tm) - { - var clientDependency = new ClientDependency(null); - return $@".WithTimeout({tm.Name} ?? {clientDependency.GetDependencyName($"I{Settings.ClientInterfaceName}")}.{nameof(IClientWrapper.Timeout)})"; - } - else if (modifier is ConstantHeader ch) - { - return $@".WithHeader(""{ch.Key}"", ""{ch.Value}"")"; - } - else if (modifier is ParameterHeader ph) - { - return $@".WithHeader(""{ph.Name}"", {ph.Name})"; - } - else if (modifier is CancellationTokenModifier ctm) - { - return null; - } - else - { - return $@"#warning IRequestModifier of type {modifier.GetType().Name} is not supported"; - } - } - - - - public static string WriteResponseType(ResponseType responseType, bool async, bool raw) - { - if (raw) - { - return null; - } - - return -$@" -{GetResponseTypeCheck(responseType)} -{GetResponseTypeInvoke(responseType, async)} -"; - } - - public static string GetResponseTypeCheck(ResponseType responseType) - { - return -$@" -if({responseType.Name} != null && {responseType.Name}.Method.IsDefined(typeof(AsyncStateMachineAttribute), true)) -{{ - throw new NotSupportedException(""Async void action delegates for {responseType.Name} are not supported.As they will run out of the scope of this call.""); -}} -"; - } - - public static string GetResponseTypeInvoke(ResponseType responseType, bool async) - { - if (responseType is ExceptionResponseType) - { - return -$@"if({responseType.Name} != null) -{{ - {Constants.ResponseHandledVariable} = true; - {responseType.Name}?.Invoke(fhex); -}} -else -{{ - throw fhex; -}} -"; - } - - if (responseType.Status == null) - { - return -$@" -if({responseType.Name} != null) -{{ - {Constants.ResponseHandledVariable} = true; - {responseType.Name}.Invoke(response); -}} -"; - } - else - { - string content = null; - if (responseType.ActionType == nameof(Stream)) - { - content = $@"{GetAwait(async)}response.Content.ReadAsStreamAsync(){GetAsyncEnding(async)}"; - } - else if (responseType.ActionType != null) - { - content = $@"{GetAwait(async)}Serializer.Deserialize<{responseType.ActionType}>(response.Content){GetAsyncEnding(async)}"; - } - - string statusValue = null; - if (responseType.Status == HttpStatusCode.RedirectMethod) - { - statusValue = nameof(HttpStatusCode.SeeOther); - } - else - { - statusValue = responseType.Status?.ToString(); - } - - return -$@" -if(response.StatusCode == System.Net.HttpStatusCode.{statusValue}) -{{ - if({responseType.Name} != null) - {{ - {Constants.ResponseHandledVariable} = true; - {responseType.Name}.Invoke({content}); - }} -}} -"; - } - } - - public static string WriteRouteConstraint(RouteConstraint constraint) - { - if (!Settings.ClientRouteConstraints) - { - return string.Empty; - } - - if (constraint is AlphaConstraint) - { - return $@" -if(string.IsNullOrWhiteSpace({constraint.ParameterName}) || {constraint.ParameterName}.Any(x=>char.IsNumber(x))) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} must only contain characters that are not numbers.""); -}}"; - } - else if (constraint is BoolConstraint) - { - return $@" -if(!bool.TryParse({constraint.ParameterName}.ToString(),out _)) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an bool.""); -}}"; - } - else if (constraint is DateTimeConstraint) - { - return $@" -if(!DateTime.TryParse({constraint.ParameterName}.ToString(),out _)) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an DateTime.""); -}}"; - } - else if (constraint is DecimalConstraint) - { - return $@" -if(!decimal.TryParse({constraint.ParameterName}.ToString(),out _)) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an decimal.""); -}}"; - } - else if (constraint is FloatConstraint) - { - return $@" -if(!float.TryParse({constraint.ParameterName}.ToString(),out _)) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an float.""); -}}"; - } - else if (constraint is GuidConstraint) - { - return $@" -if(!Guid.TryParse({constraint.ParameterName}.ToString(),out _)) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an Guid.""); -}}"; - } - else if (constraint is IntConstraint) - { - return $@" -if(!int.TryParse({constraint.ParameterName}.ToString(),out _)) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an int.""); -}}"; - } - else if (constraint is LengthConstraint) - { - var value = constraint.GetConstraintValue(); - if (value.Contains(',')) - { - var split = value.Split(','); - string minL = split[0]; - string maxL = split[1]; - - return $@" -if({constraint.ParameterName}.Length <= {minL} || {constraint.ParameterName}.Length >= {maxL}) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a length that is not between {minL} and {maxL}.""); -}}"; - } - else - { - return $@" -if({constraint.ParameterName}.Length == {value}) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a length that is not {value}.""); -}}"; - } - } - else if (constraint is LongConstraint) - { - return $@" -if(!long.TryParse({constraint.ParameterName}.ToString(),out _)) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an long.""); -}}"; - } - else if (constraint is MaxConstraint) - { - var value = constraint.GetConstraintValue(); - - return $@" -if({constraint.ParameterName} >= {value}) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a value more than {value}.""); -}}"; - } - else if (constraint is MaxLengthConstraint) - { - var value = constraint.GetConstraintValue(); - - return $@" -if({constraint.ParameterName}.Length >= {value}) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a length greater than {value}.""); -}}"; - } - else if (constraint is MinConstraint) - { - var value = constraint.GetConstraintValue(); - - return $@" -if({constraint.ParameterName} <= {value}) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a value less than {value}.""); -}}"; - } - else if (constraint is MinLengthConstraint) - { - var value = constraint.GetConstraintValue(); - - return $@" -if({constraint.ParameterName}.Length <= {value}) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a length less than {value}.""); -}}"; - } - else if (constraint is RangeConstraint) - { - var value = constraint.GetConstraintValue(); - - var split = value.Split(','); - string minL = split[0]; - string maxL = split[1]; - - return $@" -if({constraint.ParameterName} <= {minL} || {constraint.ParameterName} >= {maxL}) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a value that is not between {minL} and {maxL}.""); -}}"; - } - else if (constraint is RegexConstraint) - { - var value = constraint.GetConstraintValue(); - - return $@" -if(!(new Regex(@""{value}"").IsMatch({constraint.ParameterName}))) -{{ - throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not follow the regex \""{value}\"".""); -}}"; - } - else if (constraint is RequiredConstraint) - { - return null; - } - else if (constraint is ApiVersionContraint) - { - return null; - } - else - { - return $@"#error A route constraint of type {constraint.GetType().Name} and text of {constraint.Constraint} is not supported"; - } - } - - - public static string GetHttpMethod(HttpMethod method) - { - if (method.Method.Equals(HttpMethod.Delete.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $"{nameof(HttpMethod)}.{nameof(HttpMethod.Delete)}"; - } - else if (method.Method.Equals(HttpMethod.Get.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $"{nameof(HttpMethod)}.{nameof(HttpMethod.Get)}"; - } - else if (method.Method.Equals(HttpMethod.Put.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $"{nameof(HttpMethod)}.{nameof(HttpMethod.Put)}"; - } - else if (method.Method.Equals(new HttpMethod("PATCH").Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $@"new {nameof(HttpMethod)}(""PATCH"")"; - } - else if (method.Method.Equals(HttpMethod.Post.Method, StringComparison.CurrentCultureIgnoreCase)) - { - return $"{nameof(HttpMethod)}.{nameof(HttpMethod.Post)}"; - } - else - { - return $"#error Unsupported HttpMethod of {method.Method}"; - } - } - - - - public static string GetRelativePath(string file) - { - var root = Path.GetFullPath($"{Environment.CurrentDirectory}/{Settings.RouteToServiceProjectFolder}"); - var fullFile = Path.GetFullPath(file); - - return fullFile.Replace(root, "").Trim('\\'); - } - } -} - +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using Beffyman.AspNetCore.Client.Generator.Framework; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Dependencies; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Functions; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Headers; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Parameters; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.ResponseTypes; +using Beffyman.AspNetCore.Client.Generator.Framework.AspNetCoreHttp.Routes.Constraints; +using Beffyman.AspNetCore.Client.Generator.Framework.AttributeInterfaces; +using Beffyman.AspNetCore.Client.Generator.Framework.RequestModifiers; +using Beffyman.AspNetCore.Client.Generator.Framework.SignalR; +using Flurl.Http; +using Microsoft.AspNetCore.Routing.Template; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Extensions.DependencyInjection; + +namespace Beffyman.AspNetCore.Client.Generator.Output +{ + public static class ClassWriter + { + public static void WriteClientsFile(GenerationContext context) + { + var files = WriteFiles(context); + + + var generatorUsings = new List(); + + if (context.HubEndpoints.Any(x => !x.Ignored)) + { + generatorUsings.Add("//Requires nuget Microsoft.AspNetCore.SignalR.Client"); + generatorUsings.Add("//Requires nuget System.Threading.Channels"); + generatorUsings.Add("//Requires nuget Microsoft.Extensions.Logging"); + generatorUsings.Add("using Microsoft.AspNetCore.SignalR.Client;"); + generatorUsings.Add("using Microsoft.AspNetCore.SignalR.Protocol;"); + generatorUsings.Add("using System.Threading.Channels;"); + generatorUsings.Add("using Microsoft.AspNetCore.Http.Connections;"); + generatorUsings.Add("using Microsoft.AspNetCore.Http.Connections.Client;"); + generatorUsings.Add("using Microsoft.Extensions.Logging;"); + generatorUsings.Add("using Microsoft.AspNetCore.Connections;"); + } + + var usings = new List + { + @"using Beffyman.AspNetCore.Client;", + "using Beffyman.AspNetCore.Client.Authorization;", + "using Beffyman.AspNetCore.Client.Exceptions;", + "using Beffyman.AspNetCore.Client.Http;", + "using Beffyman.AspNetCore.Client.RequestModifiers;", + "using Beffyman.AspNetCore.Client.Serializers;", + "using Flurl.Http;", + "using Microsoft.Extensions.DependencyInjection;", + "using System;", + "using System.Linq;", + "using System.Collections.Generic;", + "using System.Net;", + "using System.Net.Http;", + "using System.Runtime.CompilerServices;", + "using System.Threading;", + "using System.Threading.Tasks;", + "using System.IO;", + "using Beffyman.AspNetCore.Client.GeneratorExtensions;" + } + .Union(context.UsingStatements) + .Union(generatorUsings) + .Distinct() + .OrderBy(x => x) + .ToList(); + + var fileHeader = +$@"//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +{string.Join(Environment.NewLine, usings)} +"; + //Can also probably make the split into file per controller + + if (Settings.MultipleFiles) + { + foreach (var fil in files) + { + if (fil.Value == null) + { + Helpers.SafelyDeleteFile($"{Environment.CurrentDirectory}/{fil.Key}.cs"); + continue; + } + + + var file = fileHeader + fil.Value; + + var syntaxTree = CSharpSyntaxTree.ParseText(file, new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.None, SourceCodeKind.Regular)); + file = syntaxTree.GetRoot().NormalizeWhitespace(" ", false).ToFullString(); + Helpers.SafelyWriteToFile($"{Environment.CurrentDirectory}/{fil.Key}.cs", file); + } + } + else + { + var file = fileHeader + string.Join(Environment.NewLine, files.Values.NotNull()); + + var syntaxTree = CSharpSyntaxTree.ParseText(file, new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.None, SourceCodeKind.Regular)); + file = syntaxTree.GetRoot().NormalizeWhitespace(" ", false).ToFullString(); + Helpers.SafelyWriteToFile($"{Environment.CurrentDirectory}/Clients.cs", file); + } + } + + public static IDictionary WriteFiles(GenerationContext context) + { + Dictionary files = new Dictionary(); + + if (Settings.GenerateStaticRoutes) + { + files.Add("Routes", SharedWriter.WriteStaticRoutes(context)); + } + else + { + files.Add("Routes", null); + } + + files.Add("Installer", SharedWriter.WriteInstallFile(context)); + + if (context.HttpClients.Any(x => x.Generated)) + { + files.Add("Clients", HttpClassWriter.WriteVersionBlocks(context)); + } + else + { + files.Add("Clients", null); + } + + if (context.Functions.Any(x => x.Generated)) + { + files.Add("Functions", FunctionClassWriter.WriteBlocks(context)); + } + else + { + files.Add("Functions", null); + } + + if (context.HubClients.Any(x => x.Generated)) + { + files.Add("Hubs", SignalRClassWriter.WriteVersionBlocks(context)); + } + else + { + files.Add("Hubs", null); + } + + return files; + } + } + + #region SignalR + + public static class SignalRClassWriter + { + public static string WriteErrorMessage(HubController controller) + { + if (controller.Failed) + { + return $@"{(controller.UnexpectedFailure ? "#error PLEASE MAKE A GITHUB REPO ISSUE" : "#warning")} {controller.Name}Hub {(controller.UnexpectedFailure ? "has failed generation with unexpected error" : "is misconfigured for generation")} :: {controller.Error.Replace('\r', ' ').Replace('\n', ' ')}"; + } + else + { + return null; + } + } + + + + public static string WriteVersionBlocks(GenerationContext context) + { + var versions = context.HubClients.Where(x => x.Generated) + .GroupBy(x => x.NamespaceVersion) + .OrderBy(x => x.Key) + .ToList(); + + if (!versions.Any()) + { + return string.Empty; + } + + return +$@" + +{string.Join(Environment.NewLine, versions.Select(WriteVersionGroup))} + +"; + + } + + public static string WriteVersionGroup(IGrouping version) + { + return +$@" +namespace { Settings.HubNamespace }{(version.Key != null ? "." : "")}{version.Key} +{{ +{string.Join(Environment.NewLine, version.OrderBy(x => x.Name).Select(WriteHub))} +}} +"; + } + + + + public static string WriteHub(HubController controller) + { + return +$@" +{(controller.NamespaceSuffix != null ? $@"namespace {controller.NamespaceSuffix} +{{" : string.Empty)} + + {WriteConnectionBuilder(controller)} + + {WriteConnection(controller)} + +{(controller.NamespaceSuffix != null ? $@"}}" : string.Empty)} +"; + } + + private static string WriteConnectionBuilder(HubController controller) + { + return +$@" +{SharedWriter.GetObsolete(controller)} +public class {controller.Name}HubConnectionBuilder : IHubConnectionBuilder +{{ + private bool _hubConnectionBuilt; + public IServiceCollection Services {{ get; }} + + public {controller.Name}HubConnectionBuilder(Uri host, HttpTransportType? transports = null, Action configureHttpConnection = null) : base() + {{ + Services = new ServiceCollection(); + Services.AddSingleton<{controller.Name}HubConnection>(); + Services.AddLogging(); + this.AddJsonProtocol(); + + if(transports != null) + {{ + this.WithUrl(new Uri(host, ""{controller.Route}""), transports.Value, configureHttpConnection); + }} + else + {{ + this.WithUrl(new Uri(host, ""{controller.Route}""), configureHttpConnection); + }} + }} + + HubConnection IHubConnectionBuilder.Build() + {{ + return this.Build(); + }} + + public {controller.Name}HubConnection Build() + {{ + // Build can only be used once + if (_hubConnectionBuilt) + {{ + throw new InvalidOperationException($""{{nameof({controller.Name}HubConnectionBuilder)}} allows creation only of a single instance of {{nameof({controller.Name}HubConnection)}}.""); + }} + + _hubConnectionBuilt = true; + + // The service provider is disposed by the HubConnection + var serviceProvider = Services.BuildServiceProvider(); + + var connectionFactory = serviceProvider.GetService() ?? + throw new InvalidOperationException($""Cannot create {{ nameof({controller.Name}HubConnection)}} instance.An {{ nameof(IConnectionFactory)}} was not configured.""); + + var endPoint = serviceProvider.GetService() ?? + throw new InvalidOperationException($""Cannot create {{ nameof({controller.Name}HubConnection)}} instance.An {{ nameof(EndPoint)}} was not configured.""); + + return serviceProvider.GetService<{controller.Name}HubConnection>(); + }} +}} +"; + } + + private static string WriteConnection(HubController controller) + { + return $@" +{SharedWriter.GetObsolete(controller)} +public class {controller.Name}HubConnection : HubConnection +{{ + + public {controller.Name}HubConnection(IConnectionFactory connectionFactory, IHubProtocol protocol, EndPoint endPoint, IServiceProvider serviceProvider, ILoggerFactory loggerFactory) + : base(connectionFactory, protocol, endPoint, serviceProvider,loggerFactory) {{ }} + + + public {controller.Name}HubConnection(IConnectionFactory connectionFactory, IHubProtocol protocol, EndPoint endPoint, IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IRetryPolicy reconnectPolicy) + : base(connectionFactory, protocol, endPoint, serviceProvider, loggerFactory, reconnectPolicy) {{ }} + + + {string.Join(Environment.NewLine, controller.GetEndpoints().Select(WriteEndpoint))} + {string.Join(Environment.NewLine, controller.GetMessages().Select(WriteMessage))} +}} +"; + } + + private static string WriteEndpoint(HubEndpoint endpoint) + { + var parameters = endpoint.GetParameters().NotOfType().Select(x => x.Name); + var cancellationToken = endpoint.GetParameters().OfType().Select(x => x.Name).SingleOrDefault(); + + string parameterText = null; + if (parameters.Any()) + { + parameterText = $"new object[]{{{string.Join(", ", parameters)}}}, "; + } + else + { + + parameterText = $"null, "; + } + + if (endpoint.Channel) + { + return $@" +{SharedWriter.GetObsolete(endpoint)} +public Task> Stream{endpoint.Name}Async({string.Join(", ", endpoint.GetParameters().Select(SharedWriter.GetParameter).NotNull())}) +{{ + return this.StreamAsChannelCoreAsync<{endpoint.ChannelType}>(""{endpoint.Name}"", {parameterText}{cancellationToken}); +}} + +{SharedWriter.GetObsolete(endpoint)} +public async Task> Read{endpoint.Name}BlockingAsync({string.Join(", ", endpoint.GetParameters().Select(SharedWriter.GetParameter).NotNull())}) +{{ + var channel = await this.StreamAsChannelCoreAsync<{endpoint.ChannelType}>(""{endpoint.Name}"", {parameterText}{cancellationToken}); + IList<{endpoint.ChannelType}> items = new List<{endpoint.ChannelType}>(); + while(await channel.WaitToReadAsync()) + {{ + while (channel.TryRead(out var item)) + {{ + items.Add(item); + }} + }} + return items; +}} +"; + } + else + { + return $@" +{SharedWriter.GetObsolete(endpoint)} +public Task {endpoint.Name}Async({string.Join(", ", endpoint.GetParameters().Select(SharedWriter.GetParameter).NotNull())}) +{{ + return this.InvokeCoreAsync(""{endpoint.Name}"", {parameterText}{cancellationToken}); +}} +"; + } + + } + + private static string WriteMessage(Message message) + { + return $@" +public IDisposable On{message.Name}(Action<{string.Join(",", message.Types)}> action) +{{ + return this.On(""{message.Name}"", action); +}} +"; + } + } + + + + + #endregion SignalR + + #region HTTP + + public static class HttpClassWriter + { + public static string WriteErrorMessage(AspNetCoreHttpController controller) + { + if (controller.Failed) + { + return $@"#warning {(controller.UnexpectedFailure ? "PLEASE MAKE A GITHUB REPO ISSUE" : "")} {controller.Name}Controller {(controller.UnexpectedFailure ? "has failed generation with unexpected error" : "is misconfigured for generation")} :: {controller.Error.Replace('\r', ' ').Replace('\n', ' ')}"; + } + else + { + return null; + } + } + + public static string GetRelativePath(string file) + { + var root = Path.GetFullPath($"{Environment.CurrentDirectory}/{Settings.RouteToServiceProjectFolder}"); + var fullFile = Path.GetFullPath(file); + + return fullFile.Replace(root, "").Trim('\\'); + } + + #region Installer + + + public static string WriteRepositoryRegistration(string version) + { + return $@" services.AddScoped();"; + } + + public static string WriteClientRegistration(AspNetCoreHttpController controller) + { + string namespaceVersion = $@"{(controller.NamespaceVersion != null ? $"{controller.NamespaceVersion}." : "")}{(controller.NamespaceSuffix != null ? $"{controller.NamespaceSuffix}." : string.Empty)}"; + string interfaceName = $@"{namespaceVersion}I{controller.ClientName}"; + string implementationName = $@"{namespaceVersion}{controller.ClientName}"; + return $@" services.AddScoped<{interfaceName}, {implementationName}>();"; + } + + + #endregion Installer + + #region Static Routes + + public static string WriteStaticRoutesRepository(GenerationContext context) + { + var versions = context.HttpClients.Where(x => x.Generated) + .GroupBy(x => x.NamespaceVersion) + .OrderBy(x => x.Key) + .ToList(); + + + return string.Join(Environment.NewLine, versions.Select(WriteRouteRepositoryVersion)); + } + + private static string WriteRouteRepositoryVersion(IGrouping version) + { + return +$@" +namespace { Settings.ClientNamespace }{(version.Key != null ? "." : "")}{version.Key}{(string.IsNullOrEmpty(Settings.RoutesNamespace) ? string.Empty : ".")}{Settings.RoutesNamespace} +{{ +{string.Join($@"{Environment.NewLine}", version.OrderBy(x => x.Name).Select(WriteRouteRepositoryController))} +}} +"; + } + + private static string WriteRouteRepositoryController(AspNetCoreHttpController controller) + { + return +$@" +{(controller.NamespaceSuffix != null ? $@"namespace {controller.NamespaceSuffix} +{{" : string.Empty)} +public static class {controller.ClientName}Routes +{{ +{string.Join($@"{Environment.NewLine}", controller.GetEndpoints().Select(x => WriteRouteRepositoryEndpoint(controller, x)))} +}} +{(controller.NamespaceSuffix != null ? $@"}}" : string.Empty)} +"; + } + + private static string WriteRouteRepositoryEndpoint(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint) + { + return +$@" + public static string {endpoint.FormattedName}({string.Join(",", endpoint.GetStaticRouteParameters().Select(SharedWriter.GetParameter).NotNull())}) + {{ + {GetEndpointInfoVariables(controller, endpoint)} + string url = $@""{GetRoute(controller, endpoint, false)}""; + return url; + }} +"; + } + + #endregion + + + #region Repository + + public static string WriteRepositories(GenerationContext context) + { + var versions = context.HttpClients.Where(x => x.Generated) + .GroupBy(x => x.NamespaceVersion) + .OrderBy(x => x.Key) + .ToList(); + + + return +$@" + +{string.Join(Environment.NewLine, versions.Select(WriteRepository))} + +"; + } + + public static string WriteRepository(IGrouping version) + { + + + return +$@" +public interface I{Settings.ClientInterfaceName}{version.Key}Repository +{{ +{string.Join($@"{Environment.NewLine}", version.OrderBy(x => x.Name).Select(x => WriteRepositoryInterfaceProperty(version.Key, x)))} +}} + +{(Settings.UseInternalClients ? "internal" : "public")} class {Settings.ClientInterfaceName}{version.Key}Repository : I{Settings.ClientInterfaceName}{version.Key}Repository +{{ +{string.Join($@"{Environment.NewLine}", version.OrderBy(x => x.Name).Select(x => WriteRepositoryProperty(version.Key, x)))} + + public {Settings.ClientInterfaceName}{version.Key}Repository + ( +{string.Join($@",{Environment.NewLine}", version.OrderBy(x => x.Name).Select(x => WriteRepositoryParameter(version.Key, x)))} + ) + {{ +{string.Join($@"{Environment.NewLine}", version.OrderBy(x => x.Name).Select(x => WriteRepositoryAssignment(version.Key, x)))} + }} +}} + +"; + } + + + public static string WriteRepositoryInterfaceProperty(string key, AspNetCoreHttpController controller) + { + return $@"{key}{(key != null ? "." : "")}{(controller.NamespaceSuffix != null ? $"{controller.NamespaceSuffix}." : string.Empty)}I{controller.ClientName} {controller.Name} {{ get; }}"; + } + + public static string WriteRepositoryProperty(string key, AspNetCoreHttpController controller) + { + return $@"public {key}{(key != null ? "." : "")}{(controller.NamespaceSuffix != null ? $"{controller.NamespaceSuffix}." : string.Empty)}I{controller.ClientName} {controller.Name} {{ get; }}"; + } + + public static string WriteRepositoryParameter(string key, AspNetCoreHttpController controller) + { + return $@"{key}{(key != null ? "." : "")}{(controller.NamespaceSuffix != null ? $"{controller.NamespaceSuffix}." : string.Empty)}I{controller.ClientName} param_{controller.Name.ToLower()}"; + } + + public static string WriteRepositoryAssignment(string key, AspNetCoreHttpController controller) + { + return $@"this.{controller.Name} = param_{controller.Name.ToLower()};"; + } + + + #endregion Repository + + #region Version Blocks + + public static string WriteVersionBlocks(GenerationContext context) + { + var versions = context.HttpClients.Where(x => x.Generated) + .GroupBy(x => x.NamespaceVersion) + .OrderBy(x => x.Key) + .ToList(); + + if (!versions.Any()) + { + return string.Empty; + } + + return +$@" + +{string.Join(Environment.NewLine, versions.Select(WriteVersionGroup))} + +"; + + } + + public static string WriteVersionGroup(IGrouping version) + { + return +$@" +namespace { Settings.ClientNamespace }{(version.Key != null ? "." : "")}{version.Key} +{{ +{string.Join(Environment.NewLine, version.OrderBy(x => x.Name).Select(WriteController))} +}} +"; + } + + #endregion Version Blocks + + + #region Class + + + public static string WriteController(AspNetCoreHttpController controller) + { + return +$@" +{(controller.NamespaceSuffix != null ? $@"namespace {controller.NamespaceSuffix} +{{" : string.Empty)} + +{WriteClassInterface(controller)} + +{WriteClassImplementation(controller)} + +{(controller.NamespaceSuffix != null ? $@"}}" : string.Empty)} +"; + } + + public static string WriteClassInterface(AspNetCoreHttpController controller) + { + return +$@" +{SharedWriter.GetObsolete(controller)} +public interface I{controller.ClientName} : I{Settings.ClientInterfaceName} +{{ +{string.Join($"{Environment.NewLine}", controller.GetEndpoints().Select(WriteEndpointInterface))} +}} +"; + } + + public static string WriteClassImplementation(AspNetCoreHttpController controller) + { + var dependencies = controller.GetInjectionDependencies().ToList(); + dependencies.Insert(0, new ClientDependency($"I{Settings.ClientInterfaceName}Wrapper")); + + return +$@" +{SharedWriter.GetObsolete(controller)} +{(Settings.UseInternalClients ? "internal" : "public")} class {controller.ClientName} : I{controller.ClientName} +{{ +{string.Join($"{Environment.NewLine}", dependencies.Select(WriteDependenciesField))} + + public {controller.ClientName}( +{string.Join($",{Environment.NewLine}", dependencies.Select(WriteDependenciesParameter))}) + {{ +{string.Join($"{Environment.NewLine}", dependencies.Select(WriteDependenciesAssignment))} + }} + +{string.Join($"{Environment.NewLine}", controller.GetEndpoints().Select(x => WriteEndpointImplementation(controller, x)))} +}} +"; + } + + #endregion Class + + #region Dependencies + + public static string WriteDependenciesField(IDependency dependency) + { + return $@"protected readonly {dependency.GetDependencyFieldType($"I{Settings.ClientInterfaceName}")} {dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")};"; + } + + public static string WriteDependenciesParameter(IDependency dependency) + { + return $@"{dependency.GetDependencyParameterType($"I{Settings.ClientInterfaceName}")} param_{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}").ToLower()}"; + } + + public static string WriteDependenciesAssignment(IDependency dependency) + { + string assignmentValue = $"param_{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}").ToLower()}"; + + if (dependency.HasAssignmentOverride) + { + return $@"{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")} = {dependency.GetAssignmentOverride(assignmentValue)};"; + } + else + { + return $@"{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")} = {assignmentValue};"; + } + + } + + #endregion Dependencies + + + #region Endpoint + + public static string WriteEndpointInterface(AspNetCoreHttpEndpoint endpoint) + { + return +$@" +{SharedWriter.GetObsolete(endpoint)} +{SharedWriter.GetInterfaceReturnType(endpoint.ReturnType, false)} {endpoint.FormattedName} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParameters().FilterResponseTypes(endpoint.ResponseTypes).Select(SharedWriter.GetParameter).NotNull())} +); + +{SharedWriter.GetObsolete(endpoint)} +{SharedWriter.GetInterfaceReturnType(nameof(HttpResponseMessage), false)} {endpoint.FormattedName}Raw +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypes().Select(SharedWriter.GetParameter).NotNull())} +); + +{SharedWriter.GetObsolete(endpoint)} +{SharedWriter.GetInterfaceReturnType(endpoint.ReturnType, true)} {endpoint.FormattedName}Async +( +{string.Join($",{Environment.NewLine}", endpoint.GetParameters().FilterResponseTypes(endpoint.ResponseTypes).Select(SharedWriter.GetParameter).NotNull())} +); + +{SharedWriter.GetObsolete(endpoint)} +{SharedWriter.GetInterfaceReturnType(nameof(HttpResponseMessage), true)} {endpoint.FormattedName}RawAsync +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypes().Select(SharedWriter.GetParameter).NotNull())} +); + +"; + } + + public static string WriteEndpointImplementation(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint) + { + return +$@" + +{SharedWriter.GetObsolete(endpoint)} +public {SharedWriter.GetImplementationReturnType(endpoint.ReturnType, false)} {endpoint.FormattedName} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParameters().FilterResponseTypes(endpoint.ResponseTypes).Select(SharedWriter.GetParameter).NotNull())} +) +{{ +{GetMethodDetails(controller, endpoint, false, false)} +}} + +{SharedWriter.GetObsolete(endpoint)} +public {SharedWriter.GetImplementationReturnType(nameof(HttpResponseMessage), false)} {endpoint.FormattedName}Raw +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypes().Select(SharedWriter.GetParameter).NotNull())} +) +{{ +{GetMethodDetails(controller, endpoint, false, true)} +}} + +{SharedWriter.GetObsolete(endpoint)} +public {SharedWriter.GetImplementationReturnType(endpoint.ReturnType, true)} {endpoint.FormattedName}Async +( +{string.Join($",{Environment.NewLine}", endpoint.GetParameters().FilterResponseTypes(endpoint.ResponseTypes).Select(SharedWriter.GetParameter).NotNull())} +) +{{ +{GetMethodDetails(controller, endpoint, true, false)} +}} + +{SharedWriter.GetObsolete(endpoint)} +public {SharedWriter.GetImplementationReturnType(nameof(HttpResponseMessage), true)} {endpoint.FormattedName}RawAsync +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypes().Select(SharedWriter.GetParameter).NotNull())} +) +{{ +{GetMethodDetails(controller, endpoint, true, true)} +}} + +"; + } + + + public static string GetMethodDetails(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint, bool async, bool raw) + { + var cancellationToken = endpoint.GetRequestModifiers().OfType().SingleOrDefault(); + var clientDependency = new ClientDependency($"I{Settings.ClientInterfaceName}Wrapper"); + + var requestModifiers = endpoint.GetRequestModifiers().ToList(); + + var bodyParameter = endpoint.GetBodyParameter(); + string bodyVariable = bodyParameter?.Name ?? "null"; + + var responseTypes = endpoint.GetResponseTypes(); + + var routeConstraints = endpoint.GetRouteConstraints(controller); + + return +$@"{string.Join(Environment.NewLine, routeConstraints.Select(SharedWriter.WriteRouteConstraint).NotNull())} +{GetEndpointInfoVariables(controller, endpoint)} +string url = $@""{GetRoute(controller, endpoint, async)}""; +HttpResponseMessage response = null; +response = {SharedWriter.GetAwait(async)}HttpOverride.GetResponseAsync({SharedWriter.GetHttpMethod(endpoint.HttpType)}, url, null, {cancellationToken.Name}){SharedWriter.GetAsyncEnding(async)}; +bool {Constants.ResponseHandledVariable} = response != null; + +if(response == null) +{{ + try + {{ + response = {SharedWriter.GetAwait(async)}{clientDependency.GetDependencyName($"I{Settings.ClientInterfaceName}")}.{nameof(IClientWrapper.ClientWrapper)} + .Request(url) +{string.Join($" {Environment.NewLine}", requestModifiers.Select(SharedWriter.WriteRequestModifiers).NotNull())} + .AllowAnyHttpStatus() + {GetHttpMethod(endpoint)} + {SharedWriter.GetAsyncEnding(async)}; + }} + catch({nameof(FlurlHttpException)} fhex) + {{ +{SharedWriter.WriteResponseType(responseTypes.OfType().Single(), async, false)} +{WriteErrorActionResultReturn(endpoint, async, raw)} + }} + + {SharedWriter.GetAwait(async)}HttpOverride.OnNonOverridedResponseAsync({SharedWriter.GetHttpMethod(endpoint.HttpType)}, url, {bodyVariable}, response, {cancellationToken.Name}){SharedWriter.GetAsyncEnding(async)}; +}} +{string.Join(Environment.NewLine, responseTypes.Where(x => x.GetType() != typeof(ExceptionResponseType)).Select(x => SharedWriter.WriteResponseType(x, async, raw)).NotNull())} +{WriteActionResultReturn(endpoint, async, raw)}"; + } + + + + public static string WriteErrorActionResultReturn(AspNetCoreHttpEndpoint endpoint, bool async, bool raw) + { + if (raw) + { + return @"return null;"; + } + + if (endpoint.ReturnType != null) + { + return $@"return default({endpoint.ReturnType});"; + } + + return "return;"; + } + + public static string WriteActionResultReturn(AspNetCoreHttpEndpoint endpoint, bool async, bool raw) + { + if (raw) + { + return @"return response;"; + } + + if (endpoint.ReturnsStream) + { + return +$@" +if(response.IsSuccessStatusCode) +{{ + return {SharedWriter.GetAwait(async)}response.Content.ReadAsStreamAsync(){SharedWriter.GetAsyncEnding(async)}; +}} +else +{{" ++ + (Settings.ErrorOnUnhandledCallback ? +$@" +if(!{Constants.ResponseHandledVariable}) + {{ + throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); + }} +" : string.Empty) ++ +$@" + return default({endpoint.ReturnType}); +}} +"; + } + else + { + if (endpoint.ReturnType != null) + { + return + $@" +if(response.IsSuccessStatusCode) +{{ + return {SharedWriter.GetAwait(async)}Serializer.Deserialize<{endpoint.ReturnType}>(response.Content){SharedWriter.GetAsyncEnding(async)}; +}} +else +{{" ++ + (Settings.ErrorOnUnhandledCallback ? +$@" +if(!{Constants.ResponseHandledVariable}) + {{ + throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); + }} +" : string.Empty) ++ +$@" + return default({endpoint.ReturnType}); +}} +"; + } + } + + if (Settings.ErrorOnUnhandledCallback) + { + return +$@" +if(!{Constants.ResponseHandledVariable}) +{{ + throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); +}} + +return;"; + } + else + { + return $@"return;"; + } + + } + + + public static string GetHttpMethod(AspNetCoreHttpEndpoint endpoint) + { + var cancellationToken = endpoint.GetRequestModifiers().OfType().SingleOrDefault(); + var bodyParameter = endpoint.GetBodyParameter(); + + string bodyString = null; + + if (bodyParameter?.Name == null) + { + bodyString = "null"; + } + else + { + bodyString = $@"Serializer.Serialize({bodyParameter?.Name})"; + } + + if (endpoint.HttpType.Method.Equals(HttpMethod.Delete.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.DeleteAsync)}({cancellationToken?.Name})"; + } + else if (endpoint.HttpType.Method.Equals(HttpMethod.Get.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.GetAsync)}({cancellationToken?.Name})"; + } + else if (endpoint.HttpType.Method.Equals(HttpMethod.Put.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.PutAsync)}({bodyString}, {cancellationToken?.Name})"; + } + else if (endpoint.HttpType.Method.Equals(new HttpMethod("PATCH").Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.PatchAsync)}({bodyString}, {cancellationToken?.Name})"; + } + else if (endpoint.HttpType.Method.Equals(HttpMethod.Post.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.PostAsync)}({bodyString}, {cancellationToken?.Name})"; + } + else + { + return $"#error Unsupported HttpMethod of {endpoint.HttpType.Method}"; + } + } + + public static string GetEndpointInfoVariables(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint) + { + + var controllerVar = $@"var {Constants.ControllerRouteReserved} = ""{endpoint.Parent.Name}"";"; + var actionVar = $@"var {Constants.ActionRouteReserved} = ""{endpoint.FormattedName}"";"; + + + if (!endpoint.GetFullRoute(controller)?.Contains($"[{Constants.ControllerRouteReserved}]") ?? false) + { + controllerVar = null; + } + + if (!endpoint.GetFullRoute(controller)?.Contains($"[{Constants.ActionRouteReserved}]") ?? false) + { + actionVar = null; + } + + + return +$@"{controllerVar} +{actionVar}"; + } + + public static string GetRoute(AspNetCoreHttpController controller, AspNetCoreHttpEndpoint endpoint, bool async) + { + var route = endpoint.GetFullRoute(controller); + string routeUnformatted = route?.Value; + + + var template = TemplateParser.Parse(routeUnformatted); + + var routeParameters = endpoint.GetRouteParameters().ToList(); + var queryParameters = endpoint.GetQueryParameters().ToList(); + + foreach (var parameter in template.Parameters) + { + if (parameter.Name.Equals("version", StringComparison.CurrentCultureIgnoreCase)) + { + routeUnformatted = routeUnformatted.Replace(parameter.BackToRouteParameter(), route.Version.Version); + continue; + } + + if (!routeParameters.Any(x => x.Name.Equals(parameter.Name, StringComparison.CurrentCultureIgnoreCase))) + { + throw new Exception($"{parameter.Name} is missing from passed in parameters. Please check your route."); + } + + var routeParameter = routeParameters.SingleOrDefault(x => x.Name.Equals(parameter.Name, StringComparison.CurrentCultureIgnoreCase)); + if (Helpers.IsRoutableType(routeParameter.Type)) + { + routeUnformatted = routeUnformatted.Replace(parameter.BackToRouteParameter(), $"{{{Helpers.GetRouteStringTransform(routeParameter.Name, routeParameter.Type)}.{nameof(GeneratorExtensions.GeneratorExtensions.EncodeForUrl)}()}}"); + } + } + + if (queryParameters.Any()) + { + string queryString = $"?{string.Join("&", queryParameters.Select(x => SharedWriter.WriteQueryParameter(x, async)))}"; + + routeUnformatted += $"{queryString}"; + } + + + + routeUnformatted = routeUnformatted.Replace($"[{Constants.ControllerRouteReserved}]", $"{{{Constants.ControllerRouteReserved}}}"); + routeUnformatted = routeUnformatted.Replace($"[{Constants.ActionRouteReserved}]", $"{{{Constants.ActionRouteReserved}}}"); + return routeUnformatted; + } + + + + #endregion Endpoint + } + + #endregion HTTP + + #region Functions + + public static class FunctionClassWriter + { + public static string WriteErrorMessage(FunctionEndpoint endpoint) + { + if (endpoint.Failed) + { + return $@"#warning {(endpoint.UnexpectedFailure ? "PLEASE MAKE A GITHUB REPO ISSUE" : "")} {endpoint.Name}Controller {(endpoint.UnexpectedFailure ? "has failed generation with unexpected error" : "is misconfigured for generation")} :: {endpoint.Error.Replace('\r', ' ').Replace('\n', ' ')}"; + } + else + { + return null; + } + } + + #region Installer + + public static string WriteClientRegistration(FunctionEndpoint controller) + { + string interfaceName = $@"I{controller.ClientName}"; + string implementationName = $@"{controller.ClientName}"; + return $@" services.AddScoped<{interfaceName}, {implementationName}>();"; + } + + + #endregion Installer + + + #region Class + + + public static string WriteBlocks(GenerationContext context) + { + var functions = context.Functions.Where(x => x.Generated) + .OrderBy(x => x.Name) + .ToList(); + + if (!functions.Any()) + { + return string.Empty; + } + + return +$@" +namespace {Settings.ClientNamespace} +{{ +{string.Join(Environment.NewLine, functions.Select(WriteController))} +}} + +"; + + } + + + public static string WriteController(FunctionEndpoint endpoint) + { + return +$@" +{WriteClassInterface(endpoint)} + +{WriteClassImplementation(endpoint)} +"; + } + + public static string WriteClassInterface(FunctionEndpoint endpoint) + { + return +$@" +{SharedWriter.GetObsolete(endpoint)} +public interface I{endpoint.ClientName} : I{Settings.ClientInterfaceName} +{{ +{string.Join($"{Environment.NewLine}", endpoint.SupportedMethods.Select(x => WriteEndpointInterface(endpoint, x)))} +}} +"; + } + + public static string WriteClassImplementation(FunctionEndpoint endpoint) + { + var dependencies = endpoint.GetInjectionDependencies().ToList(); + dependencies.Insert(0, new ClientDependency($"I{Settings.ClientInterfaceName}Wrapper")); + + return +$@" +{SharedWriter.GetObsolete(endpoint)} +{(Settings.UseInternalClients ? "internal" : "public")} class {endpoint.ClientName} : I{endpoint.ClientName} +{{ +{string.Join($"{Environment.NewLine}", dependencies.Select(WriteDependenciesField))} + + public {endpoint.ClientName}( +{string.Join($",{Environment.NewLine}", dependencies.Select(WriteDependenciesParameter))}) + {{ +{string.Join($"{Environment.NewLine}", dependencies.Select(WriteDependenciesAssignment))} + }} + +{string.Join($"{Environment.NewLine}", endpoint.SupportedMethods.Select(x => WriteEndpointImplementation(endpoint, x)))} +}} +"; + } + + #endregion Class + + #region Dependencies + + public static string WriteDependenciesField(IDependency dependency) + { + return $@"protected readonly {dependency.GetDependencyFieldType($"I{Settings.ClientInterfaceName}")} {dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")};"; + } + + public static string WriteDependenciesParameter(IDependency dependency) + { + return $@"{dependency.GetDependencyParameterType($"I{Settings.ClientInterfaceName}")} param_{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}").ToLower()}"; + } + + public static string WriteDependenciesAssignment(IDependency dependency) + { + string assignmentValue = $"param_{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}").ToLower()}"; + + if (dependency.HasAssignmentOverride) + { + return $@"{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")} = {dependency.GetAssignmentOverride(assignmentValue)};"; + } + else + { + return $@"{dependency.GetDependencyName($"I{Settings.ClientInterfaceName}")} = {assignmentValue};"; + } + + } + + #endregion Dependencies + + + #region Endpoint + + public static string WriteEndpointInterface(FunctionEndpoint endpoint, HttpMethod method) + { + return +$@" +{SharedWriter.GetObsolete(endpoint)} +{SharedWriter.GetInterfaceReturnType(endpoint.ReturnType, false)} {endpoint.GetEndpointName(method, false, false)} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} +); + +{SharedWriter.GetObsolete(endpoint)} +{SharedWriter.GetInterfaceReturnType(nameof(HttpResponseMessage), false)} {endpoint.GetEndpointName(method, true, false)} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} +); + +{SharedWriter.GetObsolete(endpoint)} +{SharedWriter.GetInterfaceReturnType(endpoint.ReturnType, true)} {endpoint.GetEndpointName(method, false, true)} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} +); + +{SharedWriter.GetObsolete(endpoint)} +{SharedWriter.GetInterfaceReturnType(nameof(HttpResponseMessage), true)} {endpoint.GetEndpointName(method, true, true)} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} +); + +"; + } + + public static string WriteEndpointImplementation(FunctionEndpoint endpoint, HttpMethod method) + { + return +$@" + +{SharedWriter.GetObsolete(endpoint)} +public {SharedWriter.GetImplementationReturnType(endpoint.ReturnType, false)} {endpoint.GetEndpointName(method, false, false)} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} +) +{{ +{GetMethodDetails(endpoint, method, false, false)} +}} + +{SharedWriter.GetObsolete(endpoint)} +public {SharedWriter.GetImplementationReturnType(nameof(HttpResponseMessage), false)} {endpoint.GetEndpointName(method, true, false)} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} +) +{{ +{GetMethodDetails(endpoint, method, false, true)} +}} + +{SharedWriter.GetObsolete(endpoint)} +public {SharedWriter.GetImplementationReturnType(endpoint.ReturnType, true)} {endpoint.GetEndpointName(method, false, true)} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} +) +{{ +{GetMethodDetails(endpoint, method, true, false)} +}} + +{SharedWriter.GetObsolete(endpoint)} +public {SharedWriter.GetImplementationReturnType(nameof(HttpResponseMessage), true)} {endpoint.GetEndpointName(method, true, true)} +( +{string.Join($",{Environment.NewLine}", endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).Select(SharedWriter.GetParameter).NotNull())} +) +{{ +{GetMethodDetails(endpoint, method, true, true)} +}} + +"; + } + + + public static string GetMethodDetails(FunctionEndpoint endpoint, HttpMethod method, bool async, bool raw) + { + var cancellationToken = endpoint.GetRequestModifiers().OfType().SingleOrDefault(); + var clientDependency = new ClientDependency($"I{Settings.ClientInterfaceName}Wrapper"); + + var requestModifiers = endpoint.GetRequestModifiers().ToList(); + + var bodyParameter = endpoint.GetBodyParameter(method); + string bodyVariable = bodyParameter?.Name ?? "null"; + + var responseTypes = endpoint.GetResponseTypes(); + + var routeConstraints = endpoint.GetRouteConstraints(); + + return +$@"{string.Join(Environment.NewLine, routeConstraints.Select(SharedWriter.WriteRouteConstraint).NotNull())} +string url = $@""{GetRoute(endpoint, method, async)}""; + +HttpResponseMessage response = null; +response = {SharedWriter.GetAwait(async)}HttpOverride.GetResponseAsync({SharedWriter.GetHttpMethod(method)}, url, null, {cancellationToken.Name}){SharedWriter.GetAsyncEnding(async)}; + +bool {Constants.ResponseHandledVariable} = response != null; + +if(response == null) +{{ + try + {{ + response = {SharedWriter.GetAwait(async)}{clientDependency.GetDependencyName($"I{Settings.ClientInterfaceName}")}.{nameof(IClientWrapper.ClientWrapper)} + .Request(url) +{string.Join($" {Environment.NewLine}", requestModifiers.Select(SharedWriter.WriteRequestModifiers).NotNull())} + .AllowAnyHttpStatus() + {GetHttpMethod(endpoint, method)} + {SharedWriter.GetAsyncEnding(async)}; + }} + catch({nameof(FlurlHttpException)} fhex) + {{ +{SharedWriter.WriteResponseType(responseTypes.OfType().Single(), async, false)} +{WriteErrorActionResultReturn(endpoint, async, raw)} + }} + + {SharedWriter.GetAwait(async)}HttpOverride.OnNonOverridedResponseAsync({SharedWriter.GetHttpMethod(method)}, url, {bodyVariable}, response, {cancellationToken.Name}){SharedWriter.GetAsyncEnding(async)}; +}} +{string.Join(Environment.NewLine, responseTypes.Where(x => x.GetType() != typeof(ExceptionResponseType)).Select(x => SharedWriter.WriteResponseType(x, async, raw)).NotNull())} +{WriteActionResultReturn(endpoint, async, raw)}"; + } + + + public static string WriteErrorActionResultReturn(FunctionEndpoint endpoint, bool async, bool raw) + { + if (raw) + { + return @"return null;"; + } + + if (endpoint.ReturnType != null) + { + return $@"return default({endpoint.ReturnType});"; + } + + return "return;"; + } + + public static string WriteActionResultReturn(FunctionEndpoint endpoint, bool async, bool raw) + { + if (raw) + { + return @"return response;"; + } + + if (endpoint.ReturnsStream) + { + return +$@" +if(response.IsSuccessStatusCode) +{{ + return {SharedWriter.GetAwait(async)}response.Content.ReadAsStreamAsync(){SharedWriter.GetAsyncEnding(async)}; +}} +else +{{ + if(!{Constants.ResponseHandledVariable}) + {{ + throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); + }} + + return default({endpoint.ReturnType}); +}} +"; + } + else + { + if (endpoint.ReturnType != null) + { + return + $@" +if(response.IsSuccessStatusCode) +{{ + return {SharedWriter.GetAwait(async)}Serializer.Deserialize<{endpoint.ReturnType}>(response.Content){SharedWriter.GetAsyncEnding(async)}; +}} +else +{{ + if(!{Constants.ResponseHandledVariable}) + {{ + throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); + }} + + return default({endpoint.ReturnType}); +}} +"; + } + } + + + + if (Settings.ErrorOnUnhandledCallback) + { + return +$@" +if(!{Constants.ResponseHandledVariable}) +{{ + throw new System.InvalidOperationException($""Response Status of {{response.StatusCode}} was not handled properly.""); +}} + +return;"; + } + else + { + return $@"return;"; + } + } + + public static string GetHttpMethod(FunctionEndpoint endpoint, HttpMethod method) + { + var cancellationToken = endpoint.GetRequestModifiers().OfType().SingleOrDefault(); + var bodyParameter = endpoint.GetBodyParameter(method); + + string bodyString = null; + + if (bodyParameter?.Name == null) + { + bodyString = "null"; + } + else + { + bodyString = $@"Serializer.Serialize({bodyParameter?.Name})"; + } + + if (method.Method.Equals(HttpMethod.Delete.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.DeleteAsync)}({cancellationToken?.Name})"; + } + else if (method.Method.Equals(HttpMethod.Get.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.GetAsync)}({cancellationToken?.Name})"; + } + else if (method.Method.Equals(HttpMethod.Put.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.PutAsync)}({bodyString}, {cancellationToken?.Name})"; + } + else if (method.Method.Equals(new HttpMethod("PATCH").Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.PatchAsync)}({bodyString}, {cancellationToken?.Name})"; + } + else if (method.Method.Equals(HttpMethod.Post.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $".{nameof(GeneratedExtensions.PostAsync)}({bodyString}, {cancellationToken?.Name})"; + } + else + { + return $"#error Unsupported HttpMethod of {method}"; + } + } + + public static string GetRoute(FunctionEndpoint endpoint, HttpMethod method, bool async) + { + var route = endpoint.GetFullRoute(); + string routeUnformatted = endpoint.GetFullRoute()?.Value; + + if (routeUnformatted == null) + { + return string.Empty; + } + + var template = TemplateParser.Parse(routeUnformatted); + + var routeParameters = endpoint.GetRouteParameters().ToList(); + var queryParameters = endpoint.GetQueryParameters(method).ToList(); + + foreach (var parameter in template.Parameters) + { + if (parameter.Name.Equals("version", StringComparison.CurrentCultureIgnoreCase)) + { + routeUnformatted = routeUnformatted.Replace(parameter.BackToRouteParameter(), route.Version.Version); + continue; + } + + if (!routeParameters.Any(x => x.Name.Equals(parameter.Name, StringComparison.CurrentCultureIgnoreCase))) + { + throw new Exception($"{parameter.Name} is missing from passed in parameters. Please check your route."); + } + + var routeParameter = routeParameters.SingleOrDefault(x => x.Name.Equals(parameter.Name, StringComparison.CurrentCultureIgnoreCase)); + if (Helpers.IsRoutableType(routeParameter.Type)) + { + routeUnformatted = routeUnformatted.Replace(parameter.BackToRouteParameter(), $"{{{Helpers.GetRouteStringTransform(routeParameter.Name, routeParameter.Type)}}}"); + } + } + + if (queryParameters.Any()) + { + string queryString = $"?{string.Join("&", queryParameters.Select(x => SharedWriter.WriteQueryParameter(x, async)))}"; + + routeUnformatted += $"{queryString}"; + } + + + + routeUnformatted = routeUnformatted.Replace($"[{Constants.ControllerRouteReserved}]", $"{{{Constants.ControllerRouteReserved}}}"); + routeUnformatted = routeUnformatted.Replace($"[{Constants.ActionRouteReserved}]", $"{{{Constants.ActionRouteReserved}}}"); + return routeUnformatted; + } + + + + #endregion Endpoint + } + + #endregion Functions + + public static class SharedWriter + { + + public static string WriteStaticRoutes(GenerationContext context) + { + return +$@" +{HttpClassWriter.WriteStaticRoutesRepository(context)} +"; + } + + public static string WriteInstallFile(GenerationContext context) + { + return +$@"namespace {Settings.ClientNamespace} +{{ + +{SharedWriter.WriteInstaller(context)} + +{SharedWriter.WriteBaseClients()} + +{HttpClassWriter.WriteRepositories(context)} + +}}"; + } + + + public static string GetParameter(IParameter parameter) + { + if (parameter is QueryParameter qp + && qp.IsConstant) + { + return null; + } + + return $@"{parameter.Type} {parameter.Name}{(parameter.DefaultValue != null ? $" = {parameter.DefaultValue}" : $"")}"; + } + + public static string GetObsolete(IObsolete ob) + { + if (ob.Obsolete) + { + return $@"[{nameof(ObsoleteAttribute)}(""{ob.ObsoleteMessage}"")]"; + } + else + { + return string.Empty; + } + + } + + + public static string WriteInstaller(GenerationContext context) + { + var versions = context.HttpClients.Where(x => x.Generated) + .GroupBy(x => x.NamespaceVersion) + .OrderBy(x => x.Key) + .Select(x => x.Key) + .ToList(); + + return +$@" + +{string.Join(Environment.NewLine, context.HttpClients.Select(HttpClassWriter.WriteErrorMessage).NotNull())} +{string.Join(Environment.NewLine, context.HubClients.Select(SignalRClassWriter.WriteErrorMessage).NotNull())} +{string.Join(Environment.NewLine, context.Functions.Select(FunctionClassWriter.WriteErrorMessage).NotNull())} + + + public static class {Settings.ClientInterfaceName}Installer + {{ + /// + /// Register the autogenerated clients into the container with a lifecycle of scoped. + /// + /// + /// Overrides for client configuration + /// + public static {nameof(IServiceCollection)} Add{Settings.RegisterName}Clients(this {nameof(IServiceCollection)} services, Action<{nameof(ClientConfiguration)}> configure) + {{ + var configuration = new {nameof(ClientConfiguration)}(); + + configuration.{nameof(ClientConfiguration.RegisterClientWrapperCreator)}({Settings.ClientInterfaceName}Wrapper.Create); + configuration.{nameof(ClientConfiguration.UseClientWrapper)}((provider) => new {Settings.ClientInterfaceName}Wrapper(provider.GetService>(), configuration.{nameof(ClientConfiguration.GetSettings)}(), provider)); + + configure?.Invoke(configuration); + +{string.Join(Environment.NewLine, versions.Select(HttpClassWriter.WriteRepositoryRegistration))} +{string.Join(Environment.NewLine, context.HttpClients.Where(x => x.Generated).OrderBy(x => x.Name).Select(HttpClassWriter.WriteClientRegistration))} + +{string.Join(Environment.NewLine, context.Functions.Where(x => x.Generated).OrderBy(x => x.Name).Select(FunctionClassWriter.WriteClientRegistration))} + + return configuration.{nameof(ClientConfiguration.ApplyConfiguration)}(services); + }} + }} +"; + } + + + + public static string WriteBaseClients() + { + return +$@" +public interface I{Settings.ClientInterfaceName}Wrapper : IClientWrapper {{ }} + +public class {Settings.ClientInterfaceName}Wrapper : I{Settings.ClientInterfaceName}Wrapper +{{ + public TimeSpan Timeout {{ get; internal set; }} + public {nameof(IFlurlClient)} {Constants.FlurlClientVariable} {{ get; internal set; }} + + public {Settings.ClientInterfaceName}Wrapper(Func client, {nameof(ClientSettings)} settings, {nameof(IServiceProvider)} provider) + {{ + {Constants.FlurlClientVariable} = client(null); + if (settings.{nameof(ClientSettings.BaseAddress)} != null) + {{ + {Constants.FlurlClientVariable}.BaseUrl = settings.{nameof(ClientSettings.BaseAddress)}(provider); + }} + + Timeout = settings.{nameof(ClientSettings.Timeout)}; + }} + + public static I{Settings.ClientInterfaceName}Wrapper Create(Func client, {nameof(ClientSettings)} settings, {nameof(IServiceProvider)} provider) + {{ + return new {Settings.ClientInterfaceName}Wrapper(client, settings, provider); + }} +}} + +public interface I{Settings.ClientInterfaceName} : {nameof(IClient)} {{ }} +"; + } + + + + public static string GetAsyncEnding(bool async) + { + if (async) + { + return $@".ConfigureAwait(false)"; + } + else + { + return $@".ConfigureAwait(false).GetAwaiter().GetResult()"; + } + } + + public static string GetAwait(bool async) + { + if (async) + { + return "await "; + } + return null; + } + + public static string GetInterfaceReturnType(string returnType, bool async) + { + if (async) + { + if (returnType == null) + { + return $"Task"; + } + else + { + return $"{Helpers.GetTaskType()}<{returnType}>"; + } + } + else + { + if (returnType == null) + { + return $"void"; + } + else + { + return $"{returnType}"; + } + } + } + + public static string GetImplementationReturnType(string returnType, bool async) + { + if (async) + { + if (returnType == null) + { + return $"async Task"; + } + else + { + return $"async {Helpers.GetTaskType()}<{returnType}>"; + } + } + else + { + if (returnType == null) + { + return $"void"; + } + else + { + return $"{returnType}"; + } + } + } + + + public static string WriteQueryParameter(QueryParameter parameter, bool async) + { + if (parameter.IsConstant) + { + return parameter.Name; + } + + string name = $"{{nameof({parameter.Name})}}"; + + if (Helpers.IsEnumerable(parameter.Type)) + { + return $@"{{string.Join(""&"",{parameter.Name}.Select(x => $""{name}={{{Helpers.GetRouteStringTransform("x", parameter.Type)}.{nameof(GeneratorExtensions.GeneratorExtensions.EncodeForUrl)}()}}""))}}"; + } + else + { + if (parameter.QueryObject) + { + return $"{{{GetAwait(async)}{parameter.Name}.{nameof(GeneratorExtensions.GeneratorExtensions.GetQueryObjectString)}(nameof({parameter.Name})){GetAsyncEnding(async)}}}"; + } + else + { + return $"{name}={{{Helpers.GetRouteStringTransform(parameter.Name, parameter.Type)}.{nameof(GeneratorExtensions.GeneratorExtensions.EncodeForUrl)}()}}"; + } + } + } + + + public static string WriteRequestModifiers(IRequestModifier modifier) + { + if (modifier is CookieModifier cm) + { + return $@".WithCookies({cm.Name})"; + } + else if (modifier is HeadersModifier hm) + { + return $@".WithHeaders({hm.Name})"; + } + else if (modifier is RequestModifierDependency rm) + { + return $@".WithRequestModifiers({rm.GetDependencyName($"I{Settings.ClientInterfaceName}")})"; + } + else if (modifier is SecurityModifier sm) + { + return $@".{nameof(GeneratorExtensions.GeneratorExtensions.WithAuth)}({sm.Name})"; + } + else if (modifier is FunctionAuthModifier fam) + { + return $@".{nameof(GeneratorExtensions.GeneratorExtensions.WithFunctionAuthorizationKey)}({fam.Name})"; + } + else if (modifier is TimeoutModifier tm) + { + var clientDependency = new ClientDependency(null); + return $@".WithTimeout({tm.Name} ?? {clientDependency.GetDependencyName($"I{Settings.ClientInterfaceName}")}.{nameof(IClientWrapper.Timeout)})"; + } + else if (modifier is ConstantHeader ch) + { + return $@".WithHeader(""{ch.Key}"", ""{ch.Value}"")"; + } + else if (modifier is ParameterHeader ph) + { + return $@".WithHeader(""{ph.Name}"", {ph.Name})"; + } + else if (modifier is CancellationTokenModifier ctm) + { + return null; + } + else + { + return $@"#warning IRequestModifier of type {modifier.GetType().Name} is not supported"; + } + } + + + + public static string WriteResponseType(ResponseType responseType, bool async, bool raw) + { + if (raw) + { + return null; + } + + return +$@" +{GetResponseTypeCheck(responseType)} +{GetResponseTypeInvoke(responseType, async)} +"; + } + + public static string GetResponseTypeCheck(ResponseType responseType) + { + return +$@" +if({responseType.Name} != null && {responseType.Name}.Method.IsDefined(typeof(AsyncStateMachineAttribute), true)) +{{ + throw new NotSupportedException(""Async void action delegates for {responseType.Name} are not supported.As they will run out of the scope of this call.""); +}} +"; + } + + public static string GetResponseTypeInvoke(ResponseType responseType, bool async) + { + if (responseType is ExceptionResponseType) + { + return +$@"if({responseType.Name} != null) +{{ + {Constants.ResponseHandledVariable} = true; + {responseType.Name}?.Invoke(fhex); +}} +else +{{ + throw fhex; +}} +"; + } + + if (responseType.Status == null) + { + return +$@" +if({responseType.Name} != null) +{{ + {Constants.ResponseHandledVariable} = true; + {responseType.Name}.Invoke(response); +}} +"; + } + else + { + string content = null; + if (responseType.ActionType == nameof(Stream)) + { + content = $@"{GetAwait(async)}response.Content.ReadAsStreamAsync(){GetAsyncEnding(async)}"; + } + else if (responseType.ActionType != null) + { + content = $@"{GetAwait(async)}Serializer.Deserialize<{responseType.ActionType}>(response.Content){GetAsyncEnding(async)}"; + } + + string statusValue = null; + if (responseType.Status == HttpStatusCode.RedirectMethod) + { + statusValue = nameof(HttpStatusCode.SeeOther); + } + else + { + statusValue = responseType.Status?.ToString(); + } + + return +$@" +if(response.StatusCode == System.Net.HttpStatusCode.{statusValue}) +{{ + if({responseType.Name} != null) + {{ + {Constants.ResponseHandledVariable} = true; + {responseType.Name}.Invoke({content}); + }} +}} +"; + } + } + + public static string WriteRouteConstraint(RouteConstraint constraint) + { + if (!Settings.ClientRouteConstraints) + { + return string.Empty; + } + + if (constraint is AlphaConstraint) + { + return $@" +if(string.IsNullOrWhiteSpace({constraint.ParameterName}) || {constraint.ParameterName}.Any(x=>char.IsNumber(x))) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} must only contain characters that are not numbers.""); +}}"; + } + else if (constraint is BoolConstraint) + { + return $@" +if(!bool.TryParse({constraint.ParameterName}.ToString(),out _)) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an bool.""); +}}"; + } + else if (constraint is DateTimeConstraint) + { + return $@" +if(!DateTime.TryParse({constraint.ParameterName}.ToString(),out _)) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an DateTime.""); +}}"; + } + else if (constraint is DecimalConstraint) + { + return $@" +if(!decimal.TryParse({constraint.ParameterName}.ToString(),out _)) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an decimal.""); +}}"; + } + else if (constraint is FloatConstraint) + { + return $@" +if(!float.TryParse({constraint.ParameterName}.ToString(),out _)) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an float.""); +}}"; + } + else if (constraint is GuidConstraint) + { + return $@" +if(!Guid.TryParse({constraint.ParameterName}.ToString(),out _)) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an Guid.""); +}}"; + } + else if (constraint is IntConstraint) + { + return $@" +if(!int.TryParse({constraint.ParameterName}.ToString(),out _)) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an int.""); +}}"; + } + else if (constraint is LengthConstraint) + { + var value = constraint.GetConstraintValue(); + if (value.Contains(',')) + { + var split = value.Split(','); + string minL = split[0]; + string maxL = split[1]; + + return $@" +if({constraint.ParameterName}.Length <= {minL} || {constraint.ParameterName}.Length >= {maxL}) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a length that is not between {minL} and {maxL}.""); +}}"; + } + else + { + return $@" +if({constraint.ParameterName}.Length == {value}) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a length that is not {value}.""); +}}"; + } + } + else if (constraint is LongConstraint) + { + return $@" +if(!long.TryParse({constraint.ParameterName}.ToString(),out _)) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not parse into an long.""); +}}"; + } + else if (constraint is MaxConstraint) + { + var value = constraint.GetConstraintValue(); + + return $@" +if({constraint.ParameterName} >= {value}) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a value more than {value}.""); +}}"; + } + else if (constraint is MaxLengthConstraint) + { + var value = constraint.GetConstraintValue(); + + return $@" +if({constraint.ParameterName}.Length >= {value}) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a length greater than {value}.""); +}}"; + } + else if (constraint is MinConstraint) + { + var value = constraint.GetConstraintValue(); + + return $@" +if({constraint.ParameterName} <= {value}) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a value less than {value}.""); +}}"; + } + else if (constraint is MinLengthConstraint) + { + var value = constraint.GetConstraintValue(); + + return $@" +if({constraint.ParameterName}.Length <= {value}) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a length less than {value}.""); +}}"; + } + else if (constraint is RangeConstraint) + { + var value = constraint.GetConstraintValue(); + + var split = value.Split(','); + string minL = split[0]; + string maxL = split[1]; + + return $@" +if({constraint.ParameterName} <= {minL} || {constraint.ParameterName} >= {maxL}) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} has a value that is not between {minL} and {maxL}.""); +}}"; + } + else if (constraint is RegexConstraint) + { + var value = constraint.GetConstraintValue(); + + return $@" +if(!(new Regex(@""{value}"").IsMatch({constraint.ParameterName}))) +{{ + throw new InvalidRouteException(""Parameter {constraint.ParameterName} does not follow the regex \""{value}\"".""); +}}"; + } + else if (constraint is RequiredConstraint) + { + return null; + } + else if (constraint is ApiVersionContraint) + { + return null; + } + else + { + return $@"#error A route constraint of type {constraint.GetType().Name} and text of {constraint.Constraint} is not supported"; + } + } + + + public static string GetHttpMethod(HttpMethod method) + { + if (method.Method.Equals(HttpMethod.Delete.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $"{nameof(HttpMethod)}.{nameof(HttpMethod.Delete)}"; + } + else if (method.Method.Equals(HttpMethod.Get.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $"{nameof(HttpMethod)}.{nameof(HttpMethod.Get)}"; + } + else if (method.Method.Equals(HttpMethod.Put.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $"{nameof(HttpMethod)}.{nameof(HttpMethod.Put)}"; + } + else if (method.Method.Equals(new HttpMethod("PATCH").Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $@"new {nameof(HttpMethod)}(""PATCH"")"; + } + else if (method.Method.Equals(HttpMethod.Post.Method, StringComparison.CurrentCultureIgnoreCase)) + { + return $"{nameof(HttpMethod)}.{nameof(HttpMethod.Post)}"; + } + else + { + return $"#error Unsupported HttpMethod of {method.Method}"; + } + } + + + + public static string GetRelativePath(string file) + { + var root = Path.GetFullPath($"{Environment.CurrentDirectory}/{Settings.RouteToServiceProjectFolder}"); + var fullFile = Path.GetFullPath(file); + + return fullFile.Replace(root, "").Trim('\\'); + } + } +} + diff --git a/src/AspNetCore.Client.Generator/Settings.cs b/src/Beffyman.AspNetCore.Client.Generator/Settings.cs similarity index 90% rename from src/AspNetCore.Client.Generator/Settings.cs rename to src/Beffyman.AspNetCore.Client.Generator/Settings.cs index 476d844..167940b 100644 --- a/src/AspNetCore.Client.Generator/Settings.cs +++ b/src/Beffyman.AspNetCore.Client.Generator/Settings.cs @@ -1,6 +1,6 @@ -namespace AspNetCore.Client.Generator +namespace Beffyman.AspNetCore.Client.Generator { - internal class Settings + internal static class Settings { public static string RouteToServiceProjectFolder { get; set; } public static string ClientInterfaceName { get; set; } diff --git a/src/AspNetCore.Client.Generator/build/AspNetCore.Client.Generator.props b/src/Beffyman.AspNetCore.Client.Generator/build/Beffyman.AspNetCore.Client.Generator.props similarity index 62% rename from src/AspNetCore.Client.Generator/build/AspNetCore.Client.Generator.props rename to src/Beffyman.AspNetCore.Client.Generator/build/Beffyman.AspNetCore.Client.Generator.props index 2d75d80..3ea0651 100644 --- a/src/AspNetCore.Client.Generator/build/AspNetCore.Client.Generator.props +++ b/src/Beffyman.AspNetCore.Client.Generator/build/Beffyman.AspNetCore.Client.Generator.props @@ -1,8 +1,8 @@  - <_AspNetCoreClientGeneratorTaskAssembly Condition="'$(MSBuildRuntimeType)' == 'Core'">.\netstandard2.0\AspNetCore.Client.Generator.dll - <_AspNetCoreClientGeneratorTaskAssembly Condition="'$(MSBuildRuntimeType)' != 'Core'">.\net462\AspNetCore.Client.Generator.dll + <_BeffymanAspNetCoreClientGeneratorTaskAssembly Condition="'$(MSBuildRuntimeType)' == 'Core'">.\netstandard2.1\Beffyman.AspNetCore.Client.Generator.dll + <_BeffymanAspNetCoreClientGeneratorTaskAssembly Condition="'$(MSBuildRuntimeType)' != 'Core'">.\net472\Beffyman.AspNetCore.Client.Generator.dll @@ -23,6 +23,6 @@ - + \ No newline at end of file diff --git a/src/AspNetCore.Client.Generator/build/AspNetCore.Client.Generator.targets b/src/Beffyman.AspNetCore.Client.Generator/build/Beffyman.AspNetCore.Client.Generator.targets similarity index 80% rename from src/AspNetCore.Client.Generator/build/AspNetCore.Client.Generator.targets rename to src/Beffyman.AspNetCore.Client.Generator/build/Beffyman.AspNetCore.Client.Generator.targets index f1c3f89..3557649 100644 --- a/src/AspNetCore.Client.Generator/build/AspNetCore.Client.Generator.targets +++ b/src/Beffyman.AspNetCore.Client.Generator/build/Beffyman.AspNetCore.Client.Generator.targets @@ -1,12 +1,12 @@  - - + - + + + netstandard2.0 + bin\$(Configuration)\$(TargetFramework)\Beffyman.AspNetCore.Client.Http.xml + Contains extension method to install the HttpClientFactory as the used http clients + true + + + + + + + + + + diff --git a/src/Beffyman.AspNetCore.Client.Http/HttpInstaller.cs b/src/Beffyman.AspNetCore.Client.Http/HttpInstaller.cs new file mode 100644 index 0000000..62a4d65 --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.Http/HttpInstaller.cs @@ -0,0 +1,51 @@ +using System; +using System.Net.Http; +using Flurl.Http; +using Flurl.Http.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Beffyman.AspNetCore.Client +{ + /// + /// Used to install an HttpClientFactory HttpClient provider + /// + public static class HttpInstaller + { + /// + /// Enables the use of Microsoft.Extensions.Http for injecting HttpClients that the IFlurlClient will use. + /// + /// + /// + public static ClientConfiguration UseHttpClientFactory(this ClientConfiguration config) where T : IClient + { + return config.UseCustomHttpClient((services, constantBaseAddress, httpBaseAddress) => + { + if (constantBaseAddress) + { + services.AddSingleton(); + + services.AddScoped>(provider => + { + var factory = provider.GetService(); + return _ => factory.Get(new Flurl.Url(httpBaseAddress(provider))); + }); + } + else + { + services.AddHttpClient(typeof(T).Name); + + services.AddTransient(provider => + { + return provider.GetService().CreateClient(typeof(T).Name); + }); + + + services.AddTransient>(provider => + { + return _ => new FlurlClient(provider.GetService()); + }); + } + }); + } + } +} diff --git a/src/Beffyman.AspNetCore.Client.MessagePack/Beffyman.AspNetCore.Client.MessagePack.csproj b/src/Beffyman.AspNetCore.Client.MessagePack/Beffyman.AspNetCore.Client.MessagePack.csproj new file mode 100644 index 0000000..1a6f077 --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.MessagePack/Beffyman.AspNetCore.Client.MessagePack.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + bin\$(Configuration)\$(TargetFramework)\Beffyman.AspNetCore.Client.MessagePack.xml + Contains MessagePack serializer for Beffyman.AspNetCore.Client.Generator clients + true + + + + + + + + + + + diff --git a/src/AspNetCore.Client.MessagePack/MessagePackInstaller.cs b/src/Beffyman.AspNetCore.Client.MessagePack/MessagePackInstaller.cs similarity index 77% rename from src/AspNetCore.Client.MessagePack/MessagePackInstaller.cs rename to src/Beffyman.AspNetCore.Client.MessagePack/MessagePackInstaller.cs index 9787840..574f8e1 100644 --- a/src/AspNetCore.Client.MessagePack/MessagePackInstaller.cs +++ b/src/Beffyman.AspNetCore.Client.MessagePack/MessagePackInstaller.cs @@ -1,6 +1,6 @@ -using AspNetCore.Client.Serializers; +using Beffyman.AspNetCore.Client.Serializers; -namespace AspNetCore.Client +namespace Beffyman.AspNetCore.Client { /// /// Static extension class for the AspnetCore.Client.BlazorJson library @@ -13,7 +13,7 @@ public static class MessagePackInstaller /// public static ClientConfiguration UseMessagePackSerializer(this ClientConfiguration config) { - return config.UseSerializer(); + return config.UseSerializer(); } /// @@ -22,7 +22,7 @@ public static ClientConfiguration UseMessagePackSerializer(this ClientConfigurat /// public static ClientConfiguration UseMessagePackDeserializer(this ClientConfiguration config) { - return config.UseDeserializer(); + return config.UseDeserializer(); } /// diff --git a/src/AspNetCore.Client.MessagePack/MessagePackSerializer.cs b/src/Beffyman.AspNetCore.Client.MessagePack/MessagePackSerializer.cs similarity index 82% rename from src/AspNetCore.Client.MessagePack/MessagePackSerializer.cs rename to src/Beffyman.AspNetCore.Client.MessagePack/MessagePackSerializer.cs index af16e47..27fdf15 100644 --- a/src/AspNetCore.Client.MessagePack/MessagePackSerializer.cs +++ b/src/Beffyman.AspNetCore.Client.MessagePack/MessagePackSerializer.cs @@ -5,7 +5,7 @@ using System.Net.Http.Headers; using System.Threading.Tasks; -namespace AspNetCore.Client.Serializers +namespace Beffyman.AspNetCore.Client.Serializers { /// /// Uses MessagePack for serializing and deserializing the http content @@ -23,7 +23,8 @@ internal class MessagePackSerializer : IHttpContentSerializer /// public async Task Deserialize(HttpContent content) { - return MessagePack.MessagePackSerializer.Deserialize(await content.ReadAsStreamAsync().ConfigureAwait(false), ContractlessStandardResolver.Instance); + await content.LoadIntoBufferAsync().ConfigureAwait(false); + return await MessagePack.MessagePackSerializer.DeserializeAsync(await content.ReadAsStreamAsync().ConfigureAwait(false), ContractlessStandardResolver.Instance); } /// diff --git a/src/AspNetCore.Client.BlazorJson/.editorconfig b/src/Beffyman.AspNetCore.Client.NewtonsoftJson/.editorconfig similarity index 100% rename from src/AspNetCore.Client.BlazorJson/.editorconfig rename to src/Beffyman.AspNetCore.Client.NewtonsoftJson/.editorconfig diff --git a/src/Beffyman.AspNetCore.Client.NewtonsoftJson/Beffyman.AspNetCore.Client.NewtonsoftJson.csproj b/src/Beffyman.AspNetCore.Client.NewtonsoftJson/Beffyman.AspNetCore.Client.NewtonsoftJson.csproj new file mode 100644 index 0000000..260806a --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.NewtonsoftJson/Beffyman.AspNetCore.Client.NewtonsoftJson.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + bin\$(Configuration)\$(TargetFramework)\Beffyman.AspNetCore.Client.SystemJson.xml + Contains System.Text.Json serializer for Beffyman.AspNetCore.Client.Generator clients + true + + + + + + + + + + diff --git a/src/AspNetCore.Client/Serializers/JsonHttpSerializer.cs b/src/Beffyman.AspNetCore.Client.NewtonsoftJson/NewtonsoftJsonSerializer.cs similarity index 92% rename from src/AspNetCore.Client/Serializers/JsonHttpSerializer.cs rename to src/Beffyman.AspNetCore.Client.NewtonsoftJson/NewtonsoftJsonSerializer.cs index 031fd54..2e2c9e5 100644 --- a/src/AspNetCore.Client/Serializers/JsonHttpSerializer.cs +++ b/src/Beffyman.AspNetCore.Client.NewtonsoftJson/NewtonsoftJsonSerializer.cs @@ -7,12 +7,12 @@ using System.Threading.Tasks; using Newtonsoft.Json; -namespace AspNetCore.Client.Serializers +namespace Beffyman.AspNetCore.Client.Serializers { /// /// Uses Newtonsoft.Json for serializing and deserializing the http content /// - internal class JsonHttpSerializer : IHttpContentSerializer + internal class NewtonsoftJsonHttpSerializer : IHttpContentSerializer { internal static readonly string CONTENT_TYPE = "application/json"; internal static readonly string PROBLEM_TYPE = "application/problem+json"; @@ -34,6 +34,7 @@ internal class JsonHttpSerializer : IHttpContentSerializer { typeof(string), (_)=> _.TrimStart('"').TrimEnd('"') }, { typeof(bool), (_)=> bool.Parse(_) }, { typeof(DateTime), (_)=> DateTime.Parse(_.TrimStart('"').TrimEnd('"')) }, + { typeof(DateTimeOffset), (_)=> DateTimeOffset.Parse(_.TrimStart('"').TrimEnd('"')) }, { typeof(Guid), (_)=> Guid.Parse(_.TrimStart('"').TrimEnd('"')) }, }; diff --git a/src/Beffyman.AspNetCore.Client.NewtonsoftJson/NewtonsoftJsonSerializerInstaller.cs b/src/Beffyman.AspNetCore.Client.NewtonsoftJson/NewtonsoftJsonSerializerInstaller.cs new file mode 100644 index 0000000..e1d924d --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.NewtonsoftJson/NewtonsoftJsonSerializerInstaller.cs @@ -0,0 +1,29 @@ +using Beffyman.AspNetCore.Client.Serializers; + +namespace Beffyman.AspNetCore.Client +{ + /// + /// Static extension class for the Beffyman.AspNetCore.Client.SystemJson library + /// + public static class NewtonsoftJsonSerializerInstaller + { + /// + /// Uses to serialize requests + /// + /// + public static ClientConfiguration UseNewtonsoftJsonHttpSerializer(this ClientConfiguration config) + { + return config.UseSerializer(); + } + + /// + /// Uses to deserialize requests when Json is detected + /// + /// + /// + public static ClientConfiguration UseNewtonsoftJsonHttpDeserializer(this ClientConfiguration config) + { + return config.UseDeserializer(); + } + } +} diff --git a/src/AspNetCore.Client.Protobuf/.editorconfig b/src/Beffyman.AspNetCore.Client.Protobuf/.editorconfig similarity index 88% rename from src/AspNetCore.Client.Protobuf/.editorconfig rename to src/Beffyman.AspNetCore.Client.Protobuf/.editorconfig index 7d5ba78..89aea01 100644 --- a/src/AspNetCore.Client.Protobuf/.editorconfig +++ b/src/Beffyman.AspNetCore.Client.Protobuf/.editorconfig @@ -1,4 +1,4 @@ -# Rules in this file were initially inferred by Visual Studio IntelliCode from the E:\Git_Github\AspNetCore.Client\src\AspNetCore.Client.BlazorJson\ codebase based on best match to current usage at 10/25/2018 +# Rules in this file were initially inferred by Visual Studio IntelliCode from the E:\Git_Github\Beffyman.AspNetCore.Client\src\Beffyman.AspNetCore.Client.BlazorJson\ codebase based on best match to current usage at 10/25/2018 # You can modify the rules from these initially generated values to suit your own policies # You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference [*.cs] diff --git a/src/Beffyman.AspNetCore.Client.Protobuf/Beffyman.AspNetCore.Client.Protobuf.csproj b/src/Beffyman.AspNetCore.Client.Protobuf/Beffyman.AspNetCore.Client.Protobuf.csproj new file mode 100644 index 0000000..7ca6b5f --- /dev/null +++ b/src/Beffyman.AspNetCore.Client.Protobuf/Beffyman.AspNetCore.Client.Protobuf.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + bin\$(Configuration)\$(TargetFramework)\Beffyman.AspNetCore.Client.Protobuf.xml + Contains protobuf serializer for Beffyman.AspNetCore.Client.Generator clients + true + + + + + + + + + + diff --git a/src/AspNetCore.Client.Protobuf/ProtobufInstaller.cs b/src/Beffyman.AspNetCore.Client.Protobuf/ProtobufInstaller.cs similarity index 92% rename from src/AspNetCore.Client.Protobuf/ProtobufInstaller.cs rename to src/Beffyman.AspNetCore.Client.Protobuf/ProtobufInstaller.cs index 67085c7..4fbf6b0 100644 --- a/src/AspNetCore.Client.Protobuf/ProtobufInstaller.cs +++ b/src/Beffyman.AspNetCore.Client.Protobuf/ProtobufInstaller.cs @@ -1,6 +1,6 @@ -using AspNetCore.Client.Serializers; +using Beffyman.AspNetCore.Client.Serializers; -namespace AspNetCore.Client +namespace Beffyman.AspNetCore.Client { /// diff --git a/src/AspNetCore.Client.Protobuf/ProtobufSerializer.cs b/src/Beffyman.AspNetCore.Client.Protobuf/ProtobufSerializer.cs similarity index 96% rename from src/AspNetCore.Client.Protobuf/ProtobufSerializer.cs rename to src/Beffyman.AspNetCore.Client.Protobuf/ProtobufSerializer.cs index 0c07c70..c653e31 100644 --- a/src/AspNetCore.Client.Protobuf/ProtobufSerializer.cs +++ b/src/Beffyman.AspNetCore.Client.Protobuf/ProtobufSerializer.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using ProtoBuf; -namespace AspNetCore.Client.Serializers +namespace Beffyman.AspNetCore.Client.Serializers { /// /// Uses Google.Protobuf for serializing and deserializing the http content diff --git a/src/AspNetCore.Client.JSInterop/.editorconfig b/src/Beffyman.AspNetCore.Client/.editorconfig similarity index 100% rename from src/AspNetCore.Client.JSInterop/.editorconfig rename to src/Beffyman.AspNetCore.Client/.editorconfig diff --git a/src/AspNetCore.Client/Authorization/BasicAuthHeader.cs b/src/Beffyman.AspNetCore.Client/Authorization/BasicAuthHeader.cs similarity index 94% rename from src/AspNetCore.Client/Authorization/BasicAuthHeader.cs rename to src/Beffyman.AspNetCore.Client/Authorization/BasicAuthHeader.cs index 2454f06..6b6ba7b 100644 --- a/src/AspNetCore.Client/Authorization/BasicAuthHeader.cs +++ b/src/Beffyman.AspNetCore.Client/Authorization/BasicAuthHeader.cs @@ -1,6 +1,6 @@ using Flurl.Http; -namespace AspNetCore.Client.Authorization +namespace Beffyman.AspNetCore.Client.Authorization { /// /// Basic Authentication protocol diff --git a/src/AspNetCore.Client/Authorization/OAuthHeader.cs b/src/Beffyman.AspNetCore.Client/Authorization/OAuthHeader.cs similarity index 96% rename from src/AspNetCore.Client/Authorization/OAuthHeader.cs rename to src/Beffyman.AspNetCore.Client/Authorization/OAuthHeader.cs index 63731ac..7351a0c 100644 --- a/src/AspNetCore.Client/Authorization/OAuthHeader.cs +++ b/src/Beffyman.AspNetCore.Client/Authorization/OAuthHeader.cs @@ -2,7 +2,7 @@ using System.Text; using Flurl.Http; -namespace AspNetCore.Client.Authorization +namespace Beffyman.AspNetCore.Client.Authorization { /// /// OAuth Authentication protocol diff --git a/src/AspNetCore.Client/Authorization/SecurityHeader.cs b/src/Beffyman.AspNetCore.Client/Authorization/SecurityHeader.cs similarity index 89% rename from src/AspNetCore.Client/Authorization/SecurityHeader.cs rename to src/Beffyman.AspNetCore.Client/Authorization/SecurityHeader.cs index 6c53bb6..6a7cf9d 100644 --- a/src/AspNetCore.Client/Authorization/SecurityHeader.cs +++ b/src/Beffyman.AspNetCore.Client/Authorization/SecurityHeader.cs @@ -1,6 +1,6 @@ using Flurl.Http; -namespace AspNetCore.Client.Authorization +namespace Beffyman.AspNetCore.Client.Authorization { /// /// Abstraction around security modifier to http request diff --git a/src/Beffyman.AspNetCore.Client/Beffyman.AspNetCore.Client.csproj b/src/Beffyman.AspNetCore.Client/Beffyman.AspNetCore.Client.csproj new file mode 100644 index 0000000..a646561 --- /dev/null +++ b/src/Beffyman.AspNetCore.Client/Beffyman.AspNetCore.Client.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + bin\$(Configuration)\$(TargetFramework)\Beffyman.AspNetCore.Client.xml + Implements registration of clients into a ServiceCollection + true + + + + + + + + + diff --git a/src/AspNetCore.Client/ClientConfiguration.cs b/src/Beffyman.AspNetCore.Client/ClientConfiguration.cs similarity index 90% rename from src/AspNetCore.Client/ClientConfiguration.cs rename to src/Beffyman.AspNetCore.Client/ClientConfiguration.cs index 4e59f4d..c6a27bb 100644 --- a/src/AspNetCore.Client/ClientConfiguration.cs +++ b/src/Beffyman.AspNetCore.Client/ClientConfiguration.cs @@ -3,15 +3,15 @@ using System.Linq; using System.Net; using System.Net.Http; -using AspNetCore.Client.Authorization; -using AspNetCore.Client.Http; -using AspNetCore.Client.RequestModifiers; -using AspNetCore.Client.Serializers; +using Beffyman.AspNetCore.Client.Authorization; +using Beffyman.AspNetCore.Client.Http; +using Beffyman.AspNetCore.Client.RequestModifiers; +using Beffyman.AspNetCore.Client.Serializers; using Flurl.Http; using Flurl.Http.Configuration; using Microsoft.Extensions.DependencyInjection; -namespace AspNetCore.Client +namespace Beffyman.AspNetCore.Client { /// @@ -79,6 +79,11 @@ public class ClientConfiguration /// private bool HttpPool = false; + /// + /// Custom Http Reistry + /// + private Action> _customHttpClientRegistry = null; + /// /// Does the container already have an httpclient injected? AKA blazor? /// @@ -144,32 +149,7 @@ public class ClientConfiguration { if (HttpPool) { - if (ConstantBaseAddress) - { - services.AddSingleton(); - - services.AddScoped>(provider => - { - var factory = provider.GetService(); - return _ => factory.Get(new Flurl.Url(HttpBaseAddress(provider))); - }); - } - else - { - services.AddHttpClient(typeof(T).Name); - - services.AddTransient(provider => - { - return provider.GetService().CreateClient(typeof(T).Name); - }); - - - services.AddTransient>(provider => - { - return _ => new FlurlClient(provider.GetService()); - }); - } - + _customHttpClientRegistry(services, ConstantBaseAddress, HttpBaseAddress); } else { @@ -370,17 +350,20 @@ public ClientConfiguration UseJsonClientDeserializer() } /// - /// Enables the use of Microsoft.Extensions.Http for injecting HttpClients that the IFlurlClient will use. + /// If you want to use a custom http client /// + /// + /// The Services collection, whether the BaseUrl is constant, and the func to get the BaseAddress /// - public ClientConfiguration UseHttpClientFactory() + public ClientConfiguration UseCustomHttpClient(Action> customHttpClientRegistry) where T : IClient { + _customHttpClientRegistry = customHttpClientRegistry; HttpPool = true; return this; } /// - /// Uses the existing http client injection inside the container, not compatible with and + /// Uses the existing http client injection inside the container, not compatible with and /// /// public ClientConfiguration UseExistingHttpClient() diff --git a/src/AspNetCore.Client/ClientSettings.cs b/src/Beffyman.AspNetCore.Client/ClientSettings.cs similarity index 91% rename from src/AspNetCore.Client/ClientSettings.cs rename to src/Beffyman.AspNetCore.Client/ClientSettings.cs index 9500a54..0b8f0aa 100644 --- a/src/AspNetCore.Client/ClientSettings.cs +++ b/src/Beffyman.AspNetCore.Client/ClientSettings.cs @@ -1,6 +1,6 @@ using System; -namespace AspNetCore.Client +namespace Beffyman.AspNetCore.Client { /// /// Settings to be passed down into the generated client wrapper diff --git a/src/AspNetCore.Client/Exceptions/InvalidRouteException.cs b/src/Beffyman.AspNetCore.Client/Exceptions/InvalidRouteException.cs similarity index 91% rename from src/AspNetCore.Client/Exceptions/InvalidRouteException.cs rename to src/Beffyman.AspNetCore.Client/Exceptions/InvalidRouteException.cs index 45b2506..d985660 100644 --- a/src/AspNetCore.Client/Exceptions/InvalidRouteException.cs +++ b/src/Beffyman.AspNetCore.Client/Exceptions/InvalidRouteException.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace AspNetCore.Client.Exceptions +namespace Beffyman.AspNetCore.Client.Exceptions { /// /// Thrown when a route constraint is violated during the parameter validation diff --git a/src/AspNetCore.Client/GeneratorExtensions.cs b/src/Beffyman.AspNetCore.Client/GeneratorExtensions.cs similarity index 97% rename from src/AspNetCore.Client/GeneratorExtensions.cs rename to src/Beffyman.AspNetCore.Client/GeneratorExtensions.cs index 7efbcee..ec80e01 100644 --- a/src/AspNetCore.Client/GeneratorExtensions.cs +++ b/src/Beffyman.AspNetCore.Client/GeneratorExtensions.cs @@ -5,12 +5,12 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; -using AspNetCore.Client.Authorization; -using AspNetCore.Client.RequestModifiers; +using Beffyman.AspNetCore.Client.Authorization; +using Beffyman.AspNetCore.Client.RequestModifiers; using Flurl.Http; using Newtonsoft.Json.Linq; -namespace AspNetCore.Client.GeneratorExtensions +namespace Beffyman.AspNetCore.Client.GeneratorExtensions { /// /// Extensions to be used inside the generated clients diff --git a/src/AspNetCore.Client/Http/DefaultHttpOverride.cs b/src/Beffyman.AspNetCore.Client/Http/DefaultHttpOverride.cs similarity index 97% rename from src/AspNetCore.Client/Http/DefaultHttpOverride.cs rename to src/Beffyman.AspNetCore.Client/Http/DefaultHttpOverride.cs index a41f7de..b61e19a 100644 --- a/src/AspNetCore.Client/Http/DefaultHttpOverride.cs +++ b/src/Beffyman.AspNetCore.Client/Http/DefaultHttpOverride.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; -namespace AspNetCore.Client.Http +namespace Beffyman.AspNetCore.Client.Http { /// /// Default implementation for which doesn't do anything diff --git a/src/AspNetCore.Client/Http/IHttpOverride.cs b/src/Beffyman.AspNetCore.Client/Http/IHttpOverride.cs similarity index 96% rename from src/AspNetCore.Client/Http/IHttpOverride.cs rename to src/Beffyman.AspNetCore.Client/Http/IHttpOverride.cs index 0bf8c03..62b56d1 100644 --- a/src/AspNetCore.Client/Http/IHttpOverride.cs +++ b/src/Beffyman.AspNetCore.Client/Http/IHttpOverride.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; -namespace AspNetCore.Client.Http +namespace Beffyman.AspNetCore.Client.Http { /// /// Overrides the request and allows for request interception before it even goes out diff --git a/src/AspNetCore.Client/IClient.cs b/src/Beffyman.AspNetCore.Client/IClient.cs similarity index 93% rename from src/AspNetCore.Client/IClient.cs rename to src/Beffyman.AspNetCore.Client/IClient.cs index f102486..61e4220 100644 --- a/src/AspNetCore.Client/IClient.cs +++ b/src/Beffyman.AspNetCore.Client/IClient.cs @@ -1,7 +1,7 @@ using System; using Flurl.Http; -namespace AspNetCore.Client +namespace Beffyman.AspNetCore.Client { /// /// Client interface used by all clients generated, if you want to use reflection over all the clients, use this as the lookup diff --git a/src/AspNetCore.Client/RequestModifiers/RequestModifier.cs b/src/Beffyman.AspNetCore.Client/RequestModifiers/RequestModifier.cs similarity index 97% rename from src/AspNetCore.Client/RequestModifiers/RequestModifier.cs rename to src/Beffyman.AspNetCore.Client/RequestModifiers/RequestModifier.cs index 169154d..857a0da 100644 --- a/src/AspNetCore.Client/RequestModifiers/RequestModifier.cs +++ b/src/Beffyman.AspNetCore.Client/RequestModifiers/RequestModifier.cs @@ -3,7 +3,7 @@ using System.Net; using Flurl.Http; -namespace AspNetCore.Client.RequestModifiers +namespace Beffyman.AspNetCore.Client.RequestModifiers { /// /// Applies header modifications predefined inside the configuration diff --git a/src/AspNetCore.Client/Serializers/HttpSerializer.cs b/src/Beffyman.AspNetCore.Client/Serializers/HttpSerializer.cs similarity index 98% rename from src/AspNetCore.Client/Serializers/HttpSerializer.cs rename to src/Beffyman.AspNetCore.Client/Serializers/HttpSerializer.cs index 2864ec8..0886892 100644 --- a/src/AspNetCore.Client/Serializers/HttpSerializer.cs +++ b/src/Beffyman.AspNetCore.Client/Serializers/HttpSerializer.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -namespace AspNetCore.Client.Serializers +namespace Beffyman.AspNetCore.Client.Serializers { /// /// This is used to register all deserializers and the serializer that the endpoint uses diff --git a/src/AspNetCore.Client/Serializers/IHttpContentSerializer.cs b/src/Beffyman.AspNetCore.Client/Serializers/IHttpContentSerializer.cs similarity index 94% rename from src/AspNetCore.Client/Serializers/IHttpContentSerializer.cs rename to src/Beffyman.AspNetCore.Client/Serializers/IHttpContentSerializer.cs index 7fa5880..504505d 100644 --- a/src/AspNetCore.Client/Serializers/IHttpContentSerializer.cs +++ b/src/Beffyman.AspNetCore.Client/Serializers/IHttpContentSerializer.cs @@ -2,7 +2,7 @@ using System.Net.Http.Headers; using System.Threading.Tasks; -namespace AspNetCore.Client.Serializers +namespace Beffyman.AspNetCore.Client.Serializers { /// /// Used for implementing custom serializers for the http content diff --git a/src/AspNetCore.Client.JSInterop/JSInteropJsonSerializer.cs b/src/Beffyman.AspNetCore.Client/Serializers/JsonHttpSerializer.cs similarity index 69% rename from src/AspNetCore.Client.JSInterop/JSInteropJsonSerializer.cs rename to src/Beffyman.AspNetCore.Client/Serializers/JsonHttpSerializer.cs index c26a679..04339c5 100644 --- a/src/AspNetCore.Client.JSInterop/JSInteropJsonSerializer.cs +++ b/src/Beffyman.AspNetCore.Client/Serializers/JsonHttpSerializer.cs @@ -4,19 +4,23 @@ using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; -using Microsoft.JSInterop; +using System.Text.Json; -namespace AspNetCore.Client.Serializers +namespace Beffyman.AspNetCore.Client.Serializers { /// - /// Uses Blazor's SimpleJson for serializing and deserializing the http content + /// Uses System.Texy.Json for serializing and deserializing the http content /// - internal class JSInteropJsonSerializer : IHttpContentSerializer + internal class JsonHttpSerializer : IHttpContentSerializer { internal static readonly string CONTENT_TYPE = "application/json"; internal static readonly string PROBLEM_TYPE = "application/problem+json"; public string[] ContentTypes => new string[] { CONTENT_TYPE, PROBLEM_TYPE }; + private readonly JsonSerializerOptions _options = new JsonSerializerOptions { + PropertyNameCaseInsensitive = true, + }; + private static readonly IDictionary> _knownJsonPrimitives = new Dictionary> { { typeof(char), (_)=> char.Parse(_) }, @@ -32,7 +36,7 @@ internal class JSInteropJsonSerializer : IHttpContentSerializer { typeof(string), (_)=> _.TrimStart('"').TrimEnd('"') }, { typeof(bool), (_)=> bool.Parse(_) }, { typeof(DateTime), (_)=> DateTime.Parse(_.TrimStart('"').TrimEnd('"')) }, - { typeof(DateTimeOffset), (_)=> DateTime.Parse(_.TrimStart('"').TrimEnd('"')) }, + { typeof(DateTimeOffset), (_)=> DateTimeOffset.Parse(_.TrimStart('"').TrimEnd('"')) }, { typeof(Guid), (_)=> Guid.Parse(_.TrimStart('"').TrimEnd('"')) }, }; @@ -45,15 +49,18 @@ internal class JSInteropJsonSerializer : IHttpContentSerializer /// public async Task Deserialize(HttpContent content) { + await content.LoadIntoBufferAsync().ConfigureAwait(false); if (_knownJsonPrimitives.ContainsKey(typeof(T))) { - return (T)_knownJsonPrimitives[typeof(T)](await content.ReadAsStringAsync().ConfigureAwait(false)); + var str = await content.ReadAsStringAsync().ConfigureAwait(false); + return (T)_knownJsonPrimitives[typeof(T)](str); } else { - //Can't use the same stream reading as AspNetCore.Client.Serializers.JsonHttpSerializer because Blazor's json doesn't expose those AFAIK - var str = await content.ReadAsStringAsync().ConfigureAwait(false); - return Json.Deserialize(str); + using (var stream = await content.ReadAsStreamAsync().ConfigureAwait(false)) + { + return await System.Text.Json.JsonSerializer.DeserializeAsync(stream, _options).ConfigureAwait(false); + } } } @@ -65,9 +72,8 @@ public async Task Deserialize(HttpContent content) /// public HttpContent Serialize(T request) { - //Can't use the same stream writing as AspNetCore.Client.Serializers.JsonHttpSerializer because Blazor's json doesn't expose those AFAIK - - var json = Json.Serialize(request); + //As of preview 8? It seems like the JSInterop package uses the new system json library + var json = JsonSerializer.Serialize(request, _options); return new StringContent(json, Encoding.UTF8, CONTENT_TYPE); } } diff --git a/src/AspNetCore.Client/Serializers/TextHttpSerializer.cs b/src/Beffyman.AspNetCore.Client/Serializers/TextHttpSerializer.cs similarity index 96% rename from src/AspNetCore.Client/Serializers/TextHttpSerializer.cs rename to src/Beffyman.AspNetCore.Client/Serializers/TextHttpSerializer.cs index 7d866b2..ec0888a 100644 --- a/src/AspNetCore.Client/Serializers/TextHttpSerializer.cs +++ b/src/Beffyman.AspNetCore.Client/Serializers/TextHttpSerializer.cs @@ -6,7 +6,7 @@ using System.Text; using System.Threading.Tasks; -namespace AspNetCore.Client.Serializers +namespace Beffyman.AspNetCore.Client.Serializers { /// /// Uses Newtonsoft.Json for serializing and deserializing the http content diff --git a/src/AspNetCore.Server/Attributes/Functions/ExpectedBodyParameterAttribute.cs b/src/Beffyman.AspNetCore.Server/Attributes/Functions/ExpectedBodyParameterAttribute.cs similarity index 100% rename from src/AspNetCore.Server/Attributes/Functions/ExpectedBodyParameterAttribute.cs rename to src/Beffyman.AspNetCore.Server/Attributes/Functions/ExpectedBodyParameterAttribute.cs diff --git a/src/AspNetCore.Server/Attributes/Functions/ExpectedQueryParameterAttribute.cs b/src/Beffyman.AspNetCore.Server/Attributes/Functions/ExpectedQueryParameterAttribute.cs similarity index 100% rename from src/AspNetCore.Server/Attributes/Functions/ExpectedQueryParameterAttribute.cs rename to src/Beffyman.AspNetCore.Server/Attributes/Functions/ExpectedQueryParameterAttribute.cs diff --git a/src/AspNetCore.Server/Attributes/Http/HeaderParameterAttribute.cs b/src/Beffyman.AspNetCore.Server/Attributes/Http/HeaderParameterAttribute.cs similarity index 100% rename from src/AspNetCore.Server/Attributes/Http/HeaderParameterAttribute.cs rename to src/Beffyman.AspNetCore.Server/Attributes/Http/HeaderParameterAttribute.cs diff --git a/src/AspNetCore.Server/Attributes/Http/IncludeHeaderAttribute.cs b/src/Beffyman.AspNetCore.Server/Attributes/Http/IncludeHeaderAttribute.cs similarity index 100% rename from src/AspNetCore.Server/Attributes/Http/IncludeHeaderAttribute.cs rename to src/Beffyman.AspNetCore.Server/Attributes/Http/IncludeHeaderAttribute.cs diff --git a/src/AspNetCore.Server/Attributes/Http/NotGeneratedAttribute.cs b/src/Beffyman.AspNetCore.Server/Attributes/Http/NotGeneratedAttribute.cs similarity index 100% rename from src/AspNetCore.Server/Attributes/Http/NotGeneratedAttribute.cs rename to src/Beffyman.AspNetCore.Server/Attributes/Http/NotGeneratedAttribute.cs diff --git a/src/AspNetCore.Server/Attributes/NamespaceSuffixAttribute.cs b/src/Beffyman.AspNetCore.Server/Attributes/NamespaceSuffixAttribute.cs similarity index 100% rename from src/AspNetCore.Server/Attributes/NamespaceSuffixAttribute.cs rename to src/Beffyman.AspNetCore.Server/Attributes/NamespaceSuffixAttribute.cs diff --git a/src/AspNetCore.Server/Attributes/SignalR/GenerateHubAttribute.cs b/src/Beffyman.AspNetCore.Server/Attributes/SignalR/GenerateHubAttribute.cs similarity index 100% rename from src/AspNetCore.Server/Attributes/SignalR/GenerateHubAttribute.cs rename to src/Beffyman.AspNetCore.Server/Attributes/SignalR/GenerateHubAttribute.cs diff --git a/src/AspNetCore.Server/Attributes/SignalR/ProducesMessageAttribute.cs b/src/Beffyman.AspNetCore.Server/Attributes/SignalR/ProducesMessageAttribute.cs similarity index 100% rename from src/AspNetCore.Server/Attributes/SignalR/ProducesMessageAttribute.cs rename to src/Beffyman.AspNetCore.Server/Attributes/SignalR/ProducesMessageAttribute.cs diff --git a/src/Beffyman.AspNetCore.Server/Beffyman.AspNetCore.Server.csproj b/src/Beffyman.AspNetCore.Server/Beffyman.AspNetCore.Server.csproj new file mode 100644 index 0000000..7279c8b --- /dev/null +++ b/src/Beffyman.AspNetCore.Server/Beffyman.AspNetCore.Server.csproj @@ -0,0 +1,9 @@ + + + + netstandard2.0 + bin\$(Configuration)\$(TargetFramework)\Beffyman.AspNetCore.Server.xml + A helper package for Beffyman.AspNetCore.Client.Generator that contains known attributes that affect the generation + true + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..83a04c6 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,3 @@ + + + diff --git a/test/AspNetCore.Client.Test.Generator/AspNetCore.Client.Test.Generator.csproj b/test/AspNetCore.Client.Test.Generator/AspNetCore.Client.Test.Generator.csproj deleted file mode 100644 index e4922fb..0000000 --- a/test/AspNetCore.Client.Test.Generator/AspNetCore.Client.Test.Generator.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - netcoreapp2.2;net462 - Exe - PackageReference - true - false - true - latest - false - - - - {9A19103F-16F7-4668-BE54-9A1E7A4F7556} - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/TestAzureFunction.Contracts/TestAzureFunction.Contracts.csproj b/test/TestAzureFunction.Contracts/TestAzureFunction.Contracts.csproj deleted file mode 100644 index e2c436f..0000000 --- a/test/TestAzureFunction.Contracts/TestAzureFunction.Contracts.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - false - - - - - - diff --git a/test/TestAzureFunction.Tests/TestAzureFunction.Tests.csproj b/test/TestAzureFunction.Tests/TestAzureFunction.Tests.csproj deleted file mode 100644 index c3e4d84..0000000 --- a/test/TestAzureFunction.Tests/TestAzureFunction.Tests.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - netcoreapp2.2 - latest - false - UnitTest - - - - - - - - - - - - - - - - - - - diff --git a/test/TestBlazorApp.Server/Controllers/SampleDataController.cs b/test/TestBlazorApp.Server/Controllers/SampleDataController.cs deleted file mode 100644 index 9ae7c84..0000000 --- a/test/TestBlazorApp.Server/Controllers/SampleDataController.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Linq; -using TestBlazorApp.Shared; - -namespace TestBlazorApp.Server.Controllers -{ - [Route("api/[controller]")] - public class SampleDataController : Controller - { - private static string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - [HttpGet("[action]")] - public IEnumerable WeatherForecasts() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }); - } - } -} diff --git a/test/TestBlazorApp.Server/Program.cs b/test/TestBlazorApp.Server/Program.cs deleted file mode 100644 index 044a23c..0000000 --- a/test/TestBlazorApp.Server/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; - -namespace TestBlazorApp.Server -{ - public class Program - { - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseConfiguration(new ConfigurationBuilder() - .AddCommandLine(args) - .Build()) - .UseStartup() - .Build(); - } -} diff --git a/test/TestBlazorApp.Server/Properties/launchSettings.json b/test/TestBlazorApp.Server/Properties/launchSettings.json deleted file mode 100644 index 4007363..0000000 --- a/test/TestBlazorApp.Server/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:62150/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "TestBlazorApp.Server": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:62153/" - } - } -} \ No newline at end of file diff --git a/test/TestBlazorApp.Server/Startup.cs b/test/TestBlazorApp.Server/Startup.cs deleted file mode 100644 index 0b125ad..0000000 --- a/test/TestBlazorApp.Server/Startup.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Microsoft.AspNetCore.Blazor.Server; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.ResponseCompression; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json.Serialization; -using System.Linq; -using System.Net.Mime; - -namespace TestBlazorApp.Server -{ - public class Startup - { - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) - { - services.AddMvc(); - - services.AddResponseCompression(options => - { - options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] - { - MediaTypeNames.Application.Octet, - WasmMediaTypeNames.Application.Wasm, - }); - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - app.UseResponseCompression(); - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseMvc(routes => - { - routes.MapRoute(name: "default", template: "{controller}/{action}/{id?}"); - }); - - app.UseBlazor(); - } - } -} diff --git a/test/TestBlazorApp.Server/TestBlazorApp.Server.csproj b/test/TestBlazorApp.Server/TestBlazorApp.Server.csproj deleted file mode 100644 index 106cc7f..0000000 --- a/test/TestBlazorApp.Server/TestBlazorApp.Server.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - netcoreapp2.1 - PackageReference - true - false - true - latest - true - false - - - - - - - - - - - - - diff --git a/test/TestBlazorApp.Shared/TestBlazorApp.Shared.csproj b/test/TestBlazorApp.Shared/TestBlazorApp.Shared.csproj deleted file mode 100644 index c62496f..0000000 --- a/test/TestBlazorApp.Shared/TestBlazorApp.Shared.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - netstandard2.0 - PackageReference - true - false - true - latest - true - false - - - diff --git a/test/TestBlazorApp.Shared/WeatherForecast.cs b/test/TestBlazorApp.Shared/WeatherForecast.cs deleted file mode 100644 index eb305d6..0000000 --- a/test/TestBlazorApp.Shared/WeatherForecast.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace TestBlazorApp.Shared -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - public int TemperatureC { get; set; } - public string Summary { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } -} diff --git a/test/TestBlazorApp.Tests/Properties/launchSettings.json b/test/TestBlazorApp.Tests/Properties/launchSettings.json deleted file mode 100644 index 47843bd..0000000 --- a/test/TestBlazorApp.Tests/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:53235/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "TestBlazorApp.Tests": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:53236/" - } - } -} \ No newline at end of file diff --git a/test/TestBlazorApp.Tests/TestBlazorApp.Tests.csproj b/test/TestBlazorApp.Tests/TestBlazorApp.Tests.csproj deleted file mode 100644 index 1a59c43..0000000 --- a/test/TestBlazorApp.Tests/TestBlazorApp.Tests.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - netcoreapp2.1 - latest - false - UnitTest - - - - - - - - - - - - - - - - - - - diff --git a/test/TestBlazorApp.Views/App.cshtml b/test/TestBlazorApp.Views/App.cshtml deleted file mode 100644 index 59be832..0000000 --- a/test/TestBlazorApp.Views/App.cshtml +++ /dev/null @@ -1,5 +0,0 @@ - - diff --git a/test/TestBlazorApp.Views/Pages/Counter.cshtml b/test/TestBlazorApp.Views/Pages/Counter.cshtml deleted file mode 100644 index 973af92..0000000 --- a/test/TestBlazorApp.Views/Pages/Counter.cshtml +++ /dev/null @@ -1,16 +0,0 @@ -@page "/counter" - -

Counter

- -

Current count: @currentCount

- - - -@functions { - int currentCount = 0; - - void IncrementCount() - { - currentCount++; - } -} diff --git a/test/TestBlazorApp.Views/Pages/FetchData.cshtml b/test/TestBlazorApp.Views/Pages/FetchData.cshtml deleted file mode 100644 index f15cd62..0000000 --- a/test/TestBlazorApp.Views/Pages/FetchData.cshtml +++ /dev/null @@ -1,45 +0,0 @@ -@using TestBlazorApp.Shared -@page "/fetchdata" -@inject HttpClient Http - -

Weather forecast

- -

This component demonstrates fetching data from the server.

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@functions { - WeatherForecast[] forecasts; - - protected override async Task OnInitAsync() - { - forecasts = await Http.GetJsonAsync("api/SampleData/WeatherForecasts"); - } -} diff --git a/test/TestBlazorApp.Views/Pages/_ViewImports.cshtml b/test/TestBlazorApp.Views/Pages/_ViewImports.cshtml deleted file mode 100644 index 0f24eda..0000000 --- a/test/TestBlazorApp.Views/Pages/_ViewImports.cshtml +++ /dev/null @@ -1 +0,0 @@ -@layout MainLayout diff --git a/test/TestBlazorApp.Views/Program.cs b/test/TestBlazorApp.Views/Program.cs deleted file mode 100644 index 8381908..0000000 --- a/test/TestBlazorApp.Views/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.AspNetCore.Blazor.Hosting; - -namespace TestBlazorApp.Views -{ - public class Program - { - public static void Main() - { - BlazorWebAssemblyHost.CreateDefaultBuilder() - .UseBlazorStartup() - .Build() - .Run(); - } - } -} diff --git a/test/TestBlazorApp.Views/Properties/launchSettings.json b/test/TestBlazorApp.Views/Properties/launchSettings.json deleted file mode 100644 index e8da672..0000000 --- a/test/TestBlazorApp.Views/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:62148/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "TestBlazorApp.Views": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:62151/" - } - } -} \ No newline at end of file diff --git a/test/TestBlazorApp.Views/TestBlazorApp.Views.csproj b/test/TestBlazorApp.Views/TestBlazorApp.Views.csproj deleted file mode 100644 index ddd7efc..0000000 --- a/test/TestBlazorApp.Views/TestBlazorApp.Views.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - netstandard2.0 - Exe - PackageReference - true - false - true - latest - true - false - - - - - - - - - - - - - - diff --git a/test/TestBlazorApp.Views/_ViewImports.cshtml b/test/TestBlazorApp.Views/_ViewImports.cshtml deleted file mode 100644 index 5647782..0000000 --- a/test/TestBlazorApp.Views/_ViewImports.cshtml +++ /dev/null @@ -1,5 +0,0 @@ -@using System.Net.Http -@using Microsoft.AspNetCore.Blazor.Layouts -@using Microsoft.AspNetCore.Blazor.Routing -@using TestBlazorApp.Views -@using TestBlazorApp.Views.Shared diff --git a/test/TestBlazorApp.Views/wwwroot/css/bootstrap/bootstrap.min.css b/test/TestBlazorApp.Views/wwwroot/css/bootstrap/bootstrap.min.css deleted file mode 100644 index 6561b6f..0000000 --- a/test/TestBlazorApp.Views/wwwroot/css/bootstrap/bootstrap.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.0.0 (https://getbootstrap.com) - * Copyright 2011-2018 The Bootstrap Authors - * Copyright 2011-2018 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014 \00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-sm-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-md-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-lg-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-xl-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;max-width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:not([size]):not([multiple]){height:calc(2.25rem + 2px)}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm,.input-group-lg>.form-control-plaintext.form-control,.input-group-lg>.input-group-append>.form-control-plaintext.btn,.input-group-lg>.input-group-append>.form-control-plaintext.input-group-text,.input-group-lg>.input-group-prepend>.form-control-plaintext.btn,.input-group-lg>.input-group-prepend>.form-control-plaintext.input-group-text,.input-group-sm>.form-control-plaintext.form-control,.input-group-sm>.input-group-append>.form-control-plaintext.btn,.input-group-sm>.input-group-append>.form-control-plaintext.input-group-text,.input-group-sm>.input-group-prepend>.form-control-plaintext.btn,.input-group-sm>.input-group-prepend>.form-control-plaintext.input-group-text{padding-right:0;padding-left:0}.form-control-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-sm>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-sm>select.form-control:not([size]):not([multiple]),select.form-control-sm:not([size]):not([multiple]){height:calc(1.8125rem + 2px)}.form-control-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-lg>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-lg>select.form-control:not([size]):not([multiple]),select.form-control-lg:not([size]):not([multiple]){height:calc(2.875rem + 2px)}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(40,167,69,.8);border-radius:.2rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#28a745}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{background-color:#71dd8a}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(40,167,69,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label::before,.was-validated .custom-file-input:valid~.custom-file-label::before{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(220,53,69,.8);border-radius:.2rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#dc3545}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{background-color:#efa2a9}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(220,53,69,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label::before,.was-validated .custom-file-input:invalid~.custom-file-label::before{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}.btn:not(:disabled):not(.disabled).active,.btn:not(:disabled):not(.disabled):active{background-image:none}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;background-color:transparent}.btn-link:hover{color:#0056b3;text-decoration:underline;background-color:transparent;border-color:transparent}.btn-link.focus,.btn-link:focus{text-decoration:underline;border-color:transparent;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.show{opacity:1}.collapse{display:none}.collapse.show{display:block}tr.collapse.show{display:table-row}tbody.collapse.show{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}.dropdown,.dropup{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropup .dropdown-menu{margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group,.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after{margin-left:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file:focus,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control{margin-left:-1px}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::before{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label,.input-group>.custom-file:not(:first-child) .custom-file-label::before{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-webkit-box;display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:active~.custom-control-label::before{color:#fff;background-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{margin-bottom:0}.custom-control-label::before{position:absolute;top:.25rem;left:0;display:block;width:1rem;height:1rem;pointer-events:none;content:"";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label::after{position:absolute;top:.25rem;left:0;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:inset 0 1px 2px rgba(0,0,0,.075),0 0 5px rgba(128,189,255,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:125%}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-control{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:focus~.custom-file-control::before{border-color:#80bdff}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.25rem + 2px);padding:.375rem .75rem;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(calc(2.25rem + 2px) - 1px * 2);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 .25rem .25rem 0}.nav{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .dropup .dropdown-menu{top:auto;bottom:100%}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .dropup .dropdown-menu{top:auto;bottom:100%}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem}.card-columns .card{display:inline-block;width:100%}}.breadcrumb{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;padding-left:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#fff;text-decoration:none;background-color:#0062cc}.badge-secondary{color:#fff;background-color:#6c757d}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#212529;text-decoration:none;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#212529;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-webkit-box;display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;background-color:#007bff;transition:width .6s ease}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:focus,.close:hover{color:#000;text-decoration:none;opacity:.75}.close:not(:disabled):not(.disabled){cursor:pointer}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.show .modal-dialog{-webkit-transform:translate(0,0);transform:translate(0,0)}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-content{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-top .arrow::before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-top .arrow::after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-right .arrow::before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-right .arrow::after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-bottom .arrow::before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-bottom .arrow::after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-left .arrow::before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-left .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.active.carousel-item-right,.carousel-item-next{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-right,.carousel-item-next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;background-color:rgba(255,255,255,.5)}.carousel-indicators li::before{position:absolute;top:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li::after{position:absolute;bottom:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-sm-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-md-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-lg-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-xl-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;-webkit-clip-path:inset(50%);clip-path:inset(50%);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal;-webkit-clip-path:none;clip-path:none}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0062cc!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#545b62!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#1e7e34!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#117a8b!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#d39e00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#bd2130!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#1d2124!important}.text-muted{color:#6c757d!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/test/TestBlazorApp.Views/wwwroot/css/bootstrap/bootstrap.min.css.map b/test/TestBlazorApp.Views/wwwroot/css/bootstrap/bootstrap.min.css.map deleted file mode 100644 index ee5c523..0000000 --- a/test/TestBlazorApp.Views/wwwroot/css/bootstrap/bootstrap.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-framework.scss","../../scss/_tables.scss","../../scss/mixins/_table-row.scss","../../scss/_forms.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_forms.scss","../../scss/mixins/_gradients.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/mixins/_nav-divider.scss","../../scss/_button-group.scss","../../scss/_input-group.scss","../../scss/_custom-forms.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/mixins/_badge.scss","../../scss/_jumbotron.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_media.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_modal.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/utilities/_align.scss","../../scss/mixins/_background-variant.scss","../../scss/utilities/_background.scss","../../scss/utilities/_borders.scss","../../scss/mixins/_clearfix.scss","../../scss/utilities/_display.scss","../../scss/utilities/_embed.scss","../../scss/utilities/_flex.scss","../../scss/utilities/_float.scss","../../scss/mixins/_float.scss","../../scss/utilities/_position.scss","../../scss/utilities/_screenreaders.scss","../../scss/mixins/_screen-reader.scss","../../scss/utilities/_sizing.scss","../../scss/utilities/_spacing.scss","../../scss/utilities/_text.scss","../../scss/mixins/_text-truncate.scss","../../scss/mixins/_text-emphasis.scss","../../scss/mixins/_text-hide.scss","../../scss/utilities/_visibility.scss","../../scss/mixins/_visibility.scss","../../scss/_print.scss"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Bootstrap v4.0.0 (https://getbootstrap.com)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"root\";\n@import \"reboot\";\n@import \"type\";\n@import \"images\";\n@import \"code\";\n@import \"grid\";\n@import \"tables\";\n@import \"forms\";\n@import \"buttons\";\n@import \"transitions\";\n@import \"dropdown\";\n@import \"button-group\";\n@import \"input-group\";\n@import \"custom-forms\";\n@import \"nav\";\n@import \"navbar\";\n@import \"card\";\n@import \"breadcrumb\";\n@import \"pagination\";\n@import \"badge\";\n@import \"jumbotron\";\n@import \"alert\";\n@import \"progress\";\n@import \"media\";\n@import \"list-group\";\n@import \"close\";\n@import \"modal\";\n@import \"tooltip\";\n@import \"popover\";\n@import \"carousel\";\n@import \"utilities\";\n@import \"print\";\n",":root {\n // Custom variable values only support SassScript inside `#{}`.\n @each $color, $value in $colors {\n --#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$color}: #{$value};\n }\n\n @each $bp, $value in $grid-breakpoints {\n --breakpoint-#{$bp}: #{$value};\n }\n\n // Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --font-family-sans-serif: #{inspect($font-family-sans-serif)};\n --font-family-monospace: #{inspect($font-family-monospace)};\n}\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so\n// we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n// 6. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -ms-text-size-adjust: 100%; // 4\n -ms-overflow-style: scrollbar; // 5\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0); // 6\n}\n\n// IE10+ doesn't honor `` in some cases.\n@at-root {\n @-ms-viewport {\n width: device-width;\n }\n}\n\n// stylelint-disable selector-list-comma-newline-after\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\narticle, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use the\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n font-size: $font-size-base;\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Suppress the focus outline on elements that cannot be accessed via keyboard.\n// This prevents an unwanted focus outline from appearing around elements that\n// might still respond to pointer events.\n//\n// Credit: https://github.com/suitcss/base\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Remove the bottom border in Firefox 39-.\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Duplicate behavior to the data-* attribute for our tooltip plugin\n\nabbr[title],\nabbr[data-original-title] { // 4\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 1\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic; // Add the correct font style in Android 4.3-\n}\n\n// stylelint-disable font-weight-notation\nb,\nstrong {\n font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n// stylelint-enable font-weight-notation\n\nsmall {\n font-size: 80%; // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.\n\n @include hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href)\n// which have not been made explicitly keyboard-focusable (without tabindex).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n\n @include hover-focus {\n color: inherit;\n text-decoration: none;\n }\n\n &:focus {\n outline: 0;\n }\n}\n\n\n//\n// Code\n//\n\n// stylelint-disable font-family-no-duplicate-names\npre,\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; // Correct the inheritance and scaling of font size in all browsers.\n font-size: 1em; // Correct the odd `em` font sizing in all browsers.\n}\n// stylelint-enable font-family-no-duplicate-names\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so\n // we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n -ms-overflow-style: scrollbar;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg:not(:root) {\n overflow: hidden; // Hide the overflow in IE\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $text-muted;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: .5rem;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\nhtml [type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap v4.0.0 (https://getbootstrap.com)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: transparent;\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: .5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-family: inherit;\n font-weight: 500;\n line-height: 1.2;\n color: inherit;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014 \\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode,\nkbd,\npre,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -webkit-box-flex: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n}\n\n.col-1 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n -webkit-box-ordinal-group: 0;\n -ms-flex-order: -1;\n order: -1;\n}\n\n.order-last {\n -webkit-box-ordinal-group: 14;\n -ms-flex-order: 13;\n order: 13;\n}\n\n.order-0 {\n -webkit-box-ordinal-group: 1;\n -ms-flex-order: 0;\n order: 0;\n}\n\n.order-1 {\n -webkit-box-ordinal-group: 2;\n -ms-flex-order: 1;\n order: 1;\n}\n\n.order-2 {\n -webkit-box-ordinal-group: 3;\n -ms-flex-order: 2;\n order: 2;\n}\n\n.order-3 {\n -webkit-box-ordinal-group: 4;\n -ms-flex-order: 3;\n order: 3;\n}\n\n.order-4 {\n -webkit-box-ordinal-group: 5;\n -ms-flex-order: 4;\n order: 4;\n}\n\n.order-5 {\n -webkit-box-ordinal-group: 6;\n -ms-flex-order: 5;\n order: 5;\n}\n\n.order-6 {\n -webkit-box-ordinal-group: 7;\n -ms-flex-order: 6;\n order: 6;\n}\n\n.order-7 {\n -webkit-box-ordinal-group: 8;\n -ms-flex-order: 7;\n order: 7;\n}\n\n.order-8 {\n -webkit-box-ordinal-group: 9;\n -ms-flex-order: 8;\n order: 8;\n}\n\n.order-9 {\n -webkit-box-ordinal-group: 10;\n -ms-flex-order: 9;\n order: 9;\n}\n\n.order-10 {\n -webkit-box-ordinal-group: 11;\n -ms-flex-order: 10;\n order: 10;\n}\n\n.order-11 {\n -webkit-box-ordinal-group: 12;\n -ms-flex-order: 11;\n order: 11;\n}\n\n.order-12 {\n -webkit-box-ordinal-group: 13;\n -ms-flex-order: 12;\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -webkit-box-flex: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-sm-1 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n -webkit-box-ordinal-group: 0;\n -ms-flex-order: -1;\n order: -1;\n }\n .order-sm-last {\n -webkit-box-ordinal-group: 14;\n -ms-flex-order: 13;\n order: 13;\n }\n .order-sm-0 {\n -webkit-box-ordinal-group: 1;\n -ms-flex-order: 0;\n order: 0;\n }\n .order-sm-1 {\n -webkit-box-ordinal-group: 2;\n -ms-flex-order: 1;\n order: 1;\n }\n .order-sm-2 {\n -webkit-box-ordinal-group: 3;\n -ms-flex-order: 2;\n order: 2;\n }\n .order-sm-3 {\n -webkit-box-ordinal-group: 4;\n -ms-flex-order: 3;\n order: 3;\n }\n .order-sm-4 {\n -webkit-box-ordinal-group: 5;\n -ms-flex-order: 4;\n order: 4;\n }\n .order-sm-5 {\n -webkit-box-ordinal-group: 6;\n -ms-flex-order: 5;\n order: 5;\n }\n .order-sm-6 {\n -webkit-box-ordinal-group: 7;\n -ms-flex-order: 6;\n order: 6;\n }\n .order-sm-7 {\n -webkit-box-ordinal-group: 8;\n -ms-flex-order: 7;\n order: 7;\n }\n .order-sm-8 {\n -webkit-box-ordinal-group: 9;\n -ms-flex-order: 8;\n order: 8;\n }\n .order-sm-9 {\n -webkit-box-ordinal-group: 10;\n -ms-flex-order: 9;\n order: 9;\n }\n .order-sm-10 {\n -webkit-box-ordinal-group: 11;\n -ms-flex-order: 10;\n order: 10;\n }\n .order-sm-11 {\n -webkit-box-ordinal-group: 12;\n -ms-flex-order: 11;\n order: 11;\n }\n .order-sm-12 {\n -webkit-box-ordinal-group: 13;\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -webkit-box-flex: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-md-1 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n -webkit-box-ordinal-group: 0;\n -ms-flex-order: -1;\n order: -1;\n }\n .order-md-last {\n -webkit-box-ordinal-group: 14;\n -ms-flex-order: 13;\n order: 13;\n }\n .order-md-0 {\n -webkit-box-ordinal-group: 1;\n -ms-flex-order: 0;\n order: 0;\n }\n .order-md-1 {\n -webkit-box-ordinal-group: 2;\n -ms-flex-order: 1;\n order: 1;\n }\n .order-md-2 {\n -webkit-box-ordinal-group: 3;\n -ms-flex-order: 2;\n order: 2;\n }\n .order-md-3 {\n -webkit-box-ordinal-group: 4;\n -ms-flex-order: 3;\n order: 3;\n }\n .order-md-4 {\n -webkit-box-ordinal-group: 5;\n -ms-flex-order: 4;\n order: 4;\n }\n .order-md-5 {\n -webkit-box-ordinal-group: 6;\n -ms-flex-order: 5;\n order: 5;\n }\n .order-md-6 {\n -webkit-box-ordinal-group: 7;\n -ms-flex-order: 6;\n order: 6;\n }\n .order-md-7 {\n -webkit-box-ordinal-group: 8;\n -ms-flex-order: 7;\n order: 7;\n }\n .order-md-8 {\n -webkit-box-ordinal-group: 9;\n -ms-flex-order: 8;\n order: 8;\n }\n .order-md-9 {\n -webkit-box-ordinal-group: 10;\n -ms-flex-order: 9;\n order: 9;\n }\n .order-md-10 {\n -webkit-box-ordinal-group: 11;\n -ms-flex-order: 10;\n order: 10;\n }\n .order-md-11 {\n -webkit-box-ordinal-group: 12;\n -ms-flex-order: 11;\n order: 11;\n }\n .order-md-12 {\n -webkit-box-ordinal-group: 13;\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -webkit-box-flex: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-lg-1 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n -webkit-box-ordinal-group: 0;\n -ms-flex-order: -1;\n order: -1;\n }\n .order-lg-last {\n -webkit-box-ordinal-group: 14;\n -ms-flex-order: 13;\n order: 13;\n }\n .order-lg-0 {\n -webkit-box-ordinal-group: 1;\n -ms-flex-order: 0;\n order: 0;\n }\n .order-lg-1 {\n -webkit-box-ordinal-group: 2;\n -ms-flex-order: 1;\n order: 1;\n }\n .order-lg-2 {\n -webkit-box-ordinal-group: 3;\n -ms-flex-order: 2;\n order: 2;\n }\n .order-lg-3 {\n -webkit-box-ordinal-group: 4;\n -ms-flex-order: 3;\n order: 3;\n }\n .order-lg-4 {\n -webkit-box-ordinal-group: 5;\n -ms-flex-order: 4;\n order: 4;\n }\n .order-lg-5 {\n -webkit-box-ordinal-group: 6;\n -ms-flex-order: 5;\n order: 5;\n }\n .order-lg-6 {\n -webkit-box-ordinal-group: 7;\n -ms-flex-order: 6;\n order: 6;\n }\n .order-lg-7 {\n -webkit-box-ordinal-group: 8;\n -ms-flex-order: 7;\n order: 7;\n }\n .order-lg-8 {\n -webkit-box-ordinal-group: 9;\n -ms-flex-order: 8;\n order: 8;\n }\n .order-lg-9 {\n -webkit-box-ordinal-group: 10;\n -ms-flex-order: 9;\n order: 9;\n }\n .order-lg-10 {\n -webkit-box-ordinal-group: 11;\n -ms-flex-order: 10;\n order: 10;\n }\n .order-lg-11 {\n -webkit-box-ordinal-group: 12;\n -ms-flex-order: 11;\n order: 11;\n }\n .order-lg-12 {\n -webkit-box-ordinal-group: 13;\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -webkit-box-flex: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-xl-1 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n -webkit-box-ordinal-group: 0;\n -ms-flex-order: -1;\n order: -1;\n }\n .order-xl-last {\n -webkit-box-ordinal-group: 14;\n -ms-flex-order: 13;\n order: 13;\n }\n .order-xl-0 {\n -webkit-box-ordinal-group: 1;\n -ms-flex-order: 0;\n order: 0;\n }\n .order-xl-1 {\n -webkit-box-ordinal-group: 2;\n -ms-flex-order: 1;\n order: 1;\n }\n .order-xl-2 {\n -webkit-box-ordinal-group: 3;\n -ms-flex-order: 2;\n order: 2;\n }\n .order-xl-3 {\n -webkit-box-ordinal-group: 4;\n -ms-flex-order: 3;\n order: 3;\n }\n .order-xl-4 {\n -webkit-box-ordinal-group: 5;\n -ms-flex-order: 4;\n order: 4;\n }\n .order-xl-5 {\n -webkit-box-ordinal-group: 6;\n -ms-flex-order: 5;\n order: 5;\n }\n .order-xl-6 {\n -webkit-box-ordinal-group: 7;\n -ms-flex-order: 6;\n order: 6;\n }\n .order-xl-7 {\n -webkit-box-ordinal-group: 8;\n -ms-flex-order: 7;\n order: 7;\n }\n .order-xl-8 {\n -webkit-box-ordinal-group: 9;\n -ms-flex-order: 8;\n order: 8;\n }\n .order-xl-9 {\n -webkit-box-ordinal-group: 10;\n -ms-flex-order: 9;\n order: 9;\n }\n .order-xl-10 {\n -webkit-box-ordinal-group: 11;\n -ms-flex-order: 10;\n order: 10;\n }\n .order-xl-11 {\n -webkit-box-ordinal-group: 12;\n -ms-flex-order: 11;\n order: 11;\n }\n .order-xl-12 {\n -webkit-box-ordinal-group: 13;\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 1rem;\n background-color: transparent;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table .table {\n background-color: #fff;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #212529;\n border-color: #32383e;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #32383e;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::-webkit-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::-moz-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:-ms-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::-ms-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:not([size]):not([multiple]) {\n height: calc(2.25rem + 2px);\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-lg > .input-group-append > .form-control-plaintext.btn {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm, .input-group-sm > .form-control,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\nselect.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(1.8125rem + 2px);\n}\n\n.form-control-lg, .input-group-lg > .form-control,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(2.875rem + 2px);\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: -webkit-inline-box;\n display: -ms-inline-flexbox;\n display: inline-flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid, .was-validated\n.custom-select:valid,\n.custom-select.is-valid {\n border-color: #28a745;\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated\n.custom-select:valid:focus,\n.custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip, .was-validated\n.custom-select:valid ~ .valid-feedback,\n.was-validated\n.custom-select:valid ~ .valid-tooltip,\n.custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n background-color: #71dd8a;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated\n.custom-select:invalid,\n.custom-select.is-invalid {\n border-color: #dc3545;\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated\n.custom-select:invalid:focus,\n.custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip, .was-validated\n.custom-select:invalid ~ .invalid-feedback,\n.was-validated\n.custom-select:invalid ~ .invalid-tooltip,\n.custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n background-color: #efa2a9;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-flex: 0;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group {\n width: auto;\n }\n .form-inline .form-check {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.btn:hover, .btn:focus {\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\n.btn:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n background-image: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n background-color: transparent;\n background-image: none;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n background-color: transparent;\n background-image: none;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n background-color: transparent;\n background-image: none;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n background-color: transparent;\n background-image: none;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n background-color: transparent;\n background-image: none;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n background-color: transparent;\n background-image: none;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n background-color: transparent;\n background-image: none;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n background-color: transparent;\n background-image: none;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n background-color: transparent;\n border-color: transparent;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n border-color: transparent;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n opacity: 0;\n transition: opacity 0.15s linear;\n}\n\n.fade.show {\n opacity: 1;\n}\n\n.collapse {\n display: none;\n}\n\n.collapse.show {\n display: block;\n}\n\ntr.collapse.show {\n display: table-row;\n}\n\ntbody.collapse.show {\n display: table-row-group;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n.dropup,\n.dropdown {\n position: relative;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropup .dropdown-menu {\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n width: 0;\n height: 0;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: -webkit-inline-box;\n display: -ms-inline-flexbox;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n -webkit-box-flex: 0;\n -ms-flex: 0 1 auto;\n flex: 0 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group,\n.btn-group-vertical .btn + .btn,\n.btn-group-vertical .btn + .btn-group,\n.btn-group-vertical .btn-group + .btn,\n.btn-group-vertical .btn-group + .btn-group {\n margin-left: -1px;\n}\n\n.btn-toolbar {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -webkit-box-pack: start;\n -ms-flex-pack: start;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-box-align: start;\n -ms-flex-align: start;\n align-items: flex-start;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n}\n\n.btn-group-vertical .btn,\n.btn-group-vertical .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -webkit-box-align: stretch;\n -ms-flex-align: stretch;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n -webkit-box-flex: 1;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file:focus {\n z-index: 3;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::before {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label,\n.input-group > .custom-file:not(:first-child) .custom-file-label::before {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: -webkit-inline-box;\n display: -ms-inline-flexbox;\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n margin-bottom: 0;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: 0;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n background-color: #dee2e6;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: 0;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background-repeat: no-repeat;\n background-position: center center;\n background-size: 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: #fff url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E\") no-repeat right 0.75rem center;\n background-size: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n opacity: 0;\n}\n\n.custom-select-sm {\n height: calc(1.8125rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 75%;\n}\n\n.custom-select-lg {\n height: calc(2.875rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 125%;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-control {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:focus ~ .custom-file-control::before {\n border-color: #80bdff;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: calc(calc(2.25rem + 2px) - 1px * 2);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: 1px solid #ced4da;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.nav {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n -webkit-box-flex: 1;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -webkit-box-flex: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: justify;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: justify;\n -ms-flex-pack: justify;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n -ms-flex-preferred-size: 100%;\n flex-basis: 100%;\n -webkit-box-flex: 1;\n -ms-flex-positive: 1;\n flex-grow: 1;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -webkit-box-pack: start;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n .navbar-expand-sm .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -webkit-box-pack: start;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n .navbar-expand-md .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -webkit-box-pack: start;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n .navbar-expand-lg .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -webkit-box-pack: start;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n .navbar-expand-xl .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n }\n}\n\n.navbar-expand {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -webkit-box-pack: start;\n -ms-flex-pack: start;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-direction: row;\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-expand .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n -webkit-box-flex: 1;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-flex: 1;\n -ms-flex: 1 0 0%;\n flex: 1 0 0%;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n -webkit-box-orient: horizontal;\n -webkit-box-direction: normal;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n }\n .card-group > .card {\n -webkit-box-flex: 1;\n -ms-flex: 1 0 0%;\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-top,\n .card-group > .card:first-child .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-bottom,\n .card-group > .card:first-child .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-top,\n .card-group > .card:last-child .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-bottom,\n .card-group > .card:last-child .card-footer {\n border-bottom-left-radius: 0;\n }\n .card-group > .card:only-child {\n border-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-top,\n .card-group > .card:only-child .card-header {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-bottom,\n .card-group > .card:only-child .card-footer {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) {\n border-radius: 0;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer {\n border-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n -webkit-column-count: 3;\n -moz-column-count: 3;\n column-count: 3;\n -webkit-column-gap: 1.25rem;\n -moz-column-gap: 1.25rem;\n column-gap: 1.25rem;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.breadcrumb {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-link:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\n.badge-primary[href]:hover, .badge-primary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #0062cc;\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\n.badge-secondary[href]:hover, .badge-secondary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #545b62;\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\n.badge-success[href]:hover, .badge-success[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1e7e34;\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\n.badge-info[href]:hover, .badge-info[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #117a8b;\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\n.badge-warning[href]:hover, .badge-warning[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #d39e00;\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\n.badge-danger[href]:hover, .badge-danger[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #bd2130;\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\n.badge-light[href]:hover, .badge-light[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #dae0e5;\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.badge-dark[href]:hover, .badge-dark[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1d2124;\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n color: #fff;\n text-align: center;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n -webkit-animation: progress-bar-stripes 1s linear infinite;\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n.media {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: start;\n -ms-flex-align: start;\n align-items: flex-start;\n}\n\n.media-body {\n -webkit-box-flex: 1;\n -ms-flex: 1;\n flex: 1;\n}\n\n.list-group {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item:hover, .list-group-item:focus {\n z-index: 1;\n text-decoration: none;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover, .close:focus {\n color: #000;\n text-decoration: none;\n opacity: .75;\n}\n\n.close:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: -webkit-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;\n -webkit-transform: translate(0, -25%);\n transform: translate(0, -25%);\n}\n\n.modal.show .modal-dialog {\n -webkit-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n\n.modal-dialog-centered {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n min-height: calc(100% - (0.5rem * 2));\n}\n\n.modal-content {\n position: relative;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: start;\n -ms-flex-align: start;\n align-items: flex-start;\n -webkit-box-pack: justify;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 1rem;\n border-bottom: 1px solid #e9ecef;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n -webkit-box-flex: 1;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: end;\n -ms-flex-pack: end;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #e9ecef;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-centered {\n min-height: calc(100% - (1.75rem * 2));\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg {\n max-width: 800px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top .arrow, .bs-popover-auto[x-placement^=\"top\"] .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before,\n.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0;\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before {\n bottom: 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n bottom: 1px;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right .arrow, .bs-popover-auto[x-placement^=\"right\"] .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before,\n.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0.5rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before {\n left: 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n left: 1px;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^=\"bottom\"] .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before,\n.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n border-width: 0 0.5rem 0.5rem 0.5rem;\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before {\n top: 0;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n top: 1px;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left .arrow, .bs-popover-auto[x-placement^=\"left\"] .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before,\n.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n border-width: 0.5rem 0 0.5rem 0.5rem;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before {\n right: 0;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n right: 1px;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n color: inherit;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-item {\n position: relative;\n display: none;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n width: 100%;\n transition: -webkit-transform 0.6s ease;\n transition: transform 0.6s ease;\n transition: transform 0.6s ease, -webkit-transform 0.6s ease;\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n perspective: 1000px;\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next,\n.carousel-item-prev {\n position: absolute;\n top: 0;\n}\n\n.carousel-item-next.carousel-item-left,\n.carousel-item-prev.carousel-item-right {\n -webkit-transform: translateX(0);\n transform: translateX(0);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-next.carousel-item-left,\n .carousel-item-prev.carousel-item-right {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-item-next,\n.active.carousel-item-right {\n -webkit-transform: translateX(100%);\n transform: translateX(100%);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-next,\n .active.carousel-item-right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n }\n}\n\n.carousel-item-prev,\n.active.carousel-item-left {\n -webkit-transform: translateX(-100%);\n transform: translateX(-100%);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-prev,\n .active.carousel-item-left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: .9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: transparent no-repeat center center;\n background-size: 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 10px;\n left: 0;\n z-index: 15;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n position: relative;\n -webkit-box-flex: 0;\n -ms-flex: 0 1 auto;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n background-color: rgba(255, 255, 255, 0.5);\n}\n\n.carousel-indicators li::before {\n position: absolute;\n top: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators li::after {\n position: absolute;\n bottom: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators .active {\n background-color: #fff;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n}\n\n.d-inline-flex {\n display: -webkit-inline-box !important;\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: -webkit-inline-box !important;\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-md-inline-flex {\n display: -webkit-inline-box !important;\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: -webkit-inline-box !important;\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: -webkit-inline-box !important;\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: -webkit-box !important;\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-print-inline-flex {\n display: -webkit-inline-box !important;\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n}\n\n.flex-column {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n -webkit-box-pack: start !important;\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n -webkit-box-pack: end !important;\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n}\n\n.justify-content-between {\n -webkit-box-pack: justify !important;\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n}\n\n.align-items-start {\n -webkit-box-align: start !important;\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n}\n\n.align-items-end {\n -webkit-box-align: end !important;\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n}\n\n.align-items-center {\n -webkit-box-align: center !important;\n -ms-flex-align: center !important;\n align-items: center !important;\n}\n\n.align-items-baseline {\n -webkit-box-align: baseline !important;\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n -webkit-box-align: stretch !important;\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n}\n\n.align-content-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n}\n\n.align-content-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n}\n\n.align-content-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n}\n\n.align-content-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n}\n\n.align-content-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n}\n\n.align-self-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n}\n\n.align-self-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n}\n\n.align-self-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n}\n\n.align-self-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n}\n\n.align-self-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-sm-column {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-sm-start {\n -webkit-box-pack: start !important;\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n -webkit-box-pack: end !important;\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-sm-between {\n -webkit-box-pack: justify !important;\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n -webkit-box-align: start !important;\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n -webkit-box-align: end !important;\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n -webkit-box-align: center !important;\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-sm-baseline {\n -webkit-box-align: baseline !important;\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n -webkit-box-align: stretch !important;\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-sm-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-sm-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-sm-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-sm-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-sm-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-md-column {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-md-start {\n -webkit-box-pack: start !important;\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n -webkit-box-pack: end !important;\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-md-between {\n -webkit-box-pack: justify !important;\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-md-start {\n -webkit-box-align: start !important;\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-md-end {\n -webkit-box-align: end !important;\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-md-center {\n -webkit-box-align: center !important;\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-md-baseline {\n -webkit-box-align: baseline !important;\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n -webkit-box-align: stretch !important;\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-md-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-md-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-md-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-md-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-md-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-md-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-md-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-md-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-md-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-md-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-lg-column {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-lg-start {\n -webkit-box-pack: start !important;\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n -webkit-box-pack: end !important;\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-lg-between {\n -webkit-box-pack: justify !important;\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n -webkit-box-align: start !important;\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n -webkit-box-align: end !important;\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n -webkit-box-align: center !important;\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-lg-baseline {\n -webkit-box-align: baseline !important;\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n -webkit-box-align: stretch !important;\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-lg-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-lg-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-lg-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-lg-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-lg-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-xl-column {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: normal !important;\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n -webkit-box-orient: horizontal !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n -webkit-box-orient: vertical !important;\n -webkit-box-direction: reverse !important;\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xl-start {\n -webkit-box-pack: start !important;\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n -webkit-box-pack: end !important;\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-xl-between {\n -webkit-box-pack: justify !important;\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n -webkit-box-align: start !important;\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n -webkit-box-align: end !important;\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n -webkit-box-align: center !important;\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-xl-baseline {\n -webkit-box-align: baseline !important;\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n -webkit-box-align: stretch !important;\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-xl-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-xl-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-xl-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-xl-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-xl-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: -webkit-sticky !important;\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports ((position: -webkit-sticky) or (position: sticky)) {\n .sticky-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n -webkit-clip-path: inset(50%);\n clip-path: inset(50%);\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n -webkit-clip-path: none;\n clip-path: none;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0062cc !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #545b62 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #1e7e34 !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #117a8b !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #d39e00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #bd2130 !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #dae0e5 !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #1d2124 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*!\n * Bootstrap v4.0.0 (https://getbootstrap.com)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: transparent;\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: .5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-family: inherit;\n font-weight: 500;\n line-height: 1.2;\n color: inherit;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014 \\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode,\nkbd,\npre,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n}\n\n.col-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n order: -1;\n}\n\n.order-last {\n order: 13;\n}\n\n.order-0 {\n order: 0;\n}\n\n.order-1 {\n order: 1;\n}\n\n.order-2 {\n order: 2;\n}\n\n.order-3 {\n order: 3;\n}\n\n.order-4 {\n order: 4;\n}\n\n.order-5 {\n order: 5;\n}\n\n.order-6 {\n order: 6;\n}\n\n.order-7 {\n order: 7;\n}\n\n.order-8 {\n order: 8;\n}\n\n.order-9 {\n order: 9;\n}\n\n.order-10 {\n order: 10;\n}\n\n.order-11 {\n order: 11;\n}\n\n.order-12 {\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-sm-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n order: -1;\n }\n .order-sm-last {\n order: 13;\n }\n .order-sm-0 {\n order: 0;\n }\n .order-sm-1 {\n order: 1;\n }\n .order-sm-2 {\n order: 2;\n }\n .order-sm-3 {\n order: 3;\n }\n .order-sm-4 {\n order: 4;\n }\n .order-sm-5 {\n order: 5;\n }\n .order-sm-6 {\n order: 6;\n }\n .order-sm-7 {\n order: 7;\n }\n .order-sm-8 {\n order: 8;\n }\n .order-sm-9 {\n order: 9;\n }\n .order-sm-10 {\n order: 10;\n }\n .order-sm-11 {\n order: 11;\n }\n .order-sm-12 {\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-md-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n order: -1;\n }\n .order-md-last {\n order: 13;\n }\n .order-md-0 {\n order: 0;\n }\n .order-md-1 {\n order: 1;\n }\n .order-md-2 {\n order: 2;\n }\n .order-md-3 {\n order: 3;\n }\n .order-md-4 {\n order: 4;\n }\n .order-md-5 {\n order: 5;\n }\n .order-md-6 {\n order: 6;\n }\n .order-md-7 {\n order: 7;\n }\n .order-md-8 {\n order: 8;\n }\n .order-md-9 {\n order: 9;\n }\n .order-md-10 {\n order: 10;\n }\n .order-md-11 {\n order: 11;\n }\n .order-md-12 {\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-lg-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n order: -1;\n }\n .order-lg-last {\n order: 13;\n }\n .order-lg-0 {\n order: 0;\n }\n .order-lg-1 {\n order: 1;\n }\n .order-lg-2 {\n order: 2;\n }\n .order-lg-3 {\n order: 3;\n }\n .order-lg-4 {\n order: 4;\n }\n .order-lg-5 {\n order: 5;\n }\n .order-lg-6 {\n order: 6;\n }\n .order-lg-7 {\n order: 7;\n }\n .order-lg-8 {\n order: 8;\n }\n .order-lg-9 {\n order: 9;\n }\n .order-lg-10 {\n order: 10;\n }\n .order-lg-11 {\n order: 11;\n }\n .order-lg-12 {\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-xl-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n order: -1;\n }\n .order-xl-last {\n order: 13;\n }\n .order-xl-0 {\n order: 0;\n }\n .order-xl-1 {\n order: 1;\n }\n .order-xl-2 {\n order: 2;\n }\n .order-xl-3 {\n order: 3;\n }\n .order-xl-4 {\n order: 4;\n }\n .order-xl-5 {\n order: 5;\n }\n .order-xl-6 {\n order: 6;\n }\n .order-xl-7 {\n order: 7;\n }\n .order-xl-8 {\n order: 8;\n }\n .order-xl-9 {\n order: 9;\n }\n .order-xl-10 {\n order: 10;\n }\n .order-xl-11 {\n order: 11;\n }\n .order-xl-12 {\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 1rem;\n background-color: transparent;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table .table {\n background-color: #fff;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #212529;\n border-color: #32383e;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #32383e;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:not([size]):not([multiple]) {\n height: calc(2.25rem + 2px);\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-lg > .input-group-append > .form-control-plaintext.btn {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm, .input-group-sm > .form-control,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\nselect.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(1.8125rem + 2px);\n}\n\n.form-control-lg, .input-group-lg > .form-control,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(2.875rem + 2px);\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: inline-flex;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid, .was-validated\n.custom-select:valid,\n.custom-select.is-valid {\n border-color: #28a745;\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated\n.custom-select:valid:focus,\n.custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip, .was-validated\n.custom-select:valid ~ .valid-feedback,\n.was-validated\n.custom-select:valid ~ .valid-tooltip,\n.custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n background-color: #71dd8a;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated\n.custom-select:invalid,\n.custom-select.is-invalid {\n border-color: #dc3545;\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated\n.custom-select:invalid:focus,\n.custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip, .was-validated\n.custom-select:invalid ~ .invalid-feedback,\n.was-validated\n.custom-select:invalid ~ .invalid-tooltip,\n.custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n background-color: #efa2a9;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: flex;\n flex-flow: row wrap;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: flex;\n flex: 0 0 auto;\n flex-flow: row wrap;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group {\n width: auto;\n }\n .form-inline .form-check {\n display: flex;\n align-items: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n align-items: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n user-select: none;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.btn:hover, .btn:focus {\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\n.btn:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n background-image: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n background-color: transparent;\n background-image: none;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n background-color: transparent;\n background-image: none;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n background-color: transparent;\n background-image: none;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n background-color: transparent;\n background-image: none;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n background-color: transparent;\n background-image: none;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n background-color: transparent;\n background-image: none;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n background-color: transparent;\n background-image: none;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n background-color: transparent;\n background-image: none;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n background-color: transparent;\n border-color: transparent;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n border-color: transparent;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n opacity: 0;\n transition: opacity 0.15s linear;\n}\n\n.fade.show {\n opacity: 1;\n}\n\n.collapse {\n display: none;\n}\n\n.collapse.show {\n display: block;\n}\n\ntr.collapse.show {\n display: table-row;\n}\n\ntbody.collapse.show {\n display: table-row-group;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n.dropup,\n.dropdown {\n position: relative;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropup .dropdown-menu {\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n width: 0;\n height: 0;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n flex: 0 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group,\n.btn-group-vertical .btn + .btn,\n.btn-group-vertical .btn + .btn-group,\n.btn-group-vertical .btn-group + .btn,\n.btn-group-vertical .btn-group + .btn-group {\n margin-left: -1px;\n}\n\n.btn-toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.btn-group-vertical .btn,\n.btn-group-vertical .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file:focus {\n z-index: 3;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: flex;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::before {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label,\n.input-group > .custom-file:not(:first-child) .custom-file-label::before {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: flex;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n margin-bottom: 0;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: 0;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n user-select: none;\n background-color: #dee2e6;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: 0;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background-repeat: no-repeat;\n background-position: center center;\n background-size: 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: #fff url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E\") no-repeat right 0.75rem center;\n background-size: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n opacity: 0;\n}\n\n.custom-select-sm {\n height: calc(1.8125rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 75%;\n}\n\n.custom-select-lg {\n height: calc(2.875rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 125%;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-control {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:focus ~ .custom-file-control::before {\n border-color: #80bdff;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: calc(calc(2.25rem + 2px) - 1px * 2);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: 1px solid #ced4da;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n flex-basis: 0;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n flex-basis: 100%;\n flex-grow: 1;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n .navbar-expand-sm .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n .navbar-expand-md .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n .navbar-expand-lg .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n .navbar-expand-xl .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n }\n}\n\n.navbar-expand {\n flex-flow: row nowrap;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-expand .dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: flex;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: flex;\n flex: 1 0 0%;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: flex;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n flex-flow: row wrap;\n }\n .card-group > .card {\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-top,\n .card-group > .card:first-child .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-bottom,\n .card-group > .card:first-child .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-top,\n .card-group > .card:last-child .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-bottom,\n .card-group > .card:last-child .card-footer {\n border-bottom-left-radius: 0;\n }\n .card-group > .card:only-child {\n border-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-top,\n .card-group > .card:only-child .card-header {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-bottom,\n .card-group > .card:only-child .card-footer {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) {\n border-radius: 0;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer {\n border-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n column-count: 3;\n column-gap: 1.25rem;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.breadcrumb {\n display: flex;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-link:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\n.badge-primary[href]:hover, .badge-primary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #0062cc;\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\n.badge-secondary[href]:hover, .badge-secondary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #545b62;\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\n.badge-success[href]:hover, .badge-success[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1e7e34;\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\n.badge-info[href]:hover, .badge-info[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #117a8b;\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\n.badge-warning[href]:hover, .badge-warning[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #d39e00;\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\n.badge-danger[href]:hover, .badge-danger[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #bd2130;\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\n.badge-light[href]:hover, .badge-light[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #dae0e5;\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.badge-dark[href]:hover, .badge-dark[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1d2124;\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: flex;\n flex-direction: column;\n justify-content: center;\n color: #fff;\n text-align: center;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n.media {\n display: flex;\n align-items: flex-start;\n}\n\n.media-body {\n flex: 1;\n}\n\n.list-group {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item:hover, .list-group-item:focus {\n z-index: 1;\n text-decoration: none;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover, .close:focus {\n color: #000;\n text-decoration: none;\n opacity: .75;\n}\n\n.close:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: transform 0.3s ease-out;\n transform: translate(0, -25%);\n}\n\n.modal.show .modal-dialog {\n transform: translate(0, 0);\n}\n\n.modal-dialog-centered {\n display: flex;\n align-items: center;\n min-height: calc(100% - (0.5rem * 2));\n}\n\n.modal-content {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 1rem;\n border-bottom: 1px solid #e9ecef;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #e9ecef;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-centered {\n min-height: calc(100% - (1.75rem * 2));\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg {\n max-width: 800px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top .arrow, .bs-popover-auto[x-placement^=\"top\"] .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before,\n.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0;\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before {\n bottom: 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n bottom: 1px;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right .arrow, .bs-popover-auto[x-placement^=\"right\"] .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before,\n.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0.5rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before {\n left: 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n left: 1px;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^=\"bottom\"] .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before,\n.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n border-width: 0 0.5rem 0.5rem 0.5rem;\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before {\n top: 0;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n top: 1px;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left .arrow, .bs-popover-auto[x-placement^=\"left\"] .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before,\n.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n border-width: 0.5rem 0 0.5rem 0.5rem;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before {\n right: 0;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n right: 1px;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n color: inherit;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-item {\n position: relative;\n display: none;\n align-items: center;\n width: 100%;\n transition: transform 0.6s ease;\n backface-visibility: hidden;\n perspective: 1000px;\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next,\n.carousel-item-prev {\n position: absolute;\n top: 0;\n}\n\n.carousel-item-next.carousel-item-left,\n.carousel-item-prev.carousel-item-right {\n transform: translateX(0);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-next.carousel-item-left,\n .carousel-item-prev.carousel-item-right {\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-item-next,\n.active.carousel-item-right {\n transform: translateX(100%);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-next,\n .active.carousel-item-right {\n transform: translate3d(100%, 0, 0);\n }\n}\n\n.carousel-item-prev,\n.active.carousel-item-left {\n transform: translateX(-100%);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-prev,\n .active.carousel-item-left {\n transform: translate3d(-100%, 0, 0);\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: .9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: transparent no-repeat center center;\n background-size: 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 10px;\n left: 0;\n z-index: 15;\n display: flex;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n position: relative;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n background-color: rgba(255, 255, 255, 0.5);\n}\n\n.carousel-indicators li::before {\n position: absolute;\n top: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators li::after {\n position: absolute;\n bottom: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators .active {\n background-color: #fff;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports (position: sticky) {\n .sticky-top {\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n clip-path: inset(50%);\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n clip-path: none;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0062cc !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #545b62 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #1e7e34 !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #117a8b !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #d39e00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #bd2130 !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #dae0e5 !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #1d2124 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */","// stylelint-disable indentation\n\n// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Origally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS—an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular psuedo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover {\n &:hover { @content; }\n}\n\n@mixin hover-focus {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n","// stylelint-disable declaration-no-important, selector-list-comma-newline-after\n\n//\n// Headings\n//\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1, .h1 { font-size: $h1-font-size; }\nh2, .h2 { font-size: $h2-font-size; }\nh3, .h3 { font-size: $h3-font-size; }\nh4, .h4 { font-size: $h4-font-size; }\nh5, .h5 { font-size: $h5-font-size; }\nh6, .h6 { font-size: $h6-font-size; }\n\n.lead {\n font-size: $lead-font-size;\n font-weight: $lead-font-weight;\n}\n\n// Type display classes\n.display-1 {\n font-size: $display1-size;\n font-weight: $display1-weight;\n line-height: $display-line-height;\n}\n.display-2 {\n font-size: $display2-size;\n font-weight: $display2-weight;\n line-height: $display-line-height;\n}\n.display-3 {\n font-size: $display3-size;\n font-weight: $display3-weight;\n line-height: $display-line-height;\n}\n.display-4 {\n font-size: $display4-size;\n font-weight: $display4-weight;\n line-height: $display-line-height;\n}\n\n\n//\n// Horizontal rules\n//\n\nhr {\n margin-top: $hr-margin-y;\n margin-bottom: $hr-margin-y;\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n}\n\n\n//\n// Emphasis\n//\n\nsmall,\n.small {\n font-size: $small-font-size;\n font-weight: $font-weight-normal;\n}\n\nmark,\n.mark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n//\n// Lists\n//\n\n.list-unstyled {\n @include list-unstyled;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n @include list-unstyled;\n}\n.list-inline-item {\n display: inline-block;\n\n &:not(:last-child) {\n margin-right: $list-inline-padding;\n }\n}\n\n\n//\n// Misc\n//\n\n// Builds on `abbr`\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n// Blockquotes\n.blockquote {\n margin-bottom: $spacer;\n font-size: $blockquote-font-size;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%; // back to default font-size\n color: $blockquote-small-color;\n\n &::before {\n content: \"\\2014 \\00A0\"; // em dash, nbsp\n }\n}\n","// Lists\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n@mixin list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n","// Responsive images (ensure images don't scale beyond their parents)\n//\n// This is purposefully opt-in via an explicit class rather than being the default for all ``s.\n// We previously tried the \"images are responsive by default\" approach in Bootstrap v2,\n// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)\n// which weren't expecting the images within themselves to be involuntarily resized.\n// See also https://github.com/twbs/bootstrap/issues/18178\n.img-fluid {\n @include img-fluid;\n}\n\n\n// Image thumbnails\n.img-thumbnail {\n padding: $thumbnail-padding;\n background-color: $thumbnail-bg;\n border: $thumbnail-border-width solid $thumbnail-border-color;\n @include border-radius($thumbnail-border-radius);\n @include box-shadow($thumbnail-box-shadow);\n\n // Keep them at most 100% wide\n @include img-fluid;\n}\n\n//\n// Figures\n//\n\n.figure {\n // Ensures the caption's text aligns with the image.\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: ($spacer / 2);\n line-height: 1;\n}\n\n.figure-caption {\n font-size: $figure-caption-font-size;\n color: $figure-caption-color;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n@mixin img-fluid {\n // Part 1: Set a maximum relative to the parent\n max-width: 100%;\n // Part 2: Override the height to auto, otherwise images will be stretched\n // when setting a width and height attribute on the img element.\n height: auto;\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size.\n\n// stylelint-disable indentation, media-query-list-comma-newline-after\n@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {\n background-image: url($file-1x);\n\n // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,\n // but doesn't convert dppx=>dpi.\n // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.\n // Compatibility info: https://caniuse.com/#feat=css-media-resolution\n @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx\n only screen and (min-resolution: 2dppx) { // Standardized\n background-image: url($file-2x);\n background-size: $width-1x $height-1x;\n }\n}\n","// Single side border-radius\n\n@mixin border-radius($radius: $border-radius) {\n @if $enable-rounded {\n border-radius: $radius;\n }\n}\n\n@mixin border-top-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n","// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: $font-family-monospace;\n}\n\n// Inline code\ncode {\n font-size: $code-font-size;\n color: $code-color;\n word-break: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n font-size: $kbd-font-size;\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n @include box-shadow($kbd-box-shadow);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: $nested-kbd-font-weight;\n @include box-shadow(none);\n }\n}\n\n// Blocks of code\npre {\n display: block;\n font-size: $code-font-size;\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: $pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n}\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but with 100% width for\n// fluid, full width layouts.\n\n@if $enable-grid-classes {\n .container-fluid {\n @include make-container();\n }\n}\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n }\n}\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","/// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-container() {\n width: 100%;\n padding-right: ($grid-gutter-width / 2);\n padding-left: ($grid-gutter-width / 2);\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row() {\n display: flex;\n flex-wrap: wrap;\n margin-right: ($grid-gutter-width / -2);\n margin-left: ($grid-gutter-width / -2);\n}\n\n@mixin make-col-ready() {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n min-height: 1px; // Prevent collapsing\n padding-right: ($grid-gutter-width / 2);\n padding-left: ($grid-gutter-width / 2);\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .02px, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $max: breakpoint-max($name, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($name, $breakpoints) {\n @content;\n }\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n min-height: 1px; // Prevent columns from collapsing when empty\n padding-right: ($gutter / 2);\n padding-left: ($gutter / 2);\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col#{$infix}-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none; // Reset earlier grid tiers\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n }\n}\n","//\n// Basic Bootstrap table\n//\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: $spacer;\n background-color: $table-bg; // Reset for nesting within parents with `background-color`.\n\n th,\n td {\n padding: $table-cell-padding;\n vertical-align: top;\n border-top: $table-border-width solid $table-border-color;\n }\n\n thead th {\n vertical-align: bottom;\n border-bottom: (2 * $table-border-width) solid $table-border-color;\n }\n\n tbody + tbody {\n border-top: (2 * $table-border-width) solid $table-border-color;\n }\n\n .table {\n background-color: $body-bg;\n }\n}\n\n\n//\n// Condensed table w/ half padding\n//\n\n.table-sm {\n th,\n td {\n padding: $table-cell-padding-sm;\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: $table-border-width solid $table-border-color;\n\n th,\n td {\n border: $table-border-width solid $table-border-color;\n }\n\n thead {\n th,\n td {\n border-bottom-width: (2 * $table-border-width);\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n tbody tr:nth-of-type(odd) {\n background-color: $table-accent-bg;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n tbody tr {\n @include hover {\n background-color: $table-hover-bg;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n@each $color, $value in $theme-colors {\n @include table-row-variant($color, theme-color-level($color, -9));\n}\n\n@include table-row-variant(active, $table-active-bg);\n\n\n// Dark styles\n//\n// Same table markup, but inverted color scheme: dark background and light text.\n\n// stylelint-disable-next-line no-duplicate-selectors\n.table {\n .thead-dark {\n th {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n border-color: $table-dark-border-color;\n }\n }\n\n .thead-light {\n th {\n color: $table-head-color;\n background-color: $table-head-bg;\n border-color: $table-border-color;\n }\n }\n}\n\n.table-dark {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n\n th,\n td,\n thead th {\n border-color: $table-dark-border-color;\n }\n\n &.table-bordered {\n border: 0;\n }\n\n &.table-striped {\n tbody tr:nth-of-type(odd) {\n background-color: $table-dark-accent-bg;\n }\n }\n\n &.table-hover {\n tbody tr {\n @include hover {\n background-color: $table-dark-hover-bg;\n }\n }\n }\n}\n\n\n// Responsive tables\n//\n// Generate series of `.table-responsive-*` classes for configuring the screen\n// size of where your table will overflow.\n\n.table-responsive {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $next: breakpoint-next($breakpoint, $grid-breakpoints);\n $infix: breakpoint-infix($next, $grid-breakpoints);\n\n &#{$infix} {\n @include media-breakpoint-down($breakpoint) {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057\n\n // Prevent double border on horizontal scroll due to use of `display: block;`\n > .table-bordered {\n border: 0;\n }\n }\n }\n }\n}\n","// Tables\n\n@mixin table-row-variant($state, $background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table-#{$state} {\n &,\n > th,\n > td {\n background-color: $background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover {\n $hover-background: darken($background, 5%);\n\n .table-#{$state} {\n @include hover {\n background-color: $hover-background;\n\n > td,\n > th {\n background-color: $hover-background;\n }\n }\n }\n }\n}\n","// stylelint-disable selector-no-qualifying-type\n\n//\n// Textual form controls\n//\n\n.form-control {\n display: block;\n width: 100%;\n padding: $input-padding-y $input-padding-x;\n font-size: $font-size-base;\n line-height: $input-line-height;\n color: $input-color;\n background-color: $input-bg;\n background-clip: padding-box;\n border: $input-border-width solid $input-border-color;\n\n // Note: This has no effect on `s in CSS.\n @if $enable-rounded {\n // Manually use the if/else instead of the mixin to account for iOS override\n border-radius: $input-border-radius;\n } @else {\n // Otherwise undo the iOS default\n border-radius: 0;\n }\n\n @include box-shadow($input-box-shadow);\n @include transition($input-transition);\n\n // Unstyle the caret on ` receives focus\n // in IE and (under certain conditions) Edge, as it looks bad and cannot be made to\n // match the appearance of the native widget.\n // See https://github.com/twbs/bootstrap/issues/19398.\n color: $input-color;\n background-color: $input-bg;\n }\n}\n\n// Make file inputs better match text inputs by forcing them to new lines.\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n\n//\n// Labels\n//\n\n// For use with horizontal and inline forms, when you need the label (or legend)\n// text to align with the form controls.\n.col-form-label {\n padding-top: calc(#{$input-padding-y} + #{$input-border-width});\n padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});\n margin-bottom: 0; // Override the `
/// - //[Fact] + //[Fact(Timeout = Constants.TEST_TIMEOUT)] //public async Task CancelTestAsync() //{ // var endpoint = new ProtobufServerInfo(); diff --git a/test/TestWebApp.Tests/ProtobufClientTest.cs b/tests/TestWebApp.Tests/ProtobufClientTest.cs similarity index 69% rename from test/TestWebApp.Tests/ProtobufClientTest.cs rename to tests/TestWebApp.Tests/ProtobufClientTest.cs index a7f4037..8904daf 100644 --- a/test/TestWebApp.Tests/ProtobufClientTest.cs +++ b/tests/TestWebApp.Tests/ProtobufClientTest.cs @@ -1,65 +1,56 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; -using System; -using NUnit.Framework; using TestWebApp.Clients; -using System.Net.Http; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Flurl.Http; using TestWebApp.Contracts; -using Microsoft.AspNetCore.TestHost; -using AspNetCore.Client; +using Xunit; namespace TestWebApp.Tests { - [TestFixture] public class ProtobufClientTest { - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public void GetTest() { using (var endpoint = new ProtobufServerInfo()) { var valuesClient = endpoint.Provider.GetService(); - var values = valuesClient.GetEnumerable(); + var values = valuesClient.GetEnumerable(cancellationToken: endpoint.TimeoutToken); - Assert.AreEqual(new List { "value1", "value2" }, values); + Assert.Equal(new List { "value1", "value2" }, values); } } - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public void HeaderTestString() { using (var endpoint = new ProtobufServerInfo()) { var valuesClient = endpoint.Provider.GetService(); - var value = valuesClient.HeaderTestString("Val1", "Val2"); + var value = valuesClient.HeaderTestString("Val1", "Val2", cancellationToken: endpoint.TimeoutToken); - Assert.AreEqual("Val1", value); + Assert.Equal("Val1", value); } } - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public void HeaderTestInt() { using (var endpoint = new ProtobufServerInfo()) { var valuesClient = endpoint.Provider.GetService(); - var value = valuesClient.HeaderTestInt(15); + var value = valuesClient.HeaderTestInt(15, cancellationToken: endpoint.TimeoutToken); - Assert.AreEqual(15, value); + Assert.Equal(15, value); } } - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public void DtoReturns() { using (var endpoint = new ProtobufServerInfo()) @@ -71,15 +62,15 @@ public void DtoReturns() OKCallback: (_) => { dto = _; - }); + }, cancellationToken: endpoint.TimeoutToken); - Assert.AreEqual(15, dto.Id); + Assert.Equal(15, dto.Id); } } - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public void RequestAndResponseChecks() { using (var endpoint = new ProtobufServerInfo()) @@ -94,20 +85,20 @@ public void RequestAndResponseChecks() When = DateTime.UtcNow }; - var response = valuesClient.DtoForDtoRaw(dto); + var response = valuesClient.DtoForDtoRaw(dto, cancellationToken: endpoint.TimeoutToken); Assert.True(response.RequestMessage.Content.Headers?.ContentType?.MediaType == "application/x-protobuf"); Assert.True(response.Content.Headers.ContentType.MediaType == "application/x-protobuf"); - var actual = valuesClient.DtoForDto(dto); + var actual = valuesClient.DtoForDto(dto, cancellationToken: endpoint.TimeoutToken); - Assert.AreEqual(dto.Collision, actual.Collision); - Assert.AreEqual(dto.Description, actual.Description); - Assert.AreEqual(dto.Id, actual.Id); - Assert.AreEqual(dto.When, actual.When); + Assert.Equal(dto.Collision, actual.Collision); + Assert.Equal(dto.Description, actual.Description); + Assert.Equal(dto.Id, actual.Id); + Assert.Equal(dto.When, actual.When); } } @@ -116,7 +107,7 @@ public void RequestAndResponseChecks() /// When the HttpClient has the default HttpMessageHandler, the SendAsync will cancel approriately, until they match this functionality, this test will be disabled ///
/// - //[Fact] + //[Fact(Timeout = Constants.TEST_TIMEOUT)] //public async Task CancelTestAsync() //{ // var endpoint = new ProtobufServerInfo(); diff --git a/test/TestWebApp.Tests/ServerInfo.cs b/tests/TestWebApp.Tests/ServerInfo.cs similarity index 65% rename from test/TestWebApp.Tests/ServerInfo.cs rename to tests/TestWebApp.Tests/ServerInfo.cs index f20b191..cf41d42 100644 --- a/test/TestWebApp.Tests/ServerInfo.cs +++ b/tests/TestWebApp.Tests/ServerInfo.cs @@ -1,26 +1,57 @@ -using AspNetCore.Client; +using System; +using System.Net.Http; +using System.Threading; +using Beffyman.AspNetCore.Client; using Flurl.Http; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using TestWebApp.Clients; namespace TestWebApp.Tests { public abstract class ServerInfo : IDisposable where T : class { + public IHost Host { get; } public IServiceProvider Provider { get; } public TestServer Server { get; } public HttpClient Client { get; } - public ServerInfo() + private readonly CancellationTokenSource _tokenSource; + public readonly CancellationToken TimeoutToken; + + public ServerInfo(int testTimeout = Constants.TEST_TIMEOUT) { - Server = new TestServer(new WebHostBuilder() - .UseStartup()); + _tokenSource = new CancellationTokenSource(testTimeout); + + TimeoutToken = _tokenSource.Token; + + TimeoutToken.Register(() => + { + try + { + Host?.StopAsync().GetAwaiter().GetResult(); + } + catch { } + }); + + Host = new HostBuilder() + .ConfigureWebHost(builder => + { + builder.UseTestServer() + .UseShutdownTimeout(TimeSpan.FromMilliseconds(testTimeout)) + .UseStartup() + .ConfigureLogging(options => + { + options.AddDebug(); + }); + }).Start(); + + Server = Host.GetTestServer(); + + Server.AllowSynchronousIO = true; Client = Server.CreateClient(); @@ -28,6 +59,7 @@ public ServerInfo() services.AddTestWebClients(ConfigureClient); Provider = services.BuildServiceProvider(); + } protected abstract void ConfigureClient(ClientConfiguration configure); @@ -36,6 +68,7 @@ public void Dispose() { Client.Dispose(); Server.Dispose(); + Host.Dispose(); } } diff --git a/test/TestWebApp.Tests/SignalRTests.cs b/tests/TestWebApp.Tests/SignalRTests.cs similarity index 69% rename from test/TestWebApp.Tests/SignalRTests.cs rename to tests/TestWebApp.Tests/SignalRTests.cs index 0069649..d712115 100644 --- a/test/TestWebApp.Tests/SignalRTests.cs +++ b/tests/TestWebApp.Tests/SignalRTests.cs @@ -1,14 +1,12 @@ -using Microsoft.AspNetCore.SignalR.Client; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using TestWebApp.Contracts; using TestWebApp.Hubs; +using Xunit; namespace TestWebApp.Tests { @@ -22,11 +20,10 @@ public static Task WhenCanceled(this CancellationToken cancellationToken) } } - [TestFixture] public class SignalRTests { - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public async Task SendReceiveMessageAsync() { using (var endpoint = new JsonServerInfo()) @@ -51,21 +48,21 @@ public async Task SendReceiveMessageAsync() tokenSource.Cancel(); }); - await hub.StartAsync(); + await hub.StartAsync(endpoint.TimeoutToken); - await hub.SendMessageAsync("Test", "Hello World"); + await hub.SendMessageAsync("Test", "Hello World", endpoint.TimeoutToken); await token.WhenCanceled(); await hub.StopAsync(); - Assert.AreEqual("Test", user); - Assert.AreEqual("Hello World", message); + Assert.Equal("Test", user); + Assert.Equal("Hello World", message); } } - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public async Task CounterChannelTest() { using (var endpoint = new JsonServerInfo()) @@ -82,11 +79,11 @@ public async Task CounterChannelTest() IList results = new List(); - await hub.StartAsync(); + await hub.StartAsync(endpoint.TimeoutToken); - var channel = await hub.StreamCounterAsync(count, delay); + var channel = await hub.StreamCounterAsync(count, delay, endpoint.TimeoutToken); - while (await channel.WaitToReadAsync()) + while (await channel.WaitToReadAsync(endpoint.TimeoutToken)) { while (channel.TryRead(out int item)) { @@ -94,14 +91,14 @@ public async Task CounterChannelTest() } } - await hub.StopAsync(); + await hub.StopAsync(endpoint.TimeoutToken); - Assert.AreEqual(count, results.Count()); + Assert.Equal(count, results.Count()); } } - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public async Task CounterBlockingTest() { using (var endpoint = new JsonServerInfo()) @@ -116,18 +113,18 @@ public async Task CounterBlockingTest() int count = 100; int delay = 20; - await hub.StartAsync(); + await hub.StartAsync(endpoint.TimeoutToken); - IEnumerable results = await hub.ReadCounterBlockingAsync(count, delay); + IEnumerable results = await hub.ReadCounterBlockingAsync(count, delay, endpoint.TimeoutToken); - await hub.StopAsync(); + await hub.StopAsync(endpoint.TimeoutToken); - Assert.AreEqual(count, results.Count()); + Assert.Equal(count, results.Count()); } } - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public async Task MessagePackTest() { using (var endpoint = new MessagePackServerInfo()) @@ -158,18 +155,18 @@ public async Task MessagePackTest() tokenSource.Cancel(); }); - await hub.StartAsync(); + await hub.StartAsync(endpoint.TimeoutToken); - await hub.DtoMessageAsync(expected); + await hub.DtoMessageAsync(expected, endpoint.TimeoutToken); await token.WhenCanceled(); - await hub.StopAsync(); + await hub.StopAsync(endpoint.TimeoutToken); - Assert.AreEqual(expected.Collision, actual.Collision); - Assert.AreEqual(expected.Description, actual.Description); - Assert.AreEqual(expected.Id, actual.Id); - Assert.AreEqual(expected.When.ToLocalTime(), actual.When.ToLocalTime()); + Assert.Equal(expected.Collision, actual.Collision); + Assert.Equal(expected.Description, actual.Description); + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.When.ToLocalTime(), actual.When.ToLocalTime()); } } diff --git a/test/TestWebApp.Tests/StaticRouteTests.cs b/tests/TestWebApp.Tests/StaticRouteTests.cs similarity index 65% rename from test/TestWebApp.Tests/StaticRouteTests.cs rename to tests/TestWebApp.Tests/StaticRouteTests.cs index 63dcc7b..df87c7e 100644 --- a/test/TestWebApp.Tests/StaticRouteTests.cs +++ b/tests/TestWebApp.Tests/StaticRouteTests.cs @@ -1,31 +1,24 @@ -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using TestWebApp.Clients; +using System.Collections.Generic; +using Xunit; namespace TestWebApp.Tests { - [TestFixture] public class StaticRouteTests { - - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public void TestRoute() { var actual = TestWebApp.Clients.Routes.ValuesClientRoutes.Get(5); var expected = "api/Values/5"; - Assert.AreEqual(expected, actual); + Assert.Equal(expected, actual); } - - [Test] + [Fact(Timeout = Constants.TEST_TIMEOUT)] public void TestQuery() { var actual = TestWebApp.Clients.Routes.ValuesClientRoutes.EnumerableGet(new List { 1, 2, 3, 4 }, new List { true, false, true }); var expected = "api/Values/EnumerableGet?ids=1&ids=2&ids=3&ids=4&truth=True&truth=False&truth=True"; - Assert.AreEqual(expected, actual); + Assert.Equal(expected, actual); } } } diff --git a/tests/TestWebApp.Tests/TestWebApp.Tests.csproj b/tests/TestWebApp.Tests/TestWebApp.Tests.csproj new file mode 100644 index 0000000..162b83f --- /dev/null +++ b/tests/TestWebApp.Tests/TestWebApp.Tests.csproj @@ -0,0 +1,34 @@ + + + + netcoreapp3.1 + UnitTest + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + diff --git a/tests/TestWebApp.Tests/xunit.runner.json b/tests/TestWebApp.Tests/xunit.runner.json new file mode 100644 index 0000000..d16b63c --- /dev/null +++ b/tests/TestWebApp.Tests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "maxParallelThreads": 1, + "parallelizeTestCollections": false +} \ No newline at end of file diff --git a/test/TestWebApp/Boilerplate/BaseInheritanceController.cs b/tests/TestWebApp/Boilerplate/BaseInheritanceController.cs similarity index 100% rename from test/TestWebApp/Boilerplate/BaseInheritanceController.cs rename to tests/TestWebApp/Boilerplate/BaseInheritanceController.cs diff --git a/test/TestWebApp/Boilerplate/BaseTestController.cs b/tests/TestWebApp/Boilerplate/BaseTestController.cs similarity index 100% rename from test/TestWebApp/Boilerplate/BaseTestController.cs rename to tests/TestWebApp/Boilerplate/BaseTestController.cs diff --git a/test/TestWebApp/Controllers/DuplicateParameterErrorController.cs b/tests/TestWebApp/Controllers/DuplicateParameterErrorController.cs similarity index 100% rename from test/TestWebApp/Controllers/DuplicateParameterErrorController.cs rename to tests/TestWebApp/Controllers/DuplicateParameterErrorController.cs diff --git a/test/TestWebApp/Controllers/ErrorController.cs b/tests/TestWebApp/Controllers/ErrorController.cs similarity index 100% rename from test/TestWebApp/Controllers/ErrorController.cs rename to tests/TestWebApp/Controllers/ErrorController.cs diff --git a/test/TestWebApp/Controllers/FullController.cs b/tests/TestWebApp/Controllers/FullController.cs similarity index 100% rename from test/TestWebApp/Controllers/FullController.cs rename to tests/TestWebApp/Controllers/FullController.cs diff --git a/test/TestWebApp/Controllers/IgnoredController.cs b/tests/TestWebApp/Controllers/IgnoredController.cs similarity index 100% rename from test/TestWebApp/Controllers/IgnoredController.cs rename to tests/TestWebApp/Controllers/IgnoredController.cs diff --git a/test/TestWebApp/Controllers/InheritanceErrorsController.cs b/tests/TestWebApp/Controllers/InheritanceErrorsController.cs similarity index 100% rename from test/TestWebApp/Controllers/InheritanceErrorsController.cs rename to tests/TestWebApp/Controllers/InheritanceErrorsController.cs diff --git a/test/TestWebApp/Controllers/InheritanceGenerationController.cs b/tests/TestWebApp/Controllers/InheritanceGenerationController.cs similarity index 100% rename from test/TestWebApp/Controllers/InheritanceGenerationController.cs rename to tests/TestWebApp/Controllers/InheritanceGenerationController.cs diff --git a/test/TestWebApp/Controllers/NamespacedController.cs b/tests/TestWebApp/Controllers/NamespacedController.cs similarity index 100% rename from test/TestWebApp/Controllers/NamespacedController.cs rename to tests/TestWebApp/Controllers/NamespacedController.cs diff --git a/test/TestWebApp/Controllers/ResponseTypeErrorController.cs b/tests/TestWebApp/Controllers/ResponseTypeErrorController.cs similarity index 100% rename from test/TestWebApp/Controllers/ResponseTypeErrorController.cs rename to tests/TestWebApp/Controllers/ResponseTypeErrorController.cs diff --git a/test/TestWebApp/Controllers/RouteInheritanceController.cs b/tests/TestWebApp/Controllers/RouteInheritanceController.cs similarity index 100% rename from test/TestWebApp/Controllers/RouteInheritanceController.cs rename to tests/TestWebApp/Controllers/RouteInheritanceController.cs diff --git a/test/TestWebApp/Controllers/V1/TestController.cs b/tests/TestWebApp/Controllers/V1/TestController.cs similarity index 100% rename from test/TestWebApp/Controllers/V1/TestController.cs rename to tests/TestWebApp/Controllers/V1/TestController.cs diff --git a/test/TestWebApp/Controllers/V2/TestController.cs b/tests/TestWebApp/Controllers/V2/TestController.cs similarity index 100% rename from test/TestWebApp/Controllers/V2/TestController.cs rename to tests/TestWebApp/Controllers/V2/TestController.cs diff --git a/test/TestWebApp/Controllers/V3/TestQueryController.cs b/tests/TestWebApp/Controllers/V3/TestQueryController.cs similarity index 91% rename from test/TestWebApp/Controllers/V3/TestQueryController.cs rename to tests/TestWebApp/Controllers/V3/TestQueryController.cs index 8101122..a4faad3 100644 --- a/test/TestWebApp/Controllers/V3/TestQueryController.cs +++ b/tests/TestWebApp/Controllers/V3/TestQueryController.cs @@ -16,7 +16,7 @@ public TestQueryController() } - [HttpPost("endpoint/{index:int}")] + [HttpGet("endpoint/{index:int}")] public int Endpoint(int index) { return index + 1; diff --git a/test/TestWebApp/Controllers/V3/TestRouteController.cs b/tests/TestWebApp/Controllers/V3/TestRouteController.cs similarity index 91% rename from test/TestWebApp/Controllers/V3/TestRouteController.cs rename to tests/TestWebApp/Controllers/V3/TestRouteController.cs index d97e5ab..aca4db8 100644 --- a/test/TestWebApp/Controllers/V3/TestRouteController.cs +++ b/tests/TestWebApp/Controllers/V3/TestRouteController.cs @@ -16,7 +16,7 @@ public TestRouteController() } - [HttpPost("endpoint/{index:int}")] + [HttpGet("endpoint/{index:int}")] public int Endpoint(int index) { return index + 1; diff --git a/test/TestWebApp/Controllers/ValuesController.cs b/tests/TestWebApp/Controllers/ValuesController.cs similarity index 60% rename from test/TestWebApp/Controllers/ValuesController.cs rename to tests/TestWebApp/Controllers/ValuesController.cs index 6079963..13b98d3 100644 --- a/test/TestWebApp/Controllers/ValuesController.cs +++ b/tests/TestWebApp/Controllers/ValuesController.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Threading; using System.Threading.Tasks; using TestWebApp.Contracts; using TestWebApp.GoodServices; @@ -33,13 +34,20 @@ public ValuesController(IGoodService goodService) _goodService = goodService; } - // GET api/values + /// + /// Basic Enumerable get + /// + /// [HttpGet] public ActionResult> GetEnumerable() { return new string[] { "value1", "value2" }; } + /// + /// Async ActionResult get + /// + /// [HttpGet("getAsync")] public async Task>> GetEnumerableTaskAsync() { @@ -47,6 +55,10 @@ public async Task>> GetEnumerableTaskAsync() return new string[] { "value1", "value2" }; } + /// + /// Qualified namespace checks + /// + /// [HttpGet("getQualified")] public async System.Threading.Tasks.Task>> GetFullyQualified() { @@ -54,6 +66,10 @@ public async System.Threading.Tasks.Task + /// Tuple support + ///
+ /// [HttpGet("getTuple")] public ActionResult> GetTuple() { @@ -63,6 +79,10 @@ public ActionResult> GetTuple() }; } + /// + /// Large amount of nested type support, as well as Obsolete attribute copying + /// + /// [HttpGet("getNested")] [Obsolete("Testing Obsolete")] public async Task>>>> GetNestedTypesAsync() @@ -74,6 +94,11 @@ public ActionResult> GetTuple() }; } + /// + /// Header injection support + /// + /// + /// [HttpGet("{id}")] [IncludeHeader("GEEET", "FULL")] public ActionResult Get(int id) @@ -81,25 +106,39 @@ public ActionResult Get(int id) return "value"; } + /// + /// private methods should not be copied + /// [HttpGet("dontGenerateMeImPrivate")] private void NonClientEndpoint() { } - // POST api/values + /// + /// Basic Post + /// + /// [HttpPost] public void Post([FromBody] string value) { } - // PUT api/values/5 + /// + /// Basic Put + /// + /// + /// [HttpPut("{id}")] public void Put(int id, [FromBody] string value) { } - // DELETE api/values/5 + /// + /// Delete example with Authoize attribute support adding the security parameter + /// + /// + /// [HttpDelete("{id}")] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [Authorize] @@ -109,6 +148,10 @@ public bool Delete(int id) } + /// + /// Async naming support (truncates) + /// + /// [HttpGet("[action]")] [ProducesResponseType(StatusCodes.Status200OK)] public IActionResult ActionRouteAsync() @@ -116,18 +159,29 @@ public IActionResult ActionRouteAsync() return Ok(); } + /// + /// Check if custom headers are being passed via clients + /// + /// [HttpGet("[action]")] public bool TestPreFunc() { return Request.Headers.ContainsKey("TestPre"); } + /// + /// Check if timeouts are handled via the server to the client + /// + /// [HttpGet("[action]")] public async Task CancellationTestEndpoint() { await Task.Delay(10000); } + /// + /// Ensure the ignore attribute works + /// [NotGenerated] [HttpGet("[action]")] public void IgnoreMe() @@ -135,6 +189,11 @@ public void IgnoreMe() } + /// + /// make sure parameter ordering on the client method is correctish + /// + /// + /// [HttpGet("[action]/{id:int}")] [HeaderParameter("TestId", typeof(int?))] public void NullableParameterOrdering(int id, bool deleted = false) @@ -142,6 +201,10 @@ public void NullableParameterOrdering(int id, bool deleted = false) } + /// + /// Make sure header parameters are being passed through + /// + /// [HeaderParameterAttribute("SpecialValue1", typeof(String))] [HeaderParameterAttribute("SpecialValue2", "string")] [HttpGet("[action]")] @@ -150,6 +213,10 @@ public string HeaderTestString() return Request.Headers["SpecialValue1"].SingleOrDefault(); } + /// + /// support other types + /// + /// [HeaderParameterAttribute("SpecialValue1", typeof(int))] [HttpGet("[action]")] public int HeaderTestInt() @@ -157,6 +224,11 @@ public int HeaderTestInt() return int.Parse(Request.Headers["SpecialValue1"].SingleOrDefault()); } + /// + /// Make sure "complex" objects can get returned and deserialized + /// + /// + /// [HttpGet("[action]/{id:int}")] [ProducesResponseType(typeof(MyFancyDto), (int)HttpStatusCode.OK)] public IActionResult FancyDtoReturn(int id) @@ -170,6 +242,11 @@ public IActionResult FancyDtoReturn(int id) }); } + /// + /// Make sure ValueTasks are detected as Tasks + /// + /// + /// [HttpPost("[action]")] public async ValueTask TaskReturn(MyFancyDto dto) { @@ -177,12 +254,21 @@ public async ValueTask TaskReturn(MyFancyDto dto) return Ok(); } + /// + /// Make sure postbacks are correct + /// + /// + /// [HttpPost("[action]")] public MyFancyDto DtoForDto(MyFancyDto dto) { return dto; } + /// + /// Different return type support (primitives) + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(Guid), (int)HttpStatusCode.OK)] public IActionResult GuidReturn() @@ -190,6 +276,10 @@ public IActionResult GuidReturn() return Ok(Guid.NewGuid()); } + /// + /// Different return type support (primitives) + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(DateTime), (int)HttpStatusCode.OK)] public IActionResult DateTimeReturns() @@ -197,6 +287,10 @@ public IActionResult DateTimeReturns() return Ok(DateTime.Now); } + /// + /// Different return type support (primitives) + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(bool), (int)HttpStatusCode.OK)] public IActionResult BoolReturns() @@ -204,7 +298,11 @@ public IActionResult BoolReturns() return Ok(true); } - + /// + /// Posts with no bodies are allowed + /// + /// + /// [HttpPost("[action]/{id:guid}")] [ProducesResponseType((int)HttpStatusCode.OK)] public IActionResult PostWithNoBody(Guid id) @@ -212,6 +310,12 @@ public IActionResult PostWithNoBody(Guid id) return Ok(); } + /// + /// Posts with multiple parts for parameters work + /// + /// + /// + /// [HttpPost("[action]/{testId:guid}")] [ProducesResponseType(typeof(MyFancyDto), (int)HttpStatusCode.OK)] public IActionResult ComplexPost([FromRoute(Name = "testId")]Guid id, MyFancyDto dto) @@ -219,7 +323,11 @@ public IActionResult ComplexPost([FromRoute(Name = "testId")]Guid id, MyFancyDto return Ok(dto); } - + /// + /// Posts with primitives as the body works + /// + /// + /// [HttpPost("[action]")] [ProducesResponseType(typeof(Guid), (int)HttpStatusCode.OK)] public IActionResult PostWithSimpleBody([FromBody]Guid id) @@ -227,6 +335,12 @@ public IActionResult PostWithSimpleBody([FromBody]Guid id) return Ok(id); } + /// + /// Enumerable query string supported via the client + /// + /// + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] public IActionResult EnumerableGet([FromQuery]IEnumerable ids, [FromQuery]IEnumerable truth) @@ -238,6 +352,12 @@ public IActionResult EnumerableGet([FromQuery]IEnumerable ids, [FromQuery]I return BadRequest("BAD"); } + /// + /// Make sure the name attribute works + /// + /// + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] public IActionResult EnumerableGetCustom([FromQuery(Name = "customIds")]IEnumerable ids, [FromQuery]IEnumerable truth) @@ -249,6 +369,10 @@ public IActionResult EnumerableGetCustom([FromQuery(Name = "customIds")]IEnumera return BadRequest("BAD"); } + /// + /// Make sure the different types of response attributes work + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] [ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(int))] @@ -260,6 +384,11 @@ public IActionResult AttributeFormatting() } + /// + /// ensure that parameters not in route are passed correctly + /// + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)] public IActionResult QueryParameter(string name) @@ -267,7 +396,10 @@ public IActionResult QueryParameter(string name) return Ok(name); } - + /// + /// Make sure file returns are supported + /// + /// [HttpGet("[action]")] public FileResult FileReturn() { @@ -281,6 +413,11 @@ public FileResult FileReturn() return File(randomizeFile, "text/plain"); } + /// + /// Make sure "stream" response types are supported + /// + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(Stream), (int)HttpStatusCode.OK)] public IActionResult FileReturnResponseTypes(bool pass) @@ -296,38 +433,66 @@ public IActionResult FileReturnResponseTypes(bool pass) } } + /// + /// make sure default constraints are applied in code if wanted + /// + /// + /// [HttpGet("[action]/defaultConstraint/{x=5}")] public int? DefaultRouteConstraint(int? x) { return x; } + /// + /// Ensure that different route constrains works + /// + /// + /// [HttpGet("[action]/optional/{x?}")] public int? OptionalRouteConstraint(int? x) { return x; } - + /// + /// Make sure dates are passed along correctly + /// + /// + /// [HttpGet("[action]/checkDate/{date}")] public DateTime CheckDateTime(DateTime date) { return date; } + /// + /// Make sure dates are passed along correctly + /// + /// + /// [HttpGet("[action]/checkDate/{date?}")] public DateTime CheckDateTimeNullable(DateTime? date) { return date ?? DateTime.UtcNow; } - + /// + /// Make sure dates are passed along correctly + /// + /// + /// [HttpGet("[action]/checkDateOffset/{date}")] public DateTimeOffset CheckDateTimeOffset(DateTimeOffset date) { return date; } + /// + /// Make sure dates are passed along correctly + /// + /// + /// [HttpGet("[action]/checkDateOffset/{date?}")] public DateTimeOffset CheckDateTimeOffsetNullable(DateTimeOffset? date) { @@ -335,13 +500,22 @@ public DateTimeOffset CheckDateTimeOffsetNullable(DateTimeOffset? date) } + /// + /// Checks more route constrains + /// + /// + /// + /// [HttpGet("[action]/routeCheck/{name}/{id:int}/{val}")] public void RouteConstraintCheck(string name, int id, bool val) { return; } - + /// + /// Make sure that when the method return matches the OK response type, it doesn't make two, detect them as the same + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(MyFancyDto), StatusCodes.Status200OK)] public ActionResult DuplicateMethodReturnAndResponse() @@ -355,6 +529,11 @@ public ActionResult DuplicateMethodReturnAndResponse() }; } + /// + /// Allow for the problem report return content type + /// + /// + /// [HttpPost("[action]")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] @@ -363,6 +542,10 @@ public IActionResult ProblemDetailsRequest(RequiredDto dto) return Ok(); } + /// + /// handle model state errors + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(IReadOnlyDictionary>), StatusCodes.Status400BadRequest)] public IActionResult ModelStateBadRequest() @@ -372,6 +555,11 @@ public IActionResult ModelStateBadRequest() } + /// + /// Make sure encoding on the url is handled on the client + /// + /// + /// [HttpGet("[action]/{code}")] [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] public IActionResult UrlEncodingCheck(string code) @@ -379,7 +567,11 @@ public IActionResult UrlEncodingCheck(string code) return Ok(code); } - + /// + /// make sure query parameters are encoded properly + /// + /// + /// [HttpGet("[action]")] [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] public IActionResult UrlEncodingQueryCheck(string code) @@ -387,5 +579,20 @@ public IActionResult UrlEncodingQueryCheck(string code) return Ok(code); } + + /// + /// Need to make sure the token parameter isn't copied to the client + /// + /// + /// + [HttpGet("[action]")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task CancellationTokenApi(CancellationToken token = default) + { + await Task.Delay(2500, token); + + return Ok(); + } + } } diff --git a/test/TestWebApp/FakeServices/FakeService.cs b/tests/TestWebApp/FakeServices/FakeService.cs similarity index 100% rename from test/TestWebApp/FakeServices/FakeService.cs rename to tests/TestWebApp/FakeServices/FakeService.cs diff --git a/test/TestWebApp/GoodServices/GoodService.cs b/tests/TestWebApp/GoodServices/GoodService.cs similarity index 100% rename from test/TestWebApp/GoodServices/GoodService.cs rename to tests/TestWebApp/GoodServices/GoodService.cs diff --git a/test/TestWebApp/Hubs/ChatHub.cs b/tests/TestWebApp/Hubs/ChatHub.cs similarity index 98% rename from test/TestWebApp/Hubs/ChatHub.cs rename to tests/TestWebApp/Hubs/ChatHub.cs index 8173121..3534ff5 100644 --- a/test/TestWebApp/Hubs/ChatHub.cs +++ b/tests/TestWebApp/Hubs/ChatHub.cs @@ -1,6 +1,7 @@ using AspNetCore.Server.Attributes.SignalR; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; +using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using TestWebApp.Contracts; diff --git a/test/TestWebApp/Hubs/ErrorHub.cs b/tests/TestWebApp/Hubs/ErrorHub.cs similarity index 100% rename from test/TestWebApp/Hubs/ErrorHub.cs rename to tests/TestWebApp/Hubs/ErrorHub.cs diff --git a/test/TestWebApp/Hubs/IgnoredHub.cs b/tests/TestWebApp/Hubs/IgnoredHub.cs similarity index 100% rename from test/TestWebApp/Hubs/IgnoredHub.cs rename to tests/TestWebApp/Hubs/IgnoredHub.cs diff --git a/test/TestWebApp/Hubs/NamespacedHub.cs b/tests/TestWebApp/Hubs/NamespacedHub.cs similarity index 100% rename from test/TestWebApp/Hubs/NamespacedHub.cs rename to tests/TestWebApp/Hubs/NamespacedHub.cs diff --git a/test/TestWebApp/JsonStartup.cs b/tests/TestWebApp/JsonStartup.cs similarity index 84% rename from test/TestWebApp/JsonStartup.cs rename to tests/TestWebApp/JsonStartup.cs index 5e89270..c45a0da 100644 --- a/test/TestWebApp/JsonStartup.cs +++ b/tests/TestWebApp/JsonStartup.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using TestWebApp.FakeServices; @@ -29,7 +30,7 @@ public JsonStartup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + services.AddControllers(); services.AddApiVersioning(options => { @@ -40,29 +41,28 @@ public void ConfigureServices(IServiceCollection services) .AddScheme("BasicAuthentication", null); services.AddSignalR(); - services.AddTransient(); services.AddTransient(); - } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); - app.UseSignalR(routes => + app.UseEndpoints(endpoints => { - routes.MapHub("/Chat"); + endpoints.MapControllers(); + endpoints.MapHub("/Chat"); }); - - - app.UseMvc(); } } } diff --git a/test/TestWebApp/MessagePackStartup.cs b/tests/TestWebApp/MessagePackStartup.cs similarity index 58% rename from test/TestWebApp/MessagePackStartup.cs rename to tests/TestWebApp/MessagePackStartup.cs index b5b37fc..697fe29 100644 --- a/test/TestWebApp/MessagePackStartup.cs +++ b/tests/TestWebApp/MessagePackStartup.cs @@ -1,20 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using MessagePack.AspNetCoreMvcFormatter; +using MessagePack.AspNetCoreMvcFormatter; using MessagePack.Resolvers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Hosting; using TestWebApp.FakeServices; using TestWebApp.GoodServices; using TestWebApp.Hubs; -using WebApiContrib.Core.Formatter.Protobuf; namespace TestWebApp { @@ -30,22 +25,20 @@ public MessagePackStartup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddMvc() - .AddMvcOptions(option => - { - option.OutputFormatters.Clear(); - option.OutputFormatters.Add(new MessagePackOutputFormatter(ContractlessStandardResolver.Instance)); - option.InputFormatters.Clear(); - option.InputFormatters.Add(new MessagePackInputFormatter(ContractlessStandardResolver.Instance)); - }) - .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + services.AddControllers() + .AddMvcOptions(option => + { + option.OutputFormatters.Clear(); + option.OutputFormatters.Add(new MessagePackOutputFormatter(ContractlessStandardResolver.Instance)); + option.InputFormatters.Clear(); + option.InputFormatters.Add(new MessagePackInputFormatter(ContractlessStandardResolver.Instance)); + }); services.AddApiVersioning(options => { options.AssumeDefaultVersionWhenUnspecified = true; }); - services.AddSignalR() .AddMessagePackProtocol(); @@ -54,20 +47,19 @@ public void ConfigureServices(IServiceCollection services) } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } - - app.UseSignalR(routes => + app.UseRouting(); + app.UseEndpoints(endpoints => { - routes.MapHub("/Chat"); + endpoints.MapControllers(); + endpoints.MapHub("/Chat"); }); - - app.UseMvc(); } } } diff --git a/tests/TestWebApp/Program.cs b/tests/TestWebApp/Program.cs new file mode 100644 index 0000000..d535f5e --- /dev/null +++ b/tests/TestWebApp/Program.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace TestWebApp +{ + public static class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureKestrel(serverOptions => + { + // Set properties and call methods on options + }) + .UseStartup(); + }); + } +} diff --git a/tests/TestWebApp/Properties/launchSettings.json b/tests/TestWebApp/Properties/launchSettings.json new file mode 100644 index 0000000..b437661 --- /dev/null +++ b/tests/TestWebApp/Properties/launchSettings.json @@ -0,0 +1,16 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true + }, + "profiles": { + "TestWebApp": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:62154/" + } + } +} \ No newline at end of file diff --git a/test/TestWebApp/ProtobufStartup.cs b/tests/TestWebApp/ProtobufStartup.cs similarity index 82% rename from test/TestWebApp/ProtobufStartup.cs rename to tests/TestWebApp/ProtobufStartup.cs index 1cf980e..228ffec 100644 --- a/test/TestWebApp/ProtobufStartup.cs +++ b/tests/TestWebApp/ProtobufStartup.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using TestWebApp.FakeServices; @@ -28,16 +29,14 @@ public ProtobufStartup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddMvc() - .AddProtobufFormatters() - .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + services.AddControllers() + .AddProtobufFormatters(); services.AddApiVersioning(options => { options.AssumeDefaultVersionWhenUnspecified = true; }); - services.AddSignalR(); services.AddTransient(); @@ -45,20 +44,20 @@ public void ConfigureServices(IServiceCollection services) } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } - - app.UseSignalR(routes => + app.UseRouting(); + app.UseEndpoints(endpoints => { - routes.MapHub("/Chat"); + endpoints.MapControllers(); + endpoints.MapHub("/Chat"); }); - app.UseMvc(); } } } diff --git a/test/TestWebApp/Security/BasicAuthHandler.cs b/tests/TestWebApp/Security/BasicAuthHandler.cs similarity index 100% rename from test/TestWebApp/Security/BasicAuthHandler.cs rename to tests/TestWebApp/Security/BasicAuthHandler.cs diff --git a/tests/TestWebApp/TestWebApp.csproj b/tests/TestWebApp/TestWebApp.csproj new file mode 100644 index 0000000..df2e851 --- /dev/null +++ b/tests/TestWebApp/TestWebApp.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + diff --git a/test/TestWebApp/appsettings.Development.json b/tests/TestWebApp/appsettings.Development.json similarity index 100% rename from test/TestWebApp/appsettings.Development.json rename to tests/TestWebApp/appsettings.Development.json diff --git a/test/TestWebApp/appsettings.json b/tests/TestWebApp/appsettings.json similarity index 100% rename from test/TestWebApp/appsettings.json rename to tests/TestWebApp/appsettings.json